<commit_before>/*
 * This istream filter removes HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_dechunk.hxx"
#include "http/ChunkParser.hxx"
#include "FacadeIstream.hxx"
#include "pool.hxx"

#include <algorithm>

#include <glib.h>

#include <assert.h>
#include <string.h>

class DechunkIstream final : public FacadeIstream {
    HttpChunkParser parser;

    bool eof = false, closed = false;

    bool had_input, had_output;

    /**
     * Copy chunked data verbatim to handler?
     *
     * @see istream_dechunk_check_verbatim()
     */
    bool verbatim = false;

    /**
     * Was the end-of-file chunk seen at the end of #pending_verbatim?
     */
    bool eof_verbatim;

    /**
     * Number of bytes to be passed to handler verbatim, which have
     * already been parsed but have not yet been consumed by the
     * handler.
     */
    size_t pending_verbatim;

    DechunkHandler &dechunk_handler;

public:
    DechunkIstream(struct pool &p, Istream &_input,
                   DechunkHandler &_dechunk_handler)
        :FacadeIstream(p, _input), dechunk_handler(_dechunk_handler)
    {
    }

    static DechunkIstream *CheckCast(Istream &i) {
        return dynamic_cast<DechunkIstream *>(&i);
    }

    void SetVerbatim() {
        verbatim = true;
        eof_verbatim = false;
        pending_verbatim = 0;
    }

private:
    void Abort(GError *error);

    /**
     * @return false if the istream_dechunk has been aborted
     * indirectly (by a callback)
     */
    bool EofDetected();

    size_t Feed(const void *data, size_t length);

public:
    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;

protected:
    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;
};

void
DechunkIstream::Abort(GError *error)
{
    assert(!closed);
    assert(!parser.HasEnded());
    assert(input.IsDefined());

    closed = true;

    input.ClearAndClose();
    DestroyError(error);
}

bool
DechunkIstream::EofDetected()
{
    assert(input.IsDefined());
    assert(parser.HasEnded());
    assert(!eof);

    eof = true;

    dechunk_handler.OnDechunkEnd();

    assert(input.IsDefined());
    assert(parser.HasEnded());

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    DestroyEof();

    if (closed) {
        assert(!input.IsDefined());

        return false;
    } else {
        /* we must deinitialize the "input" after emitting "eof",
           because we must give the callback a chance to call
           dechunk_input_abort() on us; if we'd clear the handler too
           early, we wouldn't receive that event, and
           dechunk_input_data() couldn't change its return value to
           0 */
        assert(input.IsDefined());

        input.ClearHandler();
        return true;
    }
}

size_t
DechunkIstream::Feed(const void *data0, size_t length)
{
    assert(!closed);
    assert(input.IsDefined());
    assert(!verbatim || !eof_verbatim);

    had_input = true;

    const auto src_begin = (const char *)data0;
    const auto src_end = src_begin + length;

    auto src = src_begin;
    if (verbatim)
        /* skip the part that has already been parsed in the last
           invocation, but could not be consumed by the handler */
        src += pending_verbatim;

    while (src != src_end) {
        GError *error = nullptr;
        const ConstBuffer<char> src_remaining(src, src_end - src);
        auto data = ConstBuffer<char>::FromVoid(parser.Parse(src_remaining.ToVoid(),
                                                             &error));
        if (data.IsNull()) {
            Abort(error);
            return 0;
        }

        assert(data.data >= src);
        assert(data.data <= src_end);
        assert(data.end() <= src_end);

        src = data.data;

        if (!data.IsEmpty()) {
            assert(!parser.HasEnded());

            size_t nbytes;

            if (verbatim) {
                /* postpone this data chunk; try to send it all later in
                   one big block */
                nbytes = data.size;
            } else {
                had_output = true;
                nbytes = InvokeData(src, data.size);
                assert(nbytes <= data.size);

                if (nbytes == 0) {
                    if (closed)
                        return 0;
                    else
                        break;
                }
            }

            src += nbytes;

            bool finished = parser.Consume(nbytes);
            if (!finished && !verbatim)
                break;
        } else if (parser.HasEnded()) {
            break;
        } else {
            assert(src == src_end);
        }
    }

    const size_t position = src - src_begin;
    if (verbatim && position > 0) {
        /* send all chunks in one big block */
        had_output = true;
        size_t nbytes = InvokeData(src_begin, position);
        if (closed)
            return 0;

        /* postpone the rest that was not handled; it will not be
           parsed again */
        pending_verbatim = position - nbytes;
        if (parser.HasEnded()) {
                if (pending_verbatim > 0)
                    /* not everything could be sent; postpone to
                       next call */
                    eof_verbatim = true;
                else if (!EofDetected())
                    return 0;
        }

        return nbytes;
    } else if (parser.HasEnded() && !EofDetected())
        return 0;

    return position;
}

/*
 * istream handler
 *
 */

size_t
DechunkIstream::OnData(const void *data, size_t length)
{
    assert(!verbatim || length >= pending_verbatim);

    if (verbatim && eof_verbatim) {
        /* during the last call, the EOF chunk was parsed, but we
           could not handle it yet, because the handler did not
           consume all data yet; try to send the remaining pre-EOF
           data again and then handle the EOF chunk */

        assert(pending_verbatim > 0);

        assert(length >= pending_verbatim);

        had_output = true;
        size_t nbytes = InvokeData(data, pending_verbatim);
        if (nbytes == 0)
            return 0;

        pending_verbatim -= nbytes;
        if (pending_verbatim > 0)
            /* more data needed */
            return nbytes;

        return EofDetected() ? nbytes : 0;
    }

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    return Feed(data, length);
}

void
DechunkIstream::OnEof()
{
    assert(!closed);

    input.Clear();

    closed = true;

    if (eof)
        return;

    GError *error =
        g_error_new_literal(dechunk_quark(), 0,
                            "premature EOF in dechunker");
    DestroyError(error);
}

void
DechunkIstream::OnError(GError *error)
{
    input.Clear();

    closed = true;

    if (eof)
        g_error_free(error);
    else
        DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
DechunkIstream::_GetAvailable(bool partial)
{
    if (partial)
        return (off_t)parser.GetAvailable();

    return (off_t)-1;
}

void
DechunkIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (input.IsDefined() && had_input && !had_output);
}

void
DechunkIstream::_Close()
{
    assert(!eof);
    assert(!closed);

    closed = true;

    input.ClearAndClose();
    Destroy();
}

/*
 * constructor
 *
 */

Istream *
istream_dechunk_new(struct pool *pool, Istream &input,
                    DechunkHandler &dechunk_handler)
{
    return NewIstream<DechunkIstream>(*pool, input, dechunk_handler);
}

bool
istream_dechunk_check_verbatim(Istream &i)
{
    auto *dechunk = DechunkIstream::CheckCast(i);
    if (dechunk == nullptr)
        /* not a DechunkIstream instance */
        return false;

    dechunk->SetVerbatim();
    return true;
}
<commit_msg>istream/dechunk: fix indent<commit_after>/*
 * This istream filter removes HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_dechunk.hxx"
#include "http/ChunkParser.hxx"
#include "FacadeIstream.hxx"
#include "pool.hxx"

#include <algorithm>

#include <glib.h>

#include <assert.h>
#include <string.h>

class DechunkIstream final : public FacadeIstream {
    HttpChunkParser parser;

    bool eof = false, closed = false;

    bool had_input, had_output;

    /**
     * Copy chunked data verbatim to handler?
     *
     * @see istream_dechunk_check_verbatim()
     */
    bool verbatim = false;

    /**
     * Was the end-of-file chunk seen at the end of #pending_verbatim?
     */
    bool eof_verbatim;

    /**
     * Number of bytes to be passed to handler verbatim, which have
     * already been parsed but have not yet been consumed by the
     * handler.
     */
    size_t pending_verbatim;

    DechunkHandler &dechunk_handler;

public:
    DechunkIstream(struct pool &p, Istream &_input,
                   DechunkHandler &_dechunk_handler)
        :FacadeIstream(p, _input), dechunk_handler(_dechunk_handler)
    {
    }

    static DechunkIstream *CheckCast(Istream &i) {
        return dynamic_cast<DechunkIstream *>(&i);
    }

    void SetVerbatim() {
        verbatim = true;
        eof_verbatim = false;
        pending_verbatim = 0;
    }

private:
    void Abort(GError *error);

    /**
     * @return false if the istream_dechunk has been aborted
     * indirectly (by a callback)
     */
    bool EofDetected();

    size_t Feed(const void *data, size_t length);

public:
    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;

protected:
    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;
};

void
DechunkIstream::Abort(GError *error)
{
    assert(!closed);
    assert(!parser.HasEnded());
    assert(input.IsDefined());

    closed = true;

    input.ClearAndClose();
    DestroyError(error);
}

bool
DechunkIstream::EofDetected()
{
    assert(input.IsDefined());
    assert(parser.HasEnded());
    assert(!eof);

    eof = true;

    dechunk_handler.OnDechunkEnd();

    assert(input.IsDefined());
    assert(parser.HasEnded());

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    DestroyEof();

    if (closed) {
        assert(!input.IsDefined());

        return false;
    } else {
        /* we must deinitialize the "input" after emitting "eof",
           because we must give the callback a chance to call
           dechunk_input_abort() on us; if we'd clear the handler too
           early, we wouldn't receive that event, and
           dechunk_input_data() couldn't change its return value to
           0 */
        assert(input.IsDefined());

        input.ClearHandler();
        return true;
    }
}

size_t
DechunkIstream::Feed(const void *data0, size_t length)
{
    assert(!closed);
    assert(input.IsDefined());
    assert(!verbatim || !eof_verbatim);

    had_input = true;

    const auto src_begin = (const char *)data0;
    const auto src_end = src_begin + length;

    auto src = src_begin;
    if (verbatim)
        /* skip the part that has already been parsed in the last
           invocation, but could not be consumed by the handler */
        src += pending_verbatim;

    while (src != src_end) {
        GError *error = nullptr;
        const ConstBuffer<char> src_remaining(src, src_end - src);
        auto data = ConstBuffer<char>::FromVoid(parser.Parse(src_remaining.ToVoid(),
                                                             &error));
        if (data.IsNull()) {
            Abort(error);
            return 0;
        }

        assert(data.data >= src);
        assert(data.data <= src_end);
        assert(data.end() <= src_end);

        src = data.data;

        if (!data.IsEmpty()) {
            assert(!parser.HasEnded());

            size_t nbytes;

            if (verbatim) {
                /* postpone this data chunk; try to send it all later in
                   one big block */
                nbytes = data.size;
            } else {
                had_output = true;
                nbytes = InvokeData(src, data.size);
                assert(nbytes <= data.size);

                if (nbytes == 0) {
                    if (closed)
                        return 0;
                    else
                        break;
                }
            }

            src += nbytes;

            bool finished = parser.Consume(nbytes);
            if (!finished && !verbatim)
                break;
        } else if (parser.HasEnded()) {
            break;
        } else {
            assert(src == src_end);
        }
    }

    const size_t position = src - src_begin;
    if (verbatim && position > 0) {
        /* send all chunks in one big block */
        had_output = true;
        size_t nbytes = InvokeData(src_begin, position);
        if (closed)
            return 0;

        /* postpone the rest that was not handled; it will not be
           parsed again */
        pending_verbatim = position - nbytes;
        if (parser.HasEnded()) {
            if (pending_verbatim > 0)
                /* not everything could be sent; postpone to
                   next call */
                eof_verbatim = true;
            else if (!EofDetected())
                return 0;
        }

        return nbytes;
    } else if (parser.HasEnded() && !EofDetected())
        return 0;

    return position;
}

/*
 * istream handler
 *
 */

size_t
DechunkIstream::OnData(const void *data, size_t length)
{
    assert(!verbatim || length >= pending_verbatim);

    if (verbatim && eof_verbatim) {
        /* during the last call, the EOF chunk was parsed, but we
           could not handle it yet, because the handler did not
           consume all data yet; try to send the remaining pre-EOF
           data again and then handle the EOF chunk */

        assert(pending_verbatim > 0);

        assert(length >= pending_verbatim);

        had_output = true;
        size_t nbytes = InvokeData(data, pending_verbatim);
        if (nbytes == 0)
            return 0;

        pending_verbatim -= nbytes;
        if (pending_verbatim > 0)
            /* more data needed */
            return nbytes;

        return EofDetected() ? nbytes : 0;
    }

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    return Feed(data, length);
}

void
DechunkIstream::OnEof()
{
    assert(!closed);

    input.Clear();

    closed = true;

    if (eof)
        return;

    GError *error =
        g_error_new_literal(dechunk_quark(), 0,
                            "premature EOF in dechunker");
    DestroyError(error);
}

void
DechunkIstream::OnError(GError *error)
{
    input.Clear();

    closed = true;

    if (eof)
        g_error_free(error);
    else
        DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
DechunkIstream::_GetAvailable(bool partial)
{
    if (partial)
        return (off_t)parser.GetAvailable();

    return (off_t)-1;
}

void
DechunkIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (input.IsDefined() && had_input && !had_output);
}

void
DechunkIstream::_Close()
{
    assert(!eof);
    assert(!closed);

    closed = true;

    input.ClearAndClose();
    Destroy();
}

/*
 * constructor
 *
 */

Istream *
istream_dechunk_new(struct pool *pool, Istream &input,
                    DechunkHandler &dechunk_handler)
{
    return NewIstream<DechunkIstream>(*pool, input, dechunk_handler);
}

bool
istream_dechunk_check_verbatim(Istream &i)
{
    auto *dechunk = DechunkIstream::CheckCast(i);
    if (dechunk == nullptr)
        /* not a DechunkIstream instance */
        return false;

    dechunk->SetVerbatim();
    return true;
}
<|endoftext|>


<commit_before>#pragma once

#include "physics/jacobi_coordinates.hpp"

#include <experimental/optional>
#include <iterator>

namespace principia {

namespace physics {

template<typename Frame>
JacobiCoordinates<Frame>::JacobiCoordinates(MassiveBody const& primary) {
  DegreesOfFreedom<PrimocentricFrame> motionless_origin = {
      PrimocentricFrame::origin, Velocity<PrimocentricFrame>()};
  primocentric_dof_.emplace_back(motionless_origin);
  system_barycentre_.Add(primocentric_dof_.back(),
                         primary.gravitational_parameter());
}

template<typename Frame>
void JacobiCoordinates<Frame>::Add(
    MassiveBody const& body,
    RelativeDegreesOfFreedom<Frame> const& dof_wrt_system) {
  primocentric_dof_.emplace_back(system_barycentre_.Get() +
                                 id_fp_(dof_wrt_system));
  system_barycentre_.Add(primocentric_dof_.back(),
                         body.gravitational_parameter());
}

template<typename Frame>
void JacobiCoordinates<Frame>::Add(
    MassiveBody const& body,
    KeplerianElements<Frame> const& osculating_elements_wrt_system) {
  Instant const epoch;
  Add(body,
      KeplerOrbit<Frame>(/*primary=*/System(),
                         /*secondary=*/body,
                         osculating_elements_wrt_system,
                         epoch).StateVectors(epoch));
}

template<typename Frame>
MassiveBody JacobiCoordinates<Frame>::System() const {
  return MassiveBody(system_barycentre_.weight());
}

template<typename Frame>
std::vector<RelativeDegreesOfFreedom<Frame>>
JacobiCoordinates<Frame>::BarycentricCoordinates() const {
  std::vector<RelativeDegreesOfFreedom<Frame>> result;
  DegreesOfFreedom<PrimocentricFrame> system_barycentre =
      system_barycentre_.Get();
  for (auto const& dof : primocentric_dof_) {
    result.emplace_back(id_pf_(dof - system_barycentre));
  }
  return result;
}

template<typename Frame>
Identity<typename JacobiCoordinates<Frame>::PrimocentricFrame, Frame> const
    JacobiCoordinates<Frame>::id_pf_;
template<typename Frame>
Identity<Frame, typename JacobiCoordinates<Frame>::PrimocentricFrame> const
    JacobiCoordinates<Frame>::id_fp_;

template<typename Frame>
HierarchicalSystem<Frame>::HierarchicalSystem(
    not_null<std::unique_ptr<MassiveBody const>> primary)
    : system_(std::move(primary)) {
  subsystems_[system_.primary.get()] = &system_;
}

template<typename Frame>
void HierarchicalSystem<Frame>::Add(
    not_null<std::unique_ptr<MassiveBody const>> body,
    not_null<MassiveBody const*> const parent,
    KeplerianElements<Frame> const& jacobi_osculating_elements) {
  System& parent_system = *subsystems_[parent];
  parent_system.satellites.emplace_back(
      make_not_null_unique<Subsystem>(std::move(body)));
  not_null<Subsystem*> inserted_system = parent_system.satellites.back().get();
  {
    not_null<MassiveBody const*> body = inserted_system->primary.get();
    subsystems_[body] = inserted_system;
    inserted_system->jacobi_osculating_elements = jacobi_osculating_elements;
  }
}

template<typename Frame>
typename HierarchicalSystem<Frame>::BarycentricSystem
HierarchicalSystem<Frame>::Get() {
  auto const semimajor_axis_less_than = [](
      not_null<std::unique_ptr<Subsystem>> const& left,
      not_null<std::unique_ptr<Subsystem>> const& right) -> bool {
    return left->jacobi_osculating_elements.semimajor_axis <
           right->jacobi_osculating_elements.semimajor_axis;
  };

  // Data about a |Subsystem|.
  struct BarycentricSubystem {
    // A |MassiveBody| with the mass of the whole subsystem.
    std::unique_ptr<MassiveBody> equivalent_body;
    // The bodies composing the subsystem.
    std::vector<not_null<std::unique_ptr<MassiveBody const>>> bodies;
    // Their |DegreesOfFreedom| with respect to the barycentre of the subsystem.
    std::vector<RelativeDegreesOfFreedom<Frame>> barycentric_degrees_of_freedom;
  };

  std::function<BarycentricSubystem(System&)> to_barycentric =
      [&semimajor_axis_less_than, &to_barycentric](System& system) {
        std::sort(system.satellites.begin(),
                  system.satellites.end(),
                  semimajor_axis_less_than);

        BarycentricSubystem result;

        // A reference frame wherein the barycentre of |system| is motionless
        // at the origin.
        // TODO(egg): declaring these frame tags to make sure that local frames
        // don't go out of scope is a bit cumbersome.
        enum class LocalFrameTag { kFrameTag };
        using SystemBarycentre = geometry::Frame<LocalFrameTag,
                                                 LocalFrameTag::kFrameTag,
                                                 /*frame_is_inertial=*/false>;
        static DegreesOfFreedom<SystemBarycentre> const system_barycentre = {
            SystemBarycentre::origin, Velocity<SystemBarycentre>()};
        static Identity<SystemBarycentre, Frame> const id_bf;
        static Identity<Frame, SystemBarycentre> const id_fb;

        // Jacobi coordinates for |system|, with satellite subsystems treated
        // as point masses at their barycentres.
        JacobiCoordinates<Frame> jacobi_coordinates(*system.primary);
        result.bodies.emplace_back(std::move(system.primary));

        // The |n|th element of |satellite_degrees_of_freedom| contains the list
        // of degrees of freedom of the bodies in the |n|th satellite subsystem
        // with respect to their barycentre.
        std::vector<std::vector<RelativeDegreesOfFreedom<Frame>>>
            satellite_degrees_of_freedom;

        // Fill |satellite_degrees_of_freedom|, |jacobi_coordinates|, and
        // |result.bodies|.
        for (auto const& subsystem : system.satellites) {
          BarycentricSubystem barycentric_satellite_subsystem =
              to_barycentric(*subsystem);
          satellite_degrees_of_freedom.emplace_back(std::move(
              barycentric_satellite_subsystem.barycentric_degrees_of_freedom));
          jacobi_coordinates.Add(
              *barycentric_satellite_subsystem.equivalent_body,
              subsystem->jacobi_osculating_elements);
          std::move(barycentric_satellite_subsystem.bodies.begin(),
                    barycentric_satellite_subsystem.bodies.end(),
                    std::back_inserter(result.bodies));
        }

        std::vector<DegreesOfFreedom<SystemBarycentre>>
            barycentres_of_subsystems;
        {
          // TODO(egg): should BarycentricCoordinates return DegreesOfFreedom?
          // In what frame?
          auto const barycentric_coordinates =
              jacobi_coordinates.BarycentricCoordinates();
          for (auto const& dof : barycentric_coordinates) {
            barycentres_of_subsystems.push_back(system_barycentre + id_fb(dof));
          }
        }

        // Fill |result.barycentric_degrees_of_freedom|.
        // The primary.
        result.barycentric_degrees_of_freedom.emplace_back(
            id_bf(barycentres_of_subsystems.front() - system_barycentre));
        for (int n = 0; n < satellite_degrees_of_freedom.size(); ++n) {
          // |n + 1| because the primary is at |barycentres_of_subsystems[0]|.
          DegreesOfFreedom<SystemBarycentre> const subsystem_barycentre =
              barycentres_of_subsystems[n + 1];
          for (RelativeDegreesOfFreedom<Frame> const& body_dof_wrt_subsystem_barycentre :
               satellite_degrees_of_freedom[n]) {
            DegreesOfFreedom<SystemBarycentre> const body_dof =
                subsystem_barycentre + id_fb(body_dof_wrt_subsystem_barycentre);
            result.barycentric_degrees_of_freedom.emplace_back(
                id_bf(body_dof - system_barycentre));
          }
        }

        result.equivalent_body =
            std::make_unique<MassiveBody>(jacobi_coordinates.System());
        return std::move(result);
      };

  to_barycentric(system_);

  return BarycentricSystem();
}

}  // namespace physics
}  // namespace principia
<commit_msg>minor cleanup<commit_after>#pragma once

#include "physics/jacobi_coordinates.hpp"

#include <experimental/optional>
#include <iterator>

namespace principia {

namespace physics {

template<typename Frame>
JacobiCoordinates<Frame>::JacobiCoordinates(MassiveBody const& primary) {
  DegreesOfFreedom<PrimocentricFrame> motionless_origin = {
      PrimocentricFrame::origin, Velocity<PrimocentricFrame>()};
  primocentric_dof_.emplace_back(motionless_origin);
  system_barycentre_.Add(primocentric_dof_.back(),
                         primary.gravitational_parameter());
}

template<typename Frame>
void JacobiCoordinates<Frame>::Add(
    MassiveBody const& body,
    RelativeDegreesOfFreedom<Frame> const& dof_wrt_system) {
  primocentric_dof_.emplace_back(system_barycentre_.Get() +
                                 id_fp_(dof_wrt_system));
  system_barycentre_.Add(primocentric_dof_.back(),
                         body.gravitational_parameter());
}

template<typename Frame>
void JacobiCoordinates<Frame>::Add(
    MassiveBody const& body,
    KeplerianElements<Frame> const& osculating_elements_wrt_system) {
  Instant const epoch;
  Add(body,
      KeplerOrbit<Frame>(/*primary=*/System(),
                         /*secondary=*/body,
                         osculating_elements_wrt_system,
                         epoch).StateVectors(epoch));
}

template<typename Frame>
MassiveBody JacobiCoordinates<Frame>::System() const {
  return MassiveBody(system_barycentre_.weight());
}

template<typename Frame>
std::vector<RelativeDegreesOfFreedom<Frame>>
JacobiCoordinates<Frame>::BarycentricCoordinates() const {
  std::vector<RelativeDegreesOfFreedom<Frame>> result;
  DegreesOfFreedom<PrimocentricFrame> system_barycentre =
      system_barycentre_.Get();
  for (auto const& dof : primocentric_dof_) {
    result.emplace_back(id_pf_(dof - system_barycentre));
  }
  return result;
}

template<typename Frame>
Identity<typename JacobiCoordinates<Frame>::PrimocentricFrame, Frame> const
    JacobiCoordinates<Frame>::id_pf_;
template<typename Frame>
Identity<Frame, typename JacobiCoordinates<Frame>::PrimocentricFrame> const
    JacobiCoordinates<Frame>::id_fp_;

template<typename Frame>
HierarchicalSystem<Frame>::HierarchicalSystem(
    not_null<std::unique_ptr<MassiveBody const>> primary)
    : system_(std::move(primary)) {
  subsystems_[system_.primary.get()] = &system_;
}

template<typename Frame>
void HierarchicalSystem<Frame>::Add(
    not_null<std::unique_ptr<MassiveBody const>> body,
    not_null<MassiveBody const*> const parent,
    KeplerianElements<Frame> const& jacobi_osculating_elements) {
  System& parent_system = *subsystems_[parent];
  parent_system.satellites.emplace_back(
      make_not_null_unique<Subsystem>(std::move(body)));
  not_null<Subsystem*> inserted_system = parent_system.satellites.back().get();
  {
    not_null<MassiveBody const*> body = inserted_system->primary.get();
    subsystems_[body] = inserted_system;
    inserted_system->jacobi_osculating_elements = jacobi_osculating_elements;
  }
}

template<typename Frame>
typename HierarchicalSystem<Frame>::BarycentricSystem
HierarchicalSystem<Frame>::Get() {
  auto const semimajor_axis_less_than = [](
      not_null<std::unique_ptr<Subsystem>> const& left,
      not_null<std::unique_ptr<Subsystem>> const& right) -> bool {
    return left->jacobi_osculating_elements.semimajor_axis <
           right->jacobi_osculating_elements.semimajor_axis;
  };

  // Data about a |Subsystem|.
  struct BarycentricSubystem {
    // A |MassiveBody| with the mass of the whole subsystem.
    std::unique_ptr<MassiveBody> equivalent_body;
    // The bodies composing the subsystem.
    std::vector<not_null<std::unique_ptr<MassiveBody const>>> bodies;
    // Their |DegreesOfFreedom| with respect to the barycentre of the subsystem.
    std::vector<RelativeDegreesOfFreedom<Frame>> barycentric_degrees_of_freedom;
  };

  std::function<BarycentricSubystem(System&)> to_barycentric =
      [&semimajor_axis_less_than, &to_barycentric](System& system) {
        std::sort(system.satellites.begin(),
                  system.satellites.end(),
                  semimajor_axis_less_than);

        BarycentricSubystem result;

        // A reference frame wherein the barycentre of |system| is motionless
        // at the origin.
        // TODO(egg): declaring these frame tags to make sure that local frames
        // don't go out of scope is a bit cumbersome.
        enum class LocalFrameTag { kFrameTag };
        using SystemBarycentre = geometry::Frame<LocalFrameTag,
                                                 LocalFrameTag::kFrameTag,
                                                 /*frame_is_inertial=*/false>;
        static DegreesOfFreedom<SystemBarycentre> const system_barycentre = {
            SystemBarycentre::origin, Velocity<SystemBarycentre>()};
        static Identity<SystemBarycentre, Frame> const id_bf;
        static Identity<Frame, SystemBarycentre> const id_fb;

        // Jacobi coordinates for |system|, with satellite subsystems treated
        // as point masses at their barycentres.
        JacobiCoordinates<Frame> jacobi_coordinates(*system.primary);
        result.bodies.emplace_back(std::move(system.primary));

        // The |n|th element of |satellite_degrees_of_freedom| contains the list
        // of degrees of freedom of the bodies in the |n|th satellite subsystem
        // with respect to their barycentre.
        std::vector<std::vector<RelativeDegreesOfFreedom<Frame>>>
            satellite_degrees_of_freedom;

        // Fill |satellite_degrees_of_freedom|, |jacobi_coordinates|, and
        // |result.bodies|.
        for (auto const& subsystem : system.satellites) {
          BarycentricSubystem barycentric_satellite_subsystem =
              to_barycentric(*subsystem);
          satellite_degrees_of_freedom.emplace_back(std::move(
              barycentric_satellite_subsystem.barycentric_degrees_of_freedom));
          jacobi_coordinates.Add(
              *barycentric_satellite_subsystem.equivalent_body,
              subsystem->jacobi_osculating_elements);
          std::move(barycentric_satellite_subsystem.bodies.begin(),
                    barycentric_satellite_subsystem.bodies.end(),
                    std::back_inserter(result.bodies));
        }

        std::vector<DegreesOfFreedom<SystemBarycentre>>
            barycentres_of_subsystems;
        {
          // TODO(egg): should BarycentricCoordinates return DegreesOfFreedom?
          // In what frame?
          auto const barycentric_coordinates =
              jacobi_coordinates.BarycentricCoordinates();
          for (auto const& barycentric_dof : barycentric_coordinates) {
            barycentres_of_subsystems.push_back(system_barycentre +
                                                id_fb(barycentric_dof));
          }
        }

        // Fill |result.barycentric_degrees_of_freedom|.
        // The primary.
        result.barycentric_degrees_of_freedom.emplace_back(
            id_bf(barycentres_of_subsystems.front() - system_barycentre));
        // The bodies in satellite subsystems.
        for (int n = 0; n < satellite_degrees_of_freedom.size(); ++n) {
          // |n + 1| because the primary is at |barycentres_of_subsystems[0]|.
          DegreesOfFreedom<SystemBarycentre> const subsystem_barycentre =
              barycentres_of_subsystems[n + 1];
          for (auto const& body_dof_wrt_subsystem_barycentre :
               satellite_degrees_of_freedom[n]) {
            DegreesOfFreedom<SystemBarycentre> const body_dof =
                subsystem_barycentre + id_fb(body_dof_wrt_subsystem_barycentre);
            result.barycentric_degrees_of_freedom.emplace_back(
                id_bf(body_dof - system_barycentre));
          }
        }

        result.equivalent_body =
            std::make_unique<MassiveBody>(jacobi_coordinates.System());
        return std::move(result);
      };

  to_barycentric(system_);

  return BarycentricSystem();
}

}  // namespace physics
}  // namespace principia
<|endoftext|>


<commit_before>/*
 * Part of HTTPP.
 *
 * Distributed under the 3-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2014 Thomas Sanchez.  All rights reserved.
 *
 */

#include "Connection.hpp"

#include <curl/curl.h>
#include <memory>
#include <iostream>
#include <vector>
#include <thread>

#include <boost/log/trivial.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/exception_ptr.hpp>

#include "httpp/http/Protocol.hpp"
#include "httpp/http/client/Request.hpp"
#include "httpp/http/client/Response.hpp"
#include "httpp/utils/Exception.hpp"

#include "Manager.hpp"

namespace HTTPP {
namespace HTTP {
namespace client {

namespace detail {

Connection::Connection(Manager& manager, boost::asio::io_service& service)
: handler(manager)
, handle(curl_easy_init())
, service(service)
{
    if (!handle)
    {
        throw std::runtime_error("Cannot initialize curl handle");
    }
}

Connection::~Connection()
{
    if (http_headers)
    {
        curl_slist_free_all(http_headers);
    }

    if (handle)
    {
        curl_easy_cleanup(handle);
        handle = nullptr;
    }

    if (!cancelled && !result_notified)
    {
        try
        {
            BOOST_LOG_TRIVIAL(error)
                << "Destroy a not completed connection: " << this;
            complete(std::make_exception_ptr(
                std::runtime_error("Destroy a non completed connection")));
        }
        catch (const std::exception& ex)
        {
            BOOST_LOG_TRIVIAL(error)
                << "Error happened completing the connection: " << ex.what();
        }
    }
}

void Connection::init(std::map<curl_socket_t, boost::asio::ip::tcp::socket*>& sockets)
{
    if (!result_notified)
    {
        throw std::runtime_error("Recycle a not completed connection");
    }

    curl_easy_reset(handle);

    this->sockets = std::addressof(sockets);

    conn_setopt(CURLOPT_HEADERFUNCTION, &writeHd);
    conn_setopt(CURLOPT_WRITEHEADER, this);

    conn_setopt(CURLOPT_WRITEFUNCTION, &writefn);
    conn_setopt(CURLOPT_WRITEDATA, this);

    conn_setopt(CURLOPT_ERRORBUFFER, this->error_buffer);

    conn_setopt(CURLOPT_PRIVATE, this);

    conn_setopt(CURLOPT_OPENSOCKETDATA, this);
    conn_setopt(CURLOPT_OPENSOCKETFUNCTION, &opensocket);

    conn_setopt(CURLOPT_CLOSESOCKETDATA, this->sockets);
    conn_setopt(CURLOPT_CLOSESOCKETFUNCTION, &closesocket);

    conn_setopt(CURLOPT_ERRORBUFFER, this->error_buffer);
    conn_setopt(CURLOPT_PRIVATE, this);

    if (http_headers)
    {
        curl_slist_free_all(http_headers);
        http_headers = nullptr;
    }

    expect_continue = false;
    header.clear();
    buffer.clear();

    promise = std::promise<client::Response>();
    completion_handler = CompletionHandler();
    response = Response();
    poll_action = 0;
}

Connection::ConnectionPtr
Connection::createConnection(Manager& manager, boost::asio::io_service& service)
{
    return std::make_shared<Connection>(manager, service);
}

size_t Connection::writeHd(char* buffer, size_t size, size_t nmemb, void* userdata)
{
    Connection* conn = (Connection*)userdata;
    auto actual_size = size * nmemb;
    conn->header.insert(std::end(conn->header), buffer, buffer + actual_size);
    return actual_size;
}

size_t Connection::writefn(char* buffer, size_t size, size_t nmemb, void* userdata)
{
    Connection* conn = (Connection*)userdata;

    auto actual_size = size * nmemb;
    conn->buffer.insert(std::end(conn->buffer), buffer, buffer + actual_size);
    return actual_size;
}

curl_socket_t Connection::opensocket(void* clientp,
                                     curlsocktype purpose,
                                     struct curl_sockaddr* address)
{
    Connection* conn = (Connection*)clientp;

    if (purpose == CURLSOCKTYPE_IPCXN)
    {
        boost::system::error_code ec;
        boost::asio::ip::tcp::socket* socket =
            new boost::asio::ip::tcp::socket(conn->service);

        if (address->family == AF_INET)
        {
            socket->open(boost::asio::ip::tcp::v4(), ec);
        }
        else
        {
            socket->open(boost::asio::ip::tcp::v6(), ec);
        }

        if (ec)
        {
            BOOST_LOG_TRIVIAL(error)
                << "Cannot open a socket: " << ec.message();
            return CURL_SOCKET_BAD;
        }

        auto handle = socket->native_handle();
        (*conn->sockets)[handle] = socket;
        return handle;
    }

    return CURL_SOCKET_BAD;
}

int Connection::closesocket(void* clientp, curl_socket_t curl_socket)
{
    std::map<curl_socket_t, boost::asio::ip::tcp::socket*>* sockets;
    sockets = (decltype(sockets)) clientp;

    auto it = sockets->find(curl_socket);
    if (it == std::end(*sockets))
    {
        BOOST_LOG_TRIVIAL(error) << "Cannot find a socket to close";
        return 1;
    }

    delete it->second;
    sockets->erase(it);
    return 0;
}

void Connection::setSocket(curl_socket_t curl_socket)
{
    auto it = sockets->find(curl_socket);
    if (it == std::end(*sockets))
    {
        throw std::runtime_error("Cannot find socket: " +
                                 std::to_string(curl_socket));
    }

    socket = it->second;
}

void Connection::configureRequest(HTTPP::HTTP::Method method)
{
    switch (method)
    {
     case HTTPP::HTTP::Method::GET:
        conn_setopt(CURLOPT_HTTPGET, 1L);
        break;
     case HTTPP::HTTP::Method::POST:
        conn_setopt(CURLOPT_POST, 1L);
        break;
     case HTTPP::HTTP::Method::HEAD:
        conn_setopt(CURLOPT_NOBODY, 1L);
        break;

     case HTTPP::HTTP::Method::PUT:
     case HTTPP::HTTP::Method::DELETE_:
     case HTTPP::HTTP::Method::OPTIONS:
     case HTTPP::HTTP::Method::TRACE:
     case HTTPP::HTTP::Method::CONNECT:
         std::string method_str = to_string(method);
         conn_setopt(CURLOPT_CUSTOMREQUEST, method_str.data());
         break;
    }

    if (!request.query_params_.empty())
    {
        std::string url = request.url_ + "?";

        bool add_sep = false;

        for (const auto& query_param : request.query_params_)
        {
            if (add_sep)
            {
                url += "&";
            }
            url += query_param.first + "=" + query_param.second;
            add_sep = true;
        }

        conn_setopt(CURLOPT_URL, url.data());
    }
    else
    {
        conn_setopt(CURLOPT_URL, request.url_.data());
    }

    if (request.follow_redirect_)
    {
        conn_setopt(CURLOPT_FOLLOWLOCATION, 1L);
        conn_setopt(CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
    }

    if (!request.post_params_.empty() && !request.content_.empty())
    {
        throw std::runtime_error(
            "Cannot mix multipart and x-formurl-encoded post data");
    }

    struct curl_httppost* post = NULL;
    struct curl_httppost* last = NULL;
    if (!request.post_params_.empty())
    {
        for (const auto& e : request.post_params_)
        {
            curl_formadd(&post,
                            &last,
                            CURLFORM_COPYNAME,
                            e.first.data(),
                            CURLFORM_COPYCONTENTS,
                            e.second.data(),
                            CURLFORM_END);
        }

        conn_setopt(CURLOPT_HTTPPOST, post);
        expect_continue = true;
    }

    if (!request.content_.empty())
    {
        conn_setopt(CURLOPT_POSTFIELDS, request.content_.data());
        conn_setopt(CURLOPT_POSTFIELDSIZE, request.content_.size());
    }

    conn_setopt(CURLOPT_NOSIGNAL, 1L);

    // only on curl > 7.25, disable it for now
    //conn_setopt(CURLOPT_TCP_KEEPALIVE, 1L);

    if (!request.http_headers_.empty())
    {
        http_headers = nullptr;
        for (const auto& h : request.http_headers_)
        {
            http_headers = curl_slist_append(
                http_headers, (h.first + ": " + h.second).data());
        }

        conn_setopt(CURLOPT_HTTPHEADER, http_headers);
    }

    cancelled = false;
    result_notified = false;
}

void Connection::cancel()
{
    bool expected = false;
    if (cancelled.compare_exchange_strong(expected, true))
    {
        handler.cancelConnection(shared_from_this());
    }
    else
    {
        BOOST_LOG_TRIVIAL(warning) << "Connection already cancelled";
    }
}

void Connection::buildResponse(CURLcode code)
{
    if (code != CURLE_OK)
    {
        complete(std::make_exception_ptr(std::runtime_error(
            curl_easy_strerror(code) + std::string(this->error_buffer))));
        return;
    }
    try
    {
        response.code = static_cast<HTTPP::HTTP::HttpCode>(
            conn_getinfo<long>(CURLINFO_RESPONSE_CODE));

        response.request = std::move(request);

        long redirection = 0;
        if (request.follow_redirect_)
        {
            redirection = conn_getinfo<long>(CURLINFO_REDIRECT_COUNT);
        }

        redirection += expect_continue ? 1 : 0;
        while (redirection)
        {
            auto begin = std::begin(header);
            auto end = std::end(header);
            auto it = std::search(begin,
                                  end,
                                  std::begin(HEADER_BODY_SEP),
                                  std::end(HEADER_BODY_SEP));

            if (it != end)
            {
                header.erase(std::begin(header), it + 4);
            }

            --redirection;
        }

        response.body.swap(buffer);
        parseCurlResponseHeader(header, response);
        complete();
    }
    catch (const std::exception& exc)
    {
        BOOST_LOG_TRIVIAL(error)
            << "Error when building the response: " << exc.what();
        complete(std::current_exception());
        return;
    }

}

void Connection::complete(std::exception_ptr ex)
{
    bool expected = false;
    if (!result_notified.compare_exchange_strong(expected, true))
    {
        BOOST_LOG_TRIVIAL(error)
            << "Response already notified, cancel notification";
        return;
    }

    if (ex)
    {
        promise.set_exception(ex);
    }
    else
    {
        promise.set_value(std::move(response));
    }

    if (completion_handler)
    {
        auto ptr = shared_from_this();
        dispatch->post([ptr]
                       { ptr->completion_handler(ptr->promise.get_future()); });
    }
}

} // namespace detail
} // namespace client
} // namespace HTTP
} // namespace HTTPP
<commit_msg>Do not dispatch the completion_handler call when we call complete from the destructor<commit_after>/*
 * Part of HTTPP.
 *
 * Distributed under the 3-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2014 Thomas Sanchez.  All rights reserved.
 *
 */

#include "Connection.hpp"

#include <curl/curl.h>
#include <memory>
#include <iostream>
#include <vector>
#include <thread>

#include <boost/log/trivial.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/exception_ptr.hpp>

#include "httpp/http/Protocol.hpp"
#include "httpp/http/client/Request.hpp"
#include "httpp/http/client/Response.hpp"
#include "httpp/utils/Exception.hpp"

#include "Manager.hpp"

namespace HTTPP {
namespace HTTP {
namespace client {

namespace detail {

Connection::Connection(Manager& manager, boost::asio::io_service& service)
: handler(manager)
, handle(curl_easy_init())
, service(service)
{
    if (!handle)
    {
        throw std::runtime_error("Cannot initialize curl handle");
    }
}

Connection::~Connection()
{
    if (http_headers)
    {
        curl_slist_free_all(http_headers);
    }

    if (handle)
    {
        curl_easy_cleanup(handle);
        handle = nullptr;
    }

    dispatch = nullptr;

    if (!cancelled && !result_notified)
    {
        try
        {
            BOOST_LOG_TRIVIAL(error)
                << "Destroy a not completed connection: " << this;
            complete(std::make_exception_ptr(
                std::runtime_error("Destroy a non completed connection")));
        }
        catch (const std::exception& ex)
        {
            BOOST_LOG_TRIVIAL(error)
                << "Error happened completing the connection: " << ex.what();
        }
    }
}

void Connection::init(std::map<curl_socket_t, boost::asio::ip::tcp::socket*>& sockets)
{
    if (!result_notified)
    {
        throw std::runtime_error("Recycle a not completed connection");
    }

    curl_easy_reset(handle);

    this->sockets = std::addressof(sockets);

    conn_setopt(CURLOPT_HEADERFUNCTION, &writeHd);
    conn_setopt(CURLOPT_WRITEHEADER, this);

    conn_setopt(CURLOPT_WRITEFUNCTION, &writefn);
    conn_setopt(CURLOPT_WRITEDATA, this);

    conn_setopt(CURLOPT_ERRORBUFFER, this->error_buffer);

    conn_setopt(CURLOPT_PRIVATE, this);

    conn_setopt(CURLOPT_OPENSOCKETDATA, this);
    conn_setopt(CURLOPT_OPENSOCKETFUNCTION, &opensocket);

    conn_setopt(CURLOPT_CLOSESOCKETDATA, this->sockets);
    conn_setopt(CURLOPT_CLOSESOCKETFUNCTION, &closesocket);

    conn_setopt(CURLOPT_ERRORBUFFER, this->error_buffer);
    conn_setopt(CURLOPT_PRIVATE, this);

    if (http_headers)
    {
        curl_slist_free_all(http_headers);
        http_headers = nullptr;
    }

    expect_continue = false;
    header.clear();
    buffer.clear();

    promise = std::promise<client::Response>();
    completion_handler = CompletionHandler();
    response = Response();
    poll_action = 0;
}

Connection::ConnectionPtr
Connection::createConnection(Manager& manager, boost::asio::io_service& service)
{
    return std::make_shared<Connection>(manager, service);
}

size_t Connection::writeHd(char* buffer, size_t size, size_t nmemb, void* userdata)
{
    Connection* conn = (Connection*)userdata;
    auto actual_size = size * nmemb;
    conn->header.insert(std::end(conn->header), buffer, buffer + actual_size);
    return actual_size;
}

size_t Connection::writefn(char* buffer, size_t size, size_t nmemb, void* userdata)
{
    Connection* conn = (Connection*)userdata;

    auto actual_size = size * nmemb;
    conn->buffer.insert(std::end(conn->buffer), buffer, buffer + actual_size);
    return actual_size;
}

curl_socket_t Connection::opensocket(void* clientp,
                                     curlsocktype purpose,
                                     struct curl_sockaddr* address)
{
    Connection* conn = (Connection*)clientp;

    if (purpose == CURLSOCKTYPE_IPCXN)
    {
        boost::system::error_code ec;
        boost::asio::ip::tcp::socket* socket =
            new boost::asio::ip::tcp::socket(conn->service);

        if (address->family == AF_INET)
        {
            socket->open(boost::asio::ip::tcp::v4(), ec);
        }
        else
        {
            socket->open(boost::asio::ip::tcp::v6(), ec);
        }

        if (ec)
        {
            BOOST_LOG_TRIVIAL(error)
                << "Cannot open a socket: " << ec.message();
            return CURL_SOCKET_BAD;
        }

        auto handle = socket->native_handle();
        (*conn->sockets)[handle] = socket;
        return handle;
    }

    return CURL_SOCKET_BAD;
}

int Connection::closesocket(void* clientp, curl_socket_t curl_socket)
{
    std::map<curl_socket_t, boost::asio::ip::tcp::socket*>* sockets;
    sockets = (decltype(sockets)) clientp;

    auto it = sockets->find(curl_socket);
    if (it == std::end(*sockets))
    {
        BOOST_LOG_TRIVIAL(error) << "Cannot find a socket to close";
        return 1;
    }

    delete it->second;
    sockets->erase(it);
    return 0;
}

void Connection::setSocket(curl_socket_t curl_socket)
{
    auto it = sockets->find(curl_socket);
    if (it == std::end(*sockets))
    {
        throw std::runtime_error("Cannot find socket: " +
                                 std::to_string(curl_socket));
    }

    socket = it->second;
}

void Connection::configureRequest(HTTPP::HTTP::Method method)
{
    switch (method)
    {
     case HTTPP::HTTP::Method::GET:
        conn_setopt(CURLOPT_HTTPGET, 1L);
        break;
     case HTTPP::HTTP::Method::POST:
        conn_setopt(CURLOPT_POST, 1L);
        break;
     case HTTPP::HTTP::Method::HEAD:
        conn_setopt(CURLOPT_NOBODY, 1L);
        break;

     case HTTPP::HTTP::Method::PUT:
     case HTTPP::HTTP::Method::DELETE_:
     case HTTPP::HTTP::Method::OPTIONS:
     case HTTPP::HTTP::Method::TRACE:
     case HTTPP::HTTP::Method::CONNECT:
         std::string method_str = to_string(method);
         conn_setopt(CURLOPT_CUSTOMREQUEST, method_str.data());
         break;
    }

    if (!request.query_params_.empty())
    {
        std::string url = request.url_ + "?";

        bool add_sep = false;

        for (const auto& query_param : request.query_params_)
        {
            if (add_sep)
            {
                url += "&";
            }
            url += query_param.first + "=" + query_param.second;
            add_sep = true;
        }

        conn_setopt(CURLOPT_URL, url.data());
    }
    else
    {
        conn_setopt(CURLOPT_URL, request.url_.data());
    }

    if (request.follow_redirect_)
    {
        conn_setopt(CURLOPT_FOLLOWLOCATION, 1L);
        conn_setopt(CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
    }

    if (!request.post_params_.empty() && !request.content_.empty())
    {
        throw std::runtime_error(
            "Cannot mix multipart and x-formurl-encoded post data");
    }

    struct curl_httppost* post = NULL;
    struct curl_httppost* last = NULL;
    if (!request.post_params_.empty())
    {
        for (const auto& e : request.post_params_)
        {
            curl_formadd(&post,
                            &last,
                            CURLFORM_COPYNAME,
                            e.first.data(),
                            CURLFORM_COPYCONTENTS,
                            e.second.data(),
                            CURLFORM_END);
        }

        conn_setopt(CURLOPT_HTTPPOST, post);
        expect_continue = true;
    }

    if (!request.content_.empty())
    {
        conn_setopt(CURLOPT_POSTFIELDS, request.content_.data());
        conn_setopt(CURLOPT_POSTFIELDSIZE, request.content_.size());
    }

    conn_setopt(CURLOPT_NOSIGNAL, 1L);

    // only on curl > 7.25, disable it for now
    //conn_setopt(CURLOPT_TCP_KEEPALIVE, 1L);

    if (!request.http_headers_.empty())
    {
        http_headers = nullptr;
        for (const auto& h : request.http_headers_)
        {
            http_headers = curl_slist_append(
                http_headers, (h.first + ": " + h.second).data());
        }

        conn_setopt(CURLOPT_HTTPHEADER, http_headers);
    }

    cancelled = false;
    result_notified = false;
}

void Connection::cancel()
{
    bool expected = false;
    if (cancelled.compare_exchange_strong(expected, true))
    {
        handler.cancelConnection(shared_from_this());
    }
    else
    {
        BOOST_LOG_TRIVIAL(warning) << "Connection already cancelled";
    }
}

void Connection::buildResponse(CURLcode code)
{
    if (code != CURLE_OK)
    {
        complete(std::make_exception_ptr(std::runtime_error(
            curl_easy_strerror(code) + std::string(this->error_buffer))));
        return;
    }
    try
    {
        response.code = static_cast<HTTPP::HTTP::HttpCode>(
            conn_getinfo<long>(CURLINFO_RESPONSE_CODE));

        response.request = std::move(request);

        long redirection = 0;
        if (request.follow_redirect_)
        {
            redirection = conn_getinfo<long>(CURLINFO_REDIRECT_COUNT);
        }

        redirection += expect_continue ? 1 : 0;
        while (redirection)
        {
            auto begin = std::begin(header);
            auto end = std::end(header);
            auto it = std::search(begin,
                                  end,
                                  std::begin(HEADER_BODY_SEP),
                                  std::end(HEADER_BODY_SEP));

            if (it != end)
            {
                header.erase(std::begin(header), it + 4);
            }

            --redirection;
        }

        response.body.swap(buffer);
        parseCurlResponseHeader(header, response);
        complete();
    }
    catch (const std::exception& exc)
    {
        BOOST_LOG_TRIVIAL(error)
            << "Error when building the response: " << exc.what();
        complete(std::current_exception());
        return;
    }

}

void Connection::complete(std::exception_ptr ex)
{
    bool expected = false;
    if (!result_notified.compare_exchange_strong(expected, true))
    {
        BOOST_LOG_TRIVIAL(error)
            << "Response already notified, cancel notification";
        return;
    }

    if (ex)
    {
        promise.set_exception(ex);
    }
    else
    {
        promise.set_value(std::move(response));
    }

    if (completion_handler)
    {
        if (dispatch)
        {
            auto ptr = shared_from_this();
            dispatch->post([ptr]
                           { ptr->completion_handler(ptr->promise.get_future()); });
        }
        else
        {
            completion_handler(promise.get_future());
        }
    }
}

} // namespace detail
} // namespace client
} // namespace HTTP
} // namespace HTTPP
<|endoftext|>


<commit_before>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)

#include "bin/file_system_watcher.h"

#include <WinIoCtl.h>  // NOLINT

#include "bin/builtin.h"
#include "bin/eventhandler.h"
#include "bin/utils.h"
#include "bin/utils_win.h"
#include "platform/syslog.h"

namespace dart {
namespace bin {

bool FileSystemWatcher::IsSupported() {
  return true;
}

intptr_t FileSystemWatcher::Init() {
  return 0;
}

void FileSystemWatcher::Close(intptr_t id) {
  USE(id);
}

intptr_t FileSystemWatcher::WatchPath(intptr_t id,
                                      Namespace* namespc,
                                      const char* path,
                                      int events,
                                      bool recursive) {
  USE(id);
  Utf8ToWideScope name(path);
  HANDLE dir = CreateFileW(
      name.wide(), FILE_LIST_DIRECTORY,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

  if (dir == INVALID_HANDLE_VALUE) {
    return -1;
  }

  int list_events = 0;
  if ((events & (kCreate | kMove | kDelete)) != 0) {
    list_events |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
  }
  if ((events & kModifyContent) != 0) {
    list_events |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  }

  DirectoryWatchHandle* handle =
      new DirectoryWatchHandle(dir, list_events, recursive);
  // Issue a read directly, to be sure events are tracked from now on. This is
  // okay, since in Dart, we create the socket and start reading immidiately.
  handle->EnsureInitialized(EventHandler::delegate());
  handle->IssueRead();
  return reinterpret_cast<intptr_t>(handle);
}

void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) {
  USE(id);
  DirectoryWatchHandle* handle =
      reinterpret_cast<DirectoryWatchHandle*>(path_id);
  handle->Stop();
}

intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) {
  USE(id);
  return path_id;
}

Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
  USE(id);
  const intptr_t kEventSize = sizeof(FILE_NOTIFY_INFORMATION);
  DirectoryWatchHandle* dir = reinterpret_cast<DirectoryWatchHandle*>(path_id);
  intptr_t available = dir->Available();
  intptr_t max_count = available / kEventSize + 1;
  Dart_Handle events = Dart_NewList(max_count);
  uint8_t* buffer = Dart_ScopeAllocate(available);
  intptr_t bytes = dir->Read(buffer, available);
  intptr_t offset = 0;
  intptr_t i = 0;
  while (offset < bytes) {
    FILE_NOTIFY_INFORMATION* e =
        reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer + offset);

    Dart_Handle event = Dart_NewList(5);
    int mask = 0;
    if (e->Action == FILE_ACTION_ADDED) {
      mask |= kCreate;
    }
    if (e->Action == FILE_ACTION_REMOVED) {
      mask |= kDelete;
    }
    if (e->Action == FILE_ACTION_MODIFIED) {
      mask |= kModifyContent;
    }
    if (e->Action == FILE_ACTION_RENAMED_OLD_NAME) {
      mask |= kMove;
    }
    if (e->Action == FILE_ACTION_RENAMED_NEW_NAME) {
      mask |= kMove;
    }
    Dart_ListSetAt(event, 0, Dart_NewInteger(mask));
    // Move events come in pairs. Just 'enable' by default.
    Dart_ListSetAt(event, 1, Dart_NewInteger(1));
    Dart_ListSetAt(
        event, 2,
        Dart_NewStringFromUTF16(reinterpret_cast<uint16_t*>(e->FileName),
                                e->FileNameLength / 2));
    Dart_ListSetAt(event, 3, Dart_NewBoolean(true));
    Dart_ListSetAt(event, 4, Dart_NewInteger(path_id));
    Dart_ListSetAt(events, i, event);
    i++;
    if (e->NextEntryOffset == 0) {
      break;
    }
    offset += e->NextEntryOffset;
  }
  return events;
}

}  // namespace bin
}  // namespace dart

#endif  // defined(HOST_OS_WINDOWS)
<commit_msg>[io/watcher] Fix crash in file event watcher functionality on Windows.<commit_after>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)

#include "bin/file_system_watcher.h"

#include <WinIoCtl.h>  // NOLINT

#include "bin/builtin.h"
#include "bin/eventhandler.h"
#include "bin/utils.h"
#include "bin/utils_win.h"
#include "platform/syslog.h"

namespace dart {
namespace bin {

bool FileSystemWatcher::IsSupported() {
  return true;
}

intptr_t FileSystemWatcher::Init() {
  return 0;
}

void FileSystemWatcher::Close(intptr_t id) {
  USE(id);
}

intptr_t FileSystemWatcher::WatchPath(intptr_t id,
                                      Namespace* namespc,
                                      const char* path,
                                      int events,
                                      bool recursive) {
  USE(id);
  Utf8ToWideScope name(path);
  HANDLE dir = CreateFileW(
      name.wide(), FILE_LIST_DIRECTORY,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

  if (dir == INVALID_HANDLE_VALUE) {
    return -1;
  }

  int list_events = 0;
  if ((events & (kCreate | kMove | kDelete)) != 0) {
    list_events |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
  }
  if ((events & kModifyContent) != 0) {
    list_events |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  }

  DirectoryWatchHandle* handle =
      new DirectoryWatchHandle(dir, list_events, recursive);
  // Issue a read directly, to be sure events are tracked from now on. This is
  // okay, since in Dart, we create the socket and start reading immidiately.
  handle->EnsureInitialized(EventHandler::delegate());
  handle->IssueRead();
  return reinterpret_cast<intptr_t>(handle);
}

void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) {
  USE(id);
  DirectoryWatchHandle* handle =
      reinterpret_cast<DirectoryWatchHandle*>(path_id);
  handle->Stop();
}

intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) {
  USE(id);
  return path_id;
}

Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
  USE(id);
  const intptr_t kEventSize = sizeof(FILE_NOTIFY_INFORMATION);
  DirectoryWatchHandle* dir = reinterpret_cast<DirectoryWatchHandle*>(path_id);
  intptr_t available = dir->Available();
  if (available <= 0) {
    return Dart_NewList(0);
  }
  intptr_t max_count = available / kEventSize + 1;
  Dart_Handle events = Dart_NewList(max_count);
  uint8_t* buffer = Dart_ScopeAllocate(available);
  intptr_t bytes = dir->Read(buffer, available);
  intptr_t offset = 0;
  intptr_t i = 0;
  while (offset < bytes) {
    FILE_NOTIFY_INFORMATION* e =
        reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer + offset);

    Dart_Handle event = Dart_NewList(5);
    int mask = 0;
    if (e->Action == FILE_ACTION_ADDED) {
      mask |= kCreate;
    }
    if (e->Action == FILE_ACTION_REMOVED) {
      mask |= kDelete;
    }
    if (e->Action == FILE_ACTION_MODIFIED) {
      mask |= kModifyContent;
    }
    if (e->Action == FILE_ACTION_RENAMED_OLD_NAME) {
      mask |= kMove;
    }
    if (e->Action == FILE_ACTION_RENAMED_NEW_NAME) {
      mask |= kMove;
    }
    Dart_ListSetAt(event, 0, Dart_NewInteger(mask));
    // Move events come in pairs. Just 'enable' by default.
    Dart_ListSetAt(event, 1, Dart_NewInteger(1));
    Dart_ListSetAt(
        event, 2,
        Dart_NewStringFromUTF16(reinterpret_cast<uint16_t*>(e->FileName),
                                e->FileNameLength / 2));
    Dart_ListSetAt(event, 3, Dart_NewBoolean(true));
    Dart_ListSetAt(event, 4, Dart_NewInteger(path_id));
    Dart_ListSetAt(events, i, event);
    i++;
    if (e->NextEntryOffset == 0) {
      break;
    }
    offset += e->NextEntryOffset;
  }
  return events;
}

}  // namespace bin
}  // namespace dart

#endif  // defined(HOST_OS_WINDOWS)
<|endoftext|>


<commit_before>//
//  MultiClassify.cpp
//
//
//  Created by tim on 23/03/2017.
//
//
#include <fstream>
#include <memory>

#include "SingleShotDetector.hpp"
#include "MultiClassify.hpp"
#include "GraphLoader.hpp"
#include "Classifier.hpp"
#include "FaceAlign.hpp"
#include "Multibox.hpp"

#include "timer.hpp"
#include "log.hpp"

#include "tensorflow/cc/client/client_session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/public/session.h"

using namespace ml;
using namespace image;
using namespace metrics;
using namespace tensorflow;
using namespace tensorflow::ops;

//
//  Create the sesson by loading all the models
//  - we need all models to load correctly so throw exceptions on failure
//
//
MultiClassify::MultiClassify(std::string root, std::string logLevel){

    FILELog::ReportingLevel() = FILELog::FromString(logLevel);

    LOG(INFO) << "Starting init";

    inceptionSize = 299;
    multiboxSize = 224;

    GraphDef ssdGraphDef;
    GraphDef boxGraphDef;
    GraphDef classifyGraphDef;
    GraphDef faceGraphDef;

    std::unique_ptr<GraphLoader> graphLoader;
    graphLoader.reset(new GraphLoader(root));

    //load the ssd graph
    std::string ssdGraphPath = "data/ssd.pb";

    auto ssdLoadStatus = graphLoader->LoadGraphDef(&ssdGraphDef, ssdGraphPath);

    if (!ssdLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << ssdLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded ssd model";
    }

    auto ssdSessionStatus = graphLoader->InitializeSessionGraph(&ssdSession, &ssdGraphDef);

    if (!ssdSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << ssdSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started ssd session";
    }
    //finished ssd graph loading

    std::string boxGraphPath = "data/multibox_model.pb";

    auto multiboxLoadStatus = graphLoader->LoadGraphDef(&boxGraphDef, boxGraphPath);

    if (!multiboxLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << multiboxLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded multibox model";
    }

    auto multiboxSessionStatus = graphLoader->InitializeSessionGraph(&boxSession, &boxGraphDef);

    if (!multiboxSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << multiboxSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started multibox session";
    }

    //graphLoader->InitializeSessionGraph(&boxSession, &boxGraphDef);

    std::string classifyGraphPath = "data/inception_v4.pb";
    auto inceptionLoadStatus = graphLoader->LoadGraphDef(&classifyGraphDef, classifyGraphPath);

    if (!inceptionLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << inceptionLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded inception model";
    }

    auto inceptionSessionStatus = graphLoader->InitializeSessionGraph(&classifySession, &classifyGraphDef);

    if (!inceptionSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << inceptionSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started inception session";
    }

    std::string faceGraphPath = "data/face_align.pb";
    auto faceLoadStatus = graphLoader->LoadGraphDef(&faceGraphDef, faceGraphPath);

    if (!faceLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << faceLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded face model";
    }

    auto faceSessionStatus = graphLoader->InitializeSessionGraph(&faceSession, &faceGraphDef);

    if (!faceSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << faceSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started face session";
    }

    std::string labelsFile = root + "data/inception_labels.txt";

    //inception v3 "mul" "softmax"
    //inception v4 "input:0" "InceptionV4/Logits/Predictions"
    classifier.reset(new Classifier(labelsFile, "input:0", "InceptionV4/Logits/Predictions"));
    multibox.reset(new Multibox(root));
    faceAlign.reset(new FaceAlign());

    std::string ssdLabelsFile = root + "data/ssd_labels.txt";
    singleShotDetector.reset(new SingleShotDetector(ssdLabelsFile, &ssdSession));

    imageGraph.reset(new ImageGraph());

    LOG(INFO) << "Finished multiclassify init";
}

void MultiClassify::ClassifyFile(std::string fileName){

    FILE_LOG(logDEBUG) << "Reading image from file: " << fileName;
    auto root = tensorflow::Scope::NewRootScope();

    tensorflow::Output fileReader = tensorflow::ops::ReadFile(root.WithOpName("file_reader"),
                                                              fileName);
    ClientSession session(root);
    std::vector<Tensor> outputs;

    Status result = session.Run({}, {fileReader}, &outputs);

    auto imageString = outputs[0];

    auto asString = imageString.scalar<string>()();

    LOG(INFO) << "Got imageString";

    std::string scores = "";
    std::string* json = &scores;

    timestamp_t t0 = timer::get_timestamp();

    Align(asString, 1, json);
    LOG(INFO) << "Got align: ";

    Box(asString, 1, json);
    LOG(INFO) << "Got boxes: ";

    Classify(asString, 1, json);
    LOG(INFO) << "Got classify: ";

    Detect(asString, 1, json);
    LOG(INFO) << "Got detect: ";

    LOG(INFO) << *json;

    timestamp_t t1 = timer::get_timestamp();

    double classifyTime = (t1 - t0);
    LOG(INFO) << "Classify time was: " << classifyTime;
}

int MultiClassify::Align(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    LOG(INFO) << "Processed align image";
    int width = imageTensors[0].shape().dim_size(2);
    int height = imageTensors[0].shape().dim_size(1);

    float maximum = std::max(width, height);

    if (maximum>600){
        float scaling = 600.0 / maximum;
        int newWidth = (int)(width * scaling);
        int newHeight = (int)(height * scaling);

        LOG(INFO) << "Scaling to max size " << newWidth << ", " << newHeight;

        Scope localRoot = Scope::NewRootScope();

        std::vector<Tensor> resizeOutputs;

        auto imageOutput = ResizeBilinear(localRoot,
                                          imageTensors[0],
                                          {newHeight, newWidth});

        FILE_LOG(logDEBUG) << "Scaling to max size " << newWidth << ", " << newHeight;

        ClientSession session(localRoot);
        auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

        imageTensors[0] = resizeOutputs[0];

    }

    return faceAlign -> ReadAndRun(&imageTensors, json, &faceSession);
}

int MultiClassify::Classify(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    Scope localRoot = Scope::NewRootScope();

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {inceptionSize, inceptionSize});

    FILE_LOG(logDEBUG) << "Scaling to inception input: " << imageTensors[0].DebugString();

    ClientSession session(localRoot);
    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    FILE_LOG(logDEBUG) << "resizeOutputs: " << resizeOutputs[0].DebugString();

    return classifier -> ReadAndRun(&resizeOutputs, json, &classifySession);
}

int MultiClassify::Detect(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    //do a basic dimension check on the returned image

    auto imageShape = imageTensors[0].shape();

    FILE_LOG(logDEBUG) << "Image dimensions: " << imageShape.dim_size(0) << ", " << imageShape.dim_size(1) << ", " << imageShape.dim_size(2);

    if (imageShape.dim_size(1) == 0){
        return -1;
    }

    Scope localRoot = Scope::NewRootScope();

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {inceptionSize, inceptionSize});

    FILE_LOG(logDEBUG) << "Scaling to sdd input: " << imageTensors[0].DebugString();

    ClientSession session(localRoot);
    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    FILE_LOG(logDEBUG) << "resizeOutputs: " << resizeOutputs[0].DebugString();

    imageTensors.push_back(resizeOutputs[0]);
    return singleShotDetector -> ReadAndRun(&imageTensors, json, &ssdSession);
}

int MultiClassify::Box(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    Scope localRoot = Scope::NewRootScope();
    ClientSession session(localRoot);

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {multiboxSize, multiboxSize});

    FILE_LOG(logDEBUG) << "Scaling to box input: " << imageTensors[0].DebugString();

    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    imageTensors.push_back(resizeOutputs[0]);

    return multibox -> ReadAndRun(&imageTensors, json, &boxSession);
}

MultiClassify::~MultiClassify(){
    LOG(INFO) << "destructing";
    auto closedBoxSession = boxSession->Close();
    auto closedClassifySession = classifySession->Close();
    auto closedFaceSession = faceSession->Close();
    auto closedSsdSession = ssdSession->Close();

    FILE_LOG(logDEBUG) << "Closed sessions: " << closedBoxSession << ", " << closedClassifySession << ", " << closedFaceSession << ", " << closedSsdSession;
}
<commit_msg>Image process guard<commit_after>//
//  MultiClassify.cpp
//
//
//  Created by tim on 23/03/2017.
//
//
#include <fstream>
#include <memory>

#include "SingleShotDetector.hpp"
#include "MultiClassify.hpp"
#include "GraphLoader.hpp"
#include "Classifier.hpp"
#include "FaceAlign.hpp"
#include "Multibox.hpp"

#include "timer.hpp"
#include "log.hpp"

#include "tensorflow/cc/client/client_session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/public/session.h"

using namespace ml;
using namespace image;
using namespace metrics;
using namespace tensorflow;
using namespace tensorflow::ops;

//
//  Create the sesson by loading all the models
//  - we need all models to load correctly so throw exceptions on failure
//
//
MultiClassify::MultiClassify(std::string root, std::string logLevel){

    FILELog::ReportingLevel() = FILELog::FromString(logLevel);

    LOG(INFO) << "Starting init";

    inceptionSize = 299;
    multiboxSize = 224;

    GraphDef ssdGraphDef;
    GraphDef boxGraphDef;
    GraphDef classifyGraphDef;
    GraphDef faceGraphDef;

    std::unique_ptr<GraphLoader> graphLoader;
    graphLoader.reset(new GraphLoader(root));

    //load the ssd graph
    std::string ssdGraphPath = "data/ssd.pb";

    auto ssdLoadStatus = graphLoader->LoadGraphDef(&ssdGraphDef, ssdGraphPath);

    if (!ssdLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << ssdLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded ssd model";
    }

    auto ssdSessionStatus = graphLoader->InitializeSessionGraph(&ssdSession, &ssdGraphDef);

    if (!ssdSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << ssdSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started ssd session";
    }
    //finished ssd graph loading

    std::string boxGraphPath = "data/multibox_model.pb";

    auto multiboxLoadStatus = graphLoader->LoadGraphDef(&boxGraphDef, boxGraphPath);

    if (!multiboxLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << multiboxLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded multibox model";
    }

    auto multiboxSessionStatus = graphLoader->InitializeSessionGraph(&boxSession, &boxGraphDef);

    if (!multiboxSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << multiboxSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started multibox session";
    }

    //graphLoader->InitializeSessionGraph(&boxSession, &boxGraphDef);

    std::string classifyGraphPath = "data/inception_v4.pb";
    auto inceptionLoadStatus = graphLoader->LoadGraphDef(&classifyGraphDef, classifyGraphPath);

    if (!inceptionLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << inceptionLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded inception model";
    }

    auto inceptionSessionStatus = graphLoader->InitializeSessionGraph(&classifySession, &classifyGraphDef);

    if (!inceptionSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << inceptionSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started inception session";
    }

    std::string faceGraphPath = "data/face_align.pb";
    auto faceLoadStatus = graphLoader->LoadGraphDef(&faceGraphDef, faceGraphPath);

    if (!faceLoadStatus.ok()) {
        LOG(ERROR) << "Loading model failed: " << faceLoadStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Loaded face model";
    }

    auto faceSessionStatus = graphLoader->InitializeSessionGraph(&faceSession, &faceGraphDef);

    if (!faceSessionStatus.ok()) {
        LOG(ERROR) << "Starting session failed: " << faceSessionStatus;
        throw std::exception();
    }
    else {
        FILE_LOG(logDEBUG) << "Started face session";
    }

    std::string labelsFile = root + "data/inception_labels.txt";

    //inception v3 "mul" "softmax"
    //inception v4 "input:0" "InceptionV4/Logits/Predictions"
    classifier.reset(new Classifier(labelsFile, "input:0", "InceptionV4/Logits/Predictions"));
    multibox.reset(new Multibox(root));
    faceAlign.reset(new FaceAlign());

    std::string ssdLabelsFile = root + "data/ssd_labels.txt";
    singleShotDetector.reset(new SingleShotDetector(ssdLabelsFile, &ssdSession));

    imageGraph.reset(new ImageGraph());

    LOG(INFO) << "Finished multiclassify init";
}

void MultiClassify::ClassifyFile(std::string fileName){

    FILE_LOG(logDEBUG) << "Reading image from file: " << fileName;
    auto root = tensorflow::Scope::NewRootScope();

    tensorflow::Output fileReader = tensorflow::ops::ReadFile(root.WithOpName("file_reader"),
                                                              fileName);
    ClientSession session(root);
    std::vector<Tensor> outputs;

    Status result = session.Run({}, {fileReader}, &outputs);

    auto imageString = outputs[0];

    auto asString = imageString.scalar<string>()();

    LOG(INFO) << "Got imageString";

    std::string scores = "";
    std::string* json = &scores;

    timestamp_t t0 = timer::get_timestamp();

    Align(asString, 1, json);
    LOG(INFO) << "Got align: ";

    Box(asString, 1, json);
    LOG(INFO) << "Got boxes: ";

    Classify(asString, 1, json);
    LOG(INFO) << "Got classify: ";

    Detect(asString, 1, json);
    LOG(INFO) << "Got detect: ";

    LOG(INFO) << *json;

    timestamp_t t1 = timer::get_timestamp();

    double classifyTime = (t1 - t0);
    LOG(INFO) << "Classify time was: " << classifyTime;
}

int MultiClassify::Align(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    if (!importStatus.ok()) {
        LOG(ERROR) << "processing image failed: " << importStatus;
        return -1;
    }
    else {
        FILE_LOG(logDEBUG) << "got image";
    }

    auto imageShape = imageTensors[0].shape();

    FILE_LOG(logDEBUG) << "Image dimensions: " << imageShape.dim_size(0) << ", " << imageShape.dim_size(1) << ", " << imageShape.dim_size(2);

    if (imageShape.dim_size(1) == 0){
        return -1;
    }

    LOG(INFO) << "Processed align image";
    int width = imageTensors[0].shape().dim_size(2);
    int height = imageTensors[0].shape().dim_size(1);

    float maximum = std::max(width, height);

    if (maximum>600){
        float scaling = 600.0 / maximum;
        int newWidth = (int)(width * scaling);
        int newHeight = (int)(height * scaling);

        LOG(INFO) << "Scaling to max size " << newWidth << ", " << newHeight;

        Scope localRoot = Scope::NewRootScope();

        std::vector<Tensor> resizeOutputs;

        auto imageOutput = ResizeBilinear(localRoot,
                                          imageTensors[0],
                                          {newHeight, newWidth});

        FILE_LOG(logDEBUG) << "Scaling to max size " << newWidth << ", " << newHeight;

        ClientSession session(localRoot);
        auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

        imageTensors[0] = resizeOutputs[0];

    }

    return faceAlign -> ReadAndRun(&imageTensors, json, &faceSession);
}

int MultiClassify::Classify(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    if (!importStatus.ok()) {
        LOG(ERROR) << "processing image failed: " << importStatus;
        return -1;
    }
    else {
        FILE_LOG(logDEBUG) << "got image";
    }

    auto imageShape = imageTensors[0].shape();

    FILE_LOG(logDEBUG) << "Image dimensions: " << imageShape.dim_size(0) << ", " << imageShape.dim_size(1) << ", " << imageShape.dim_size(2);

    if (imageShape.dim_size(1) == 0){
        return -1;
    }

    Scope localRoot = Scope::NewRootScope();

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {inceptionSize, inceptionSize});

    FILE_LOG(logDEBUG) << "Scaling to inception input: " << imageTensors[0].DebugString();

    ClientSession session(localRoot);
    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    FILE_LOG(logDEBUG) << "resizeOutputs: " << resizeOutputs[0].DebugString();

    return classifier -> ReadAndRun(&resizeOutputs, json, &classifySession);
}

int MultiClassify::Detect(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    if (!importStatus.ok()) {
        LOG(ERROR) << "processing image failed: " << importStatus;
        return -1;
    }
    else {
        FILE_LOG(logDEBUG) << "got image";
    }
    //do a basic dimension check on the returned image

    auto imageShape = imageTensors[0].shape();

    FILE_LOG(logDEBUG) << "Image dimensions: " << imageShape.dim_size(0) << ", " << imageShape.dim_size(1) << ", " << imageShape.dim_size(2);

    if (imageShape.dim_size(1) == 0){
        return -1;
    }

    Scope localRoot = Scope::NewRootScope();

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {inceptionSize, inceptionSize});

    FILE_LOG(logDEBUG) << "Scaling to sdd input: " << imageTensors[0].DebugString();

    ClientSession session(localRoot);
    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    FILE_LOG(logDEBUG) << "resizeOutputs: " << resizeOutputs[0].DebugString();

    imageTensors.push_back(resizeOutputs[0]);
    return singleShotDetector -> ReadAndRun(&imageTensors, json, &ssdSession);
}

int MultiClassify::Box(std::string byteString, int encoding, std::string* json){

    if ( sizeof(byteString)==0 ){
        FILE_LOG(logWARNING) << "No data in byteString";
        return -1;
    }

    std::vector<Tensor> imageTensors;
    auto importStatus = imageGraph->ProccessImage(byteString, encoding, &imageTensors);

    if (!importStatus.ok()) {
        LOG(ERROR) << "processing image failed: " << importStatus;
        return -1;
    }
    else {
        FILE_LOG(logDEBUG) << "got image";
    }

    auto imageShape = imageTensors[0].shape();

    FILE_LOG(logDEBUG) << "Image dimensions: " << imageShape.dim_size(0) << ", " << imageShape.dim_size(1) << ", " << imageShape.dim_size(2);

    if (imageShape.dim_size(1) == 0){
        return -1;
    }

    Scope localRoot = Scope::NewRootScope();
    ClientSession session(localRoot);

    std::vector<Tensor> resizeOutputs;

    auto imageOutput = ResizeBilinear(localRoot,
                                      imageTensors[0],
                                      {multiboxSize, multiboxSize});

    FILE_LOG(logDEBUG) << "Scaling to box input: " << imageTensors[0].DebugString();

    auto scaleStatus = session.Run({imageOutput}, &resizeOutputs);

    imageTensors.push_back(resizeOutputs[0]);

    return multibox -> ReadAndRun(&imageTensors, json, &boxSession);
}

MultiClassify::~MultiClassify(){
    LOG(INFO) << "destructing";
    auto closedBoxSession = boxSession->Close();
    auto closedClassifySession = classifySession->Close();
    auto closedFaceSession = faceSession->Close();
    auto closedSsdSession = ssdSession->Close();

    FILE_LOG(logDEBUG) << "Closed sessions: " << closedBoxSession << ", " << closedClassifySession << ", " << closedFaceSession << ", " << closedSsdSession;
}
<|endoftext|>