text:
#include "o3d3xx.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "gtest/gtest.h"

class FrameGrabberTest : public ::testing::Test
{
protected:
  virtual void SetUp()
  {
    cam_ = std::make_shared<o3d3xx::Camera>();
    cam_->RequestSession();
    cam_->SetOperatingMode(o3d3xx::Camera::operating_mode::EDIT);

    dev_ = cam_->GetDeviceConfig();
    old_active_idx_ = dev_->ActiveApplication();

    idx_ = cam_->CopyApplication(old_active_idx_);
    dev_->SetActiveApplication(idx_);
    cam_->SetDeviceConfig(dev_.get());
    cam_->SaveDevice();
    cam_->CancelSession();
  }

  virtual void TearDown()
  {
    cam_->RequestSession();
    cam_->SetOperatingMode(o3d3xx::Camera::operating_mode::EDIT);
    dev_->SetActiveApplication(old_active_idx_);
    cam_->SetDeviceConfig(dev_.get());
    cam_->SaveDevice();
    cam_->DeleteApplication(idx_);
  }

  o3d3xx::Camera::Ptr cam_;
  o3d3xx::DeviceConfig::Ptr dev_;
  int idx_ = -1;
  int old_active_idx_ = -1;
};

TEST_F(FrameGrabberTest, WaitForFrame)
{
  o3d3xx::FrameGrabber::Ptr fg = std::make_shared<o3d3xx::FrameGrabber>(cam_);
  o3d3xx::ImageBuffer::Ptr img = std::make_shared<o3d3xx::ImageBuffer>();

  int i = 0;
  while (i < 10)
    {
      EXPECT_TRUE(fg->WaitForFrame(img.get(), 1000));
      i++;
    }

  EXPECT_EQ(i, 10);
  fg->Stop();
  EXPECT_FALSE(fg->WaitForFrame(img.get(), 500));
}

TEST_F(FrameGrabberTest, WaitForFrameFast)
{
  o3d3xx::FrameGrabber::Ptr fg = std::make_shared<o3d3xx::FrameGrabber>(cam_);
  o3d3xx::ImageBuffer::Ptr img = std::make_shared<o3d3xx::ImageBuffer>();

  int i = 0;
  while (i < 10)
    {
      EXPECT_TRUE(fg->WaitForFrame(img.get(), 1000, false, false));
      i++;
    }

  EXPECT_EQ(i, 10);
  fg->Stop();
  EXPECT_FALSE(fg->WaitForFrame(img.get(), 500));
}

TEST_F(FrameGrabberTest, ResultSchema)
{
  //------------------------------------------
  // Set a non-default schema on to the camera
  //------------------------------------------

  std::string schema =
  "{ \"layouter\": \"flexible\", \"format\": { \"dataencoding\": \"ascii\" }, \
  \"elements\": [ { \"type\": \"string\", \"value\": \"star\", \"id\": \
  \"start_string\" }, { \"type\": \"blob\", \"id\": \
  \"normalized_amplitude_image\" }, { \
  \"type\": \"blob\", \"id\": \"diagnostic_data\" }, { \"type\": \"string\", \
  \"value\": \"stop\", \"id\": \"end_string\" } ] }";

  cam_->RequestSession();
  cam_->SetOperatingMode(o3d3xx::Camera::operating_mode::EDIT);
  cam_->EditApplication(idx_);
  o3d3xx::AppConfig::Ptr app = cam_->GetAppConfig();
  app->SetPcicTcpResultSchema(schema);
  cam_->SetAppConfig(app.get());
  cam_->SaveApp();
  cam_->StopEditingApplication();
  cam_->CancelSession();

  //------------------------------------------
  // Run the FrameGrabber and make sure we are
  // getting image data
  //------------------------------------------
  o3d3xx::FrameGrabber::Ptr fg = std::make_shared<o3d3xx::FrameGrabber>(cam_);
  o3d3xx::ImageBuffer::Ptr img = std::make_shared<o3d3xx::ImageBuffer>();

  pcl::PointCloud<o3d3xx::PointT>::Ptr cloud;
  cv::Mat dimg;
  cv::Mat aimg;
  cv::Mat cimg;

  EXPECT_TRUE(fg->WaitForFrame(img.get(), 1000));
  cloud = img->Cloud();
  dimg = img->DepthImage();
  aimg = img->AmplitudeImage();
  cimg = img->ConfidenceImage();

  int width = cloud->width;
  int height = cloud->height;

  ASSERT_GT(cloud->width, 0);
  ASSERT_GT(cloud->height, 0);

  ASSERT_EQ(dimg.size().width, width);
  ASSERT_EQ(dimg.size().height, height);

  ASSERT_EQ(aimg.size().width, width);
  ASSERT_EQ(aimg.size().height, height);

  ASSERT_EQ(cimg.size().width, width);
  ASSERT_EQ(cimg.size().height, height);

  //------------------------------------------
  // Stop the FrameGrabber and inspect that the
  // active application's schema is the one
  // we set on the camera.
  //------------------------------------------
  fg->Stop();
  ASSERT_FALSE(fg->WaitForFrame(img.get(), 500));

  cam_->RequestSession();
  cam_->SetOperatingMode(o3d3xx::Camera::operating_mode::EDIT);
  cam_->EditApplication(idx_);
  app = cam_->GetAppConfig();
  schema = app->PcicTcpResultSchema();
  cam_->StopEditingApplication();
  cam_->CancelSession();

  boost::property_tree::ptree pt;
  std::istringstream is(schema);
  is.seekg(0, is.beg);
  boost::property_tree::read_json(is, pt);
  boost::property_tree::ptree elements_pt = pt.get_child("elements");
  int elem_idx = 1;
  for (auto& kv : elements_pt)
    {
      boost::property_tree::ptree elem_pt = kv.second;
      std::string elem_name = elem_pt.get<std::string>("id");

    switch (elem_idx)
      {
      case 1:
        ASSERT_EQ(elem_name, "start_string");
        break;

      case 2:
        ASSERT_EQ(elem_name, "normalized_amplitude_image");
        break;

      case 3:
        ASSERT_EQ(elem_name, "diagnostic_data");
        break;

      case 4:
        ASSERT_EQ(elem_name, "end_string");
        break;

      default:
        // force an exception
        ASSERT_EQ(1, 0);
        break;
      }

      elem_idx++;
    }
}


text:
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/vinniefalco/json
//

#ifndef BOOST_JSON_PARSER_HPP
#define BOOST_JSON_PARSER_HPP

#include <boost/json/config.hpp>
#include <boost/json/basic_parser.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/value.hpp>
#include <boost/json/string.hpp>
#include <boost/json/detail/except.hpp>
#include <boost/json/detail/raw_stack.hpp>
#include <new>
#include <string>
#include <type_traits>
#include <stddef.h>

namespace boost {
namespace json {

//----------------------------------------------------------

/** A DOM parser for serialized JSON.

    This parser is used to incrementally parse
    JSON from character buffers into a @ref value
    container.

    @par Usage

    Before parsing a new JSON, the function @ref start
    must be called, optionally passing the storage
    pointer to be used by the @ref value container into
    which the parsed results are stored. After the
    parse is started, the functions @ref write_some,
    @ref write, and @ref finish may be called to
    provide successive buffers of characters of the
    JSON. The caller can check that the parse is
    complete by calling @ref is_done, or that a
    non-successful error code is returned.

    @par Incremental Parsing

    The @ref write_some function allows partial data
    to be written. This is useful when not all of the
    serialized JSON is present at once and it is
    desired to process the data as it becomes available,
    such as when reading from a network socket or other
    device. The incremental interface may also be used
    to bound the amount of work performed in each
    parsing cycle.

    <br>

    The parser may dynamically allocate intermediate
    storage as needed to accommodate the nesting level
    of the JSON being parsed. This storage is freed
    when the parser is destroyed, allowing the parser
    to cheaply re-use this memory when parsing
    subsequent JSONs, improving performance.
*/
class parser final
    : public basic_parser
{
    enum class state : char;

    struct level
    {
        std::uint32_t count;
        saved_state ss;
        char align;
        state st;
    };

    storage_ptr sp_;
    detail::raw_stack rs_;
    std::uint32_t key_size_ = 0;
    std::uint32_t str_size_ = 0;
    level lev_;

    inline
    void
    destroy() noexcept;

public:
    /** Destructor.

        All dynamically allocated memory, including
        any partial parsing results, is freed.
    */
    BOOST_JSON_DECL
    virtual
    ~parser();

    /** Default constructor.

        Before any JSON can be parsed, the function
        @ref start must be called.
    */
    BOOST_JSON_DECL
    parser();

    /** Start parsing JSON incrementally.

        This function must be called once manually before
        parsing a new JSON incrementally; that is, when
        using @ref write_some, @ref write, or @ref finish.

        @param sp A pointer to the @ref storage
        to use. The parser will acquire shared
        ownership of the storage object.
    */
    BOOST_JSON_DECL
    void
    start(storage_ptr sp = {}) noexcept;

    /** Discard all parsed JSON results.

        This function destroys all intermediate parsing
        results, while preserving dynamically allocated
        internal memory which may be reused on a
        subsequent parse.

        @note

        After this call, it is necessary to call
        @ref start to parse a new JSON incrementally.
    */
    BOOST_JSON_DECL
    void
    clear() noexcept;

    /** Return the parsed JSON as a @ref value.

        If @ref is_done() returns `true`, then the
        parsed value is returned. Otherwise an
        exception is thrown.

        @throw std::logic_error `! is_done()`

        @return The parsed value. Ownership of this
        value is transferred to the caller.
    */
    BOOST_JSON_NODISCARD
    BOOST_JSON_DECL
    value
    release();

private:
    template<class T>
    void
    push(T const& t);

    inline
    void
    push_chars(string_view s);

    template<class... Args>
    void
    emplace(Args&&... args);

    template<class T>
    void
    pop(T& t);

    inline
    detail::unchecked_object
    pop_object() noexcept;

    inline
    detail::unchecked_array
    pop_array() noexcept;

    inline
    string_view
    pop_chars(
        std::size_t size) noexcept;

    BOOST_JSON_DECL
    void
    on_document_begin(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_document_end(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_object_begin(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_object_end(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_array_begin(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_array_end(
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_key_part(
        string_view s,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_key(
        string_view s,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_string_part(
        string_view s,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_string(
        string_view s,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_int64(
        int64_t i,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_uint64(
        uint64_t u,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_double(
        double d,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_bool(
        bool b,
        error_code& ec) override;

    BOOST_JSON_DECL
    void
    on_null(error_code&) override;
};

//----------------------------------------------------------

/** Parse a string of JSON.

    This function parses an entire single string in
    one step to produce a complete JSON object, returned
    as a @ref value. If the buffer does not contain a
    complete serialized JSON, an error occurs. In this
    case the returned value will be null, using the
    default storage.

    @par Complexity

    Linear in `s.size()`.

    @par Exception Safety

    Strong guarantee.
    Calls to @ref storage::allocate may throw.

    @param s The string to parse.

    @param ec Set to the error, if any occurred.

    @param sp The storage that the new value and all of
    its elements will use. If this parameter is omitted,
    the default storage is used.

    @return A value representing the parsed JSON,
    or a null if any error occurred.
*/
BOOST_JSON_DECL
value
parse(
    string_view s,
    error_code& ec,
    storage_ptr sp = {});

/** Parse a string of JSON.

    This function parses an entire single string in
    one step to produce a complete JSON object, returned
    as a @ref value. If the buffer does not contain a
    complete serialized JSON, an error occurs. In this
    case the returned value will be null, using the
    default storage.

    @par Complexity

    Linear in `s.size()`.

    @par Exception Safety

    Strong guarantee.
    Calls to @ref storage::allocate may throw.

    @param s The string to parse.

    @param sp The storage that the new value and all of
    its elements will use. If this parameter is omitted,
    the default storage is used.

    @throw system_error Thrown on failure.

    @return A value representing the parsed JSON,
    or a null if any error occurred.
*/
BOOST_JSON_DECL
value
parse(
    string_view s,
    storage_ptr sp = {});

} // json
} // boost

#ifdef BOOST_JSON_HEADER_ONLY
#include <boost/json/impl/parser.ipp>
#endif

#endif

ext: hpp

max_stars_repo_name: Vicfred/json

max_issues_repo_name: Vicfred/json

max_forks_repo_name: Vicfred/json


text:
//          Copyright Camille Gillot 2012 - 2015.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef RTTI_HOLDER_HPP
#define RTTI_HOLDER_HPP

#include "mmethod/config.hpp"
#include "mmethod/rttifwd.hpp"
#include "mmethod/rtti/holder/node.hpp"

#include <boost/type_traits/add_cv.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/type_traits/is_volatile.hpp>

namespace rtti {
namespace detail {
namespace holder_ {

//! \brief Structure holding class-wide rtti information
//! \note Arguments must be const-qualified to avoid unnecessary instanciations
template<class T>
struct holder {
private:
  BOOST_STATIC_ASSERT_MSG(
     boost::is_class<T>::value
  && boost::is_const<T>::value
  && boost::is_volatile<T>::value
  , "rtti::detail::holder_::holder<> must not be accessed directly"
  );

  typedef rtti_getter::traits<T> traits;

  BOOST_STATIC_CONSTANT(std::size_t, Arity = traits::parents_size);

  struct initializer_t {
    struct register_one;
    initializer_t();
    void touch() const {}
  };
  static initializer_t initializer;

public:
  static rtti_node_var<Arity> node;

  static inline
  const rtti_node*
  MMETHOD_ATTRIBUTE_PURE
  get_node() BOOST_NOEXCEPT_OR_NOTHROW
  { initializer.touch(); return reinterpret_cast<rtti_node*>(&node); }

  static inline
  rtti_type
  MMETHOD_ATTRIBUTE_PURE
  get_id() BOOST_NOEXCEPT_OR_NOTHROW
  { return rtti_get_id( get_node() ); }
};

//! \brief Grant access to the holder
template<typename T>
struct get_holder {
  typedef holder<typename boost::add_cv<T>::type> type;
};

//! \brief Placeholder struct used for hierarchy base
template<>
struct get_holder<const volatile void> {
  struct type {
    static BOOST_CONSTEXPR rtti_node* get_node() { return NULL; }
  };
};

template<class T>
struct holder<T>::initializer_t::register_one {
  mutable std::size_t k;

  template<typename P>
  void operator()(P* /*unused*/) const {
    typedef typename get_holder<P>::type U;
    holder::node.__base[k] = U::get_node();
    ++k;
  }
};

template<class T>
holder<T>::initializer_t::initializer_t() {
  holder::node.__arity = Arity;

  register_one reg = { 0 };
  traits::parents_foreach(reg);
}

template<class T>
typename holder<T>::initializer_t
  holder<T>::initializer;

template<class T>
rtti_node_var<holder<T>::Arity>
  holder<T>::node;

} // namespace holder_

using holder_::get_holder;

} // namespace detail
} // namespace rtti

#endif

max_stars_repo_name: cjgillot/rtti

max_issues_repo_name: cjgillot/rtti

max_forks_repo_name: cjgillot/rtti


text:
/*
	Implementation of the Asynchronous session
*/

#include <boost/thread.hpp>				/* @TODO: Replace <boost/thread.hpp> with <thread> from C++0x */
#include <boost/chrono.hpp>				/* @TODO: Replace <boost/chrono.hpp> with <chrono> from C++0x */

//--------------------------------------------------------------------------------

#include "async_session.h"

#include "../../workload/symphony/message.h"
#include "../../workload/common_data.h"

//--------------------------------------------------------------------------------

#include "../../common/format/prefix_stream.h"

//--------------------------------------------------------------------------------

using namespace asws::common;
using namespace asws::client::symphony;

//-------------------------------------------------------------------------------

void async_session::soam_callback::onException(soam::SoamException& exp) throw()
{
	format::scoped_prefix sp("[async_session::callback]");
	format::prefix_stream pserr(std::cerr);

	pserr << "Session Exception:" << exp.what() << std::endl;
	// TODO: Add extra handling to shut down book-keeping
}

//-------------------------------------------------------------------------------

void async_session::soam_callback::onResponse(soam::TaskOutputHandlePtr& task_handle) throw()
{
	std::stringstream prefix;
	prefix << "[async_session::callback][task:" << task_handle->getId() << "][output]";
	format::scoped_prefix sp(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	// Register with bookkeeper
	_bookkeeper.reg_retrieval(task_handle);

	// Deserialize
	if (task_handle->isSuccessful()) {
		workload::iworkload_ptr payload = nullptr;

		try {
			using namespace asws::workload::symphony;
			message<message_type::output> msg;

			task_handle->populateTaskOutput(&msg);

			payload = msg.payload();
			psout << payload->report() << std::endl;
		}
		catch (soam::SoamException& e) {
			pserr << "Error deserializing the payload : " << e.what() << std::endl;
		}
	}
}

//-------------------------------------------------------------------------------

void async_session::open(session_attributes& attr)
{
	format::scoped_prefix sp("[async_session::open]");
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	// Start the book-keeping
	_bookkeeper.start();

	// @TODO: Add options for connection handling - needs Windows build
	psout << "Creating new session ASYNC : { name : " << attr.name << ", type : " << attr.type
		<< "common-data : " << attr.common_data_size << " bytes }" << std::endl;

	// Extract flags
	bool async_send = false;

	if (session_flags::is_set(attr.flags, session_flags::send_overlapped))
		async_send = true;
	else
		async_send = false;

	psout << "async send mode : " << async_send << std::endl;

	// Setup the attributes
	soam::SessionCreationAttributes session_attr;
	session_attr.setSessionName(attr.name);
	session_attr.setSessionType(attr.type);
	session_attr.setSessionCallback(&_callback);

	int sym_flags = soam::Session::ReceiveAsync;

	if (async_send)
		sym_flags |= soam::Session::SendOverlapped;
	else
		sym_flags |= soam::Session::SendSync;

	session_attr.setSessionFlags(sym_flags);

	// Add common data
	if (attr.common_data_size != 0) {
		using namespace asws::workload::symphony;

		psout << "Attaching common data" << std::endl;

		workload::iworkload_ptr cdata(new workload::common_data(attr.common_data_size));
		message<message_type::input> msg(cdata);
		session_attr.setCommonData(&msg);
	}

	// Set flags
	set_flags(attr.flags);

	// ---------------------------------------------------
	// Sym connection patch code
	// ---------------------------------------------------

	while (true) {
		soam::ConnectionPtr conn = _client.get_soam_connection();

		// Wait until connection is established
		while (conn->getState() == soam::Connection::CONNECTING || conn->getState() == soam::Connection::RECONNECTING) {
			psout << "SOAM connection being (re-)established" << std::endl;
			boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
		}

		// Terminal states
		if (conn->getState() == soam::Connection::CLOSED) {
			_state = interface_isession::closed;
			throw std::runtime_error("SOAM connection terminated");
		}

		if (conn->getState() == soam::Connection::INVALID) {
			_state = interface_isession::invalid;
			throw std::runtime_error("SOAM connection invalid");
		}

		// End state
		if (conn->getState() == soam::Connection::ESTABLISHED) {
			try {
				_session = conn->createSession(session_attr);
				// Finish
				break;
			} catch (soam::SoamException& e) {
				// Connection problem - retry the whole loop
				if (e.getErrorCode() == soam::SoamException::CONNECTION_ERROR)
					pserr << "Connection problem while establishing a session : " << e.what() << std::endl;
				else {
					_state = interface_isession::invalid;
					throw std::runtime_error(e.what());
				}
			}
		}
	}

	// Identifier
	std::stringstream session_id;
	session_id << attr.name << "-" << _session->getId();
	_name = session_id.str();

	psout << "Succesfully created the session, session-id : " << _name << std::endl;

	// Mark as established
	_state = interface_isession::established;
}

//-------------------------------------------------------------------------------

void async_session::close(bool wait_for_results)
{
	std::stringstream prefix;
	prefix << "[session-" << _name << "::close]";
	format::scoped_prefix(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	if (wait_for_results) {
		psout << "Waiting for results of tasks" << std::endl;
		wait();
	}

	// Close the session
	psout << "Closing the session" << std::endl;

	// Clear
	_name = "";

	// ---------------------------------------------------
	// Sym connection patch code
	// ---------------------------------------------------

	while (true) {
		soam::ConnectionPtr conn = _client.get_soam_connection();

		// Wait until connection is established
		while (conn->getState() == soam::Connection::CONNECTING || conn->getState() == soam::Connection::RECONNECTING) {
			psout << "SOAM connection being (re-)established" << std::endl;
			boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
		}

		// Terminal states
		if (conn->getState() == soam::Connection::CLOSED) {
			_state = interface_isession::closed;
			throw std::runtime_error("SOAM connection closed, operating on invalid connection");
		}

		if (conn->getState() == soam::Connection::INVALID) {
			_state = interface_isession::invalid;
			psout << "SOAM connection invalid, will do a client side close() anyway" << std::endl;
			_session = nullptr;
			return;
		}

		// End state
		if (conn->getState() == soam::Connection::ESTABLISHED) {
			try {
				_session->close();
				_session = nullptr;
				_state = interface_isession::closed;
				break;
			} catch (soam::SoamException& e) {
				// Try again
				if (e.getErrorCode() == soam::SoamException::CONNECTION_ERROR)
					pserr << "Connection problem while closing the session : " << e.what() << std::endl;
				else {
					pserr << "Unknown error code : " << e.what() << std::endl;
					_state = interface_isession::invalid;
					throw std::runtime_error(e.what());
				}
			}
		}
	}

	psout << "Succesfully closed the session" << std::endl;
}

//-------------------------------------------------------------------------------

void async_session::submit_segment_start()
{
	std::stringstream prefix;
	prefix << "[session-" << _name << "::submit]";
	format::scoped_prefix sp(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	// Submission segment starts now
	psout << "Starting workload submission" << std::endl;

	// Start the monitoring thread anyway
	psout << "Starting the workload monitoring/bookkeeping thread" << std::endl;
	_bookkeeper.async_submission_start(_name);
}

//-------------------------------------------------------------------------------

void async_session::submit_segment_end()
{
	std::stringstream prefix;
	prefix << "[session-" << _name << "::submit]";
	format::scoped_prefix sp(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	// Submission segment ends now
	psout << "Finished workload submission" << std::endl;

	// Wait for monitoring thread to finish all workload
	psout << "Waiting for monitoring thread to confirm submissions" << std::endl;
	_bookkeeper.async_submission_end();
}

//-------------------------------------------------------------------------------

void async_session::submit(asws::workload::iworkload_ptr workload)
{
	std::stringstream prefix;
	prefix << "[session-" << _name << "::submit]";
	format::scoped_prefix ns(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	// Create the message
	using namespace asws::workload::symphony;

	// Pass the ownership down
	message<message_type::input> msg(workload);

	try {
		// Throttle down
		_bookkeeper.async_throttle_submission(500);

		soam::TaskSubmissionAttributes task_attr;
		task_attr.setTaskInput(&msg);

		// Send the task
		soam::TaskInputHandlePtr task_handle = _session->sendTaskInput(task_attr);

		// Record
		_bookkeeper.reg_submission(task_handle);
	} catch(soam::SoamException& e) {
		pserr << "Task submission failed : " << e.what() << std::endl;
		throw std::runtime_error(e.what());
	}
}

//-------------------------------------------------------------------------------

void async_session::wait()
{
	std::stringstream prefix;
	prefix << "[session-" << _name << "::wait]";
	format::scoped_prefix ns(prefix.str());
	format::prefix_stream psout(std::cout);
	format::prefix_stream pserr(std::cerr);

	psout << "Waiting for submission to finish" << std::endl;
	_bookkeeper.async_submission_wait();

	// Get the stats
	std::stringstream stats;
	stats << "[stats] Collected statistics\n";
	stats << "[stats]-----------------------------------------------------\n";
	stats << "[stats] tasks-submitted          : " << _bookkeeper.get_tasks_submitted() << "\n";
	stats << "[stats] submission-succeeded     : " << _bookkeeper.get_tasks_confirmed() << "\n";
	stats << "[stats] submission-failure       : " << _bookkeeper.get_tasks_failed() << "\n";
	stats << "[stats] submission-time [s]      : " << _bookkeeper.get_submission_time() << "\n";
	stats << "[stats] submission-rate [task/s] : " << _bookkeeper.get_tasks_confirmed() / _bookkeeper.get_submission_time() << "\n";
	stats << "[stats]-----------------------------------------------------\n";
	stats << std::flush;

	psout << stats.str() << std::flush;

	// Wait for all the messages
	psout << "Waiting for retrieval to finish" << std::endl;
	_bookkeeper.async_retrieval_wait();

	// Get the stats
	stats << "[stats]-----------------------------------------------------\n";
	stats << "[stats] tasks-retrieved          : " << _bookkeeper.get_tasks_retrieved() << "\n";
	stats << "[stats] execution-time [s]       : " << _bookkeeper.get_execution_time() << "\n";
	stats << "[stats]-----------------------------------------------------\n";
	stats << std::flush;
	psout << stats.str() << std::flush;
}

//-------------------------------------------------------------------------------

ext: cpp


text:
#include <boost/locale.hpp>
#include <sstream>
#include <spatial/common/Key.h>
#include <spatial/desktop/PlatformEvent.h>
#include <spatial/desktop/Window.h>

#if defined(SPATIAL_PLATFORM_OSX)
#define GLFW_EXPOSE_NATIVE_COCOA
#elif defined(SPATIAL_PLATFORM_WINDOWS)
#define GLFW_EXPOSE_NATIVE_WIN32
#elif defined(SPATIAL_PLATFORM_UNIX)
#define GLFW_EXPOSE_NATIVE_X11
#endif
#include <GLFW/glfw3native.h>

namespace spatial::desktop
{

bool PlatformContext::sValid{false};
EventQueue PlatformContext::sEventQueue{};

PlatformContext::PlatformContext()
{
	if (!sValid)
		sValid = glfwInit() == GLFW_TRUE;

	glfwWindowHint(GLFW_SAMPLES, GLFW_DONT_CARE);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}

PlatformContext::~PlatformContext()
{
	if (sValid)
		glfwTerminate();
}

void PlatformContext::setupCallbacks(GLFWwindow* window)
{
	if (!sValid)
		return;

	glfwSetWindowCloseCallback(window, [](auto* win) { sEventQueue.enqueue<WindowClosedEvent>(); });

	glfwSetCharCallback(window, [](auto* win, unsigned int codepoint) {
		auto ss = std::wstringstream{};
	  	ss << static_cast<wchar_t>(codepoint);

		sEventQueue.enqueue<TextEvent>(boost::locale::conv::utf_to_utf<char>(ss.str()));
	});

	glfwSetKeyCallback(window, [](auto* win, int key, int scancode, int action, int mods) {
		const auto spatialKey = mapKeyFromScancode(key);
		const auto spatialAction = mapActionFromCode(action);
		sEventQueue.enqueue<KeyEvent>(spatialKey, spatialAction);
	});

	glfwSetMouseButtonCallback(window, [](auto* win, int button, int action, int mods) {
		const auto spatialKey = mapKeyFromMouseButton(button);
		const auto spatialAction = mapActionFromCode(action);
		sEventQueue.enqueue<KeyEvent>(spatialKey, spatialAction);
	});

	glfwSetScrollCallback(window, [](auto* win, double xOffset, double yOffset) {
		sEventQueue.enqueue<MouseScrolledEvent>(xOffset, yOffset);
	});

	glfwSetScrollCallback(window, [](auto* win, double xOffset, double yOffset) {
		sEventQueue.enqueue<MouseScrolledEvent>(xOffset, yOffset);
	});

	glfwSetWindowSizeCallback(window, [](auto* win, int width, int height) {
		int fbw, fbh;
		glfwGetFramebufferSize(win, &fbw, &fbh);
		sEventQueue.enqueue<WindowResizedEvent>(math::int2{width, height}, math::int2{fbw, fbh});
	});

	glfwSetCursorPosCallback(window, [](auto* win, double xPos, double yPos) {
	  sEventQueue.enqueue<MouseMovedEvent>(xPos, yPos);
	});
}

void PlatformContext::onStartFrame(float)
{
	if (!sValid)
		return;

	glfwPollEvents();

	sEventQueue.update<WindowResizedEvent>();
	sEventQueue.update<MouseMovedEvent>();
	sEventQueue.update();
}

Window PlatformContext::createWindow(std::uint16_t width, std::uint16_t height,
											std::string_view title) const noexcept
{
	auto window = glfwCreateWindow(width, height, title.data(), nullptr, nullptr);
	setupCallbacks(window);
	return Window{window};
}

Key mapKeyFromScancode(const int scanCode) noexcept
{
	switch (scanCode)
	{
	case GLFW_KEY_LEFT_SHIFT:
		return Key::LShift;
	case GLFW_KEY_RIGHT_SHIFT:
		return Key::RShift;
	case GLFW_KEY_LEFT_CONTROL:
		return Key::LControl;
	case GLFW_KEY_RIGHT_CONTROL:
		return Key::RControl;
	case GLFW_KEY_LEFT_ALT:
		return Key::LAlt;
	case GLFW_KEY_RIGHT_ALT:
		return Key::RAlt;
	case GLFW_KEY_RIGHT_SUPER:
	case GLFW_KEY_LEFT_SUPER:
		return Key::System;
	case GLFW_KEY_MENU:
		return Key::Menu;
	case GLFW_KEY_ESCAPE:
		return Key::Escape;
	case GLFW_KEY_SEMICOLON:
		return Key::Semicolon;
	case GLFW_KEY_SLASH:
		return Key::Slash;
	case GLFW_KEY_EQUAL:
		return Key::Equal;
	case GLFW_KEY_MINUS:
		return Key::Hyphen;
	case GLFW_KEY_LEFT_BRACKET:
		return Key::LBracket;
	case GLFW_KEY_RIGHT_BRACKET:
		return Key::RBracket;
	case GLFW_KEY_COMMA:
		return Key::Comma;
	case GLFW_KEY_PERIOD:
		return Key::Period;
	case GLFW_KEY_APOSTROPHE:
		return Key::Quote;
	case GLFW_KEY_BACKSLASH:
		return Key::Backslash;
	case GLFW_KEY_GRAVE_ACCENT:
		return Key::Tilde;
	case GLFW_KEY_SPACE:
		return Key::Space;
	case GLFW_KEY_ENTER:
		return Key::Enter;
	case GLFW_KEY_BACKSPACE:
		return Key::Backspace;
	case GLFW_KEY_TAB:
		return Key::Tab;
	case GLFW_KEY_PAGE_UP:
		return Key::PageUp;
	case GLFW_KEY_PAGE_DOWN:
		return Key::PageDown;
	case GLFW_KEY_END:
		return Key::End;
	case GLFW_KEY_HOME:
		return Key::Home;
	case GLFW_KEY_INSERT:
		return Key::Insert;
	case GLFW_KEY_DELETE:
		return Key::Delete;
	case GLFW_KEY_KP_ADD:
		return Key::Add;
	case GLFW_KEY_KP_SUBTRACT:
		return Key::Subtract;
	case GLFW_KEY_KP_MULTIPLY:
		return Key::Multiply;
	case GLFW_KEY_KP_DIVIDE:
		return Key::Divide;
	case GLFW_KEY_PAUSE:
		return Key::Pause;
	case GLFW_KEY_F1:
		return Key::F1;
	case GLFW_KEY_F2:
		return Key::F2;
	case GLFW_KEY_F3:
		return Key::F3;
	case GLFW_KEY_F4:
		return Key::F4;
	case GLFW_KEY_F5:
		return Key::F5;
	case GLFW_KEY_F6:
		return Key::F6;
	case GLFW_KEY_F7:
		return Key::F7;
	case GLFW_KEY_F8:
		return Key::F8;
	case GLFW_KEY_F9:
		return Key::F9;
	case GLFW_KEY_F10:
		return Key::F10;
	case GLFW_KEY_F11:
		return Key::F11;
	case GLFW_KEY_F12:
		return Key::F12;
	case GLFW_KEY_F13:
		return Key::F13;
	case GLFW_KEY_F14:
		return Key::F14;
	case GLFW_KEY_F15:
		return Key::F15;
	case GLFW_KEY_LEFT:
		return Key::Left;
	case GLFW_KEY_RIGHT:
		return Key::Right;
	case GLFW_KEY_UP:
		return Key::Up;
	case GLFW_KEY_DOWN:
		return Key::Down;
	case GLFW_KEY_KP_0:
		return Key::Numpad0;
	case GLFW_KEY_KP_1:
		return Key::Numpad1;
	case GLFW_KEY_KP_2:
		return Key::Numpad2;
	case GLFW_KEY_KP_3:
		return Key::Numpad3;
	case GLFW_KEY_KP_4:
		return Key::Numpad4;
	case GLFW_KEY_KP_5:
		return Key::Numpad5;
	case GLFW_KEY_KP_6:
		return Key::Numpad6;
	case GLFW_KEY_KP_7:
		return Key::Numpad7;
	case GLFW_KEY_KP_8:
		return Key::Numpad8;
	case GLFW_KEY_KP_9:
		return Key::Numpad9;
	case GLFW_KEY_A:
		return Key::A;
	case GLFW_KEY_B:
		return Key::B;
	case GLFW_KEY_C:
		return Key::C;
	case GLFW_KEY_D:
		return Key::D;
	case GLFW_KEY_E:
		return Key::E;
	case GLFW_KEY_F:
		return Key::F;
	case GLFW_KEY_G:
		return Key::G;
	case GLFW_KEY_H:
		return Key::H;
	case GLFW_KEY_I:
		return Key::I;
	case GLFW_KEY_J:
		return Key::J;
	case GLFW_KEY_K:
		return Key::K;
	case GLFW_KEY_L:
		return Key::L;
	case GLFW_KEY_M:
		return Key::M;
	case GLFW_KEY_N:
		return Key::N;
	case GLFW_KEY_O:
		return Key::O;
	case GLFW_KEY_P:
		return Key::P;
	case GLFW_KEY_Q:
		return Key::Q;
	case GLFW_KEY_R:
		return Key::R;
	case GLFW_KEY_S:
		return Key::S;
	case GLFW_KEY_T:
		return Key::T;
	case GLFW_KEY_U:
		return Key::U;
	case GLFW_KEY_V:
		return Key::V;
	case GLFW_KEY_W:
		return Key::W;
	case GLFW_KEY_X:
		return Key::X;
	case GLFW_KEY_Y:
		return Key::Y;
	case GLFW_KEY_Z:
		return Key::Z;
	case GLFW_KEY_0:
		return Key::Num0;
	case GLFW_KEY_1:
		return Key::Num1;
	case GLFW_KEY_2:
		return Key::Num2;
	case GLFW_KEY_3:
		return Key::Num3;
	case GLFW_KEY_4:
		return Key::Num4;
	case GLFW_KEY_5:
		return Key::Num5;
	case GLFW_KEY_6:
		return Key::Num6;
	case GLFW_KEY_7:
		return Key::Num7;
	case GLFW_KEY_8:
		return Key::Num8;
	case GLFW_KEY_9:
		return Key::Num9;
	default:
		return Key::UnknownKey;
	}
}

Key mapKeyFromMouseButton(int mouseButton) noexcept
{
	switch (mouseButton)
	{
	case GLFW_MOUSE_BUTTON_LEFT:
		return Key::MouseLeft;
	case GLFW_MOUSE_BUTTON_RIGHT:
		return Key::MouseRight;
	case GLFW_MOUSE_BUTTON_MIDDLE:
		return Key::MouseMiddle;
	default:
		return Key::UnknownKey;
	}
}

KeyAction mapActionFromCode(int action) noexcept
{
	switch (action)
	{
	case GLFW_PRESS:
		return KeyAction::Pressed;
	case GLFW_RELEASE:
		return KeyAction::Released;
	default:
		return KeyAction::UnknownAction;
	}
}

} // namespace spatial


text:
// ==========================================================================
// Odin Net Socket
//
// Copyright (C) 2009 Matthew Chaplain, All Rights Reserved.
//
// Permission to reproduce, distribute, perform, display, and to prepare
// derivitive works from this file under the following conditions:
//
// 1. Any copy, reproduction or derivitive work of any part of this file
//    contains this copyright notice and licence in its entirety.
//
// 2. The rights granted to you under this license automatically terminate
//    should you attempt to assert any patent claims against the licensor
//    or contributors, which in any way restrict the ability of any party
//    from using this software or portions thereof in any form under the
//    terms of this license.
//
// Disclaimer: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
//             KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//             PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//             OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//             OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
//             OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//             SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ==========================================================================
#include "odin/net/socket.hpp"
#include <boost/asio.hpp>
#include <deque>

namespace odin { namespace net {

// ==========================================================================
// SOCKET::IMPLEMENTATION STRUCTURE
// ==========================================================================
struct socket::impl : std::enable_shared_from_this<impl>
{
    // ======================================================================
    // CONSTRUCTOR
    // ======================================================================
    impl(std::shared_ptr<boost::asio::ip::tcp::socket> const &socket)
        : socket_(socket)
    {
    }

    // ======================================================================
    // CLOSE
    // ======================================================================
    void close()
    {
        boost::system::error_code unused_error_code;
        socket_->close(unused_error_code);

        // Now we need to inform all readers and writers that the socket has
        // died by informing them that no bytes have been read or written.

        while (!read_requests_.empty())
        {
            read_request &top_read_request = read_requests_[0];

            if (top_read_request.callback_ != NULL)
            {
                top_read_request.callback_(socket::input_storage_type());
            }

            read_requests_.pop_front();
        }

        while (!write_requests_.empty())
        {
            write_request &top_write_request = write_requests_[0];

            if (top_write_request.callback_ != NULL)
            {
                top_write_request.callback_(socket::input_size_type(0));
            }

            write_requests_.pop_front();
        }

        if (on_death_ != NULL)
        {
            on_death_();
        }
    }

    // ======================================================================
    // AVAILABLE
    // ======================================================================
    boost::optional<socket::input_size_type> available() const
    {
        if (!is_alive())
        {
            return {};
        }
        else if (read_requests_.empty())
        {
            boost::asio::socket_base::bytes_readable command(true);
            boost::system::error_code ec;
            socket_->io_control(command, ec);

            if (!ec)
            {
                std::size_t bytes_readable = command.get();

                return bytes_readable == 0
                     ? boost::optional<input_size_type>()
                     : boost::optional<input_size_type>(bytes_readable);
            }
            else
            {
                return boost::optional<input_size_type>(0);
            }
        }
        else
        {
            return {};
        }
    }

    // ======================================================================
    // READ
    // ======================================================================
    socket::input_storage_type read(input_size_type size)
    {
        socket::input_storage_type data(size);

        boost::system::error_code ec;
        socket_->read_some(boost::asio::buffer(&*data.begin(), size), ec);

        /* INPUT DEBUGGING
        for(size_t i = 0; i < data.size(); ++i)
        {
            unsigned char ch[2] = { data[i], 0 };

            printf(" IN [0x%02X] %s\n",
                (int)(unsigned char)ch[0]
              , isprint(char(ch[0])) ? (char*)ch : "(*)");
        }
        //*/

        return data;
    }

    // ======================================================================
    // ASYNC_READ
    // ======================================================================
    void async_read(
        socket::input_size_type            amount_requested,
        socket::input_callback_type const &callback)
    {
        read_requests_.emplace_back(amount_requested, callback);

        if (!read_requests_[0].scheduled_)
        {
            boost::asio::async_read(
                *socket_.get(),
                boost::asio::buffer(
                    &*read_requests_[0].values_.begin(),
                    read_requests_[0].values_.size()),
                [this](
                    boost::system::error_code const &ec,
                    std::size_t bytes_transferred)
                {
                    read_complete(ec, bytes_transferred);
                });

            read_requests_[0].scheduled_ = true;
        }
    }

    // ======================================================================
    // WRITE
    // ======================================================================
    socket::output_size_type write(output_storage_type const &values)
    {
        /* OUTPUT DEBUGGING
        for(size_t i = 0; i < values.size(); ++i)
        {
            char ch[2] = { char(values[i]), 0 };

            printf("OUT [0x%02X] %s\n",
                (int)(unsigned char)ch[0]
              , isprint(ch[0]) ? ch : "(*)");
            fflush(stdout);
        }
        //*/

        boost::system::error_code ec;
        return socket_->write_some(
            boost::asio::buffer(&*values.begin(), values.size()), ec);
    }

    // ======================================================================
    // ASYNC_WRITE
    // ======================================================================
    void async_write(
        output_storage_type const  &values
      , output_callback_type const &callback)
    {
        bool write_now = write_requests_.empty();

        write_request request(values, callback);
        write_requests_.push_back(request);

        if (write_now)
        {
            write_first_request();
        }
    }

    // ======================================================================
    // IS_ALIVE
    // ======================================================================
    bool is_alive() const
    {
        return socket_->is_open();
    }

    // ======================================================================
    // GET_IO_SERVICE
    // ======================================================================
    boost::asio::io_service &get_io_service()
    {
        return socket_->get_io_service();
    }

    // ======================================================================
    // ON_DEATH
    // ======================================================================
    void on_death(std::function<void ()> const &callback)
    {
        on_death_ = callback;
    }

private :
    // ======================================================================
    // READ_REQUEST
    // ======================================================================
    struct read_request
    {
        // ==================================================================
        // CONSTRUCTOR
        // ==================================================================
        read_request(
            socket::input_size_type            amount_requested
          , socket::input_callback_type const &callback)
          : values_(amount_requested)
          , callback_(callback)
          , scheduled_(false)
        {
        }

        socket::input_storage_type  values_;
        socket::input_callback_type callback_;
        bool                        scheduled_;
    };

    // ======================================================================
    // WRITE_REQUEST
    // ======================================================================
    struct write_request
    {
        // ==================================================================
        // CONSTRUCTOR
        // ==================================================================
        write_request(
            socket::output_storage_type const  &values
          , socket::output_callback_type const &callback)
          : values_(values)
          , callback_(callback)
        {
        }

        socket::output_storage_type  values_;
        socket::output_callback_type callback_;
    };

    // ======================================================================
    // WRITE_FIRST_REQUEST
    // ======================================================================
    void write_first_request()
    {
        /* OUTPUT DEBUGGING
        for(size_t i = 0; i < write_requests_.front().values_.size(); ++i)
        {
            unsigned char ch[2] = { write_requests_.front().values_[i], 0 };

            printf("OUT [0x%02X] %s\n",
                (int)(unsigned char)ch[0]
              , isprint(ch[0]) ? (char*)ch : "(*)");
            fflush(stdout);
        }
        //*/

        boost::asio::async_write(
            *socket_.get(),
            boost::asio::buffer(
                &*write_requests_.front().values_.begin(),
                write_requests_.front().values_.size()),
            [this](
                boost::system::error_code const &ec,
                std::size_t bytes_transferred)
            {
                write_complete(ec, bytes_transferred);
            });
    }

    // ======================================================================
    // WRITE_COMPLETE
    // ======================================================================
    void write_complete(
        boost::system::error_code const &error
      , size_t                           bytes_transferred)
    {
        if (!is_alive())
        {
            return;
        }

        if (!error)
        {
            if (write_requests_.front().callback_)
            {
                write_requests_.front().callback_(bytes_transferred);
            }

            write_requests_.pop_front();

            if (!write_requests_.empty())
            {
                write_first_request();
            }
        }
        else
        {
            close();
        }
    }

    // ======================================================================
    // READ_COMPLETE
    // ======================================================================
    void read_complete(
        boost::system::error_code const &error
      , size_t                           bytes_transferred)
    {
        if (!is_alive())
        {
            return;
        }

        if (!error)
        {
            /* INPUT DEBUGGING
            for(size_t i = 0; i < bytes_transferred; ++i)
            {
                unsigned char ch[2] = { read_requests_.front().values_[i], 0 };

                printf(" IN [0x%02X] %s\n",
                    (int)(unsigned char)ch[0]
                  , isprint(char(ch[0])) ? (char*)ch : "(*)");
            }
            //*/

            if (bytes_transferred >= read_requests_.front().values_.size())
            {
                if (read_requests_.front().callback_)
                {
                    read_requests_.front().callback_(
                        read_requests_.front().values_);
                }

                read_requests_.pop_front();

                if (!read_requests_.empty())
                {
                    boost::asio::async_read(
                        *socket_.get(),
                        boost::asio::buffer(
                            &*read_requests_.front().values_.begin(),
                            read_requests_.front().values_.size()),
                      [this](
                          boost::system::error_code const &ec,
                          std::size_t bytes_transferred)
                      {
                          read_complete(ec, bytes_transferred);
                      });

                    read_requests_.front().scheduled_ = true;
                }
            }
        }
        else
        {
            close();
        }
    }

    std::shared_ptr<boost::asio::ip::tcp::socket> socket_;
    std::function<void ()>                        on_death_;

    std::deque<read_request>  read_requests_;
    std::deque<write_request> write_requests_;
};

// ==========================================================================
// CONSTRUCTOR
// ==========================================================================
socket::socket(
    std::shared_ptr<boost::asio::ip::tcp::socket> const &socket)
    : pimpl_(std::make_shared<impl>(socket))
{
}

// ==========================================================================
// DESTRUCTOR
// ==========================================================================
socket::~socket()
{
}

// ==========================================================================
// CLOSE
// ==========================================================================
void socket::close()
{
    pimpl_->close();
}

// ==========================================================================
// AVAILABLE
// ==========================================================================
boost::optional<socket::input_size_type> socket::available() const
{
    return pimpl_->available();
}

// ==========================================================================
// READ
// ==========================================================================
socket::input_storage_type socket::read(input_size_type size)
{
    return pimpl_->read(size);
}

// ==========================================================================
// ASYNC_READ
// ==========================================================================
void socket::async_read(
    input_size_type            size
  , input_callback_type const &callback)
{
    pimpl_->async_read(size, callback);
}

// ==========================================================================
// WRITE
// ==========================================================================
socket::output_size_type socket::write(output_storage_type const &values)
{
    return pimpl_->write(values);
}

// ==========================================================================
// ASYNC_WRITE
// ==========================================================================
void socket::async_write(
    output_storage_type const  &values
  , output_callback_type const &callback)
{
    pimpl_->async_write(values, callback);
}

// ==========================================================================
// IS_ALIVE
// ==========================================================================
bool socket::is_alive() const
{
    return pimpl_->is_alive();
}

// ==========================================================================
// GET_IO_SERVICE
// ==========================================================================
boost::asio::io_service &socket::get_io_service()
{
    return pimpl_->get_io_service();
}

// ==========================================================================
// ON_DEATH
// ==========================================================================
void socket::on_death(std::function<void ()> const &callback)
{
    pimpl_->on_death(callback);
}

}}


text:
#include "guis/GuiMetaDataEd.h"
#include "Renderer.h"
#include "Log.h"
#include "components/AsyncReqComponent.h"
#include "Settings.h"
#include "views/ViewController.h"
#include "guis/GuiGameScraper.h"
#include "guis/GuiMsgBox.h"
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <GamestationConf.h>
#include <components/SwitchComponent.h>
#include <LibretroRatio.h>

#include "components/TextEditComponent.h"
#include "components/DateTimeComponent.h"
#include "components/RatingComponent.h"
#include "guis/GuiTextEditPopup.h"
#include "guis/GuiTextEditPopupKeyboard.h"
#include "MenuThemeData.h"
#include "components/OptionListComponent.h"
#include "gamestation/GamestationSystem.h"

using namespace Eigen;

GuiMetaDataEd::GuiMetaDataEd(Window *window, MetaDataList *md, const std::vector<MetaDataDecl> &mdd,
                             ScraperSearchParams scraperParams, const std::string &header,
                             std::function<void()> saveCallback, std::function<void()> deleteFunc,
                             SystemData *system, bool main) :   GuiComponent(window),
                                                                mScraperParams(scraperParams),
                                                                mBackground(window, ":/frame.png"),
                                                                mGrid(window, Vector2i(1, 3)),
                                                                mMetaDataDecl(mdd),
                                                                mMetaData(md),
                                                                mSavedCallback(saveCallback),
                                                                mDeleteFunc(deleteFunc) {
    addChild(&mBackground);
    addChild(&mGrid);

    mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i(1, 5));

	auto menuTheme = MenuThemeData::getInstance()->getCurrentTheme();

	mBackground.setImagePath(menuTheme->menuBackground.path);
	mBackground.setCenterColor(menuTheme->menuBackground.color);
	mBackground.setEdgeColor(menuTheme->menuBackground.color);

    mTitle = std::make_shared<TextComponent>(mWindow, _("EDIT METADATA"), menuTheme->menuTitle.font, menuTheme->menuTitle.color,
                                             ALIGN_CENTER);
    mSubtitle = std::make_shared<TextComponent>(mWindow,
                                                strToUpper(scraperParams.game->getPath().filename().generic_string()),
                                                menuTheme->menuFooter.font, menuTheme->menuFooter.color, ALIGN_CENTER);
    float y = 0;
    y += mTitle->getFont()->getHeight() + mSubtitle->getFont()->getHeight();
    mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true);
    mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true);

    mGrid.setEntry(mHeaderGrid, Vector2i(0, 0), false, true);

    mList = std::make_shared<ComponentList>(mWindow);
    mGrid.setEntry(mList, Vector2i(0, 1), true, true);

    EmulatorDefaults emulatorDefaults = GamestationSystem::getInstance()->getEmulatorDefaults(system->getName());

    auto emu_choice = std::make_shared<OptionListComponent<std::string>>(mWindow, _("Emulator"), false, FONT_SIZE_MEDIUM);
    auto core_choice = std::make_shared<OptionListComponent<std::string> >(mWindow, _("core"), false, FONT_SIZE_MEDIUM);

    // populate list
    for (auto iter = mMetaDataDecl.begin(); iter != mMetaDataDecl.end(); iter++) {
        std::shared_ptr<GuiComponent> ed;

        // don't add statistics
        if (iter->isStatistic)
            continue;

        // filter on main or secondary entries depending on the requested type
        if (iter->isMain != main)
            continue;

        // create ed and add it (and any related components) to mMenu
        // ed's value will be set below
        ComponentListRow row;
        auto lbl = std::make_shared<TextComponent>(mWindow, strToUpper(iter->displayName), menuTheme->menuText.font, menuTheme->menuText.color);

        row.addElement(lbl, true); // label
        y += lbl->getFont()->getHeight();

        switch (iter->type) {
            case MD_RATING: {
                ed = std::make_shared<RatingComponent>(window, menuTheme->menuText.color);

                ed->setSize(0, lbl->getSize().y() * 0.8f);
                row.addElement(ed, false, true);

                auto spacer = std::make_shared<GuiComponent>(mWindow);
                spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0);
                row.addElement(spacer, false);

                // pass input to the actual RatingComponent instead of the spacer
                row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, std::placeholders::_2);

                break;
            }
            case MD_DATE: {
                ed = std::make_shared<DateTimeComponent>(mWindow);
                row.addElement(ed, false);

                auto spacer = std::make_shared<GuiComponent>(mWindow);
                spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0);
                row.addElement(spacer, false);

                // pass input to the actual DateTimeComponent instead of the spacer
                row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, std::placeholders::_2);

                break;
            }
            case MD_BOOL: {
                auto switchComp = std::make_shared<SwitchComponent>(mWindow);
                switchComp->setState(mMetaData->get(iter->key) == "true");
                ed = switchComp;
                row.addElement(ed, false);
                break;
            }
            case MD_LIST:
                if (iter->key == "emulator") {
                    row.addElement(emu_choice, false);
                    std::string currentEmulator = mMetaData->get(iter->key);
                    std::string mainConfigEmulator = GamestationConf::getInstance()->get(system->getName() + ".emulator");

                    if (mainConfigEmulator == "" || mainConfigEmulator == "default") {
                        mainConfigEmulator = emulatorDefaults.emulator;
                    }

                    emu_choice->add(str(boost::format(_("DEFAULT (%1%)")) % mainConfigEmulator), "default", true);

                    for (auto it = system->getEmulators()->begin(); it != system->getEmulators()->end(); it++) {
                        emu_choice->add(it->first,  it->first, it->first == currentEmulator);
                    }

                    // when emulator changes, load new core list
                    emu_choice->setSelectedChangedCallback([this, system, emulatorDefaults, core_choice](std::string emulatorName) {
                        core_choice->clear();

                        if (emulatorName == "default") {
                            std::string mainConfigCore = GamestationConf::getInstance()->get(system->getName() + ".core");
                            if (mainConfigCore == "" || mainConfigCore == "default") {
                                mainConfigCore = emulatorDefaults.core;
                            }
                            core_choice->add(str(boost::format(_("DEFAULT (%1%)")) % mainConfigCore), "default", true);
                            return ;
                        }

                        std::vector<std::string> cores = system->getCores(emulatorName);
                        std::string currentCore = mMetaData->get("core");

                        if (currentCore == "") {
                            currentCore = "default";
                        }

                        // update current core if it is not available in the emulator selected core list
                        if (currentCore != "default" && std::find(cores.begin(), cores.end(), currentCore) == cores.end()) {
                            if (emulatorName == emulatorDefaults.emulator) {
                                currentCore = emulatorDefaults.core;
                            } else {
                                // use first one
                                currentCore = *(cores.begin());
                            }
                        }

                        for (auto it = cores.begin(); it != cores.end(); it++) {
                            std::string core = *it;
                            // select at least first one in case of bad entry in config file
                            bool selected = currentCore == core || it == cores.begin();
                            if (currentCore == "default" && emulatorName == emulatorDefaults.emulator) {
                                selected = selected || core == emulatorDefaults.core;
                            }
                            core_choice->add(core, core, selected);
                        }
                    });

                    ed = emu_choice;
                }

                if (iter->key == "core") {
                    row.addElement(core_choice, false);
                    ed = core_choice;

                    // force change event to load core list
                    emu_choice->invalidate();
                }

                if (iter->key == "ratio") {
                    auto ratio_choice = std::make_shared<OptionListComponent<std::string> >(mWindow, "ratio", false, FONT_SIZE_MEDIUM);
                    row.addElement(ratio_choice, false);
                    std::map<std::string, std::string> *ratioMap = LibretroRatio::getInstance()->getRatio();
                    if (mMetaData->get("ratio") == "") {
                        mMetaData->set("ratio", "auto");
                    }
                    for (auto ratio = ratioMap->begin(); ratio != ratioMap->end(); ratio++) {
                        ratio_choice->add(ratio->first, ratio->second, mMetaData->get("ratio") == ratio->second);
                    }
                    ed = ratio_choice;
                }
                break;
            case MD_MULTILINE_STRING:
            default: {
                // MD_STRING
                ed = std::make_shared<TextComponent>(window, "", menuTheme->menuText.font, menuTheme->menuText.color, ALIGN_RIGHT);
                row.addElement(ed, true);

                auto spacer = std::make_shared<GuiComponent>(mWindow);
                spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0);
                row.addElement(spacer, false);

                auto bracket = std::make_shared<ImageComponent>(mWindow);

				bracket->setImage(menuTheme->iconSet.arrow);
				bracket->setColorShift(menuTheme->menuText.color);
                bracket->setResize(Eigen::Vector2f(0, lbl->getFont()->getLetterHeight()));
                row.addElement(bracket, false);

                bool multiLine = iter->type == MD_MULTILINE_STRING;
                const std::string title = iter->displayPrompt;
                auto updateVal = [ed](const std::string &newVal) {
                    ed->setValue(newVal);
                }; // ok callback (apply new value to ed)
                row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
                    if (Settings::getInstance()->getBool("UseOSK") && (!multiLine))
                        mWindow->pushGui(new GuiTextEditPopupKeyboard(mWindow, title, ed->getValue(), updateVal, multiLine));
                    else
                        mWindow->pushGui(new GuiTextEditPopup(mWindow, title, ed->getValue(), updateVal, multiLine));
                });
                break;
            }
        }

        assert(ed);
        mList->addRow(row);
        ed->setValue(mMetaData->get(iter->key));
        mEditors.push_back(ed);
        mMetaDataEditable.push_back(*iter);
    }

    std::vector<std::shared_ptr<ButtonComponent> > buttons;

    if (main)
    {
        // append a shortcut to the secondary menu
        ComponentListRow row;
        auto lbl = std::make_shared<TextComponent>(mWindow, _("MORE DETAILS"), menuTheme->menuText.font, menuTheme->menuText.color);
        row.addElement(lbl, true);
        y += lbl->getFont()->getHeight();

        auto bracket = std::make_shared<ImageComponent>(mWindow);
        bracket->setImage(menuTheme->iconSet.arrow);
        bracket->setColorShift(menuTheme->menuText.color);

        bracket->setResize(Eigen::Vector2f(0, lbl->getFont()->getLetterHeight()));
        row.addElement(bracket, false);

        row.makeAcceptInputHandler([this, header, system] {
            // call the same Gui with "main" set to "false"
            mWindow->pushGui(new GuiMetaDataEd(mWindow, mMetaData, mMetaDataDecl, mScraperParams, header, nullptr, nullptr, system, false));
         });

        mList->addRow(row);
    }

    if (main && !scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
        buttons.push_back(std::make_shared<ButtonComponent>(mWindow, _("SCRAPE"), _("SCRAPE"), std::bind(&GuiMetaDataEd::fetch, this)));

    buttons.push_back(std::make_shared<ButtonComponent>(mWindow, _("SAVE"), _("SAVE"), [&] { save(); delete this; }));

    buttons.push_back(std::make_shared<ButtonComponent>(mWindow, _("CANCEL"), _("CANCEL"), [&] { delete this; }));

    if (main && mDeleteFunc) {
        auto deleteFileAndSelf = [&] {
            mDeleteFunc();
            delete this;
        };
        auto deleteBtnFunc = [this, deleteFileAndSelf] {
            mWindow->pushGui(new GuiMsgBox(mWindow, _("THIS WILL DELETE A FILE!\nARE YOU SURE?"), _("YES"), deleteFileAndSelf, _("NO"), nullptr));
        };
        buttons.push_back(std::make_shared<ButtonComponent>(mWindow, _("DELETE"), _("DELETE"), deleteBtnFunc));
    }

    mButtons = makeButtonGrid(mWindow, buttons);
    mGrid.setEntry(mButtons, Vector2i(0, 2), true, false);

    mGrid.setUnhandledInputCallback([this] (InputConfig* config, Input input) -> bool {
        if (config->isMappedTo("down", input)) {
            mGrid.setCursorTo(mList);
            mList->setCursorIndex(0);
            return true;
        }
        if(config->isMappedTo("up", input)) {
            mList->setCursorIndex(mList->size() - 1);
            mGrid.moveCursor(Vector2i(0, 1));
            return true;
        }
        return false;
    });

    // resize + center
    y /= Renderer::getScreenHeight();
    setSize(Renderer::getScreenWidth() * 0.7f, Renderer::getScreenHeight() * (y + 0.15f));
    setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
}

void GuiMetaDataEd::onSizeChanged() {
    mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));

    mGrid.setSize(mSize);

    const float titleHeight = mTitle->getFont()->getLetterHeight();
    const float subtitleHeight = mSubtitle->getFont()->getLetterHeight();
    const float titleSubtitleSpacing = mSize.y() * 0.03f;

    mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y());
    mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y());

    mHeaderGrid->setRowHeightPerc(1, titleHeight / mHeaderGrid->getSize().y());
    mHeaderGrid->setRowHeightPerc(2, titleSubtitleSpacing / mHeaderGrid->getSize().y());
    mHeaderGrid->setRowHeightPerc(3, subtitleHeight / mHeaderGrid->getSize().y());
}

void GuiMetaDataEd::save() {
    for (unsigned int i = 0; i < mEditors.size(); i++) {

        if (mMetaDataEditable.at(i).type != MD_LIST)
            mMetaData->set(mMetaDataEditable.at(i).key, mEditors.at(i)->getValue());
        else {
            std::shared_ptr<GuiComponent> ed = mEditors.at(i);
            std::shared_ptr<OptionListComponent<std::string>> list = std::static_pointer_cast<OptionListComponent<std::string>>(ed);
            mMetaData->set(mMetaDataEditable.at(i).key, list->getSelected());
        }
    }

    if (mSavedCallback)
        mSavedCallback();
}

void GuiMetaDataEd::fetch() {
    GuiGameScraper *scr = new GuiGameScraper(mWindow, mScraperParams,
                                             std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1));
    mWindow->pushGui(scr);
}

void GuiMetaDataEd::fetchDone(const ScraperSearchResult &result) {
    mMetaData->merge(result.mdl);
    for (unsigned int i = 0; i < mEditors.size(); i++) {
        const std::string &key = mMetaDataEditable.at(i).key;
        mEditors.at(i)->setValue(mMetaData->get(key));
    }
}

void GuiMetaDataEd::close(bool closeAllWindows) {
    // find out if the user made any changes
    bool dirty = false;
    for (unsigned int i = 0; i < mEditors.size(); i++) {
        const std::string &key = mMetaDataEditable.at(i).key;
        if (mMetaData->get(key) != mEditors.at(i)->getValue()) {
            dirty = true;
            break;
        }
    }

    std::function<void()> closeFunc;
    if (!closeAllWindows) {
        closeFunc = [this] { delete this; };
    } else {
        Window *window = mWindow;
        closeFunc = [window, this] {
            while (window->peekGui() != ViewController::get())
                delete window->peekGui();
        };
    }

    if (dirty) {
        // changes were made, ask if the user wants to save them
        mWindow->pushGui(new GuiMsgBox(mWindow,
                                       _("SAVE CHANGES?"),
                                       _("YES"), [this, closeFunc] {
                    save();
                    closeFunc();
                },
                                       _("NO"), closeFunc
        ));
    } else {
        closeFunc();
    }
}

bool GuiMetaDataEd::input(InputConfig *config, Input input) {
    if (GuiComponent::input(config, input))
        return true;

    const bool isStart = config->isMappedTo("start", input);
    if (input.value != 0 && (config->isMappedTo("a", input) || isStart)) {
        close(isStart);
        return true;
    }

    return false;
}

std::vector<HelpPrompt> GuiMetaDataEd::getHelpPrompts() {
    std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts();
    prompts.push_back(HelpPrompt("a", _("BACK")));
    prompts.push_back(HelpPrompt("start", _("CLOSE")));
    return prompts;
}


text:
/********************************************************************\
 * guid.c -- globally unique ID implementation                      *
 * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>      *
 * Copyright (C) 2014 Aaron Laws <dartmetrash@gmail.com>            *
 *                                                                  *
 * This program is free software; you can redistribute it and/or    *
 * modify it under the terms of the GNU General Public License as   *
 * published by the Free Software Foundation; either version 2 of   *
 * the License, or (at your option) any later version.              *
 *                                                                  *
 * This program is distributed in the hope that it will be useful,  *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
 * GNU General Public License for more details.                     *
 *                                                                  *
 * You should have received a copy of the GNU General Public License*
 * along with this program; if not, contact:                        *
 *                                                                  *
 * Free Software Foundation           Voice:  +1-617-542-5942       *
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
 * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
 *                                                                  *
\********************************************************************/

#include "guid.hpp"
extern "C"
{

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <ctype.h>
#include <stdint.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#include <glib.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif
#include <time.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

}
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <sstream>
#include <string>

/**
 * gnc_value_get_guid
 *
 * @param value a @c GValue whose value we want to get.
 *
 * @return the value stored in @a value
 */
const GncGUID*
gnc_value_get_guid (const GValue *value)
{
    if (!value) return nullptr;
    GncGUID *val;

    g_return_val_if_fail (value && G_IS_VALUE (value), NULL);
    g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), NULL);

    val = (GncGUID*) g_value_get_boxed (value);

    return val;
}

GncGUID * guid_convert_create (gnc::GUID const &);

static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};

/* Memory management routines ***************************************/

/**
 * Allocates and returns a new GncGUID containing the same value as the
 * gnc::GUID passed in.
 */
GncGUID *
guid_convert_create (gnc::GUID const & guid)
{
    GncGUID temp = guid;
    return guid_copy (&temp);
}

GncGUID *
guid_malloc (void)
{
    return new GncGUID;
}

void
guid_free (GncGUID *guid)
{
    if (!guid) return;
    if (guid == s_null_gncguid)
        /* Don't delete that! */
        return;
    delete guid;
}

GncGUID *
guid_copy (const GncGUID *guid)
{
    if (!guid) return nullptr;
    auto ret = guid_malloc ();
    memcpy (ret, guid, sizeof (GncGUID));
    return ret;
}

/*It looks like we are expected to provide the same pointer every time from this function*/
const GncGUID *
guid_null (void)
{
    return s_null_gncguid;
}

void
guid_assign (GncGUID & target, gnc::GUID const & source)
{
    memcpy (&target, &source, sizeof (GncGUID));
}

/*Takes an allocated guid pointer and constructs it in place*/
void
guid_replace (GncGUID *guid)
{
    if (!guid) return;
    gnc::GUID temp_random {gnc::GUID::create_random ()};
    guid_assign (*guid, temp_random);
}

GncGUID *
guid_new (void)
{
    auto ret = guid_new_return ();
    return guid_copy (&ret);
}

GncGUID
guid_new_return (void)
{
    return gnc::GUID::create_random ();
}

gchar *
guid_to_string (const GncGUID * guid)
{
    if (!guid) return nullptr;
    gnc::GUID temp {*guid};
    auto temp_str = temp.to_string ();
    return g_strdup (temp_str.c_str ());
}

gchar *
guid_to_string_buff (const GncGUID * guid, gchar *str)
{
    if (!str || !guid) return NULL;

    gnc::GUID temp {*guid};
    auto val = temp.to_string ();
    /*We need to be sure to copy the terminating null character.*/
    std::copy (val.c_str (), val.c_str () + val.size () + 1, str);
    return str + val.size ();
}

gboolean
string_to_guid (const char * str, GncGUID * guid)
{
    if (!guid || !str) return false;

    try
    {
        guid_assign (*guid, gnc::GUID::from_string (str));
    }
    catch (...)
    {
        return false;
    }
    return true;
}

gboolean
guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
{
    if (!guid_1 || !guid_2)
        return !guid_1 && !guid_2;
    gnc::GUID temp1 {*guid_1};
    gnc::GUID temp2 {*guid_2};
    return temp1 == temp2;
}

gint
guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
{
    if (!guid_1 || !guid_2)
        return !guid_1 && !guid_2;
    gnc::GUID temp1 {*guid_1};
    gnc::GUID temp2 {*guid_2};
    if (temp1 < temp2)
        return -1;
    if (temp1 == temp2)
        return 0;
    return 1;
}

guint
guid_hash_to_guint (gconstpointer ptr)
{
    if (!ptr)
    {
        printf ("received NULL guid pointer.");
        return 0;
    }
    GncGUID const & guid = * reinterpret_cast <GncGUID const *> (ptr);
    gnc::GUID const & temp {guid};

    guint hash {0};
    unsigned retspot {0};
    std::for_each (temp.begin (), temp.end (), [&hash] (unsigned char a) {
        hash <<=4;
        hash |= a;
    });
    return hash;
}

gint
guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
{
    return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
		       reinterpret_cast<const GncGUID*> (guid_b));
}

GHashTable *
guid_hash_table_new (void)
{
    return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
}

/***************************/
static void
gnc_string_to_guid (const GValue *src, GValue *dest)
{
    /* FIXME: add more checks*/
    GncGUID *guid;
    const gchar *as_string;

    g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
                      GNC_VALUE_HOLDS_GUID (dest));

    as_string = g_value_get_string (src);

    guid = g_new0 (GncGUID, 1);
    string_to_guid (as_string, guid);

    g_value_take_boxed (dest, guid);
}

static void
gnc_guid_to_string (const GValue *src, GValue *dest)
{
    const gchar *str;

    g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
                      GNC_VALUE_HOLDS_GUID (src));

    str = guid_to_string (gnc_value_get_guid (src));

    g_value_set_string (dest, str);
}

GType
gnc_guid_get_type (void)
{
    static GType type = 0;

    if (G_UNLIKELY (type == 0))
    {
        type = g_boxed_type_register_static ("GncGUID",
                                             (GBoxedCopyFunc)guid_copy,
                                             (GBoxedFreeFunc)guid_free);

        g_value_register_transform_func (G_TYPE_STRING,
                                         type,
                                         gnc_string_to_guid);

        g_value_register_transform_func (type,
                                         G_TYPE_STRING,
                                         gnc_guid_to_string);
    }

    return type;
}

namespace gnc
{

GUID
GUID::create_random () noexcept
{
    static boost::uuids::random_generator gen;
    return {gen ()};
}

GUID::GUID (boost::uuids::uuid const & other) noexcept
    : implementation (other)
{
}

GUID const &
GUID::null_guid () noexcept
{
    return s_null_guid;
}

std::string
GUID::to_string () const noexcept
{
    auto const & val = boost::uuids::to_string (implementation);
    std::string ret;
    std::for_each (val.begin (), val.end (), [&ret] (char a) {
        if (a != '-') ret.push_back (a);
    });
    return ret;
}

GUID
GUID::from_string (std::string const & str) throw (guid_syntax_exception)
{
    try
    {
        static boost::uuids::string_generator strgen;
        return strgen (str);
    }
    catch (...)
    {
        throw guid_syntax_exception {};
    }
}

guid_syntax_exception::guid_syntax_exception () noexcept
    : invalid_argument {"Invalid syntax for guid."}
{
}

GUID::GUID (GncGUID const & other) noexcept
: implementation {{other.reserved[0] , other.reserved[1]
            , other.reserved[2], other.reserved[3]
            , other.reserved[4], other.reserved[5]
            , other.reserved[6], other.reserved[7]
            , other.reserved[8], other.reserved[9]
            , other.reserved[10], other.reserved[11]
            , other.reserved[12], other.reserved[13]
            , other.reserved[14], other.reserved[15]}
    }
{

}

auto
GUID::end () const noexcept -> decltype (implementation.end ())
{
    return implementation.end ();
}

auto
GUID::begin () const noexcept -> decltype (implementation.begin ())
{
    return implementation.begin ();
}

bool
GUID::operator < (GUID const & other) noexcept
{
    return implementation < other.implementation;
}

bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
{
    auto ret = std::mismatch (lhs.begin (), lhs.end (), rhs.reserved);
    return ret.first == lhs.end ();

}

bool
operator != (GUID const & one, GUID const & two) noexcept
{
    return one.implementation != two.implementation;
}

GUID & GUID::operator = (GUID && other) noexcept
{
    boost::uuids::swap (other.implementation, implementation);
    return *this;
}

GUID::operator GncGUID () const noexcept
{
    GncGUID ret;
    guid_assign (ret, *this);
    return ret;
}

} // namespace gnc

max_stars_repo_name: frapa/electrum

max_issues_repo_name: frapa/electrum

max_forks_repo_name: frapa/electrum


text:
#include "ayame_client.h"

// boost
#include <boost/beast/websocket/stream.hpp>
#include <boost/json.hpp>

#include "momo_version.h"
#include "ssl_verifier.h"
#include "url_parts.h"
#include "util.h"

bool AyameClient::ParseURL(URLParts& parts) const {
  std::string url = config_.signaling_url;

  if (!URLParts::Parse(url, parts)) {
    throw std::exception();
  }

  std::string default_port;
  if (parts.scheme == "wss") {
    return true;
  } else if (parts.scheme == "ws") {
    return false;
  } else {
    throw std::exception();
  }
}

void AyameClient::GetStats(
    std::function<void(const rtc::scoped_refptr<const webrtc::RTCStatsReport>&)>
        callback) {
  if (connection_ && rtc_state_ ==
                         webrtc::PeerConnectionInterface::IceConnectionState::
                             kIceConnectionConnected) {
    connection_->GetStats(std::move(callback));
  } else {
    callback(nullptr);
  }
}

AyameClient::AyameClient(boost::asio::io_context& ioc,
                         RTCManager* manager,
                         AyameClientConfig config)
    : ioc_(ioc),
      manager_(manager),
      retry_count_(0),
      config_(std::move(config)),
      watchdog_(ioc, std::bind(&AyameClient::OnWatchdogExpired, this)) {
  Reset();
}

AyameClient::~AyameClient() {
  destructed_ = true;
  // ここで OnIceConnectionStateChange が呼ばれる
  connection_ = nullptr;
}

void AyameClient::Reset() {
  connection_ = nullptr;
  is_send_offer_ = false;
  has_is_exist_user_flag_ = false;
  ice_servers_.clear();

  URLParts parts;
  if (ParseURL(parts)) {
    ws_.reset(new Websocket(Websocket::ssl_tag(), ioc_, config_.insecure));
  } else {
    ws_.reset(new Websocket(ioc_));
  }
}

void AyameClient::Connect() {
  RTC_LOG(LS_INFO) << __FUNCTION__;

  watchdog_.Enable(30);

  ws_->Connect(config_.signaling_url,
               std::bind(&AyameClient::OnConnect, shared_from_this(),
                         std::placeholders::_1));
}

void AyameClient::ReconnectAfter() {
  int interval = 5 * (2 * retry_count_);
  if (interval > 30) {
    interval = 30;
  }
  RTC_LOG(LS_INFO) << __FUNCTION__ << " reconnect after " << interval << " sec";

  watchdog_.Enable(interval);
  retry_count_++;
}

void AyameClient::OnWatchdogExpired() {
  RTC_LOG(LS_WARNING) << __FUNCTION__;

  RTC_LOG(LS_INFO) << __FUNCTION__ << " reconnecting...:";
  Reset();
  Connect();
}

void AyameClient::OnConnect(boost::system::error_code ec) {
  if (ec) {
    ReconnectAfter();
    return MOMO_BOOST_ERROR(ec, "Handshake");
  }

  DoRead();

  DoRegister();
}

void AyameClient::DoRead() {
  ws_->Read(std::bind(&AyameClient::OnRead, shared_from_this(),
                      std::placeholders::_1, std::placeholders::_2,
                      std::placeholders::_3));
}

void AyameClient::DoRegister() {
  boost::json::value json_message = {
      {"type", "register"},
      {"clientId", Util::GenerateRandomChars()},
      {"roomId", config_.room_id},
      {"ayameClient", MomoVersion::GetClientName()},
      {"libwebrtc", MomoVersion::GetLibwebrtcName()},
      {"environment", MomoVersion::GetEnvironmentName()},
  };
  if (config_.client_id != "") {
    json_message.as_object()["clientId"] = config_.client_id;
  }
  if (config_.signaling_key != "") {
    json_message.as_object()["key"] = config_.signaling_key;
  }
  ws_->WriteText(boost::json::serialize(json_message));
}

void AyameClient::DoSendPong() {
  boost::json::value json_message = {{"type", "pong"}};
  ws_->WriteText(boost::json::serialize(json_message));
}

void AyameClient::SetIceServersFromConfig(boost::json::value json_message) {
  // 返却されてきた iceServers を セットする
  if (json_message.as_object().count("iceServers") != 0) {
    auto jservers = json_message.at("iceServers");
    if (jservers.is_array()) {
      for (auto jserver : jservers.as_array()) {
        webrtc::PeerConnectionInterface::IceServer ice_server;
        if (jserver.as_object().count("username") != 0) {
          ice_server.username = jserver.at("username").as_string().c_str();
        }
        if (jserver.as_object().count("credential") != 0) {
          ice_server.password = jserver.at("credential").as_string().c_str();
        }
        auto jurls = jserver.at("urls");
        for (const auto url : jurls.as_array()) {
          ice_server.urls.push_back(url.as_string().c_str());
          RTC_LOG(LS_INFO) << __FUNCTION__
                           << ": iceserver.url=" << url.as_string();
        }
        ice_servers_.push_back(ice_server);
      }
    }
  }
  if (ice_servers_.empty() && !config_.no_google_stun) {
    // accept 時に iceServers が返却されてこなかった場合 google の stun server を用いる
    webrtc::PeerConnectionInterface::IceServer ice_server;
    ice_server.uri = "stun:stun.l.google.com:19302";
    ice_servers_.push_back(ice_server);
  }
}

void AyameClient::CreatePeerConnection() {
  webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;

  rtc_config.servers = ice_servers_;
  connection_ = manager_->CreateConnection(rtc_config, this);
  manager_->InitTracks(connection_.get());
}

void AyameClient::Close() {
  ws_->Close(std::bind(&AyameClient::OnClose, shared_from_this(),
                       std::placeholders::_1));
}

// WebSocket が閉じられたときのコールバック
void AyameClient::OnClose(boost::system::error_code ec) {
  if (ec)
    MOMO_BOOST_ERROR(ec, "Close");
  // retry_count_ は ReconnectAfter(); が以前に呼ばれている場合はインクリメントされている可能性がある。
  // WebSocket につないでいない時間をなるべく短くしたいので、
  // WebSocket を閉じたときは一度インクリメントされている可能性のある retry_count_ を0 にして
  // OnWatchdogExpired(); が発火して再接続が行われるまでの時間を最小にしておく。
  retry_count_ = 0;
  // WebSocket 接続がちゃんと閉じられたら ReconnectAfter(); を発火する。
  // ReconnectAfter(); によって OnWatchdogExpired(); が呼ばれ、ここで WebSocket の再接続が行われる。
  // 現在は WebSocket がどんな理由で閉じられても、再接続するようになっている
  ReconnectAfter();
}

void AyameClient::OnRead(boost::system::error_code ec,
                         std::size_t bytes_transferred,
                         std::string text) {
  RTC_LOG(LS_INFO) << __FUNCTION__ << ": " << ec;

  boost::ignore_unused(bytes_transferred);

  // 書き込みのために読み込み処理がキャンセルされた時にこのエラーになるので、これはエラーとして扱わない
  if (ec == boost::asio::error::operation_aborted)
    return;

  // WebSocket が closed なエラーが返ってきた場合すぐに Close(); を呼んで、OnRead 関数から抜ける
  if (ec == boost::beast::websocket::error::closed) {
    // Close(); で WebSocket が閉じられたら、OnClose(); -> ReconnectAfter(); -> OnWatchdogExpired(); の順に関数が呼ばれることで、
    // WebSocket の再接続が行われる
    Close();
    return;
  }

  if (ec)
    return MOMO_BOOST_ERROR(ec, "Read");

  RTC_LOG(LS_INFO) << __FUNCTION__ << ": text=" << text;

  auto json_message = boost::json::parse(text);
  const std::string type = json_message.at("type").as_string().c_str();
  if (type == "accept") {
    SetIceServersFromConfig(json_message);
    CreatePeerConnection();
    // isExistUser フラグが存在するか確認する
    auto is_exist_user = false;
    if (json_message.as_object().count("isExistUser") != 0) {
      has_is_exist_user_flag_ = true;
      is_exist_user = json_message.at("isExistUser").as_bool();
    }

    auto on_create_offer = [this](webrtc::SessionDescriptionInterface* desc) {
      std::string sdp;
      desc->ToString(&sdp);
      manager_->SetParameters();
      boost::json::value json_message = {{"type", "offer"}, {"sdp", sdp}};
      ws_->WriteText(boost::json::serialize(json_message));
    };

    // isExistUser フラグが存在してかつ true な場合 offer SDP を生成して送信する
    if (is_exist_user) {
      RTC_LOG(LS_INFO) << __FUNCTION__ << ": exist_user";
      is_send_offer_ = true;
      connection_->CreateOffer(on_create_offer);
    } else if (!has_is_exist_user_flag_) {
      // フラグがない場合とりあえず送信
      connection_->CreateOffer(on_create_offer);
    }
  } else if (type == "offer") {
    // isExistUser フラグがなかった場合二回 peer connection を生成する
    if (!has_is_exist_user_flag_) {
      CreatePeerConnection();
    }
    const std::string sdp = json_message.at("sdp").as_string().c_str();
    connection_->SetOffer(sdp, [this]() {
      boost::asio::post(ioc_, [this, self = shared_from_this()]() {
        if (!is_send_offer_ || !has_is_exist_user_flag_) {
          connection_->CreateAnswer(
              [this](webrtc::SessionDescriptionInterface* desc) {
                std::string sdp;
                desc->ToString(&sdp);
                manager_->SetParameters();
                boost::json::value json_message = {{"type", "answer"},
                                                   {"sdp", sdp}};
                ws_->WriteText(boost::json::serialize(json_message));
              });
        }
        is_send_offer_ = false;
      });
    });
  } else if (type == "answer") {
    const std::string sdp = json_message.at("sdp").as_string().c_str();
    connection_->SetAnswer(sdp);
  } else if (type == "candidate") {
    boost::json::value ice = json_message.at("ice");
    std::string sdp_mid = ice.at("sdpMid").as_string().c_str();
    int sdp_mlineindex = ice.at("sdpMLineIndex").to_number<int>();
    std::string candidate = ice.at("candidate").as_string().c_str();
    connection_->AddIceCandidate(sdp_mid, sdp_mlineindex, candidate);
  } else if (type == "ping") {
    watchdog_.Reset();
    DoSendPong();
  } else if (type == "bye") {
    RTC_LOG(LS_INFO) << __FUNCTION__ << ": bye";
    connection_ = nullptr;
    Close();
  }
  DoRead();
}

// WebRTC からのコールバック
// これらは別スレッドからやってくるので取り扱い注意
void AyameClient::OnIceConnectionStateChange(
    webrtc::PeerConnectionInterface::IceConnectionState new_state) {
  RTC_LOG(LS_INFO) << __FUNCTION__ << " state:" << new_state;
  // デストラクタだと shared_from_this が機能しないので無視する
  if (destructed_) {
    return;
  }
  boost::asio::post(ioc_, std::bind(&AyameClient::DoIceConnectionStateChange,
                                    shared_from_this(), new_state));
}
void AyameClient::OnIceCandidate(const std::string sdp_mid,
                                 const int sdp_mlineindex,
                                 const std::string sdp) {
  // ayame では candidate sdp の交換で `ice` プロパティを用いる。 `candidate` ではないので注意
  boost::json::value json_message = {
      {"type", "candidate"},
  };
  // ice プロパティの中に object で candidate 情報をセットして送信する
  json_message.as_object()["ice"] = {{"candidate", sdp},
                                     {"sdpMLineIndex", sdp_mlineindex},
                                     {"sdpMid", sdp_mid}};
  ws_->WriteText(boost::json::serialize(json_message));
}

void AyameClient::DoIceConnectionStateChange(
    webrtc::PeerConnectionInterface::IceConnectionState new_state) {
  RTC_LOG(LS_INFO) << __FUNCTION__ << ": newState="
                   << Util::IceConnectionStateToString(new_state);

  switch (new_state) {
    case webrtc::PeerConnectionInterface::IceConnectionState::
        kIceConnectionConnected:
      retry_count_ = 0;
      watchdog_.Enable(60);
      break;
    // ice connection state が failed になったら Close(); を呼んで、WebSocket 接続を閉じる
    case webrtc::PeerConnectionInterface::IceConnectionState::
        kIceConnectionFailed:
      // Close(); で WebSocket が閉じられたら、OnClose(); -> ReconnectAfter(); -> OnWatchdogExpired(); の順に関数が呼ばれることで
      // WebSocket の再接続が行われる
      Close();
      break;
    default:
      break;
  }
  rtc_state_ = new_state;
}

max_stars_repo_name: goforbroke/momo

max_issues_repo_name: goforbroke/momo

max_forks_repo_name: goforbroke/momo