Browse Source

Import hylia/link code

tags/1.9.7b
falkTX 8 years ago
parent
commit
1db085b2f2
100 changed files with 26277 additions and 0 deletions
  1. +103
    -0
      source/modules/hylia/hylia.cpp
  2. +45
    -0
      source/modules/hylia/hylia.h
  3. +107
    -0
      source/modules/hylia/link/AudioEngine.cpp
  4. +66
    -0
      source/modules/hylia/link/AudioEngine.hpp
  5. +306
    -0
      source/modules/hylia/link/ableton/Link.hpp
  6. +173
    -0
      source/modules/hylia/link/ableton/Link.ipp
  7. +103
    -0
      source/modules/hylia/link/ableton/discovery/InterfaceScanner.hpp
  8. +123
    -0
      source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp
  9. +50
    -0
      source/modules/hylia/link/ableton/discovery/MessageTypes.hpp
  10. +405
    -0
      source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp
  11. +293
    -0
      source/modules/hylia/link/ableton/discovery/Payload.hpp
  12. +253
    -0
      source/modules/hylia/link/ableton/discovery/PeerGateway.hpp
  13. +229
    -0
      source/modules/hylia/link/ableton/discovery/PeerGateways.hpp
  14. +72
    -0
      source/modules/hylia/link/ableton/discovery/Service.hpp
  15. +139
    -0
      source/modules/hylia/link/ableton/discovery/Socket.hpp
  16. +330
    -0
      source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp
  17. +75
    -0
      source/modules/hylia/link/ableton/discovery/test/Interface.hpp
  18. +98
    -0
      source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp
  19. +83
    -0
      source/modules/hylia/link/ableton/discovery/test/Socket.hpp
  20. +168
    -0
      source/modules/hylia/link/ableton/discovery/v1/Messages.hpp
  21. +103
    -0
      source/modules/hylia/link/ableton/link/Beats.hpp
  22. +115
    -0
      source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp
  23. +446
    -0
      source/modules/hylia/link/ableton/link/Controller.hpp
  24. +94
    -0
      source/modules/hylia/link/ableton/link/Gateway.hpp
  25. +59
    -0
      source/modules/hylia/link/ableton/link/GhostXForm.hpp
  26. +82
    -0
      source/modules/hylia/link/ableton/link/HostTimeFilter.hpp
  27. +160
    -0
      source/modules/hylia/link/ableton/link/Kalman.hpp
  28. +64
    -0
      source/modules/hylia/link/ableton/link/LinearRegression.hpp
  29. +296
    -0
      source/modules/hylia/link/ableton/link/Measurement.hpp
  30. +70
    -0
      source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp
  31. +186
    -0
      source/modules/hylia/link/ableton/link/MeasurementService.hpp
  32. +82
    -0
      source/modules/hylia/link/ableton/link/NodeId.hpp
  33. +69
    -0
      source/modules/hylia/link/ableton/link/NodeState.hpp
  34. +146
    -0
      source/modules/hylia/link/ableton/link/PayloadEntries.hpp
  35. +83
    -0
      source/modules/hylia/link/ableton/link/PeerState.hpp
  36. +355
    -0
      source/modules/hylia/link/ableton/link/Peers.hpp
  37. +100
    -0
      source/modules/hylia/link/ableton/link/Phase.hpp
  38. +185
    -0
      source/modules/hylia/link/ableton/link/PingResponder.hpp
  39. +65
    -0
      source/modules/hylia/link/ableton/link/SessionId.hpp
  40. +303
    -0
      source/modules/hylia/link/ableton/link/Sessions.hpp
  41. +90
    -0
      source/modules/hylia/link/ableton/link/Tempo.hpp
  42. +99
    -0
      source/modules/hylia/link/ableton/link/Timeline.hpp
  43. +138
    -0
      source/modules/hylia/link/ableton/link/v1/Messages.hpp
  44. +66
    -0
      source/modules/hylia/link/ableton/platforms/Config.hpp
  45. +105
    -0
      source/modules/hylia/link/ableton/platforms/asio/AsioService.hpp
  46. +132
    -0
      source/modules/hylia/link/ableton/platforms/asio/AsioTimer.hpp
  47. +76
    -0
      source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp
  48. +181
    -0
      source/modules/hylia/link/ableton/platforms/asio/Context.hpp
  49. +115
    -0
      source/modules/hylia/link/ableton/platforms/asio/PooledHandlerContext.hpp
  50. +110
    -0
      source/modules/hylia/link/ableton/platforms/asio/Socket.hpp
  51. +43
    -0
      source/modules/hylia/link/ableton/platforms/asio/Util.hpp
  52. +70
    -0
      source/modules/hylia/link/ableton/platforms/darwin/Clock.hpp
  53. +30
    -0
      source/modules/hylia/link/ableton/platforms/darwin/Darwin.hpp
  54. +30
    -0
      source/modules/hylia/link/ableton/platforms/linux/Linux.hpp
  55. +110
    -0
      source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp
  56. +51
    -0
      source/modules/hylia/link/ableton/platforms/stl/Clock.hpp
  57. +71
    -0
      source/modules/hylia/link/ableton/platforms/windows/Clock.hpp
  58. +140
    -0
      source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp
  59. +43
    -0
      source/modules/hylia/link/ableton/test/CatchWrapper.hpp
  60. +110
    -0
      source/modules/hylia/link/ableton/test/serial_io/Context.hpp
  61. +92
    -0
      source/modules/hylia/link/ableton/test/serial_io/Fixture.hpp
  62. +104
    -0
      source/modules/hylia/link/ableton/test/serial_io/SchedulerTree.hpp
  63. +83
    -0
      source/modules/hylia/link/ableton/test/serial_io/Socket.hpp
  64. +110
    -0
      source/modules/hylia/link/ableton/test/serial_io/Timer.hpp
  65. +254
    -0
      source/modules/hylia/link/ableton/util/Injected.hpp
  66. +176
    -0
      source/modules/hylia/link/ableton/util/Log.hpp
  67. +66
    -0
      source/modules/hylia/link/ableton/util/SafeAsyncHandler.hpp
  68. +52
    -0
      source/modules/hylia/link/ableton/util/SampleTiming.hpp
  69. +114
    -0
      source/modules/hylia/link/ableton/util/test/IoService.hpp
  70. +96
    -0
      source/modules/hylia/link/ableton/util/test/Timer.hpp
  71. +144
    -0
      source/modules/hylia/link/asio.hpp
  72. +123
    -0
      source/modules/hylia/link/asio/associated_allocator.hpp
  73. +142
    -0
      source/modules/hylia/link/asio/associated_executor.hpp
  74. +125
    -0
      source/modules/hylia/link/asio/async_result.hpp
  75. +945
    -0
      source/modules/hylia/link/asio/basic_datagram_socket.hpp
  76. +521
    -0
      source/modules/hylia/link/asio/basic_deadline_timer.hpp
  77. +274
    -0
      source/modules/hylia/link/asio/basic_io_object.hpp
  78. +936
    -0
      source/modules/hylia/link/asio/basic_raw_socket.hpp
  79. +561
    -0
      source/modules/hylia/link/asio/basic_seq_packet_socket.hpp
  80. +679
    -0
      source/modules/hylia/link/asio/basic_serial_port.hpp
  81. +386
    -0
      source/modules/hylia/link/asio/basic_signal_set.hpp
  82. +1599
    -0
      source/modules/hylia/link/asio/basic_socket.hpp
  83. +1734
    -0
      source/modules/hylia/link/asio/basic_socket_acceptor.hpp
  84. +325
    -0
      source/modules/hylia/link/asio/basic_socket_iostream.hpp
  85. +646
    -0
      source/modules/hylia/link/asio/basic_socket_streambuf.hpp
  86. +848
    -0
      source/modules/hylia/link/asio/basic_stream_socket.hpp
  87. +453
    -0
      source/modules/hylia/link/asio/basic_streambuf.hpp
  88. +36
    -0
      source/modules/hylia/link/asio/basic_streambuf_fwd.hpp
  89. +634
    -0
      source/modules/hylia/link/asio/basic_waitable_timer.hpp
  90. +578
    -0
      source/modules/hylia/link/asio/bind_executor.hpp
  91. +2725
    -0
      source/modules/hylia/link/asio/buffer.hpp
  92. +262
    -0
      source/modules/hylia/link/asio/buffered_read_stream.hpp
  93. +25
    -0
      source/modules/hylia/link/asio/buffered_read_stream_fwd.hpp
  94. +277
    -0
      source/modules/hylia/link/asio/buffered_stream.hpp
  95. +25
    -0
      source/modules/hylia/link/asio/buffered_stream_fwd.hpp
  96. +254
    -0
      source/modules/hylia/link/asio/buffered_write_stream.hpp
  97. +25
    -0
      source/modules/hylia/link/asio/buffered_write_stream_fwd.hpp
  98. +481
    -0
      source/modules/hylia/link/asio/buffers_iterator.hpp
  99. +218
    -0
      source/modules/hylia/link/asio/completion_condition.hpp
  100. +1055
    -0
      source/modules/hylia/link/asio/connect.hpp

+ 103
- 0
source/modules/hylia/hylia.cpp View File

@@ -0,0 +1,103 @@
/*
* This file is part of Hylia.
*
* Hylia 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.
*
* Hylia 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 Hylia. If not, see <http://www.gnu.org/licenses/>.
*/

#include "hylia.h"

#include "AudioEngine.hpp"
#include "ableton/link/HostTimeFilter.hpp"
#include <chrono>

class HyliaTransport {
public:
HyliaTransport(double bpm, double bufferSize, double sampleRate)
: link(bpm),
engine(link),
outputLatency(0),
sampleTime(0)
{
outputLatency = std::chrono::microseconds(llround(1.0e6 * bufferSize / sampleRate));
}

void setEnabled(bool enabled, double bpm)
{
link.enable(enabled);

if (enabled)
{
sampleTime = 0;
engine.setTempo(bpm);
}
}

void setTempo(double tempo)
{
engine.setTempo(tempo);
}

void process(uint32_t frames, LinkTimeInfo* info)
{
const auto hostTime = hostTimeFilter.sampleTimeToHostTime(sampleTime);
const auto bufferBeginAtOutput = hostTime + outputLatency;

engine.timelineCallback(bufferBeginAtOutput, info);

sampleTime += frames;
}

private:
ableton::Link link;
ableton::linkaudio::AudioEngine engine;

ableton::link::HostTimeFilter<ableton::platforms::stl::Clock> hostTimeFilter;
std::chrono::microseconds outputLatency;
uint32_t sampleTime;
};

hylia_t* hylia_create(double bpm, uint32_t buffer_size, uint32_t sample_rate)
{
HyliaTransport* t;

try {
t = new HyliaTransport(bpm, buffer_size, sample_rate);
} catch (...) {
return nullptr;
}

return (hylia_t*)t;
}

void hylia_enable(hylia_t* link, bool on, double bpm)
{
((HyliaTransport*)link)->setEnabled(on, bpm);
}

void hylia_set_tempo(hylia_t* link, double bpm)
{
((HyliaTransport*)link)->setTempo(bpm);
}

void hylia_process(hylia_t* link, uint32_t frames, hylia_time_info_t* info)
{
((HyliaTransport*)link)->process(frames, (LinkTimeInfo*)info);
}

void hylia_cleanup(hylia_t* link)
{
delete (HyliaTransport*)link;
}

#include "AudioEngine.cpp"

+ 45
- 0
source/modules/hylia/hylia.h View File

@@ -0,0 +1,45 @@
/*
* This file is part of Hylia.
*
* Hylia 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.
*
* Hylia 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 Hylia. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MOD_LINK_H_INCLUDED
#define MOD_LINK_H_INCLUDED

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif

typedef struct _hylia_t hylia_t;

typedef struct _hylia_time_info_t {
double bpm, beats, phase;
} hylia_time_info_t;

hylia_t* hylia_create(double bpm, uint32_t buffer_size, uint32_t sample_rate);
void hylia_enable(hylia_t* link, bool on, double bpm);
void hylia_process(hylia_t* link, uint32_t frames, hylia_time_info_t* info);
void hylia_set_tempo(hylia_t* link, double tempo);
void hylia_cleanup(hylia_t* link);

#ifdef __cplusplus
}
#endif

#endif // MOD_LINK_H_INCLUDED

+ 107
- 0
source/modules/hylia/link/AudioEngine.cpp View File

@@ -0,0 +1,107 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#include "AudioEngine.hpp"

// Make sure to define this before <cmath> is included for Windows
#define _USE_MATH_DEFINES
#include <cmath>

namespace ableton
{
namespace linkaudio
{

AudioEngine::AudioEngine(Link& link)
: mLink(link)
, mSharedEngineData({0., false, 4.})
{
}

double AudioEngine::beatTime() const
{
const auto timeline = mLink.captureAppTimeline();
return timeline.beatAtTime(mLink.clock().micros(), mSharedEngineData.quantum);
}

void AudioEngine::setTempo(double tempo)
{
std::lock_guard<std::mutex> lock(mEngineDataGuard);
mSharedEngineData.requestedTempo = tempo;
}

double AudioEngine::quantum() const
{
return mSharedEngineData.quantum;
}

void AudioEngine::setQuantum(double quantum)
{
std::lock_guard<std::mutex> lock(mEngineDataGuard);
mSharedEngineData.quantum = quantum;
mSharedEngineData.resetBeatTime = true;
}

AudioEngine::EngineData AudioEngine::pullEngineData()
{
auto engineData = EngineData{};
if (mEngineDataGuard.try_lock())
{
engineData.requestedTempo = mSharedEngineData.requestedTempo;
mSharedEngineData.requestedTempo = 0;
engineData.resetBeatTime = mSharedEngineData.resetBeatTime;
engineData.quantum = mSharedEngineData.quantum;
mSharedEngineData.resetBeatTime = false;

mEngineDataGuard.unlock();
}

return engineData;
}

void AudioEngine::timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info)
{
const auto engineData = pullEngineData();

auto timeline = mLink.captureAudioTimeline();

if (engineData.resetBeatTime)
{
// Reset the timeline so that beat 0 lands at the beginning of
// this buffer and clear the flag.
timeline.requestBeatAtTime(0, hostTime, engineData.quantum);
}

if (engineData.requestedTempo > 0)
{
// Set the newly requested tempo from the beginning of this buffer
timeline.setTempo(engineData.requestedTempo, hostTime);
}

// Timeline modifications are complete, commit the results
mLink.commitAudioTimeline(timeline);

// Save timeline info
info->bpm = timeline.tempo();
info->beats = timeline.beatAtTime(hostTime, engineData.quantum);
info->phase = timeline.phaseAtTime(hostTime, engineData.quantum);
}

} // namespace linkaudio
} // namespace ableton

+ 66
- 0
source/modules/hylia/link/AudioEngine.hpp View File

@@ -0,0 +1,66 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

// Make sure to define this before <cmath> is included for Windows
#define _USE_MATH_DEFINES
#include "ableton/Link.hpp"
#include <mutex>

struct LinkTimeInfo {
double bpm, beats, phase;
};

namespace ableton
{
namespace linkaudio
{

class AudioEngine
{
public:
AudioEngine(Link& link);
double beatTime() const;
void setTempo(double tempo);
double quantum() const;
void setQuantum(double quantum);

void timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info);

private:
struct EngineData
{
double requestedTempo;
bool resetBeatTime;
double quantum;
};

EngineData pullEngineData();

Link& mLink;
EngineData mSharedEngineData;
std::mutex mEngineDataGuard;

friend class AudioPlatform;
};


} // namespace linkaudio
} // namespace ableton

+ 306
- 0
source/modules/hylia/link/ableton/Link.hpp View File

@@ -0,0 +1,306 @@
/*! @file Link.hpp
* @copyright 2016, Ableton AG, Berlin. All rights reserved.
* @brief Library for cross-device shared tempo and quantized beat grid
*
* @license:
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/Config.hpp>
#include <chrono>
#include <mutex>

namespace ableton
{

/*! @class Link
* @brief Class that represents a participant in a Link session.
*
* @discussion Each Link instance has its own beat timeline that
* starts running from beat 0 at the initial tempo when
* constructed. A Link instance is initially disabled after
* construction, which means that it will not communicate on the
* network. Once enabled, a Link instance initiates network
* communication in an effort to discover other peers. When peers are
* discovered, they immediately become part of a shared Link session.
*
* Each method of the Link type documents its thread-safety and
* realtime-safety properties. When a method is marked thread-safe,
* it means it is safe to call from multiple threads
* concurrently. When a method is marked realtime-safe, it means that
* it does not block and is appropriate for use in the thread that
* performs audio IO.
*
* Link provides one Timeline capture/commit method pair for use in the
* audio thread and one for all other application contexts. In
* general, modifying the Link timeline should be done in the audio
* thread for the most accurate timing results. The ability to modify
* the Link timeline from application threads should only be used in
* cases where an application's audio thread is not actively running
* or if it doesn't generate audio at all. Modifying the Link
* timeline from both the audio thread and an application thread
* concurrently is not advised and will potentially lead to
* unexpected behavior.
*/
class Link
{
public:
using Clock = link::platform::Clock;
class Timeline;

/*! @brief Construct with an initial tempo. */
Link(double bpm);

/*! @brief Link instances cannot be copied or moved */
Link(const Link&) = delete;
Link& operator=(const Link&) = delete;
Link(Link&&) = delete;
Link& operator=(Link&&) = delete;

/*! @brief Is Link currently enabled?
* Thread-safe: yes
* Realtime-safe: yes
*/
bool isEnabled() const;

/*! @brief Enable/disable Link.
* Thread-safe: yes
* Realtime-safe: no
*/
void enable(bool bEnable);

/*! @brief How many peers are currently connected in a Link session?
* Thread-safe: yes
* Realtime-safe: yes
*/
std::size_t numPeers() const;

/*! @brief Register a callback to be notified when the number of
* peers in the Link session changes.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The callback is invoked on a Link-managed thread.
*
* @param callback The callback signature is:
* void (std::size_t numPeers)
*/
template <typename Callback>
void setNumPeersCallback(Callback callback);

/*! @brief Register a callback to be notified when the session
* tempo changes.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The callback is invoked on a Link-managed thread.
*
* @param callback The callback signature is: void (double bpm)
*/
template <typename Callback>
void setTempoCallback(Callback callback);

/*! @brief The clock used by Link.
* Thread-safe: yes
* Realtime-safe: yes
*
* @discussion The Clock type is a platform-dependent
* representation of the system clock. It exposes a ticks() method
* that returns the current ticks of the system clock as well as
* micros(), which is a normalized representation of the current system
* time in std::chrono::microseconds. It also provides conversion
* functions ticksToMicros() and microsToTicks() to faciliate
* converting between these units.
*/
Clock clock() const;

/*! @brief Capture the current Link timeline from the audio thread.
* Thread-safe: no
* Realtime-safe: yes
*
* @discussion This method should ONLY be called in the audio thread
* and must not be accessed from any other threads. The returned
* Timeline stores a snapshot of the current Link state, so it
* should be captured and used in a local scope. Storing the
* Timeline for later use in a different context is not advised
* because it will provide an outdated view on the Link state.
*/
Timeline captureAudioTimeline() const;

/*! @brief Commit the given timeline to the Link session from the
* audio thread.
* Thread-safe: no
* Realtime-safe: yes
*
* @discussion This method should ONLY be called in the audio
* thread. The given timeline will replace the current Link
* timeline. Modifications to the session based on the new timeline
* will be communicated to other peers in the session.
*/
void commitAudioTimeline(Timeline timeline);

/*! @brief Capture the current Link timeline from an application
* thread.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion Provides a mechanism for capturing the Link timeline
* from an application thread (other than the audio thread). The
* returned Timeline stores a snapshot of the current Link state,
* so it should be captured and used in a local scope. Storing the
* Timeline for later use in a different context is not advised
* because it will provide an outdated view on the Link state.
*/
Timeline captureAppTimeline() const;

/*! @brief Commit the given timeline to the Link session from an
* application thread.
* Thread-safe: yes
* Realtime-safe: no
*
* @discussion The given timeline will replace the current Link
* timeline. Modifications to the session based on the new timeline
* will be communicated to other peers in the session.
*/
void commitAppTimeline(Timeline timeline);

/*! @class Timeline
* @brief Representation of a mapping between time and beats for
* varying quanta.
*
* @discussion A Timeline object is intended for use in a local
* scope within a single thread - none of its methods are
* thread-safe. All of its methods are non-blocking, so it is safe
* to use from a realtime thread.
*/
class Timeline
{
public:
Timeline(const link::Timeline timeline, const bool bRespectQuantum);

/*! @brief: The tempo of the timeline, in bpm */
double tempo() const;

/*! @brief: Set the timeline tempo to the given bpm value, taking
* effect at the given time.
*/
void setTempo(double bpm, std::chrono::microseconds atTime);

/*! @brief: Get the beat value corresponding to the given time
* for the given quantum.
*
* @discussion: The magnitude of the resulting beat value is
* unique to this Link instance, but its phase with respect to
* the provided quantum is shared among all session
* peers. For non-negative beat values, the following
* property holds: fmod(beatAtTime(t, q), q) == phaseAtTime(t, q)
*/
double beatAtTime(std::chrono::microseconds time, double quantum) const;

/*! @brief: Get the session phase at the given time for the given
* quantum.
*
* @discussion: The result is in the interval [0, quantum). The
* result is equivalent to fmod(beatAtTime(t, q), q) for
* non-negative beat values. This method is convenient if the
* client is only interested in the phase and not the beat
* magnitude. Also, unlike fmod, it handles negative beat values
* correctly.
*/
double phaseAtTime(std::chrono::microseconds time, double quantum) const;

/*! @brief: Get the time at which the given beat occurs for the
* given quantum.
*
* @discussion: The inverse of beatAtTime, assuming a constant
* tempo. beatAtTime(timeAtBeat(b, q), q) === b.
*/
std::chrono::microseconds timeAtBeat(double beat, double quantum) const;

/*! @brief: Attempt to map the given beat to the given time in the
* context of the given quantum.
*
* @discussion: This method behaves differently depending on the
* state of the session. If no other peers are connected,
* then this instance is in a session by itself and is free to
* re-map the beat/time relationship whenever it pleases. In this
* case, beatAtTime(time, quantum) == beat after this method has
* been called.
*
* If there are other peers in the session, this instance
* should not abruptly re-map the beat/time relationship in the
* session because that would lead to beat discontinuities among
* the other peers. In this case, the given beat will be mapped
* to the next time value greater than the given time with the
* same phase as the given beat.
*
* This method is specifically designed to enable the concept of
* "quantized launch" in client applications. If there are no other
* peers in the session, then an event (such as starting
* transport) happens immediately when it is requested. If there
* are other peers, however, we wait until the next time at which
* the session phase matches the phase of the event, thereby
* executing the event in-phase with the other peers in the
* session. The client only needs to invoke this method to
* achieve this behavior and should not need to explicitly check
* the number of peers.
*/
void requestBeatAtTime(double beat, std::chrono::microseconds time, double quantum);

/*! @brief: Rudely re-map the beat/time relationship for all peers
* in a session.
*
* @discussion: DANGER: This method should only be needed in
* certain special circumstances. Most applications should not
* use it. It is very similar to requestBeatAtTime except that it
* does not fall back to the quantizing behavior when it is in a
* session with other peers. Calling this method will
* unconditionally map the given beat to the given time and
* broadcast the result to the session. This is very anti-social
* behavior and should be avoided.
*
* One of the few legitimate uses of this method is to
* synchronize a Link session with an external clock source. By
* periodically forcing the beat/time mapping according to an
* external clock source, a peer can effectively bridge that
* clock into a Link session. Much care must be taken at the
* application layer when implementing such a feature so that
* users do not accidentally disrupt Link sessions that they may
* join.
*/
void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum);

private:
friend Link;
link::Timeline mOriginalTimeline;
bool mbRespectQuantum;
link::Timeline mTimeline;
};

private:
std::mutex mCallbackMutex;
link::PeerCountCallback mPeerCountCallback;
link::TempoCallback mTempoCallback;
Clock mClock;
link::platform::Controller mController;
};

} // ableton

#include <ableton/Link.ipp>

+ 173
- 0
source/modules/hylia/link/ableton/Link.ipp View File

@@ -0,0 +1,173 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/Phase.hpp>

namespace ableton
{

inline Link::Link(const double bpm)
: mPeerCountCallback([](std::size_t) {})
, mTempoCallback([](link::Tempo) {})
, mClock{}
, mController(link::Tempo(bpm),
[this](const std::size_t peers) {
std::lock_guard<std::mutex> lock(mCallbackMutex);
mPeerCountCallback(peers);
},
[this](const link::Tempo tempo) {
std::lock_guard<std::mutex> lock(mCallbackMutex);
mTempoCallback(tempo);
},
mClock,
util::injectVal(link::platform::IoContext{}))
{
}

inline bool Link::isEnabled() const
{
return mController.isEnabled();
}

inline void Link::enable(const bool bEnable)
{
mController.enable(bEnable);
}

inline std::size_t Link::numPeers() const
{
return mController.numPeers();
}

template <typename Callback>
void Link::setNumPeersCallback(Callback callback)
{
std::lock_guard<std::mutex> lock(mCallbackMutex);
mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); };
}

template <typename Callback>
void Link::setTempoCallback(Callback callback)
{
std::lock_guard<std::mutex> lock(mCallbackMutex);
mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); };
}

inline Link::Clock Link::clock() const
{
return mClock;
}

inline Link::Timeline Link::captureAudioTimeline() const
{
return Link::Timeline{mController.timelineRtSafe(), numPeers() > 0};
}

inline void Link::commitAudioTimeline(const Link::Timeline timeline)
{
if (timeline.mOriginalTimeline != timeline.mTimeline)
{
mController.setTimelineRtSafe(timeline.mTimeline, mClock.micros());
}
}

inline Link::Timeline Link::captureAppTimeline() const
{
return Link::Timeline{mController.timeline(), numPeers() > 0};
}

inline void Link::commitAppTimeline(const Link::Timeline timeline)
{
if (timeline.mOriginalTimeline != timeline.mTimeline)
{
mController.setTimeline(timeline.mTimeline, mClock.micros());
}
}

// Link::Timeline

inline Link::Timeline::Timeline(const link::Timeline timeline, const bool bRespectQuantum)
: mOriginalTimeline(timeline)
, mbRespectQuantum(bRespectQuantum)
, mTimeline(timeline)
{
}

inline double Link::Timeline::tempo() const
{
return mTimeline.tempo.bpm();
}

inline void Link::Timeline::setTempo(
const double bpm, const std::chrono::microseconds atTime)
{
const auto desiredTl =
link::clampTempo(link::Timeline{link::Tempo(bpm), mTimeline.toBeats(atTime), atTime});
mTimeline.tempo = desiredTl.tempo;
mTimeline.timeOrigin = desiredTl.fromBeats(mTimeline.beatOrigin);
}

inline double Link::Timeline::beatAtTime(
const std::chrono::microseconds time, const double quantum) const
{
return link::toPhaseEncodedBeats(mTimeline, time, link::Beats{quantum}).floating();
}

inline double Link::Timeline::phaseAtTime(
const std::chrono::microseconds time, const double quantum) const
{
return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum})
.floating();
}

inline std::chrono::microseconds Link::Timeline::timeAtBeat(
const double beat, const double quantum) const
{
return link::fromPhaseEncodedBeats(mTimeline, link::Beats{beat}, link::Beats{quantum});
}

inline void Link::Timeline::requestBeatAtTime(
const double beat, std::chrono::microseconds time, const double quantum)
{
if (mbRespectQuantum)
{
time = timeAtBeat(link::nextPhaseMatch(link::Beats{beatAtTime(time, quantum)},
link::Beats{beat}, link::Beats{quantum})
.floating(),
quantum);
}
forceBeatAtTime(beat, time, quantum);
}

inline void Link::Timeline::forceBeatAtTime(
const double beat, const std::chrono::microseconds time, const double quantum)
{
// There are two components to the beat adjustment: a phase shift
// and a beat magnitude adjustment.
const auto curBeatAtTime = link::Beats{beatAtTime(time, quantum)};
const auto closestInPhase =
link::closestPhaseMatch(curBeatAtTime, link::Beats{beat}, link::Beats{quantum});
mTimeline = shiftClientTimeline(mTimeline, closestInPhase - curBeatAtTime);
// Now adjust the magnitude
mTimeline.beatOrigin = mTimeline.beatOrigin + (link::Beats{beat} - closestInPhase);
}

} // ableton

+ 103
- 0
source/modules/hylia/link/ableton/discovery/InterfaceScanner.hpp View File

@@ -0,0 +1,103 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/Injected.hpp>
#include <chrono>
#include <vector>

namespace ableton
{
namespace discovery
{

// Callback takes a range of asio::ip:address which is
// guaranteed to be sorted and unique
template <typename Callback, typename IoContext>
class InterfaceScanner
{
public:
using Timer = typename util::Injected<IoContext>::type::Timer;

InterfaceScanner(const std::chrono::seconds period,
util::Injected<Callback> callback,
util::Injected<IoContext> io)
: mPeriod(period)
, mCallback(std::move(callback))
, mIo(std::move(io))
, mTimer(mIo->makeTimer())
{
}

void enable(const bool bEnable)
{
if (bEnable)
{
scan();
}
else
{
mTimer.cancel();
}
}

void scan()
{
using namespace std;
debug(mIo->log()) << "Scanning network interfaces";
// Rescan the hardware for available network interface addresses
vector<asio::ip::address> addrs = mIo->scanNetworkInterfaces();
// Sort and unique them to guarantee consistent comparison
sort(begin(addrs), end(addrs));
addrs.erase(unique(begin(addrs), end(addrs)), end(addrs));
// Pass them to the callback
(*mCallback)(std::move(addrs));
// setup the next scanning
mTimer.expires_from_now(mPeriod);
using ErrorCode = typename Timer::ErrorCode;
mTimer.async_wait([this](const ErrorCode e) {
if (!e)
{
scan();
}
});
}

private:
const std::chrono::seconds mPeriod;
util::Injected<Callback> mCallback;
util::Injected<IoContext> mIo;
Timer mTimer;
};

// Factory function
template <typename Callback, typename IoContext>
InterfaceScanner<Callback, IoContext> makeInterfaceScanner(
const std::chrono::seconds period,
util::Injected<Callback> callback,
util::Injected<IoContext> io)
{
using namespace std;
return {period, move(callback), move(io)};
}

} // namespace discovery
} // namespace ableton

+ 123
- 0
source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp View File

@@ -0,0 +1,123 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioService.hpp>
#include <ableton/util/Injected.hpp>

namespace ableton
{
namespace discovery
{

inline asio::ip::udp::endpoint multicastEndpoint()
{
return {asio::ip::address::from_string("224.76.78.75"), 20808};
}

// Type tags for dispatching between unicast and multicast packets
struct MulticastTag
{
};
struct UnicastTag
{
};

template <typename IoContext, std::size_t MaxPacketSize>
class IpV4Interface
{
public:
using Socket = typename util::Injected<IoContext>::type::template Socket<MaxPacketSize>;

IpV4Interface(util::Injected<IoContext> io, const asio::ip::address_v4& addr)
: mIo(std::move(io))
, mMulticastReceiveSocket(mIo->template openMulticastSocket<MaxPacketSize>(addr))
, mSendSocket(mIo->template openUnicastSocket<MaxPacketSize>(addr))
{
}

IpV4Interface(const IpV4Interface&) = delete;
IpV4Interface& operator=(const IpV4Interface&) = delete;

IpV4Interface(IpV4Interface&& rhs)
: mIo(std::move(rhs.mIo))
, mMulticastReceiveSocket(std::move(rhs.mMulticastReceiveSocket))
, mSendSocket(std::move(rhs.mSendSocket))
{
}


std::size_t send(
const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to)
{
return mSendSocket.send(pData, numBytes, to);
}

template <typename Handler>
void receive(Handler handler, UnicastTag)
{
mSendSocket.receive(SocketReceiver<UnicastTag, Handler>{std::move(handler)});
}

template <typename Handler>
void receive(Handler handler, MulticastTag)
{
mMulticastReceiveSocket.receive(
SocketReceiver<MulticastTag, Handler>(std::move(handler)));
}

asio::ip::udp::endpoint endpoint() const
{
return mSendSocket.endpoint();
}

private:
template <typename Tag, typename Handler>
struct SocketReceiver
{
SocketReceiver(Handler handler)
: mHandler(std::move(handler))
{
}

template <typename It>
void operator()(
const asio::ip::udp::endpoint& from, const It messageBegin, const It messageEnd)
{
mHandler(Tag{}, from, messageBegin, messageEnd);
}

Handler mHandler;
};

util::Injected<IoContext> mIo;
Socket mMulticastReceiveSocket;
Socket mSendSocket;
};

template <std::size_t MaxPacketSize, typename IoContext>
IpV4Interface<IoContext, MaxPacketSize> makeIpV4Interface(
util::Injected<IoContext> io, const asio::ip::address_v4& addr)
{
return {std::move(io), addr};
}

} // namespace discovery
} // namespace ableton

+ 50
- 0
source/modules/hylia/link/ableton/discovery/MessageTypes.hpp View File

@@ -0,0 +1,50 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

namespace ableton
{
namespace discovery
{

// Message types used in the Ableton service discovery protocol. There
// are two logical messages: a state dump and a bye bye.
//
// A state dump provides all relevant information about the peer's
// current state as well as a Time To Live (TTL) value that indicates
// how many seconds this state should be considered valid.
//
// The bye bye indicates that the sender is leaving the session.

template <typename NodeState>
struct PeerState
{
NodeState peerState;
int ttl;
};

template <typename NodeId>
struct ByeBye
{
NodeId peerId;
};

} // namespace discovery
} // namespace ableton

+ 405
- 0
source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp View File

@@ -0,0 +1,405 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#if LINK_PLATFORM_MACOSX
#include <ableton/platforms/darwin/Darwin.hpp>
#elif LINK_PLATFORM_LINUX
#include <ableton/platforms/linux/Linux.hpp>
#endif

#include <chrono>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <vector>

#if LINK_PLATFORM_WINDOWS
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <Windows.h>
#endif

namespace ableton
{
namespace discovery
{

// Concept: NetworkByteStreamSerializable
//
// A type that can be encoded to a stream of bytes and decoded from a
// stream of bytes in network byte order. The following type is for
// documentation purposes only.

struct NetworkByteStreamSerializable
{
friend std::uint32_t sizeInByteStream(const NetworkByteStreamSerializable&);

// The byte stream pointed to by 'out' must have sufficient space to
// hold this object, as defined by sizeInByteStream.
template <typename It>
friend It toNetworkByteStream(const NetworkByteStreamSerializable&, It out);
};

// Deserialization aspect of the concept. Outside of the demonstration
// type above because clients must specify the type
// explicitly. Default implementation just defers to a class static
// method on T. For types that can't provide such a method, specialize
// this template.
template <typename T>
struct Deserialize
{
// Throws std::runtime_exception if parsing the type from the given
// byte range fails. Returns a pair of the correctly parsed value
// and an iterator to the next byte to parse.
template <typename It>
static std::pair<T, It> fromNetworkByteStream(It begin, It end)
{
return T::fromNetworkByteStream(std::move(begin), std::move(end));
}
};


// Default size implementation. Works for primitive types.

template <typename T>
std::uint32_t sizeInByteStream(T)
{
return sizeof(T);
}

namespace detail
{

// utilities for implementing concept for primitive types

template <typename T, typename It>
It copyToByteStream(T t, It out)
{
using namespace std;
return copy_n(
reinterpret_cast<typename iterator_traits<It>::pointer>(&t), sizeof(t), out);
}

template <typename T, typename It>
std::pair<T, It> copyFromByteStream(It begin, const It end)
{
using namespace std;
using ItDiff = typename iterator_traits<It>::difference_type;

if (distance(begin, end) < static_cast<ItDiff>(sizeof(T)))
{
throw range_error("Parsing type from byte stream failed");
}
else
{
T t;
const auto n = sizeof(t);
copy_n(begin, n, reinterpret_cast<uint8_t*>(&t));
return make_pair(t, begin + n);
}
}

} // namespace detail


// Model the concept for unsigned integral types

// uint8_t
template <typename It>
It toNetworkByteStream(const uint8_t byte, It out)
{
return detail::copyToByteStream(byte, std::move(out));
}

template <>
struct Deserialize<uint8_t>
{
template <typename It>
static std::pair<uint8_t, It> fromNetworkByteStream(It begin, It end)
{
return detail::copyFromByteStream<uint8_t>(std::move(begin), std::move(end));
}
};

// uint16_t
template <typename It>
It toNetworkByteStream(uint16_t s, It out)
{
return detail::copyToByteStream(htons(s), std::move(out));
}

template <>
struct Deserialize<uint16_t>
{
template <typename It>
static std::pair<uint16_t, It> fromNetworkByteStream(It begin, It end)
{
auto result = detail::copyFromByteStream<uint16_t>(std::move(begin), std::move(end));
result.first = ntohs(result.first);
return result;
}
};

// uint32_t
template <typename It>
It toNetworkByteStream(uint32_t l, It out)
{
return detail::copyToByteStream(htonl(l), std::move(out));
}

template <>
struct Deserialize<uint32_t>
{
template <typename It>
static std::pair<uint32_t, It> fromNetworkByteStream(It begin, It end)
{
auto result = detail::copyFromByteStream<uint32_t>(std::move(begin), std::move(end));
result.first = ntohl(result.first);
return result;
}
};

// int32_t in terms of uint32_t
template <typename It>
It toNetworkByteStream(int32_t l, It out)
{
return toNetworkByteStream(reinterpret_cast<const uint32_t&>(l), std::move(out));
}

template <>
struct Deserialize<int32_t>
{
template <typename It>
static std::pair<int32_t, It> fromNetworkByteStream(It begin, It end)
{
auto result =
Deserialize<uint32_t>::fromNetworkByteStream(std::move(begin), std::move(end));
return std::make_pair(reinterpret_cast<const int32_t&>(result.first), result.second);
}
};

// uint64_t
template <typename It>
It toNetworkByteStream(uint64_t ll, It out)
{
return detail::copyToByteStream(htonll(ll), std::move(out));
}

template <>
struct Deserialize<uint64_t>
{
template <typename It>
static std::pair<uint64_t, It> fromNetworkByteStream(It begin, It end)
{
auto result = detail::copyFromByteStream<uint64_t>(std::move(begin), std::move(end));
result.first = ntohll(result.first);
return result;
}
};

// int64_t in terms of uint64_t
template <typename It>
It toNetworkByteStream(int64_t ll, It out)
{
return toNetworkByteStream(reinterpret_cast<const uint64_t&>(ll), std::move(out));
}

template <>
struct Deserialize<int64_t>
{
template <typename It>
static std::pair<int64_t, It> fromNetworkByteStream(It begin, It end)
{
auto result =
Deserialize<uint64_t>::fromNetworkByteStream(std::move(begin), std::move(end));
return std::make_pair(reinterpret_cast<const int64_t&>(result.first), result.second);
}
};

// overloads for std::chrono durations
template <typename Rep, typename Ratio>
std::uint32_t sizeInByteStream(const std::chrono::duration<Rep, Ratio> dur)
{
return sizeInByteStream(dur.count());
}

template <typename Rep, typename Ratio, typename It>
It toNetworkByteStream(const std::chrono::duration<Rep, Ratio> dur, It out)
{
return toNetworkByteStream(dur.count(), std::move(out));
}

template <typename Rep, typename Ratio>
struct Deserialize<std::chrono::duration<Rep, Ratio>>
{
template <typename It>
static std::pair<std::chrono::duration<Rep, Ratio>, It> fromNetworkByteStream(
It begin, It end)
{
using namespace std;
auto result = Deserialize<Rep>::fromNetworkByteStream(move(begin), move(end));
return make_pair(std::chrono::duration<Rep, Ratio>{result.first}, result.second);
}
};

namespace detail
{

// Generic serialize/deserialize utilities for containers

template <typename Container>
std::uint32_t containerSizeInByteStream(const Container& container)
{
std::uint32_t totalSize = 0;
for (const auto& val : container)
{
totalSize += sizeInByteStream(val);
}
return totalSize;
}

template <typename Container, typename It>
It containerToNetworkByteStream(const Container& container, It out)
{
for (const auto& val : container)
{
out = toNetworkByteStream(val, out);
}
return out;
}

template <typename T, typename BytesIt, typename InsertIt>
BytesIt deserializeContainer(BytesIt bytesBegin,
const BytesIt bytesEnd,
InsertIt contBegin,
const std::uint32_t maxElements)
{
using namespace std;
std::uint32_t numElements = 0;
while (bytesBegin < bytesEnd && numElements < maxElements)
{
T newVal;
tie(newVal, bytesBegin) = Deserialize<T>::fromNetworkByteStream(bytesBegin, bytesEnd);
*contBegin++ = newVal;
++numElements;
}
return bytesBegin;
}

} // detail

// Need specific overloads for each container type, but use above
// utilities for common implementation

// array
template <typename T, std::size_t Size>
std::uint32_t sizeInByteStream(const std::array<T, Size>& arr)
{
return detail::containerSizeInByteStream(arr);
}

template <typename T, std::size_t Size, typename It>
It toNetworkByteStream(const std::array<T, Size>& arr, It out)
{
return detail::containerToNetworkByteStream(arr, std::move(out));
}

template <typename T, std::size_t Size>
struct Deserialize<std::array<T, Size>>
{
template <typename It>
static std::pair<std::array<T, Size>, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
array<T, Size> result{};
auto resultIt =
detail::deserializeContainer<T>(move(begin), move(end), move(result.begin()), Size);
return make_pair(move(result), move(resultIt));
}
};

// vector
template <typename T, typename Alloc>
std::uint32_t sizeInByteStream(const std::vector<T, Alloc>& vec)
{
return detail::containerSizeInByteStream(vec);
}

template <typename T, typename Alloc, typename It>
It toNetworkByteStream(const std::vector<T, Alloc>& vec, It out)
{
return detail::containerToNetworkByteStream(vec, std::move(out));
}

template <typename T, typename Alloc>
struct Deserialize<std::vector<T, Alloc>>
{
template <typename It>
static std::pair<std::vector<T, Alloc>, It> fromNetworkByteStream(
It bytesBegin, It bytesEnd)
{
using namespace std;
vector<T, Alloc> result;
// Use the number of bytes remaining in the stream as the upper
// bound on the number of elements that could be deserialized
// since we don't have a better heuristic.
auto resultIt = detail::deserializeContainer<T>(move(bytesBegin), move(bytesEnd),
back_inserter(result), static_cast<uint32_t>(distance(bytesBegin, bytesEnd)));

return make_pair(move(result), move(resultIt));
}
};

// 3-tuple
template <typename X, typename Y, typename Z>
std::uint32_t sizeInByteStream(const std::tuple<X, Y, Z>& tup)
{
return sizeInByteStream(std::get<0>(tup)) + sizeInByteStream(std::get<1>(tup))
+ sizeInByteStream(std::get<2>(tup));
}

template <typename X, typename Y, typename Z, typename It>
It toNetworkByteStream(const std::tuple<X, Y, Z>& tup, It out)
{
return toNetworkByteStream(
std::get<2>(tup), toNetworkByteStream(std::get<1>(tup),
toNetworkByteStream(std::get<0>(tup), std::move(out))));
}

template <typename X, typename Y, typename Z>
struct Deserialize<std::tuple<X, Y, Z>>
{
template <typename It>
static std::pair<std::tuple<X, Y, Z>, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto xres = Deserialize<X>::fromNetworkByteStream(begin, end);
auto yres = Deserialize<Y>::fromNetworkByteStream(xres.second, end);
auto zres = Deserialize<Z>::fromNetworkByteStream(yres.second, end);
return make_pair(
std::tuple<X, Y, Z>{move(xres.first), move(yres.first), move(zres.first)},
move(zres.second));
}
};

} // namespace discovery
} // namespace ableton

+ 293
- 0
source/modules/hylia/link/ableton/discovery/Payload.hpp View File

@@ -0,0 +1,293 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <functional>
#include <unordered_map>

namespace ableton
{
namespace discovery
{

struct PayloadEntryHeader
{
using Key = std::uint32_t;
using Size = std::uint32_t;

Key key;
Size size;

friend Size sizeInByteStream(const PayloadEntryHeader& header)
{
return sizeInByteStream(header.key) + sizeInByteStream(header.size);
}

template <typename It>
friend It toNetworkByteStream(const PayloadEntryHeader& header, It out)
{
return toNetworkByteStream(
header.size, toNetworkByteStream(header.key, std::move(out)));
}

template <typename It>
static std::pair<PayloadEntryHeader, It> fromNetworkByteStream(It begin, const It end)
{
using namespace std;
Key key;
Size size;
tie(key, begin) = Deserialize<Key>::fromNetworkByteStream(begin, end);
tie(size, begin) = Deserialize<Size>::fromNetworkByteStream(begin, end);
return make_pair(PayloadEntryHeader{move(key), move(size)}, move(begin));
}
};

template <typename EntryType>
struct PayloadEntry
{
PayloadEntry(EntryType entryVal)
: value(std::move(entryVal))
{
header = {EntryType::key, sizeInByteStream(value)};
}

PayloadEntryHeader header;
EntryType value;

friend std::uint32_t sizeInByteStream(const PayloadEntry& entry)
{
return sizeInByteStream(entry.header) + sizeInByteStream(entry.value);
}

template <typename It>
friend It toNetworkByteStream(const PayloadEntry& entry, It out)
{
return toNetworkByteStream(
entry.value, toNetworkByteStream(entry.header, std::move(out)));
}
};

namespace detail
{

template <typename It>
using HandlerMap =
std::unordered_map<typename PayloadEntryHeader::Key, std::function<void(It, It)>>;

// Given an index of handlers and a byte range, parse the bytes as a
// sequence of payload entries and invoke the appropriate handler for
// each entry type. Entries that are encountered that do not have a
// corresponding handler in the map are ignored. Throws
// std::runtime_error if parsing fails for any entry. Note that if an
// exception is thrown, some of the handlers may have already been called.
template <typename It>
void parseByteStream(HandlerMap<It>& map, It bsBegin, const It bsEnd)
{
using namespace std;

while (bsBegin < bsEnd)
{
// Try to parse an entry header at this location in the byte stream
PayloadEntryHeader header;
It valueBegin;
tie(header, valueBegin) =
Deserialize<PayloadEntryHeader>::fromNetworkByteStream(bsBegin, bsEnd);

// Ensure that the reported size of the entry does not exceed the
// length of the byte stream
It valueEnd = valueBegin + header.size;
if (bsEnd < valueEnd)
{
throw range_error("Payload with incorrect size.");
}

// The next entry will start at the end of this one
bsBegin = valueEnd;

// Use the appropriate handler for this entry, if available
auto handlerIt = map.find(header.key);
if (handlerIt != end(map))
{
handlerIt->second(move(valueBegin), move(valueEnd));
}
}
}

} // namespace detail


// Payload encoding
template <typename... Entries>
struct Payload;

template <typename First, typename Rest>
struct Payload<First, Rest>
{
Payload(First first, Rest rest)
: mFirst(std::move(first))
, mRest(std::move(rest))
{
}

Payload(PayloadEntry<First> first, Rest rest)
: mFirst(std::move(first))
, mRest(std::move(rest))
{
}

template <typename RhsFirst, typename RhsRest>
using PayloadSum =
Payload<First, typename Rest::template PayloadSum<RhsFirst, RhsRest>>;

// Concatenate payloads together into a single payload
template <typename RhsFirst, typename RhsRest>
friend PayloadSum<RhsFirst, RhsRest> operator+(
Payload lhs, Payload<RhsFirst, RhsRest> rhs)
{
return {std::move(lhs.mFirst), std::move(lhs.mRest) + std::move(rhs)};
}

friend std::size_t sizeInByteStream(const Payload& payload)
{
return sizeInByteStream(payload.mFirst) + sizeInByteStream(payload.mRest);
}

template <typename It>
friend It toNetworkByteStream(const Payload& payload, It streamIt)
{
return toNetworkByteStream(
payload.mRest, toNetworkByteStream(payload.mFirst, std::move(streamIt)));
}

PayloadEntry<First> mFirst;
Rest mRest;
};

template <>
struct Payload<>
{
template <typename RhsFirst, typename RhsRest>
using PayloadSum = Payload<RhsFirst, RhsRest>;

template <typename RhsFirst, typename RhsRest>
friend PayloadSum<RhsFirst, RhsRest> operator+(Payload, Payload<RhsFirst, RhsRest> rhs)
{
return rhs;
}

friend std::size_t sizeInByteStream(const Payload&)
{
return 0;
}

template <typename It>
friend It toNetworkByteStream(const Payload&, It streamIt)
{
return streamIt;
}
};

template <typename... Entries>
struct PayloadBuilder;

// Payload factory function
template <typename... Entries>
auto makePayload(Entries... entries)
-> decltype(PayloadBuilder<Entries...>{}(std::move(entries)...))
{
return PayloadBuilder<Entries...>{}(std::move(entries)...);
}

template <typename First, typename... Rest>
struct PayloadBuilder<First, Rest...>
{
auto operator()(First first, Rest... rest)
-> Payload<First, decltype(makePayload(std::move(rest)...))>
{
return {std::move(first), makePayload(std::move(rest)...)};
}
};

template <>
struct PayloadBuilder<>
{
Payload<> operator()()
{
return {};
}
};

// Parse payloads to values
template <typename... Entries>
struct ParsePayload;

template <typename First, typename... Rest>
struct ParsePayload<First, Rest...>
{
template <typename It, typename... Handlers>
static void parse(It begin, It end, Handlers... handlers)
{
detail::HandlerMap<It> map;
collectHandlers(map, std::move(handlers)...);
detail::parseByteStream(map, std::move(begin), std::move(end));
}

template <typename It, typename FirstHandler, typename... RestHandlers>
static void collectHandlers(
detail::HandlerMap<It>& map, FirstHandler handler, RestHandlers... rest)
{
using namespace std;
map[First::key] = [handler](const It begin, const It end) {
const auto res = First::fromNetworkByteStream(begin, end);
if (res.second != end)
{
std::ostringstream stringStream;
stringStream << "Parsing payload entry " << First::key
<< " did not consume the expected number of bytes. "
<< " Expected: " << distance(begin, end)
<< ", Actual: " << distance(begin, res.second);
throw range_error(stringStream.str());
}
handler(res.first);
};

ParsePayload<Rest...>::collectHandlers(map, std::move(rest)...);
}
};

template <>
struct ParsePayload<>
{
template <typename It>
static void collectHandlers(detail::HandlerMap<It>&)
{
}
};

template <typename... Entries, typename It, typename... Handlers>
void parsePayload(It begin, It end, Handlers... handlers)
{
using namespace std;
ParsePayload<Entries...>::parse(move(begin), move(end), move(handlers)...);
}

} // namespace discovery
} // namespace ableton

+ 253
- 0
source/modules/hylia/link/ableton/discovery/PeerGateway.hpp View File

@@ -0,0 +1,253 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/UdpMessenger.hpp>
#include <ableton/discovery/v1/Messages.hpp>
#include <ableton/platforms/asio/AsioService.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <memory>

namespace ableton
{
namespace discovery
{

template <typename Messenger, typename PeerObserver, typename IoContext>
class PeerGateway
{
public:
// The peer types are defined by the observer but must match with those
// used by the Messenger
using ObserverT = typename util::Injected<PeerObserver>::type;
using NodeState = typename ObserverT::GatewayObserverNodeState;
using NodeId = typename ObserverT::GatewayObserverNodeId;
using Timer = typename util::Injected<IoContext>::type::Timer;
using TimerError = typename Timer::ErrorCode;

PeerGateway(util::Injected<Messenger> messenger,
util::Injected<PeerObserver> observer,
util::Injected<IoContext> io)
: mpImpl(new Impl(std::move(messenger), std::move(observer), std::move(io)))
{
mpImpl->listen();
}

PeerGateway(const PeerGateway&) = delete;
PeerGateway& operator=(const PeerGateway&) = delete;

PeerGateway(PeerGateway&& rhs)
: mpImpl(std::move(rhs.mpImpl))
{
}

void updateState(NodeState state)
{
mpImpl->updateState(std::move(state));
}

private:
using PeerTimeout = std::pair<std::chrono::system_clock::time_point, NodeId>;
using PeerTimeouts = std::vector<PeerTimeout>;

struct Impl : std::enable_shared_from_this<Impl>
{
Impl(util::Injected<Messenger> messenger,
util::Injected<PeerObserver> observer,
util::Injected<IoContext> io)
: mMessenger(std::move(messenger))
, mObserver(std::move(observer))
, mIo(std::move(io))
, mPruneTimer(mIo->makeTimer())
{
}

void updateState(NodeState state)
{
mMessenger->updateState(std::move(state));
try
{
mMessenger->broadcastState();
}
catch (const std::runtime_error& err)
{
info(mIo->log()) << "State broadcast failed on gateway: " << err.what();
}
}

void listen()
{
mMessenger->receive(util::makeAsyncSafe(this->shared_from_this()));
}

// Operators for handling incoming messages
void operator()(const PeerState<NodeState>& msg)
{
onPeerState(msg.peerState, msg.ttl);
listen();
}

void operator()(const ByeBye<NodeId>& msg)
{
onByeBye(msg.peerId);
listen();
}

void onPeerState(const NodeState& nodeState, const int ttl)
{
using namespace std;
const auto peerId = nodeState.ident();
const auto existing = findPeer(peerId);
if (existing != end(mPeerTimeouts))
{
// If the peer is already present in our timeout list, remove it
// as it will be re-inserted below.
mPeerTimeouts.erase(existing);
}

auto newTo = make_pair(mPruneTimer.now() + std::chrono::seconds(ttl), peerId);
mPeerTimeouts.insert(
upper_bound(begin(mPeerTimeouts), end(mPeerTimeouts), newTo, TimeoutCompare{}),
move(newTo));

sawPeer(*mObserver, nodeState);
scheduleNextPruning();
}

void onByeBye(const NodeId& peerId)
{
const auto it = findPeer(peerId);
if (it != mPeerTimeouts.end())
{
peerLeft(*mObserver, it->second);
mPeerTimeouts.erase(it);
}
}

void pruneExpiredPeers()
{
using namespace std;

const auto test = make_pair(mPruneTimer.now(), NodeId{});
debug(mIo->log()) << "pruning peers @ " << test.first.time_since_epoch().count();

const auto endExpired =
lower_bound(begin(mPeerTimeouts), end(mPeerTimeouts), test, TimeoutCompare{});

for_each(begin(mPeerTimeouts), endExpired, [this](const PeerTimeout& pto) {
info(mIo->log()) << "pruning peer " << pto.second;
peerTimedOut(*mObserver, pto.second);
});
mPeerTimeouts.erase(begin(mPeerTimeouts), endExpired);
scheduleNextPruning();
}

void scheduleNextPruning()
{
// Find the next peer to expire and set the timer based on it
if (!mPeerTimeouts.empty())
{
// Add a second of padding to the timer to avoid over-eager timeouts
const auto t = mPeerTimeouts.front().first + std::chrono::seconds(1);

debug(mIo->log()) << "scheduling next pruning for "
<< t.time_since_epoch().count() << " because of peer "
<< mPeerTimeouts.front().second;

mPruneTimer.expires_at(t);
mPruneTimer.async_wait([this](const TimerError e) {
if (!e)
{
pruneExpiredPeers();
}
});
}
}

struct TimeoutCompare
{
bool operator()(const PeerTimeout& lhs, const PeerTimeout& rhs) const
{
return lhs.first < rhs.first;
}
};

typename PeerTimeouts::iterator findPeer(const NodeId& peerId)
{
return std::find_if(begin(mPeerTimeouts), end(mPeerTimeouts),
[&peerId](const PeerTimeout& pto) { return pto.second == peerId; });
}

util::Injected<Messenger> mMessenger;
util::Injected<PeerObserver> mObserver;
util::Injected<IoContext> mIo;
Timer mPruneTimer;
PeerTimeouts mPeerTimeouts; // Invariant: sorted by time_point
};

std::shared_ptr<Impl> mpImpl;
};

template <typename Messenger, typename PeerObserver, typename IoContext>
PeerGateway<Messenger, PeerObserver, IoContext> makePeerGateway(
util::Injected<Messenger> messenger,
util::Injected<PeerObserver> observer,
util::Injected<IoContext> io)
{
return {std::move(messenger), std::move(observer), std::move(io)};
}

// IpV4 gateway types
template <typename StateQuery, typename IoContext>
using IpV4Messenger =
UdpMessenger<IpV4Interface<typename util::Injected<IoContext>::type&,
v1::kMaxMessageSize>,
StateQuery,
IoContext>;

template <typename PeerObserver, typename StateQuery, typename IoContext>
using IpV4Gateway =
PeerGateway<IpV4Messenger<StateQuery, typename util::Injected<IoContext>::type&>,
PeerObserver,
IoContext>;

// Factory function to bind a PeerGateway to an IpV4Interface with the given address.
template <typename PeerObserver, typename NodeState, typename IoContext>
IpV4Gateway<PeerObserver, NodeState, IoContext> makeIpV4Gateway(
util::Injected<IoContext> io,
const asio::ip::address_v4& addr,
util::Injected<PeerObserver> observer,
NodeState state)
{
using namespace std;
using namespace util;

const uint8_t ttl = 5;
const uint8_t ttlRatio = 20;

auto iface = makeIpV4Interface<v1::kMaxMessageSize>(injectRef(*io), addr);

auto messenger =
makeUdpMessenger(injectVal(move(iface)), move(state), injectRef(*io), ttl, ttlRatio);
return {injectVal(move(messenger)), move(observer), move(io)};
}

} // namespace discovery
} // namespace ableton

+ 229
- 0
source/modules/hylia/link/ableton/discovery/PeerGateways.hpp View File

@@ -0,0 +1,229 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/InterfaceScanner.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <map>

namespace ableton
{
namespace discovery
{

// GatewayFactory must have an operator()(NodeState, IoRef, asio::ip::address)
// that constructs a new PeerGateway on a given interface address.
template <typename NodeState, typename GatewayFactory, typename IoContext>
class PeerGateways
{
public:
using IoType = typename util::Injected<IoContext>::type;
using Gateway = typename std::result_of<GatewayFactory(
NodeState, util::Injected<IoType&>, asio::ip::address)>::type;
using GatewayMap = std::map<asio::ip::address, Gateway>;

PeerGateways(const std::chrono::seconds rescanPeriod,
NodeState state,
GatewayFactory factory,
util::Injected<IoContext> io)
: mIo(std::move(io))
{
mpScannerCallback =
std::make_shared<Callback>(std::move(state), std::move(factory), *mIo);
mpScanner = std::make_shared<Scanner>(
rescanPeriod, util::injectShared(mpScannerCallback), util::injectRef(*mIo));
}

~PeerGateways()
{
// Release the callback in the io thread so that gateway cleanup
// doesn't happen in the client thread
mIo->async(Deleter{*this});
}

PeerGateways(const PeerGateways&) = delete;
PeerGateways& operator=(const PeerGateways&) = delete;

PeerGateways(PeerGateways&&) = delete;
PeerGateways& operator=(PeerGateways&&) = delete;

void enable(const bool bEnable)
{
auto pCallback = mpScannerCallback;
auto pScanner = mpScanner;

if (pCallback && pScanner)
{
mIo->async([pCallback, pScanner, bEnable] {
pCallback->mGateways.clear();
pScanner->enable(bEnable);
});
}
}

template <typename Handler>
void withGatewaysAsync(Handler handler)
{
auto pCallback = mpScannerCallback;
if (pCallback)
{
mIo->async([pCallback, handler] {
handler(pCallback->mGateways.begin(), pCallback->mGateways.end());
});
}
}

void updateNodeState(const NodeState& state)
{
auto pCallback = mpScannerCallback;
if (pCallback)
{
mIo->async([pCallback, state] {
pCallback->mState = state;
for (const auto& entry : pCallback->mGateways)
{
entry.second->updateNodeState(state);
}
});
}
}

// If a gateway has become non-responsive or is throwing exceptions,
// this method can be invoked to either fix it or discard it.
void repairGateway(const asio::ip::address& gatewayAddr)
{
auto pCallback = mpScannerCallback;
auto pScanner = mpScanner;
if (pCallback && pScanner)
{
mIo->async([pCallback, pScanner, gatewayAddr] {
if (pCallback->mGateways.erase(gatewayAddr))
{
// If we erased a gateway, rescan again immediately so that
// we will re-initialize it if it's still present
pScanner->scan();
}
});
}
}

private:
struct Callback
{
Callback(NodeState state, GatewayFactory factory, IoType& io)
: mState(std::move(state))
, mFactory(std::move(factory))
, mIo(io)
{
}

template <typename AddrRange>
void operator()(const AddrRange& range)
{
using namespace std;
// Get the set of current addresses.
vector<asio::ip::address> curAddrs;
curAddrs.reserve(mGateways.size());
transform(std::begin(mGateways), std::end(mGateways), back_inserter(curAddrs),
[](const typename GatewayMap::value_type& vt) { return vt.first; });

// Now use set_difference to determine the set of addresses that
// are new and the set of cur addresses that are no longer there
vector<asio::ip::address> newAddrs;
set_difference(std::begin(range), std::end(range), std::begin(curAddrs),
std::end(curAddrs), back_inserter(newAddrs));

vector<asio::ip::address> staleAddrs;
set_difference(std::begin(curAddrs), std::end(curAddrs), std::begin(range),
std::end(range), back_inserter(staleAddrs));

// Remove the stale addresses
for (const auto& addr : staleAddrs)
{
mGateways.erase(addr);
}

// Add the new addresses
for (const auto& addr : newAddrs)
{
try
{
// Only handle v4 for now
if (addr.is_v4())
{
info(mIo.log()) << "initializing peer gateway on interface " << addr;
mGateways.emplace(addr, mFactory(mState, util::injectRef(mIo), addr.to_v4()));
}
}
catch (const runtime_error& e)
{
warning(mIo.log()) << "failed to init gateway on interface " << addr
<< " reason: " << e.what();
}
}
}

NodeState mState;
GatewayFactory mFactory;
IoType& mIo;
GatewayMap mGateways;
};

using Scanner = InterfaceScanner<std::shared_ptr<Callback>, IoType&>;

struct Deleter
{
Deleter(PeerGateways& gateways)
: mpScannerCallback(std::move(gateways.mpScannerCallback))
, mpScanner(std::move(gateways.mpScanner))
{
}

void operator()()
{
mpScanner.reset();
mpScannerCallback.reset();
}

std::shared_ptr<Callback> mpScannerCallback;
std::shared_ptr<Scanner> mpScanner;
};

std::shared_ptr<Callback> mpScannerCallback;
std::shared_ptr<Scanner> mpScanner;
util::Injected<IoContext> mIo;
};

// Factory function
template <typename NodeState, typename GatewayFactory, typename IoContext>
std::unique_ptr<PeerGateways<NodeState, GatewayFactory, IoContext>> makePeerGateways(
const std::chrono::seconds rescanPeriod,
NodeState state,
GatewayFactory factory,
util::Injected<IoContext> io)
{
using namespace std;
using Gateways = PeerGateways<NodeState, GatewayFactory, IoContext>;
return unique_ptr<Gateways>{
new Gateways{rescanPeriod, move(state), move(factory), move(io)}};
}

} // namespace discovery
} // namespace ableton

+ 72
- 0
source/modules/hylia/link/ableton/discovery/Service.hpp View File

@@ -0,0 +1,72 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/PeerGateways.hpp>

namespace ableton
{
namespace discovery
{

template <typename NodeState, typename GatewayFactory, typename IoContext>
class Service
{
public:
using ServicePeerGateways = PeerGateways<NodeState, GatewayFactory, IoContext>;

Service(NodeState state, GatewayFactory factory, util::Injected<IoContext> io)
: mGateways(
std::chrono::seconds(5), std::move(state), std::move(factory), std::move(io))
{
}

void enable(const bool bEnable)
{
mGateways.enable(bEnable);
}

// Asynchronously operate on the current set of peer gateways. The
// handler will be invoked in the service's io context.
template <typename Handler>
void withGatewaysAsync(Handler handler)
{
mGateways.withGatewaysAsync(std::move(handler));
}

void updateNodeState(const NodeState& state)
{
mGateways.updateNodeState(state);
}

// Repair the gateway with the given address if possible. Its
// sockets may have been closed, for example, and the gateway needs
// to be regenerated.
void repairGateway(const asio::ip::address& gatewayAddr)
{
mGateways.repairGateway(gatewayAddr);
}

private:
ServicePeerGateways mGateways;
};

} // namespace discovery
} // namespace ableton

+ 139
- 0
source/modules/hylia/link/ableton/discovery/Socket.hpp View File

@@ -0,0 +1,139 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioService.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <array>
#include <cassert>

namespace ableton
{
namespace discovery
{

template <std::size_t MaxPacketSize>
struct Socket
{
Socket(platforms::asio::AsioService& io)
: mpImpl(std::make_shared<Impl>(io))
{
}

Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;

Socket(Socket&& rhs)
: mpImpl(std::move(rhs.mpImpl))
{
}

std::size_t send(
const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to)
{
assert(numBytes < MaxPacketSize);
return mpImpl->mSocket.send_to(asio::buffer(pData, numBytes), to);
}

template <typename Handler>
void receive(Handler handler)
{
mpImpl->mHandler = std::move(handler);
mpImpl->mSocket.async_receive_from(
asio::buffer(mpImpl->mReceiveBuffer, MaxPacketSize), mpImpl->mSenderEndpoint,
util::makeAsyncSafe(mpImpl));
}

asio::ip::udp::endpoint endpoint() const
{
return mpImpl->mSocket.local_endpoint();
}

struct Impl
{
Impl(platforms::asio::AsioService& io)
: mSocket(io.mService, asio::ip::udp::v4())
{
}

~Impl()
{
// Ignore error codes in shutdown and close as the socket may
// have already been forcibly closed
asio::error_code ec;
mSocket.shutdown(asio::ip::udp::socket::shutdown_both, ec);
mSocket.close(ec);
}

void operator()(const asio::error_code& error, const std::size_t numBytes)
{
if (!error && numBytes > 0 && numBytes <= MaxPacketSize)
{
const auto bufBegin = begin(mReceiveBuffer);
mHandler(mSenderEndpoint, bufBegin, bufBegin + static_cast<ptrdiff_t>(numBytes));
}
}

asio::ip::udp::socket mSocket;
asio::ip::udp::endpoint mSenderEndpoint;
using Buffer = std::array<uint8_t, MaxPacketSize>;
Buffer mReceiveBuffer;
using ByteIt = typename Buffer::const_iterator;
std::function<void(const asio::ip::udp::endpoint&, ByteIt, ByteIt)> mHandler;
};

std::shared_ptr<Impl> mpImpl;
};

// Configure an asio socket for receiving multicast messages
template <std::size_t MaxPacketSize>
void configureMulticastSocket(Socket<MaxPacketSize>& socket,
const asio::ip::address_v4& addr,
const asio::ip::udp::endpoint& multicastEndpoint)
{
socket.mpImpl->mSocket.set_option(asio::ip::udp::socket::reuse_address(true));
// ???
socket.mpImpl->mSocket.set_option(asio::socket_base::broadcast(!addr.is_loopback()));
// ???
socket.mpImpl->mSocket.set_option(
asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr));
// Is from_string("0.0.0.0") best approach?
socket.mpImpl->mSocket.bind(
{asio::ip::address::from_string("0.0.0.0"), multicastEndpoint.port()});
socket.mpImpl->mSocket.set_option(
asio::ip::multicast::join_group(multicastEndpoint.address().to_v4(), addr));
}

// Configure an asio socket for receiving unicast messages
template <std::size_t MaxPacketSize>
void configureUnicastSocket(
Socket<MaxPacketSize>& socket, const asio::ip::address_v4& addr)
{
// ??? really necessary?
socket.mpImpl->mSocket.set_option(
asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr));
socket.mpImpl->mSocket.bind(asio::ip::udp::endpoint{addr, 0});
}

} // namespace discovery
} // namespace ableton

+ 330
- 0
source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp View File

@@ -0,0 +1,330 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/IpV4Interface.hpp>
#include <ableton/discovery/MessageTypes.hpp>
#include <ableton/discovery/v1/Messages.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/Injected.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <algorithm>
#include <memory>

namespace ableton
{
namespace discovery
{

// An exception thrown when sending a udp message fails. Stores the
// interface through which the sending failed.
struct UdpSendException : std::runtime_error
{
UdpSendException(const std::runtime_error& e, asio::ip::address ifAddr)
: std::runtime_error(e.what())
, interfaceAddr(std::move(ifAddr))
{
}

asio::ip::address interfaceAddr;
};

// Throws UdpSendException
template <typename Interface, typename NodeId, typename Payload>
void sendUdpMessage(Interface& iface,
NodeId from,
const uint8_t ttl,
const v1::MessageType messageType,
const Payload& payload,
const asio::ip::udp::endpoint& to)
{
using namespace std;
v1::MessageBuffer buffer;
const auto messageBegin = begin(buffer);
const auto messageEnd =
v1::detail::encodeMessage(move(from), ttl, messageType, payload, messageBegin);
const auto numBytes = static_cast<size_t>(distance(messageBegin, messageEnd));
try
{
iface.send(buffer.data(), numBytes, to);
}
catch (const std::runtime_error& err)
{
throw UdpSendException{err, iface.endpoint().address()};
}
}

// UdpMessenger uses a "shared_ptr pImpl" pattern to make it movable
// and to support safe async handler callbacks when receiving messages
// on the given interface.
template <typename Interface, typename NodeStateT, typename IoContext>
class UdpMessenger
{
public:
using NodeState = NodeStateT;
using NodeId = typename NodeState::IdType;
using Timer = typename util::Injected<IoContext>::type::Timer;
using TimerError = typename Timer::ErrorCode;
using TimePoint = typename Timer::TimePoint;

UdpMessenger(util::Injected<Interface> iface,
NodeState state,
util::Injected<IoContext> io,
const uint8_t ttl,
const uint8_t ttlRatio)
: mpImpl(std::make_shared<Impl>(
std::move(iface), std::move(state), std::move(io), ttl, ttlRatio))
{
// We need to always listen for incoming traffic in order to
// respond to peer state broadcasts
mpImpl->listen(MulticastTag{});
mpImpl->listen(UnicastTag{});
mpImpl->broadcastState();
}

UdpMessenger(const UdpMessenger&) = delete;
UdpMessenger& operator=(const UdpMessenger&) = delete;

UdpMessenger(UdpMessenger&& rhs)
: mpImpl(std::move(rhs.mpImpl))
{
}

~UdpMessenger()
{
if (mpImpl != nullptr)
{
try
{
mpImpl->sendByeBye();
}
catch (const UdpSendException& err)
{
debug(mpImpl->mIo->log()) << "Failed to send bye bye message: " << err.what();
}
}
}

void updateState(NodeState state)
{
mpImpl->updateState(std::move(state));
}

// Broadcast the current state of the system to all peers. May throw
// std::runtime_error if assembling a broadcast message fails or if
// there is an error at the transport layer. Throws on failure.
void broadcastState()
{
mpImpl->broadcastState();
}

// Asynchronous receive function for incoming messages from peers. Will
// return immediately and the handler will be invoked when a message
// is received. Handler must have operator() overloads for PeerState and
// ByeBye messages.
template <typename Handler>
void receive(Handler handler)
{
mpImpl->setReceiveHandler(std::move(handler));
}

private:
struct Impl : std::enable_shared_from_this<Impl>
{
Impl(util::Injected<Interface> iface,
NodeState state,
util::Injected<IoContext> io,
const uint8_t ttl,
const uint8_t ttlRatio)
: mIo(std::move(io))
, mInterface(std::move(iface))
, mState(std::move(state))
, mTimer(mIo->makeTimer())
, mLastBroadcastTime{}
, mTtl(ttl)
, mTtlRatio(ttlRatio)
, mPeerStateHandler([](PeerState<NodeState>) {})
, mByeByeHandler([](ByeBye<NodeId>) {})
{
}

template <typename Handler>
void setReceiveHandler(Handler handler)
{
mPeerStateHandler = [handler](
PeerState<NodeState> state) { handler(std::move(state)); };

mByeByeHandler = [handler](ByeBye<NodeId> byeBye) { handler(std::move(byeBye)); };
}

void sendByeBye()
{
sendUdpMessage(
*mInterface, mState.ident(), 0, v1::kByeBye, makePayload(), multicastEndpoint());
}

void updateState(NodeState state)
{
mState = std::move(state);
}

void broadcastState()
{
using namespace std::chrono;

const auto minBroadcastPeriod = milliseconds{50};
const auto nominalBroadcastPeriod = milliseconds(mTtl * 1000 / mTtlRatio);
const auto timeSinceLastBroadcast =
duration_cast<milliseconds>(mTimer.now() - mLastBroadcastTime);

// The rate is limited to maxBroadcastRate to prevent flooding the network.
const auto delay = minBroadcastPeriod - timeSinceLastBroadcast;

// Schedule the next broadcast before we actually send the
// message so that if sending throws an exception we are still
// scheduled to try again. We want to keep trying at our
// interval as long as this instance is alive.
mTimer.expires_from_now(delay > milliseconds{0} ? delay : nominalBroadcastPeriod);
mTimer.async_wait([this](const TimerError e) {
if (!e)
{
broadcastState();
}
});

// If we're not delaying, broadcast now
if (delay < milliseconds{1})
{
debug(mIo->log()) << "Broadcasting state";
sendPeerState(v1::kAlive, multicastEndpoint());
}
}

void sendPeerState(
const v1::MessageType messageType, const asio::ip::udp::endpoint& to)
{
sendUdpMessage(
*mInterface, mState.ident(), mTtl, messageType, toPayload(mState), to);
mLastBroadcastTime = mTimer.now();
}

void sendResponse(const asio::ip::udp::endpoint& to)
{
sendPeerState(v1::kResponse, to);
}

template <typename Tag>
void listen(Tag tag)
{
mInterface->receive(util::makeAsyncSafe(this->shared_from_this()), tag);
}

template <typename Tag, typename It>
void operator()(Tag tag,
const asio::ip::udp::endpoint& from,
const It messageBegin,
const It messageEnd)
{
auto result = v1::parseMessageHeader<NodeId>(messageBegin, messageEnd);

const auto& header = result.first;
// Ignore messages from self and other groups
if (header.ident != mState.ident() && header.groupId == 0)
{
debug(mIo->log()) << "Received message type "
<< static_cast<int>(header.messageType) << " from peer "
<< header.ident;

switch (header.messageType)
{
case v1::kAlive:
sendResponse(from);
receivePeerState(std::move(result.first), result.second, messageEnd);
break;
case v1::kResponse:
receivePeerState(std::move(result.first), result.second, messageEnd);
break;
case v1::kByeBye:
receiveByeBye(std::move(result.first.ident));
break;
default:
info(mIo->log()) << "Unknown message received of type: " << header.messageType;
}
}
listen(tag);
}

template <typename It>
void receivePeerState(
v1::MessageHeader<NodeId> header, It payloadBegin, It payloadEnd)
{
try
{
auto state = NodeState::fromPayload(
std::move(header.ident), std::move(payloadBegin), std::move(payloadEnd));

// Handlers must only be called once
auto handler = std::move(mPeerStateHandler);
mPeerStateHandler = [](PeerState<NodeState>) {};
handler(PeerState<NodeState>{std::move(state), header.ttl});
}
catch (const std::runtime_error& err)
{
info(mIo->log()) << "Ignoring peer state message: " << err.what();
}
}

void receiveByeBye(NodeId nodeId)
{
// Handlers must only be called once
auto byeByeHandler = std::move(mByeByeHandler);
mByeByeHandler = [](ByeBye<NodeId>) {};
byeByeHandler(ByeBye<NodeId>{std::move(nodeId)});
}

util::Injected<IoContext> mIo;
util::Injected<Interface> mInterface;
NodeState mState;
Timer mTimer;
TimePoint mLastBroadcastTime;
uint8_t mTtl;
uint8_t mTtlRatio;
std::function<void(PeerState<NodeState>)> mPeerStateHandler;
std::function<void(ByeBye<NodeId>)> mByeByeHandler;
};

std::shared_ptr<Impl> mpImpl;
};

// Factory function
template <typename Interface, typename NodeState, typename IoContext>
UdpMessenger<Interface, NodeState, IoContext> makeUdpMessenger(
util::Injected<Interface> iface,
NodeState state,
util::Injected<IoContext> io,
const uint8_t ttl,
const uint8_t ttlRatio)
{
return UdpMessenger<Interface, NodeState, IoContext>{
std::move(iface), std::move(state), std::move(io), ttl, ttlRatio};
}

} // namespace discovery
} // namespace ableton

+ 75
- 0
source/modules/hylia/link/ableton/discovery/test/Interface.hpp View File

@@ -0,0 +1,75 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/util/Log.hpp>

namespace ableton
{
namespace discovery
{
namespace test
{

class Interface
{
public:
void send(const uint8_t* const bytes,
const size_t numBytes,
const asio::ip::udp::endpoint& endpoint)
{
sentMessages.push_back(
std::make_pair(std::vector<uint8_t>{bytes, bytes + numBytes}, endpoint));
}

template <typename Callback, typename Tag>
void receive(Callback callback, Tag tag)
{
mCallback = [callback, tag](
const asio::ip::udp::endpoint& from, const std::vector<uint8_t>& buffer) {
callback(tag, from, begin(buffer), end(buffer));
};
}

template <typename It>
void incomingMessage(
const asio::ip::udp::endpoint& from, It messageBegin, It messageEnd)
{
std::vector<uint8_t> buffer{messageBegin, messageEnd};
mCallback(from, buffer);
}

asio::ip::udp::endpoint endpoint() const
{
return asio::ip::udp::endpoint({}, 0);
}

using SentMessage = std::pair<std::vector<uint8_t>, asio::ip::udp::endpoint>;
std::vector<SentMessage> sentMessages;

private:
using ReceiveCallback =
std::function<void(const asio::ip::udp::endpoint&, const std::vector<uint8_t>&)>;
ReceiveCallback mCallback;
};

} // namespace test
} // namespace discovery
} // namespace ableton

+ 98
- 0
source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp View File

@@ -0,0 +1,98 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <cstdint>
#include <utility>

namespace ableton
{
namespace discovery
{
namespace test
{

// Test payload entries

// A fixed-size entry type
struct Foo
{
enum
{
key = '_foo'
};

std::int32_t fooVal;

friend std::uint32_t sizeInByteStream(const Foo& foo)
{
// Namespace qualification is needed to avoid ambiguous function definitions
return discovery::sizeInByteStream(foo.fooVal);
}

template <typename It>
friend It toNetworkByteStream(const Foo& foo, It out)
{
return discovery::toNetworkByteStream(foo.fooVal, std::move(out));
}

template <typename It>
static std::pair<Foo, It> fromNetworkByteStream(It begin, It end)
{
auto result = Deserialize<decltype(fooVal)>::fromNetworkByteStream(
std::move(begin), std::move(end));
return std::make_pair(Foo{std::move(result.first)}, std::move(result.second));
}
};

// A variable-size entry type
struct Bar
{
enum
{
key = '_bar'
};

std::vector<std::uint64_t> barVals;

friend std::uint32_t sizeInByteStream(const Bar& bar)
{
return discovery::sizeInByteStream(bar.barVals);
}

template <typename It>
friend It toNetworkByteStream(const Bar& bar, It out)
{
return discovery::toNetworkByteStream(bar.barVals, out);
}

template <typename It>
static std::pair<Bar, It> fromNetworkByteStream(It begin, It end)
{
auto result = Deserialize<decltype(barVals)>::fromNetworkByteStream(
std::move(begin), std::move(end));
return std::make_pair(Bar{std::move(result.first)}, std::move(result.second));
}
};

} // namespace test
} // namespace discovery
} // namespace ableton

+ 83
- 0
source/modules/hylia/link/ableton/discovery/test/Socket.hpp View File

@@ -0,0 +1,83 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/Log.hpp>
#include <ableton/util/test/IoService.hpp>

namespace ableton
{
namespace discovery
{
namespace test
{

class Socket
{
public:
Socket(util::test::IoService&)
{
}

friend void configureUnicastSocket(Socket&, const asio::ip::address_v4&)
{
}

std::size_t send(
const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to)
{
sentMessages.push_back(
std::make_pair(std::vector<uint8_t>{pData, pData + numBytes}, to));
return numBytes;
}

template <typename Handler>
void receive(Handler handler)
{
mCallback = [handler](const asio::ip::udp::endpoint& from,
const std::vector<uint8_t>& buffer) { handler(from, begin(buffer), end(buffer)); };
}

template <typename It>
void incomingMessage(
const asio::ip::udp::endpoint& from, It messageBegin, It messageEnd)
{
std::vector<uint8_t> buffer{messageBegin, messageEnd};
mCallback(from, buffer);
}

asio::ip::udp::endpoint endpoint() const
{
return asio::ip::udp::endpoint({}, 0);
}

using SentMessage = std::pair<std::vector<uint8_t>, asio::ip::udp::endpoint>;
std::vector<SentMessage> sentMessages;

private:
using ReceiveCallback =
std::function<void(const asio::ip::udp::endpoint&, const std::vector<uint8_t>&)>;
ReceiveCallback mCallback;
};

} // namespace test
} // namespace discovery
} // namespace ableton

+ 168
- 0
source/modules/hylia/link/ableton/discovery/v1/Messages.hpp View File

@@ -0,0 +1,168 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Payload.hpp>
#include <array>

namespace ableton
{
namespace discovery
{
namespace v1
{

// The maximum size of a message, in bytes
const std::size_t kMaxMessageSize = 512;
// Utility typedef for an array of bytes of maximum message size
using MessageBuffer = std::array<uint8_t, v1::kMaxMessageSize>;

using MessageType = uint8_t;
using SessionGroupId = uint16_t;

const MessageType kInvalid = 0;
const MessageType kAlive = 1;
const MessageType kResponse = 2;
const MessageType kByeBye = 3;

template <typename NodeId>
struct MessageHeader
{
MessageType messageType;
uint8_t ttl;
SessionGroupId groupId;
NodeId ident;

friend std::uint32_t sizeInByteStream(const MessageHeader& header)
{
return discovery::sizeInByteStream(header.messageType)
+ discovery::sizeInByteStream(header.ttl)
+ discovery::sizeInByteStream(header.groupId)
+ discovery::sizeInByteStream(header.ident);
}

template <typename It>
friend It toNetworkByteStream(const MessageHeader& header, It out)
{
return discovery::toNetworkByteStream(header.ident,
discovery::toNetworkByteStream(header.groupId,
discovery::toNetworkByteStream(header.ttl,
discovery::toNetworkByteStream(header.messageType, std::move(out)))));
}

template <typename It>
static std::pair<MessageHeader, It> fromNetworkByteStream(It begin, const It end)
{
using namespace std;

MessageHeader header;
tie(header.messageType, begin) =
Deserialize<decltype(header.messageType)>::fromNetworkByteStream(begin, end);
tie(header.ttl, begin) =
Deserialize<decltype(header.ttl)>::fromNetworkByteStream(begin, end);
tie(header.groupId, begin) =
Deserialize<decltype(header.groupId)>::fromNetworkByteStream(begin, end);
tie(header.ident, begin) =
Deserialize<decltype(header.ident)>::fromNetworkByteStream(begin, end);

return make_pair(move(header), move(begin));
}
};

namespace detail
{

// Types that are only used in the sending/parsing of messages, not
// publicly exposed.
using ProtocolHeader = std::array<char, 8>;
const ProtocolHeader kProtocolHeader = {{'_', 'a', 's', 'd', 'p', '_', 'v', 1}};

// Must have at least kMaxMessageSize bytes available in the output stream
template <typename NodeId, typename Payload, typename It>
It encodeMessage(NodeId from,
const uint8_t ttl,
const MessageType messageType,
const Payload& payload,
It out)
{
using namespace std;
const MessageHeader<NodeId> header = {messageType, ttl, 0, std::move(from)};
const auto messageSize =
kProtocolHeader.size() + sizeInByteStream(header) + sizeInByteStream(payload);

if (messageSize < kMaxMessageSize)
{
return toNetworkByteStream(
payload, toNetworkByteStream(
header, copy(begin(kProtocolHeader), end(kProtocolHeader), move(out))));
}
else
{
throw range_error("Exceeded maximum message size");
}
}

} // namespace detail

template <typename NodeId, typename Payload, typename It>
It aliveMessage(NodeId from, const uint8_t ttl, const Payload& payload, It out)
{
return detail::encodeMessage(std::move(from), ttl, kAlive, payload, std::move(out));
}

template <typename NodeId, typename Payload, typename It>
It responseMessage(NodeId from, const uint8_t ttl, const Payload& payload, It out)
{
return detail::encodeMessage(std::move(from), ttl, kResponse, payload, std::move(out));
}

template <typename NodeId, typename It>
It byeByeMessage(NodeId from, It out)
{
return detail::encodeMessage(
std::move(from), 0, kByeBye, makePayload(), std::move(out));
}

template <typename NodeId, typename It>
std::pair<MessageHeader<NodeId>, It> parseMessageHeader(It bytesBegin, const It bytesEnd)
{
using namespace std;
using ItDiff = typename iterator_traits<It>::difference_type;

MessageHeader<NodeId> header = {};
const auto protocolHeaderSize = discovery::sizeInByteStream(detail::kProtocolHeader);
const auto minMessageSize =
static_cast<ItDiff>(protocolHeaderSize + sizeInByteStream(header));

// If there are enough bytes in the stream to make a header and if
// the first bytes in the stream are the protocol header, then
// proceed to parse the stream.
if (distance(bytesBegin, bytesEnd) >= minMessageSize
&& equal(begin(detail::kProtocolHeader), end(detail::kProtocolHeader), bytesBegin))
{
tie(header, bytesBegin) = MessageHeader<NodeId>::fromNetworkByteStream(
bytesBegin + protocolHeaderSize, bytesEnd);
}
return make_pair(move(header), move(bytesBegin));
}

} // namespace v1
} // namespace discovery
} // namespace ableton

+ 103
- 0
source/modules/hylia/link/ableton/link/Beats.hpp View File

@@ -0,0 +1,103 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <cmath>
#include <cstdint>
#include <tuple>

namespace ableton
{
namespace link
{

struct Beats : std::tuple<std::int64_t>
{
Beats() = default;

explicit Beats(const double beats)
: std::tuple<std::int64_t>(llround(beats * 1e6))
{
}

explicit Beats(const std::int64_t microBeats)
: std::tuple<std::int64_t>(microBeats)
{
}

double floating() const
{
return microBeats() / 1e6;
}

std::int64_t microBeats() const
{
return std::get<0>(*this);
}

Beats operator-() const
{
return Beats{-microBeats()};
}

friend Beats abs(const Beats b)
{
return Beats{std::abs(b.microBeats())};
}

friend Beats operator+(const Beats lhs, const Beats rhs)
{
return Beats{lhs.microBeats() + rhs.microBeats()};
}

friend Beats operator-(const Beats lhs, const Beats rhs)
{
return Beats{lhs.microBeats() - rhs.microBeats()};
}

friend Beats operator%(const Beats lhs, const Beats rhs)
{
return rhs == Beats{0.} ? Beats{0.} : Beats{lhs.microBeats() % rhs.microBeats()};
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const Beats beats)
{
return discovery::sizeInByteStream(beats.microBeats());
}

template <typename It>
friend It toNetworkByteStream(const Beats beats, It out)
{
return discovery::toNetworkByteStream(beats.microBeats(), std::move(out));
}

template <typename It>
static std::pair<Beats, It> fromNetworkByteStream(It begin, It end)
{
auto result = discovery::Deserialize<std::int64_t>::fromNetworkByteStream(
std::move(begin), std::move(end));
return std::make_pair(Beats{result.first}, std::move(result.second));
}
};

} // namespace link
} // namespace ableton

+ 115
- 0
source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp View File

@@ -0,0 +1,115 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/GhostXForm.hpp>
#include <ableton/link/Timeline.hpp>

namespace ableton
{
namespace link
{

// Clamp the tempo of the given timeline to the valid Link range
inline Timeline clampTempo(const Timeline timeline)
{
const double kMinBpm = 20.0;
const double kMaxBpm = 999.0;
return {Tempo{(std::min)((std::max)(timeline.tempo.bpm(), kMinBpm), kMaxBpm)},
timeline.beatOrigin, timeline.timeOrigin};
}

// Given an existing client timeline, a session timeline, and the
// global host transform of the session, return a new version of the client
// timeline. The resulting new client timeline is continuous with the
// previous timeline so that curClient.toBeats(atTime) ==
// result.toBeats(atTime).
inline Timeline updateClientTimelineFromSession(const Timeline curClient,
const Timeline session,
const std::chrono::microseconds atTime,
const GhostXForm xform)
{
// An intermediate timeline representing the continuation of the
// existing client timeline with the tempo from the session timeline.
const auto tempTl = Timeline{session.tempo, curClient.toBeats(atTime), atTime};
// The host time corresponding to beat 0 on the session
// timeline. Beat 0 on the session timeline is important because it
// serves as the origin of the quantization grid for all participants.
const auto hostBeatZero = xform.ghostToHost(session.fromBeats(Beats{INT64_C(0)}));
// The new client timeline becomes the result of sliding the
// intermediate timeline back so that it's anchor corresponds to
// beat zero on the session timeline. The result preserves the
// magnitude of beats on the client timeline while encoding the
// quantization reference point in the time and beatOrigins.
return {tempTl.tempo, tempTl.toBeats(hostBeatZero), hostBeatZero};
}


inline Timeline updateSessionTimelineFromClient(const Timeline curSession,
const Timeline client,
const std::chrono::microseconds atTime,
const GhostXForm xform)
{
// The client timeline was constructed so that it's timeOrigin
// corresponds to beat 0 on the session timeline.
const auto ghostBeat0 = xform.hostToGhost(client.timeOrigin);

const auto zero = Beats{INT64_C(0)};
// If beat 0 was not shifted and there is not a new tempo, an update
// of the session timeline is not required. Don't create an
// equivalent timeline with different anchor points if not needed as
// this will trigger other unnecessary changes.
if (curSession.toBeats(ghostBeat0) == zero && client.tempo == curSession.tempo)
{
return curSession;
}
else
{
// An intermediate timeline representing the new tempo, the
// effective time, and a possibly adjusted origin.
const auto tempTl = Timeline{client.tempo, zero, ghostBeat0};
// The final session timeline must have the beat corresponding to
// atTime on the old session timeline as its beatOrigin because this is
// used for prioritization of timelines among peers - we can't let a
// modification applied by the client artificially increase or
// reduce the timeline's priority in the session. The new beat
// origin should be as close as possible to lining up with atTime,
// but we must also make sure that it's > curSession.beatOrigin
// because otherwise it will get ignored.
const auto newBeatOrigin = (std::max)(curSession.toBeats(xform.hostToGhost(atTime)),
curSession.beatOrigin + Beats{INT64_C(1)});
return {client.tempo, newBeatOrigin, tempTl.fromBeats(newBeatOrigin)};
}
}

// Shift timeline so result.toBeats(t) == client.toBeats(t) +
// shift. This takes into account the fact that the timeOrigin
// corresponds to beat 0 on the session timeline. Using this function
// and then setting the session timeline with the result will change
// the phase of the session by the given shift amount.
inline Timeline shiftClientTimeline(Timeline client, const Beats shift)
{
const auto timeDelta = client.fromBeats(shift) - client.fromBeats(Beats{INT64_C(0)});
client.timeOrigin = client.timeOrigin - timeDelta;
return client;
}

} // link
} // ableton

+ 446
- 0
source/modules/hylia/link/ableton/link/Controller.hpp View File

@@ -0,0 +1,446 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Service.hpp>
#include <ableton/link/ClientSessionTimelines.hpp>
#include <ableton/link/Gateway.hpp>
#include <ableton/link/GhostXForm.hpp>
#include <ableton/link/NodeState.hpp>
#include <ableton/link/Peers.hpp>
#include <ableton/link/Sessions.hpp>
#include <mutex>

namespace ableton
{
namespace link
{
namespace detail
{

template <typename Clock>
GhostXForm initXForm(const Clock& clock)
{
// Make the current time map to a ghost time of 0 with ghost time
// increasing at the same rate as clock time
return {1.0, -clock.micros()};
}

// The timespan in which local modifications to the timeline will be
// preferred over any modifications coming from the network.
const auto kLocalModGracePeriod = std::chrono::seconds(1);

} // namespace detail

// function types corresponding to the Controller callback type params
using PeerCountCallback = std::function<void(std::size_t)>;
using TempoCallback = std::function<void(ableton::link::Tempo)>;

// The main Link controller
template <typename PeerCountCallback,
typename TempoCallback,
typename Clock,
typename IoContext>
class Controller
{
public:
using Ticks = typename Clock::Ticks;

Controller(Tempo tempo,
PeerCountCallback peerCallback,
TempoCallback tempoCallback,
Clock clock,
util::Injected<IoContext> io)
: mTempoCallback(std::move(tempoCallback))
, mClock(std::move(clock))
, mNodeId(NodeId::random())
, mSessionId(mNodeId)
, mGhostXForm(detail::initXForm(mClock))
, mSessionTimeline(clampTempo({tempo, Beats{0.}, std::chrono::microseconds{0}}))
, mClientTimeline({mSessionTimeline.tempo, Beats{0.},
mGhostXForm.ghostToHost(std::chrono::microseconds{0})})
, mRtClientTimeline(mClientTimeline)
, mRtClientTimelineTimestamp(0)
, mSessionPeerCounter(*this, std::move(peerCallback))
, mEnabled(false)
, mIo(std::move(io))
, mRealtimeIo(util::injectRef(*mIo))
, mPeers(util::injectRef(*mIo),
std::ref(mSessionPeerCounter),
SessionTimelineCallback{*this})
, mSessions({mSessionId, mSessionTimeline, {mGhostXForm, mClock.micros()}},
util::injectRef(mPeers),
MeasurePeer{*this},
JoinSessionCallback{*this},
util::injectRef(*mIo),
mClock)
, mDiscovery(
std::make_pair(NodeState{mNodeId, mSessionId, mSessionTimeline}, mGhostXForm),
GatewayFactory{*this},
util::injectVal(mIo->clone(UdpSendExceptionHandler{*this})))
{
}

Controller(const Controller&) = delete;
Controller(Controller&&) = delete;

Controller& operator=(const Controller&) = delete;
Controller& operator=(Controller&&) = delete;

void enable(const bool bEnable)
{
const bool bWasEnabled = mEnabled.exchange(bEnable);
if (bWasEnabled != bEnable)
{
mRealtimeIo.async([this, bEnable] {
if (bEnable)
{
// Always reset when first enabling to avoid hijacking
// tempo in existing sessions
resetState();
}
mDiscovery.enable(bEnable);
});
}
}

bool isEnabled() const
{
return mEnabled;
}

std::size_t numPeers() const
{
return mSessionPeerCounter.mSessionPeerCount;
}

// Get the current Link timeline. Thread-safe but may block, so
// it cannot be used from audio thread.
Timeline timeline() const
{
std::lock_guard<std::mutex> lock(mClientTimelineGuard);
return mClientTimeline;
}

// Set the timeline to be used, starting at the given
// time. Thread-safe but may block, so it cannot be used from audio thread.
void setTimeline(Timeline newTimeline, const std::chrono::microseconds atTime)
{
newTimeline = clampTempo(newTimeline);
{
std::lock_guard<std::mutex> lock(mClientTimelineGuard);
mClientTimeline = newTimeline;
}
mIo->async([this, newTimeline, atTime] {
handleTimelineFromClient(updateSessionTimelineFromClient(
mSessionTimeline, newTimeline, atTime, mGhostXForm));
});
}

// Non-blocking timeline access for a realtime context. NOT
// thread-safe. Must not be called from multiple threads
// concurrently and must not be called concurrently with setTimelineRtSafe.
Timeline timelineRtSafe() const
{
// Respect the session timing guard but don't block on it. If we
// can't access it because it's being modified we fall back to our
// cached version of the timeline.
const auto now = mClock.micros();
if (now - mRtClientTimelineTimestamp > detail::kLocalModGracePeriod
&& mSessionTimingGuard.try_lock())
{
const auto clientTimeline = updateClientTimelineFromSession(
mRtClientTimeline, mSessionTimeline, now, mGhostXForm);

mSessionTimingGuard.unlock();

if (clientTimeline != mRtClientTimeline)
{
mRtClientTimeline = clientTimeline;
}
}

return mRtClientTimeline;
}

// should only be called from the audio thread
void setTimelineRtSafe(Timeline newTimeline, const std::chrono::microseconds atTime)
{
newTimeline = clampTempo(newTimeline);
// Cache the new timeline for serving back to the client
mRtClientTimeline = newTimeline;
mRtClientTimelineTimestamp =
isEnabled() ? mClock.micros() : std::chrono::microseconds(0);

// Update the session timeline from the new client timeline
mRealtimeIo.async([this, newTimeline, atTime] {
// Synchronize with the non-rt version of the client timeline
{
std::lock_guard<std::mutex> lock(mClientTimelineGuard);
mClientTimeline = newTimeline;
}
handleTimelineFromClient(updateSessionTimelineFromClient(
mSessionTimeline, newTimeline, atTime, mGhostXForm));
});
}

private:
void updateSessionTiming(const Timeline newTimeline, const GhostXForm newXForm)
{
const auto oldTimeline = mSessionTimeline;
const auto oldXForm = mGhostXForm;

if (oldTimeline != newTimeline || oldXForm != newXForm)
{
{
std::lock_guard<std::mutex> lock(mSessionTimingGuard);
mSessionTimeline = newTimeline;
mGhostXForm = newXForm;
}

// Update the client timeline based on the new session timing data
{
std::lock_guard<std::mutex> lock(mClientTimelineGuard);
mClientTimeline = updateClientTimelineFromSession(
mClientTimeline, mSessionTimeline, mClock.micros(), mGhostXForm);
}

// Push the change to the discovery service
mDiscovery.updateNodeState(
std::make_pair(NodeState{mNodeId, mSessionId, newTimeline}, newXForm));

if (oldTimeline.tempo != newTimeline.tempo)
{
mTempoCallback(newTimeline.tempo);
}
}
}

void handleTimelineFromClient(Timeline tl)
{
mSessions.resetTimeline(tl);
mPeers.setSessionTimeline(mSessionId, tl);
updateSessionTiming(std::move(tl), mGhostXForm);
}

void handleTimelineFromSession(SessionId id, Timeline timeline)
{
debug(mIo->log()) << "Received timeline with tempo: " << timeline.tempo.bpm()
<< " for session: " << id;
updateSessionTiming(
mSessions.sawSessionTimeline(std::move(id), std::move(timeline)), mGhostXForm);
}

void joinSession(const Session& session)
{
const bool sessionIdChanged = mSessionId != session.sessionId;
mSessionId = session.sessionId;
updateSessionTiming(session.timeline, session.measurement.xform);

if (sessionIdChanged)
{
debug(mIo->log()) << "Joining session " << session.sessionId << " with tempo "
<< session.timeline.tempo.bpm();
mSessionPeerCounter();
}
}

void resetState()
{
mNodeId = NodeId::random();
mSessionId = mNodeId;

const auto xform = detail::initXForm(mClock);
const auto hostTime = -xform.intercept;
// When creating the new timeline, make it continuous by finding
// the beat on the old session timeline corresponding to the
// current host time and mapping it to the new ghost time
// representation of the current host time.
const auto newTl = Timeline{mSessionTimeline.tempo,
mSessionTimeline.toBeats(mGhostXForm.hostToGhost(hostTime)),
xform.hostToGhost(hostTime)};

updateSessionTiming(newTl, xform);

mSessions.resetSession({mNodeId, newTl, {xform, hostTime}});
mPeers.resetPeers();
}

struct SessionTimelineCallback
{
void operator()(SessionId id, Timeline timeline)
{
mController.handleTimelineFromSession(std::move(id), std::move(timeline));
}

Controller& mController;
};

struct SessionPeerCounter
{
SessionPeerCounter(Controller& controller, PeerCountCallback callback)
: mController(controller)
, mCallback(std::move(callback))
, mSessionPeerCount(0)
{
}

void operator()()
{
const auto count =
mController.mPeers.uniqueSessionPeerCount(mController.mSessionId);
const auto oldCount = mSessionPeerCount.exchange(count);
if (oldCount != count)
{
if (count == 0)
{
// When the count goes down to zero, completely reset the
// state, effectively founding a new session
mController.resetState();
}
mCallback(count);
}
}

Controller& mController;
PeerCountCallback mCallback;
std::atomic<std::size_t> mSessionPeerCount;
};

struct MeasurePeer
{
template <typename Peer, typename Handler>
void operator()(Peer peer, Handler handler)
{
using It = typename Discovery::ServicePeerGateways::GatewayMap::iterator;
using ValueType = typename Discovery::ServicePeerGateways::GatewayMap::value_type;
mController.mDiscovery.withGatewaysAsync([peer, handler](It begin, const It end) {
const auto addr = peer.second;
const auto it = std::find_if(
begin, end, [&addr](const ValueType& vt) { return vt.first == addr; });
if (it != end)
{
it->second->measurePeer(std::move(peer.first), std::move(handler));
}
else
{
// invoke the handler with an empty result if we couldn't
// find the peer's gateway
handler(GhostXForm{});
}
});
}

Controller& mController;
};

struct JoinSessionCallback
{
void operator()(Session session)
{
mController.joinSession(std::move(session));
}

Controller& mController;
};

using IoType = typename util::Injected<IoContext>::type;

using ControllerPeers =
Peers<IoType&, std::reference_wrapper<SessionPeerCounter>, SessionTimelineCallback>;

using ControllerGateway =
Gateway<typename ControllerPeers::GatewayObserver, Clock, IoType&>;
using GatewayPtr = std::shared_ptr<ControllerGateway>;

struct GatewayFactory
{
GatewayPtr operator()(std::pair<NodeState, GhostXForm> state,
util::Injected<IoType&> io,
const asio::ip::address& addr)
{
if (addr.is_v4())
{
return GatewayPtr{new ControllerGateway{std::move(io), addr.to_v4(),
util::injectVal(makeGatewayObserver(mController.mPeers, addr)),
std::move(state.first), std::move(state.second), mController.mClock}};
}
else
{
throw std::runtime_error("Could not create peer gateway on non-ipV4 address");
}
}

Controller& mController;
};

struct UdpSendExceptionHandler
{
using Exception = discovery::UdpSendException;

void operator()(const Exception& exception)
{
mController.mDiscovery.repairGateway(exception.interfaceAddr);
}

Controller& mController;
};

TempoCallback mTempoCallback;
Clock mClock;
NodeId mNodeId;
SessionId mSessionId;

// Mutex that controls access to mGhostXForm and mSessionTimeline
mutable std::mutex mSessionTimingGuard;
GhostXForm mGhostXForm;
Timeline mSessionTimeline;

mutable std::mutex mClientTimelineGuard;
Timeline mClientTimeline;

mutable Timeline mRtClientTimeline;
std::chrono::microseconds mRtClientTimelineTimestamp;

SessionPeerCounter mSessionPeerCounter;

std::atomic<bool> mEnabled;

util::Injected<IoContext> mIo;
// A realtime facade over the provided IoContext. This should only
// be used by realtime code, non-realtime code should use mIo.
typename IoType::template RealTimeContext<IoType&> mRealtimeIo;

ControllerPeers mPeers;

using ControllerSessions = Sessions<ControllerPeers&,
MeasurePeer,
JoinSessionCallback,
typename util::Injected<IoContext>::type&,
Clock>;
ControllerSessions mSessions;

using Discovery =
discovery::Service<std::pair<NodeState, GhostXForm>, GatewayFactory, IoContext>;
Discovery mDiscovery;
};

} // namespace link
} // namespace ableton

+ 94
- 0
source/modules/hylia/link/ableton/link/Gateway.hpp View File

@@ -0,0 +1,94 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/PeerGateway.hpp>
#include <ableton/link/MeasurementService.hpp>
#include <ableton/link/PeerState.hpp>

namespace ableton
{
namespace link
{

template <typename PeerObserver, typename Clock, typename IoContext>
class Gateway
{
public:
Gateway(util::Injected<IoContext> io,
asio::ip::address_v4 addr,
util::Injected<PeerObserver> observer,
NodeState nodeState,
GhostXForm ghostXForm,
Clock clock)
// TODO: Measurement should have an IoContext injected
: mIo(std::move(io)),
mMeasurement(addr,
nodeState.sessionId,
std::move(ghostXForm),
std::move(clock),
util::injectVal(channel(mIo->log(), "gateway@" + addr.to_string()))),
mPeerGateway(discovery::makeIpV4Gateway(util::injectRef(*mIo),
std::move(addr),
std::move(observer),
PeerState{std::move(nodeState), mMeasurement.endpoint()}))
{
}

Gateway(const Gateway& rhs) = delete;
Gateway& operator=(const Gateway& rhs) = delete;

Gateway(Gateway&& rhs)
: mIo(std::move(rhs.mIo))
, mMeasurement(std::move(rhs.mMeasurement))
, mPeerGateway(std::move(rhs.mPeerGateway))
{
}

Gateway& operator=(Gateway&& rhs)
{
mIo = std::move(rhs.mIo);
mMeasurement = std::move(rhs.mMeasurement);
mPeerGateway = std::move(rhs.mPeerGateway);
return *this;
}

void updateNodeState(std::pair<NodeState, GhostXForm> state)
{
mMeasurement.updateNodeState(state.first.sessionId, state.second);
mPeerGateway.updateState(PeerState{std::move(state.first), mMeasurement.endpoint()});
}

template <typename Handler>
void measurePeer(const PeerState& peer, Handler handler)
{
mMeasurement.measurePeer(peer, std::move(handler));
}

private:
util::Injected<IoContext> mIo;
MeasurementService<Clock, typename util::Injected<IoContext>::type::Log> mMeasurement;
discovery::
IpV4Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&>
mPeerGateway;
};

} // namespace link
} // namespace ableton

+ 59
- 0
source/modules/hylia/link/ableton/link/GhostXForm.hpp View File

@@ -0,0 +1,59 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>
#include <cmath>

namespace ableton
{
namespace link
{

using std::chrono::microseconds;

struct GhostXForm
{
microseconds hostToGhost(const microseconds hostTime) const
{
return microseconds{llround(slope * hostTime.count())} + intercept;
}

microseconds ghostToHost(const microseconds ghostTime) const
{
return microseconds{llround((ghostTime - intercept).count() / slope)};
}

friend bool operator==(const GhostXForm lhs, const GhostXForm rhs)
{
return lhs.slope == rhs.slope && lhs.intercept == rhs.intercept;
}

friend bool operator!=(const GhostXForm lhs, const GhostXForm rhs)
{
return !(lhs == rhs);
}

double slope;
microseconds intercept;
};

} // namespace link
} // namespace ableton

+ 82
- 0
source/modules/hylia/link/ableton/link/HostTimeFilter.hpp View File

@@ -0,0 +1,82 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/LinearRegression.hpp>
#include <chrono>
#include <vector>

namespace ableton
{
namespace link
{

template <class T>
class HostTimeFilter
{
static const std::size_t kNumPoints = 512;
using Points = std::vector<std::pair<double, double>>;
using PointIt = typename Points::iterator;

public:
HostTimeFilter()
: mIndex(0)
{
mPoints.reserve(kNumPoints);
}

~HostTimeFilter() = default;

void reset()
{
mIndex = 0;
mPoints.clear();
}

std::chrono::microseconds sampleTimeToHostTime(const double sampleTime)
{
const auto micros = static_cast<double>(mHostTimeSampler.micros().count());
const auto point = std::make_pair(sampleTime, micros);

if (mPoints.size() < kNumPoints)
{
mPoints.push_back(point);
}
else
{
mPoints[mIndex] = point;
}
mIndex = (mIndex + 1) % kNumPoints;

const auto result = linearRegression(mPoints.begin(), mPoints.end());

const auto hostTime = (result.first * sampleTime) + result.second;

return std::chrono::microseconds(llround(hostTime));
}

private:
std::size_t mIndex;
Points mPoints;
T mHostTimeSampler;
};

} // namespace link
} // namespace ableton

+ 160
- 0
source/modules/hylia/link/ableton/link/Kalman.hpp View File

@@ -0,0 +1,160 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <array>
#include <cfloat>
#include <cmath>
#include <limits>

#if LINK_PLATFORM_WINDOWS
// Windows.h (or more specifically, minwindef.h) define the max(a, b) macro
// which conflicts with the symbol provided by std::numeric_limits.
#ifdef max
#undef max
#endif
#endif

namespace ableton
{
namespace link
{

template <std::size_t n>
struct Kalman
{
Kalman()
: mValue(0)
, mGain(0)
, mVVariance(1)
, mWVariance(1)
, mCoVariance(1)
, mVarianceLength(n)
, mCounter(mVarianceLength)
{
}

double getValue()
{
return mValue;
}

double calculateVVariance()
{
auto vVar = 0.;
auto meanOfDiffs = 0.;

for (size_t k = 0; k < (mVarianceLength); k++)
{
meanOfDiffs += (mMeasuredValues[k] - mFilterValues[k]);
}

meanOfDiffs /= (mVarianceLength);

for (size_t i = 0; i < (mVarianceLength); i++)
{
vVar += (pow(mMeasuredValues[i] - mFilterValues[i] - meanOfDiffs, 2.0));
}

vVar /= (mVarianceLength - 1);

return vVar;
}

double calculateWVariance()
{
auto wVar = 0.;
auto meanOfDiffs = 0.;

for (size_t k = 0; k < (mVarianceLength); k++)
{
meanOfDiffs += (mFilterValues[(mCounter - k - 1) % mVarianceLength]
- mFilterValues[(mCounter - k - 2) % mVarianceLength]);
}

meanOfDiffs /= (mVarianceLength);

for (size_t i = 0; i < (mVarianceLength); i++)
{
wVar += (pow(mFilterValues[(mCounter - i - 1) % mVarianceLength]
- mFilterValues[(mCounter - i - 2) % mVarianceLength] - meanOfDiffs,
2.0));
}

wVar /= (mVarianceLength - 1);

return wVar;
}

void iterate(const double value)
{
const std::size_t currentIndex = mCounter % mVarianceLength;
mMeasuredValues[currentIndex] = value;

if (mCounter < (mVarianceLength + mVarianceLength))
{
if (mCounter == mVarianceLength)
{
mValue = value;
}
else
{
mValue = (mValue + value) / 2;
}
}
else
{
// prediction equations
const double prevFilterValue = mFilterValues[(mCounter - 1) % mVarianceLength];
mFilterValues[currentIndex] = prevFilterValue;
mWVariance = calculateWVariance();
const double coVarianceEstimation = mCoVariance + mWVariance;

// update equations
mVVariance = calculateVVariance();
if ((coVarianceEstimation + mVVariance) != 0)
{
mGain = coVarianceEstimation / (coVarianceEstimation + mVVariance);
}
else
{
mGain = std::numeric_limits<double>::max();
}
mValue = prevFilterValue + mGain * (value - prevFilterValue);
mCoVariance = (1 - mGain) * coVarianceEstimation;
}
mFilterValues[currentIndex] = mValue;

++mCounter;
}

double mValue;
double mGain;
double mVVariance;
double mWVariance;
double mCoVariance;
size_t mVarianceLength;
size_t mCounter;
std::array<double, n> mFilterValues;
std::array<double, n> mMeasuredValues;
};

} // namespace link
} // namespace ableton

+ 64
- 0
source/modules/hylia/link/ableton/link/LinearRegression.hpp View File

@@ -0,0 +1,64 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <cfloat>
#include <cmath>
#include <numeric>
#include <utility>

namespace ableton
{
namespace link
{

template <typename It>
std::pair<double, double> linearRegression(It begin, It end)
{
using namespace std;
using Point = pair<double, double>;

const double numPoints = static_cast<double>(distance(begin, end));

const double meanX = accumulate(begin, end, 0.0, [](double a, Point b) {
return a + b.first;
}) / numPoints;

const double productXX = accumulate(begin, end, 0.0,
[&meanX](double a, Point b) { return a + pow(b.first - meanX, 2.0); });

const double meanY = accumulate(begin, end, 0.0, [](double a, Point b) {
return a + b.second;
}) / numPoints;

const double productXY =
inner_product(begin, end, begin, 0.0, [](double a, double b) { return a + b; },
[&meanX, &meanY](
Point a, Point b) { return ((a.first - meanX) * (b.second - meanY)); });

const double slope = productXX == 0.0 ? 0.0 : productXY / productXX;

const double intercept = meanY - (slope * meanX);

return make_pair(slope, intercept);
}

} // namespace link
} // namespace ableton

+ 296
- 0
source/modules/hylia/link/ableton/link/Measurement.hpp View File

@@ -0,0 +1,296 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Payload.hpp>
#include <ableton/discovery/Socket.hpp>
#include <ableton/link/PayloadEntries.hpp>
#include <ableton/link/PeerState.hpp>
#include <ableton/link/SessionId.hpp>
#include <ableton/link/v1/Messages.hpp>
#include <ableton/platforms/asio/AsioService.hpp>
#include <ableton/util/Injected.hpp>
#include <chrono>
#include <memory>

namespace ableton
{
namespace link
{

template <typename IoService, typename Clock, typename Socket, typename Log>
struct Measurement
{
using Point = std::pair<double, double>;
using Callback = std::function<void(std::vector<Point>)>;
using Micros = std::chrono::microseconds;
using Timer = typename IoService::Timer;

static const std::size_t kNumberDataPoints = 100;
static const std::size_t kNumberMeasurements = 5;

Measurement() = default;

Measurement(const PeerState& state,
Callback callback,
asio::ip::address_v4 address,
Clock clock,
util::Injected<Log> log)
: mpIo(new IoService{})
, mpImpl(std::make_shared<Impl>(*mpIo,
std::move(state),
std::move(callback),
std::move(address),
std::move(clock),
std::move(log)))
{
mpImpl->listen();
}

Measurement(Measurement&& rhs)
: mpIo(std::move(rhs.mpIo))
, mpImpl(std::move(rhs.mpImpl))
{
}

~Measurement()
{
postImplDestruction();
}

Measurement& operator=(Measurement&& rhs)
{
postImplDestruction();
mpIo = std::move(rhs.mpIo);
mpImpl = std::move(rhs.mpImpl);
return *this;
}

void postImplDestruction()
{
// Post destruction of the impl object into the io thread if valid
if (mpIo)
{
mpIo->post(ImplDeleter{*this});
}
}

struct Impl : std::enable_shared_from_this<Impl>
{
Impl(IoService& io,
const PeerState& state,
Callback callback,
asio::ip::address_v4 address,
Clock clock,
util::Injected<Log> log)
: mpSocket(std::make_shared<Socket>(io))
, mSessionId(state.nodeState.sessionId)
, mEndpoint(state.endpoint)
, mCallback(std::move(callback))
, mClock(std::move(clock))
, mTimer(util::injectVal(io.makeTimer()))
, mMeasurementsStarted(0)
, mLog(std::move(log))
, mSuccess(false)
{
configureUnicastSocket(*mpSocket, address);

const auto ht = HostTime{mClock.micros()};
sendPing(mEndpoint, discovery::makePayload(ht));
resetTimer();
}

void resetTimer()
{
mTimer->cancel();
mTimer->expires_from_now(std::chrono::milliseconds(50));
mTimer->async_wait([this](const typename Timer::ErrorCode e) {
if (!e)
{
if (mMeasurementsStarted < kNumberMeasurements)
{
const auto ht = HostTime{mClock.micros()};
sendPing(mEndpoint, discovery::makePayload(ht));
++mMeasurementsStarted;
resetTimer();
}
else
{
fail();
}
}
});
}

void listen()
{
mpSocket->receive(util::makeAsyncSafe(this->shared_from_this()));
}

// Operator to handle incoming messages on the interface
template <typename It>
void operator()(
const asio::ip::udp::endpoint& from, const It messageBegin, const It messageEnd)
{
using namespace std;
const auto result = v1::parseMessageHeader(messageBegin, messageEnd);
const auto& header = result.first;
const auto payloadBegin = result.second;

if (header.messageType == v1::kPong)
{
debug(*mLog) << "Received Pong message from " << from;

// parse for all entries
SessionId sessionId{};
std::chrono::microseconds ghostTime{0};
std::chrono::microseconds prevGHostTime{0};
std::chrono::microseconds prevHostTime{0};

try
{
discovery::parsePayload<SessionMembership, GHostTime, PrevGHostTime, HostTime>(
payloadBegin, messageEnd,
[&sessionId](const SessionMembership& sms) { sessionId = sms.sessionId; },
[&ghostTime](GHostTime gt) { ghostTime = std::move(gt.time); },
[&prevGHostTime](PrevGHostTime gt) { prevGHostTime = std::move(gt.time); },
[&prevHostTime](HostTime ht) { prevHostTime = std::move(ht.time); });
}
catch (const std::runtime_error& err)
{
warning(*mLog) << "Failed parsing payload, caught exception: " << err.what();
listen();
return;
}

if (mSessionId == sessionId)
{
const auto hostTime = mClock.micros();

const auto payload =
discovery::makePayload(HostTime{hostTime}, PrevGHostTime{ghostTime});

sendPing(from, payload);
listen();

if (prevGHostTime != Micros{0})
{
mData.push_back(
std::make_pair(static_cast<double>((hostTime + prevHostTime).count()) * 0.5,
static_cast<double>(ghostTime.count())));
mData.push_back(std::make_pair(static_cast<double>(prevHostTime.count()),
static_cast<double>((ghostTime + prevGHostTime).count()) * 0.5));
}

if (mData.size() > kNumberDataPoints)
{
finish();
}
else
{
resetTimer();
}
}
else
{
fail();
}
}
else
{
debug(*mLog) << "Received invalid message from " << from;
listen();
}
}

template <typename Payload>
void sendPing(asio::ip::udp::endpoint to, const Payload& payload)
{
v1::MessageBuffer buffer;
const auto msgBegin = std::begin(buffer);
const auto msgEnd = v1::pingMessage(payload, msgBegin);
const auto numBytes = static_cast<size_t>(std::distance(msgBegin, msgEnd));

try
{
mpSocket->send(buffer.data(), numBytes, to);
}
catch (const std::runtime_error& err)
{
info(*mLog) << "Failed to send Ping to " << to.address().to_string() << ": "
<< err.what();
}
}

void finish()
{
mTimer->cancel();
mCallback(std::move(mData));
mData = {};
mSuccess = true;
debug(*mLog) << "Measuring " << mEndpoint << " done.";
}

void fail()
{
mCallback(std::vector<Point>{});
mData = {};
debug(*mLog) << "Measuring " << mEndpoint << " failed.";
}

std::shared_ptr<Socket> mpSocket;
SessionId mSessionId;
asio::ip::udp::endpoint mEndpoint;
std::vector<std::pair<double, double>> mData;
Callback mCallback;
Clock mClock;
util::Injected<typename IoService::Timer> mTimer;
std::size_t mMeasurementsStarted;
util::Injected<Log> mLog;
bool mSuccess;
};

struct ImplDeleter
{
ImplDeleter(Measurement& measurement)
: mpImpl(std::move(measurement.mpImpl))
{
}

void operator()()
{
// Notify callback that the measurement has failed if it did
// not succeed before destruction
if (!mpImpl->mSuccess)
{
mpImpl->fail();
}
mpImpl.reset();
}

std::shared_ptr<Impl> mpImpl;
};

std::unique_ptr<IoService> mpIo;
std::shared_ptr<Impl> mpImpl;
};

} // namespace link
} // namespace ableton

+ 70
- 0
source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp View File

@@ -0,0 +1,70 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>

namespace ableton
{
namespace link
{

struct MeasurementEndpointV4
{
enum
{
key = 'mep4'
};

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const MeasurementEndpointV4 mep)
{
return discovery::sizeInByteStream(
static_cast<std::uint32_t>(mep.ep.address().to_v4().to_ulong()))
+ discovery::sizeInByteStream(mep.ep.port());
}

template <typename It>
friend It toNetworkByteStream(const MeasurementEndpointV4 mep, It out)
{
return discovery::toNetworkByteStream(mep.ep.port(),
discovery::toNetworkByteStream(
static_cast<std::uint32_t>(mep.ep.address().to_v4().to_ulong()), std::move(out)));
}

template <typename It>
static std::pair<MeasurementEndpointV4, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto addrRes =
discovery::Deserialize<std::uint32_t>::fromNetworkByteStream(move(begin), end);
auto portRes = discovery::Deserialize<std::uint16_t>::fromNetworkByteStream(
move(addrRes.second), end);
return make_pair(MeasurementEndpointV4{{asio::ip::address_v4{move(addrRes.first)},
move(portRes.first)}},
move(portRes.second));
}

asio::ip::udp::endpoint ep;
};

} // namespace link
} // namespace ableton

+ 186
- 0
source/modules/hylia/link/ableton/link/MeasurementService.hpp View File

@@ -0,0 +1,186 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Socket.hpp>
#include <ableton/link/GhostXForm.hpp>
#include <ableton/link/Kalman.hpp>
#include <ableton/link/LinearRegression.hpp>
#include <ableton/link/Measurement.hpp>
#include <ableton/link/PeerState.hpp>
#include <ableton/link/PingResponder.hpp>
#include <ableton/link/SessionId.hpp>
#include <ableton/link/v1/Messages.hpp>
#include <ableton/platforms/asio/AsioService.hpp>
#include <ableton/util/Log.hpp>
#include <map>
#include <memory>
#include <thread>

namespace ableton
{
namespace link
{

template <typename Clock, typename Log>
class MeasurementService
{
public:
using Point = std::pair<double, double>;

using MeasurementInstance = Measurement<platforms::asio::AsioService,
Clock,
discovery::Socket<v1::kMaxMessageSize>,
Log>;

using MeasurementServicePingResponder = PingResponder<platforms::asio::AsioService&,
Clock,
discovery::Socket<v1::kMaxMessageSize>,
Log>;

static const std::size_t kNumberThreads = 1;

MeasurementService(asio::ip::address_v4 address,
SessionId sessionId,
GhostXForm ghostXForm,
Clock clock,
util::Injected<Log> log)
: mClock(std::move(clock))
, mLog(std::move(log))
, mPingResponder(std::move(address),
std::move(sessionId),
std::move(ghostXForm),
util::injectRef(mIo),
mClock,
mLog)
{
}

MeasurementService(const MeasurementService&) = delete;
MeasurementService(MeasurementService&&) = delete;

~MeasurementService()
{
// Clear the measurement map in the io service so that whatever
// cleanup code executes in response to the destruction of the
// measurement objects still have access to the io service
mIo.post([this] { mMeasurementMap.clear(); });
}

void updateNodeState(const SessionId& sessionId, const GhostXForm& xform)
{
mPingResponder.updateNodeState(sessionId, xform);
}

asio::ip::udp::endpoint endpoint() const
{
return mPingResponder.endpoint();
}

// Measure the peer and invoke the handler with a GhostXForm
template <typename Handler>
void measurePeer(const PeerState& state, const Handler handler)
{
using namespace std;

mIo.post([this, state, handler] {
const auto nodeId = state.nodeState.nodeId;
auto addr = mPingResponder.endpoint().address().to_v4();
auto callback = CompletionCallback<Handler>{*this, nodeId, handler};

try
{
mMeasurementMap[nodeId] =
MeasurementInstance{state, move(callback), move(addr), mClock, mLog};
}
catch (const runtime_error& err)
{
info(*mLog) << "Failed to measure. Reason: " << err.what();
handler(GhostXForm{});
}
});
}

static GhostXForm filter(
std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end)
{
using namespace std;
using std::chrono::microseconds;

Kalman<5> kalman;
for (auto it = begin; it != end; ++it)
{
kalman.iterate(it->second - it->first);
}

return GhostXForm{1, microseconds(llround(kalman.getValue()))};
}

private:
template <typename Handler>
struct CompletionCallback
{
void operator()(const std::vector<Point> data)
{
using namespace std;
using std::chrono::microseconds;

// Post this to the measurement service's io service so that we
// don't delete the measurement object in its stack. Capture all
// needed data separately from this, since this object may be
// gone by the time the block gets executed.
auto nodeId = mNodeId;
auto handler = mHandler;
auto& measurementMap = mService.mMeasurementMap;
mService.mIo.post([nodeId, handler, &measurementMap, data] {
const auto it = measurementMap.find(nodeId);
if (it != measurementMap.end())
{
if (data.empty())
{
handler(GhostXForm{});
}
else
{
handler(MeasurementService::filter(begin(data), end(data)));
}
measurementMap.erase(it);
}
});
}

MeasurementService& mService;
NodeId mNodeId;
Handler mHandler;
};

// Make sure the measurement map outlives the io service so that the rest of
// the members are guaranteed to be valid when any final handlers
// are begin run.
using MeasurementMap = std::map<NodeId, MeasurementInstance>;
MeasurementMap mMeasurementMap;
Clock mClock;
util::Injected<Log> mLog;
platforms::asio::AsioService mIo;
MeasurementServicePingResponder mPingResponder;
};

} // namespace link
} // namespace ableton

+ 82
- 0
source/modules/hylia/link/ableton/link/NodeId.hpp View File

@@ -0,0 +1,82 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
#include <random>
#include <string>

namespace ableton
{
namespace link
{

using NodeIdArray = std::array<std::uint8_t, 8>;

struct NodeId : NodeIdArray
{
NodeId() = default;

NodeId(NodeIdArray rhs)
: NodeIdArray(std::move(rhs))
{
}

static NodeId random()
{
using namespace std;

random_device rd;
mt19937 gen(rd());
// uint8_t not standardized for this type - use unsigned
uniform_int_distribution<unsigned> dist(33, 126); // printable ascii chars

NodeId nodeId;
generate(
nodeId.begin(), nodeId.end(), [&] { return static_cast<uint8_t>(dist(gen)); });
return nodeId;
}

friend std::ostream& operator<<(std::ostream& stream, const NodeId& id)
{
return stream << std::string{id.cbegin(), id.cend()};
}

template <typename It>
friend It toNetworkByteStream(const NodeId& nodeId, It out)
{
return discovery::toNetworkByteStream(nodeId, std::move(out));
}

template <typename It>
static std::pair<NodeId, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto result =
discovery::Deserialize<NodeIdArray>::fromNetworkByteStream(move(begin), move(end));
return make_pair(NodeId(move(result.first)), move(result.second));
}
};

} // namespace link
} // namespace ableton

+ 69
- 0
source/modules/hylia/link/ableton/link/NodeState.hpp View File

@@ -0,0 +1,69 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Payload.hpp>
#include <ableton/link/NodeId.hpp>
#include <ableton/link/SessionId.hpp>
#include <ableton/link/Timeline.hpp>

namespace ableton
{
namespace link
{

struct NodeState
{
using Payload = decltype(discovery::makePayload(Timeline{}, SessionMembership{}));

NodeId ident() const
{
return nodeId;
}

friend bool operator==(const NodeState& lhs, const NodeState& rhs)
{
return std::tie(lhs.nodeId, lhs.sessionId, lhs.timeline)
== std::tie(rhs.nodeId, rhs.sessionId, rhs.timeline);
}

friend Payload toPayload(const NodeState& state)
{
return discovery::makePayload(state.timeline, SessionMembership{state.sessionId});
}

template <typename It>
static NodeState fromPayload(NodeId id, It begin, It end)
{
using namespace std;
auto state = NodeState{move(id), {}, {}};
discovery::parsePayload<Timeline, SessionMembership>(move(begin), move(end),
[&state](Timeline tl) { state.timeline = move(tl); },
[&state](SessionMembership sm) { state.sessionId = move(sm.sessionId); });
return state;
}

NodeId nodeId;
SessionId sessionId;
Timeline timeline;
};

} // namespace link
} // namespace ableton

+ 146
- 0
source/modules/hylia/link/ableton/link/PayloadEntries.hpp View File

@@ -0,0 +1,146 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <cmath>
#include <cstdint>

namespace ableton
{
namespace link
{

struct HostTime
{
enum
{
key = '__ht'
};

HostTime() = default;

HostTime(const std::chrono::microseconds tm)
: time(tm)
{
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const HostTime& sht)
{
return discovery::sizeInByteStream(std::move(sht.time));
}

template <typename It>
friend It toNetworkByteStream(const HostTime& sht, It out)
{
return discovery::toNetworkByteStream(std::move(sht.time), std::move(out));
}

template <typename It>
static std::pair<HostTime, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto result = discovery::Deserialize<chrono::microseconds>::fromNetworkByteStream(
move(begin), move(end));
return make_pair(HostTime{move(result.first)}, move(result.second));
}

std::chrono::microseconds time;
};

struct GHostTime : HostTime
{
enum
{
key = '__gt'
};

GHostTime() = default;

GHostTime(const std::chrono::microseconds tm)
: time(tm)
{
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const GHostTime& dgt)
{
return discovery::sizeInByteStream(std::move(dgt.time));
}

template <typename It>
friend It toNetworkByteStream(const GHostTime& dgt, It out)
{
return discovery::toNetworkByteStream(std::move(dgt.time), std::move(out));
}

template <typename It>
static std::pair<GHostTime, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto result = discovery::Deserialize<chrono::microseconds>::fromNetworkByteStream(
move(begin), move(end));
return make_pair(GHostTime{move(result.first)}, move(result.second));
}

std::chrono::microseconds time;
};

struct PrevGHostTime
{
enum
{
key = '_pgt'
};

PrevGHostTime() = default;

PrevGHostTime(const std::chrono::microseconds tm)
: time(tm)
{
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const PrevGHostTime& dgt)
{
return discovery::sizeInByteStream(std::move(dgt.time));
}

template <typename It>
friend It toNetworkByteStream(const PrevGHostTime& pdgt, It out)
{
return discovery::toNetworkByteStream(std::move(pdgt.time), std::move(out));
}

template <typename It>
static std::pair<PrevGHostTime, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto result = discovery::Deserialize<chrono::microseconds>::fromNetworkByteStream(
move(begin), move(end));
return make_pair(PrevGHostTime{move(result.first)}, move(result.second));
}

std::chrono::microseconds time;
};

} // namespace link
} // namespace ableton

+ 83
- 0
source/modules/hylia/link/ableton/link/PeerState.hpp View File

@@ -0,0 +1,83 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Payload.hpp>
#include <ableton/link/MeasurementEndpointV4.hpp>
#include <ableton/link/NodeState.hpp>

namespace ableton
{
namespace link
{

// A state type for peers. PeerState stores the normal NodeState plus
// additional information (the remote endpoint at which to find its
// ping/pong measurement server).

struct PeerState
{
using IdType = NodeId;

IdType ident() const
{
return nodeState.ident();
}

SessionId sessionId() const
{
return nodeState.sessionId;
}

Timeline timeline() const
{
return nodeState.timeline;
}

friend bool operator==(const PeerState& lhs, const PeerState& rhs)
{
return lhs.nodeState == rhs.nodeState && lhs.endpoint == rhs.endpoint;
}

friend auto toPayload(const PeerState& state)
-> decltype(std::declval<NodeState::Payload>()
+ discovery::makePayload(MeasurementEndpointV4{{}}))
{
return toPayload(state.nodeState)
+ discovery::makePayload(MeasurementEndpointV4{state.endpoint});
}

template <typename It>
static PeerState fromPayload(NodeId id, It begin, It end)
{
using namespace std;
auto peerState = PeerState{NodeState::fromPayload(move(id), begin, end), {}};

discovery::parsePayload<MeasurementEndpointV4>(move(begin), move(end),
[&peerState](MeasurementEndpointV4 me4) { peerState.endpoint = move(me4.ep); });
return peerState;
}

NodeState nodeState;
asio::ip::udp::endpoint endpoint;
};

} // namespace link
} // namespace ableton

+ 355
- 0
source/modules/hylia/link/ableton/link/Peers.hpp View File

@@ -0,0 +1,355 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/PeerState.hpp>
#include <ableton/util/Injected.hpp>
#include <cassert>

namespace ableton
{
namespace link
{

// SessionMembershipCallback is invoked when any change to session
// membership occurs (when any peer joins or leaves a session)
//
// SessionTimelineCallback is invoked with a session id and a timeline
// whenever a new combination of these values is seen

template <typename IoContext,
typename SessionMembershipCallback,
typename SessionTimelineCallback>
class Peers
{
// non-movable private implementation type
struct Impl;

public:
using Peer = std::pair<PeerState, asio::ip::address>;

Peers(util::Injected<IoContext> io,
SessionMembershipCallback membership,
SessionTimelineCallback timeline)
: mpImpl(
std::make_shared<Impl>(std::move(io), std::move(membership), std::move(timeline)))
{
}

// The set of peers for a given session, ordered by (peerId, addr).
// The result will possibly contain multiple entries for the same
// peer if it is visible through multiple gateways.
std::vector<Peer> sessionPeers(const SessionId& sid) const
{
using namespace std;
vector<Peer> result;
auto& peerVec = mpImpl->mPeers;
copy_if(begin(peerVec), end(peerVec), back_inserter(result), SessionMemberPred{sid});
return result;
}

// Number of individual for a given session.
std::size_t uniqueSessionPeerCount(const SessionId& sid) const
{
using namespace std;
auto peerVec = sessionPeers(sid);
auto last = unique(begin(peerVec), end(peerVec),
[](const Peer& a, const Peer& b) { return a.first.ident() == b.first.ident(); });
return static_cast<size_t>(distance(begin(peerVec), last));
}

void setSessionTimeline(const SessionId& sid, const Timeline& tl)
{
// Set the cached timeline for all peers to a new client-specified
// timeline. When we make a timeline change, we do so
// optimistically and clients assume that all peers in a session
// have adopted the newly specified timeline. We must represent
// this in our cache or else we risk failing to notify about a
// higher-priority peer timeline that was already seen.
for (auto& peer : mpImpl->mPeers)
{
if (peer.first.sessionId() == sid)
{
peer.first.nodeState.timeline = tl;
}
}
}

// Purge all cached peers that are members of the given session
void forgetSession(const SessionId& sid)
{
using namespace std;
auto& peerVec = mpImpl->mPeers;
peerVec.erase(
remove_if(begin(peerVec), end(peerVec), SessionMemberPred{sid}), end(peerVec));
}

void resetPeers()
{
mpImpl->mPeers.clear();
}

// Observer type that monitors peer discovery on a particular
// gateway and relays the information to a Peers instance.
// Models the PeerObserver concept from the discovery module.
struct GatewayObserver
{
using GatewayObserverNodeState = PeerState;
using GatewayObserverNodeId = NodeId;

GatewayObserver(std::shared_ptr<Impl> pImpl, asio::ip::address addr)
: mpImpl(std::move(pImpl))
, mAddr(std::move(addr))
{
}
GatewayObserver(const GatewayObserver&) = delete;

GatewayObserver(GatewayObserver&& rhs)
: mpImpl(std::move(rhs.mpImpl))
, mAddr(std::move(rhs.mAddr))
{
}

~GatewayObserver()
{
// Check to handle the moved from case
if (mpImpl)
{
auto& io = *mpImpl->mIo;
io.async(Deleter{*this});
}
}

// model the PeerObserver concept from discovery
friend void sawPeer(GatewayObserver& observer, const PeerState& state)
{
auto pImpl = observer.mpImpl;
auto addr = observer.mAddr;
assert(pImpl);
pImpl->mIo->async([pImpl, addr, state] {
pImpl->sawPeerOnGateway(std::move(state), std::move(addr));
});
}

friend void peerLeft(GatewayObserver& observer, const NodeId& id)
{
auto pImpl = observer.mpImpl;
auto addr = observer.mAddr;
pImpl->mIo->async(
[pImpl, addr, id] { pImpl->peerLeftGateway(std::move(id), std::move(addr)); });
}

friend void peerTimedOut(GatewayObserver& observer, const NodeId& id)
{
auto pImpl = observer.mpImpl;
auto addr = observer.mAddr;
pImpl->mIo->async(
[pImpl, addr, id] { pImpl->peerLeftGateway(std::move(id), std::move(addr)); });
}

struct Deleter
{
Deleter(GatewayObserver& observer)
: mpImpl(std::move(observer.mpImpl))
, mAddr(std::move(observer.mAddr))
{
}

void operator()()
{
mpImpl->gatewayClosed(mAddr);
}

std::shared_ptr<Impl> mpImpl;
asio::ip::address mAddr;
};

std::shared_ptr<Impl> mpImpl;
asio::ip::address mAddr;
};

// Factory function for the gateway observer
friend GatewayObserver makeGatewayObserver(Peers& peers, asio::ip::address addr)
{
return GatewayObserver{peers.mpImpl, std::move(addr)};
}

private:
struct Impl
{
Impl(util::Injected<IoContext> io,
SessionMembershipCallback membership,
SessionTimelineCallback timeline)
: mIo(std::move(io))
, mSessionMembershipCallback(std::move(membership))
, mSessionTimelineCallback(std::move(timeline))
{
}

void sawPeerOnGateway(PeerState peerState, asio::ip::address gatewayAddr)
{
using namespace std;

const auto peerSession = peerState.sessionId();
const auto peerTimeline = peerState.timeline();
bool isNewSessionTimeline = false;
bool didSessionMembershipChange = false;
{
isNewSessionTimeline = !sessionTimelineExists(peerSession, peerTimeline);

auto peer = make_pair(move(peerState), move(gatewayAddr));
const auto idRange = equal_range(begin(mPeers), end(mPeers), peer, PeerIdComp{});

if (idRange.first == idRange.second)
{
// This peer is not currently known on any gateway
didSessionMembershipChange = true;
mPeers.insert(move(idRange.first), move(peer));
}
else
{
// We've seen this peer before... does it have a new session?
didSessionMembershipChange =
all_of(idRange.first, idRange.second, [&peerSession](const Peer& test) {
return test.first.sessionId() != peerSession;
});

// was it on this gateway?
const auto addrRange =
equal_range(idRange.first, idRange.second, peer, AddrComp{});

if (addrRange.first == addrRange.second)
{
// First time on this gateway, add it
mPeers.insert(move(addrRange.first), move(peer));
}
else
{
// We have an entry for this peer on this gateway, update it
*addrRange.first = move(peer);
}
}
} // end lock

// Invoke callbacks outside the critical section
if (isNewSessionTimeline)
{
mSessionTimelineCallback(peerSession, peerTimeline);
}

if (didSessionMembershipChange)
{
mSessionMembershipCallback();
}
}

void peerLeftGateway(const NodeId& nodeId, const asio::ip::address& gatewayAddr)
{
using namespace std;

bool didSessionMembershipChange = false;
{
auto it = find_if(begin(mPeers), end(mPeers), [&](const Peer& peer) {
return peer.first.ident() == nodeId && peer.second == gatewayAddr;
});

if (it != end(mPeers))
{
mPeers.erase(move(it));
didSessionMembershipChange = true;
}
} // end lock

if (didSessionMembershipChange)
{
mSessionMembershipCallback();
}
}

void gatewayClosed(const asio::ip::address& gatewayAddr)
{
using namespace std;

{
mPeers.erase(
remove_if(begin(mPeers), end(mPeers),
[&gatewayAddr](const Peer& peer) { return peer.second == gatewayAddr; }),
end(mPeers));
} // end lock

mSessionMembershipCallback();
}

bool sessionTimelineExists(const SessionId& session, const Timeline& tl)
{
using namespace std;
return find_if(begin(mPeers), end(mPeers), [&](const Peer& peer) {
return peer.first.sessionId() == session && peer.first.timeline() == tl;
}) != end(mPeers);
}

struct PeerIdComp
{
bool operator()(const Peer& lhs, const Peer& rhs) const
{
return lhs.first.ident() < rhs.first.ident();
}
};

struct AddrComp
{
bool operator()(const Peer& lhs, const Peer& rhs) const
{
return lhs.second < rhs.second;
}
};

util::Injected<IoContext> mIo;
SessionMembershipCallback mSessionMembershipCallback;
SessionTimelineCallback mSessionTimelineCallback;
std::vector<Peer> mPeers; // sorted by peerId, unique by (peerId, addr)
};

struct SessionMemberPred
{
bool operator()(const Peer& peer) const
{
return peer.first.sessionId() == sid;
}

const SessionId& sid;
};

std::shared_ptr<Impl> mpImpl;
};

template <typename Io,
typename SessionMembershipCallback,
typename SessionTimelineCallback>
Peers<Io, SessionMembershipCallback, SessionTimelineCallback> makePeers(
util::Injected<Io> io,
SessionMembershipCallback membershipCallback,
SessionTimelineCallback timelineCallback)
{
return {std::move(io), std::move(membershipCallback), std::move(timelineCallback)};
}

} // namespace link
} // namespace ableton

+ 100
- 0
source/modules/hylia/link/ableton/link/Phase.hpp View File

@@ -0,0 +1,100 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/Beats.hpp>
#include <ableton/link/Timeline.hpp>
#include <chrono>

namespace ableton
{
namespace link
{

// Returns a value in the range [0,quantum) corresponding to beats %
// quantum except that negative beat values are handled correctly.
// If the given quantum is zero, returns zero.
inline Beats phase(const Beats beats, const Beats quantum)
{
if (quantum == Beats{INT64_C(0)})
{
return Beats{INT64_C(0)};
}
else
{
// Handle negative beat values by doing the computation relative to an
// origin that is on the nearest quantum boundary less than -(abs(x))
const auto quantumMicros = quantum.microBeats();
const auto quantumBins = (llabs(beats.microBeats()) + quantumMicros) / quantumMicros;
const std::int64_t quantumBeats{quantumBins * quantumMicros};
return (beats + Beats{quantumBeats}) % quantum;
}
}

// Return the least value greater than x that matches the phase of
// target with respect to the given quantum. If the given quantum
// quantum is 0, x is returned.
inline Beats nextPhaseMatch(const Beats x, const Beats target, const Beats quantum)
{
const auto desiredPhase = phase(target, quantum);
const auto xPhase = phase(x, quantum);
const auto phaseDiff = (desiredPhase - xPhase + quantum) % quantum;
return x + phaseDiff;
}

// Return the closest value to x that matches the phase of the target
// with respect to the given quantum. The result deviates from x by at
// most quantum/2, but may be less than x.
inline Beats closestPhaseMatch(const Beats x, const Beats target, const Beats quantum)
{
return nextPhaseMatch(x - Beats{0.5 * quantum.floating()}, target, quantum);
}

// Interprets the given timeline as encoding a quantum boundary at its
// origin. Given such a timeline, returns a phase-encoded beat value
// relative to the given quantum that corresponds to the given
// time. The phase of the resulting beat value can be calculated with
// phase(beats, quantum). The result will deviate by up to +-
// (quantum/2) beats compared to the result of tl.toBeats(time).
inline Beats toPhaseEncodedBeats(
const Timeline& tl, const std::chrono::microseconds time, const Beats quantum)
{
const auto beat = tl.toBeats(time);
return closestPhaseMatch(beat, beat - tl.beatOrigin, quantum);
}

// The inverse of toPhaseEncodedBeats. Given a phase encoded beat
// value from the given timeline and quantum, find the time value that
// it maps to.
inline std::chrono::microseconds fromPhaseEncodedBeats(
const Timeline& tl, const Beats beat, const Beats quantum)
{
const auto fromOrigin = beat - tl.beatOrigin;
const auto originOffset = fromOrigin - phase(fromOrigin, quantum);
// invert the phase calculation so that it always rounds up in the
// middle instead of down like closestPhaseMatch. Otherwise we'll
// end up rounding down twice when a value is at phase quantum/2.
const auto inversePhaseOffset = closestPhaseMatch(
quantum - phase(fromOrigin, quantum), quantum - phase(beat, quantum), quantum);
return tl.fromBeats(tl.beatOrigin + originOffset + quantum - inversePhaseOffset);
}

} // link
} // ableton

+ 185
- 0
source/modules/hylia/link/ableton/link/PingResponder.hpp View File

@@ -0,0 +1,185 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/GhostXForm.hpp>
#include <ableton/link/PayloadEntries.hpp>
#include <ableton/link/SessionId.hpp>
#include <ableton/link/v1/Messages.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/Injected.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <chrono>
#include <memory>
#include <thread>

namespace ableton
{
namespace link
{

template <typename Io, typename Clock, typename Socket, typename Log>
class PingResponder
{
public:
PingResponder(asio::ip::address_v4 address,
SessionId sessionId,
GhostXForm ghostXForm,
util::Injected<Io> io,
Clock clock,
util::Injected<Log> log)
: mIo(std::move(io))
, mpImpl(std::make_shared<Impl>(*mIo,
std::move(address),
std::move(sessionId),
std::move(ghostXForm),
std::move(clock),
std::move(log)))
{
mpImpl->listen();
}

PingResponder(const PingResponder&) = delete;
PingResponder(PingResponder&&) = delete;

~PingResponder()
{
// post the release of the impl object into the io service so that
// it happens in the same thread as its handlers
auto pImpl = mpImpl;
mIo->post([pImpl]() mutable { pImpl.reset(); });
}

void updateNodeState(const SessionId& sessionId, const GhostXForm& xform)
{
auto pImpl = mpImpl;
mIo->post([pImpl, sessionId, xform] {
pImpl->mSessionId = std::move(sessionId);
pImpl->mGhostXForm = std::move(xform);
});
}

asio::ip::udp::endpoint endpoint() const
{
return mpImpl->mSocket.endpoint();
}

asio::ip::address address() const
{
return endpoint().address();
}

Socket socket() const
{
return mpImpl->mSocket;
}

private:
struct Impl : std::enable_shared_from_this<Impl>
{
Impl(typename util::Injected<Io>::type& io,
asio::ip::address_v4 address,
SessionId sessionId,
GhostXForm ghostXForm,
Clock clock,
util::Injected<Log> log)
: mSessionId(std::move(sessionId))
, mGhostXForm(std::move(ghostXForm))
, mClock(std::move(clock))
, mLog(std::move(log))
, mSocket(io)
{
configureUnicastSocket(mSocket, address);
}

void listen()
{
mSocket.receive(util::makeAsyncSafe(this->shared_from_this()));
}

// Operator to handle incoming messages on the interface
template <typename It>
void operator()(const asio::ip::udp::endpoint& from, const It begin, const It end)
{
using namespace discovery;

// Decode Ping Message
const auto result = link::v1::parseMessageHeader(begin, end);
const auto& header = result.first;
const auto payloadBegin = result.second;

// Check Payload size
const auto payloadSize = static_cast<std::size_t>(std::distance(payloadBegin, end));
const auto maxPayloadSize =
sizeInByteStream(makePayload(HostTime{}, PrevGHostTime{}));
if (header.messageType == v1::kPing && payloadSize <= maxPayloadSize)
{
debug(*mLog) << "Received ping message from " << from;

try
{
reply(std::move(payloadBegin), std::move(end), from);
}
catch (const std::runtime_error& err)
{
info(*mLog) << "Failed to send pong to " << from << ". Reason: " << err.what();
}
}
else
{
info(*mLog) << "Received invalid Message from " << from << ".";
}
listen();
}

template <typename It>
void reply(It begin, It end, const asio::ip::udp::endpoint& to)
{
using namespace discovery;

// Encode Pong Message
const auto id = SessionMembership{mSessionId};
const auto currentGt = GHostTime{mGhostXForm.hostToGhost(mClock.micros())};
const auto pongPayload = makePayload(id, currentGt);

v1::MessageBuffer pongBuffer;
const auto pongMsgBegin = std::begin(pongBuffer);
auto pongMsgEnd = v1::pongMessage(pongPayload, pongMsgBegin);
// Append ping payload to pong message.
pongMsgEnd = std::copy(begin, end, pongMsgEnd);

const auto numBytes =
static_cast<std::size_t>(std::distance(pongMsgBegin, pongMsgEnd));
mSocket.send(pongBuffer.data(), numBytes, to);
}

SessionId mSessionId;
GhostXForm mGhostXForm;
Clock mClock;
util::Injected<Log> mLog;
Socket mSocket;
};

util::Injected<Io> mIo;
std::shared_ptr<Impl> mpImpl;
};

} // namespace link
} // namespace ableton

+ 65
- 0
source/modules/hylia/link/ableton/link/SessionId.hpp View File

@@ -0,0 +1,65 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/NodeId.hpp>

namespace ableton
{
namespace link
{

// SessionIds occupy the same value space as NodeIds and are
// identified by their founding node.
using SessionId = NodeId;

// A payload entry indicating membership in a particular session
struct SessionMembership
{
enum
{
key = 'sess'
};

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const SessionMembership& sm)
{
return discovery::sizeInByteStream(sm.sessionId);
}

template <typename It>
friend It toNetworkByteStream(const SessionMembership& sm, It out)
{
return discovery::toNetworkByteStream(sm.sessionId, std::move(out));
}

template <typename It>
static std::pair<SessionMembership, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
auto idRes = SessionId::fromNetworkByteStream(move(begin), move(end));
return make_pair(SessionMembership{move(idRes.first)}, move(idRes.second));
}

SessionId sessionId;
};

} // namespace link
} // namespace ableton

+ 303
- 0
source/modules/hylia/link/ableton/link/Sessions.hpp View File

@@ -0,0 +1,303 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/GhostXForm.hpp>
#include <ableton/link/SessionId.hpp>
#include <ableton/link/Timeline.hpp>

namespace ableton
{
namespace link
{

struct SessionMeasurement
{
GhostXForm xform;
std::chrono::microseconds timestamp;
};

struct Session
{
SessionId sessionId;
Timeline timeline;
SessionMeasurement measurement;
};

template <typename Peers,
typename MeasurePeer,
typename JoinSessionCallback,
typename IoContext,
typename Clock>
class Sessions
{
public:
using Timer = typename util::Injected<IoContext>::type::Timer;

Sessions(Session init,
util::Injected<Peers> peers,
MeasurePeer measure,
JoinSessionCallback join,
util::Injected<IoContext> io,
Clock clock)
: mPeers(std::move(peers))
, mMeasure(std::move(measure))
, mCallback(std::move(join))
, mCurrent(std::move(init))
, mIo(std::move(io))
, mTimer(mIo->makeTimer())
, mClock(std::move(clock))
{
}

void resetSession(Session session)
{
mCurrent = std::move(session);
mOtherSessions.clear();
}

void resetTimeline(Timeline timeline)
{
mCurrent.timeline = std::move(timeline);
}

// Consider the observed session/timeline pair and return a possibly
// new timeline that should be used going forward.
Timeline sawSessionTimeline(SessionId sid, Timeline timeline)
{
using namespace std;
if (sid == mCurrent.sessionId)
{
// matches our current session, update the timeline if necessary
updateTimeline(mCurrent, move(timeline));
}
else
{
auto session = Session{move(sid), move(timeline), {}};
const auto range =
equal_range(begin(mOtherSessions), end(mOtherSessions), session, SessionIdComp{});
if (range.first == range.second)
{
// brand new session, insert it into our list of known
// sessions and launch a measurement
launchSessionMeasurement(session);
mOtherSessions.insert(range.first, move(session));
}
else
{
// we've seen this session before, update its timeline if necessary
updateTimeline(*range.first, move(timeline));
}
}
return mCurrent.timeline;
}

private:
void launchSessionMeasurement(Session& session)
{
using namespace std;
auto peers = mPeers->sessionPeers(session.sessionId);
if (!peers.empty())
{
// first criteria: always prefer the founding peer
const auto it = find_if(begin(peers), end(peers),
[&session](const Peer& peer) { return session.sessionId == peer.first.ident(); });
// TODO: second criteria should be degree. We don't have that
// represented yet so just use the first peer for now
auto peer = it == end(peers) ? peers.front() : *it;
// mark that a session is in progress by clearing out the
// session's timestamp
session.measurement.timestamp = {};
mMeasure(move(peer), MeasurementResultsHandler{*this, session.sessionId});
}
}

void handleSuccessfulMeasurement(const SessionId& id, GhostXForm xform)
{
using namespace std;

debug(mIo->log()) << "Session " << id << " measurement completed with result "
<< "(" << xform.slope << ", " << xform.intercept.count() << ")";

auto measurement = SessionMeasurement{move(xform), mClock.micros()};

if (mCurrent.sessionId == id)
{
mCurrent.measurement = move(measurement);
mCallback(mCurrent);
}
else
{
const auto range = equal_range(
begin(mOtherSessions), end(mOtherSessions), Session{id, {}, {}}, SessionIdComp{});

if (range.first != range.second)
{
const auto SESSION_EPS = chrono::microseconds{500000};
// should we join this session?
const auto hostTime = mClock.micros();
const auto curGhost = mCurrent.measurement.xform.hostToGhost(hostTime);
const auto newGhost = measurement.xform.hostToGhost(hostTime);
// update the measurement for the session entry
range.first->measurement = move(measurement);
// If session times too close - fall back to session id order
const auto ghostDiff = newGhost - curGhost;
if (ghostDiff > SESSION_EPS || (std::abs(ghostDiff.count()) < SESSION_EPS.count()
&& id < mCurrent.sessionId))
{
// The new session wins, switch over to it
auto current = mCurrent;
mCurrent = move(*range.first);
mOtherSessions.erase(range.first);
// Put the old current session back into our list of known
// sessions so that we won't re-measure it
const auto it = upper_bound(
begin(mOtherSessions), end(mOtherSessions), current, SessionIdComp{});
mOtherSessions.insert(it, move(current));
// And notify that we have a new session and make sure that
// we remeasure it periodically.
mCallback(mCurrent);
scheduleRemeasurement();
}
}
}
}

void scheduleRemeasurement()
{
// set a timer to re-measure the active session after a period
mTimer.expires_from_now(std::chrono::microseconds{30000000});
mTimer.async_wait([this](const typename Timer::ErrorCode e) {
if (!e)
{
launchSessionMeasurement(mCurrent);
scheduleRemeasurement();
}
});
}

void handleFailedMeasurement(const SessionId& id)
{
using namespace std;

debug(mIo->log()) << "Session " << id << " measurement failed.";

// if we failed to measure for our current session, schedule a
// retry in the future. Otherwise, remove the session from our set
// of known sessions (if it is seen again it will be measured as
// if new).
if (mCurrent.sessionId == id)
{
scheduleRemeasurement();
}
else
{
const auto range = equal_range(
begin(mOtherSessions), end(mOtherSessions), Session{id, {}, {}}, SessionIdComp{});
if (range.first != range.second)
{
mOtherSessions.erase(range.first);
mPeers->forgetSession(id);
}
}
}

void updateTimeline(Session& session, Timeline timeline)
{
// We use beat origin magnitude to prioritize sessions.
if (timeline.beatOrigin > session.timeline.beatOrigin)
{
debug(mIo->log()) << "Adopting peer timeline (" << timeline.tempo.bpm() << ", "
<< timeline.beatOrigin.floating() << ", "
<< timeline.timeOrigin.count() << ")";

session.timeline = std::move(timeline);
}
else
{
debug(mIo->log()) << "Rejecting peer timeline with beat origin: "
<< timeline.beatOrigin.floating()
<< ". Current timeline beat origin: "
<< session.timeline.beatOrigin.floating();
}
}

struct MeasurementResultsHandler
{
void operator()(GhostXForm xform) const
{
Sessions& sessions = mSessions;
const SessionId& sessionId = mSessionId;
if (xform == GhostXForm{})
{
mSessions.mIo->async([&sessions, sessionId] {
sessions.handleFailedMeasurement(std::move(sessionId));
});
}
else
{
mSessions.mIo->async([&sessions, sessionId, xform] {
sessions.handleSuccessfulMeasurement(std::move(sessionId), std::move(xform));
});
}
}

Sessions& mSessions;
SessionId mSessionId;
};

struct SessionIdComp
{
bool operator()(const Session& lhs, const Session& rhs) const
{
return lhs.sessionId < rhs.sessionId;
}
};

using Peer = typename util::Injected<Peers>::type::Peer;
util::Injected<Peers> mPeers;
MeasurePeer mMeasure;
JoinSessionCallback mCallback;
Session mCurrent;
util::Injected<IoContext> mIo;
Timer mTimer;
Clock mClock;
std::vector<Session> mOtherSessions; // sorted/unique by session id
};

template <typename Peers,
typename MeasurePeer,
typename JoinSessionCallback,
typename IoContext,
typename Clock>
Sessions<Peers, MeasurePeer, JoinSessionCallback, IoContext, Clock> makeSessions(
Session init,
util::Injected<Peers> peers,
MeasurePeer measure,
JoinSessionCallback join,
util::Injected<IoContext> io,
Clock clock)
{
using namespace std;
return {move(init), move(peers), move(measure), move(join), move(io), move(clock)};
}

} // namespace link
} // namespace ableton

+ 90
- 0
source/modules/hylia/link/ableton/link/Tempo.hpp View File

@@ -0,0 +1,90 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/Beats.hpp>
#include <chrono>

namespace ableton
{
namespace link
{

struct Tempo : std::tuple<double>
{
Tempo() = default;

// Beats per minute
explicit Tempo(const double bpm)
: std::tuple<double>(bpm)
{
}

Tempo(const std::chrono::microseconds microsPerBeat)
: std::tuple<double>(60. * 1e6 / microsPerBeat.count())
{
}

double bpm() const
{
return std::get<0>(*this);
}

std::chrono::microseconds microsPerBeat() const
{
return std::chrono::microseconds{llround(60. * 1e6 / bpm())};
}

// Given the tempo, convert a time to a beat value
Beats microsToBeats(const std::chrono::microseconds micros) const
{
return Beats{micros.count() / static_cast<double>(microsPerBeat().count())};
}

// Given the tempo, convert a beat to a time value
std::chrono::microseconds beatsToMicros(const Beats beats) const
{
return std::chrono::microseconds{llround(beats.floating() * microsPerBeat().count())};
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const Tempo tempo)
{
return discovery::sizeInByteStream(tempo.microsPerBeat());
}

template <typename It>
friend It toNetworkByteStream(const Tempo tempo, It out)
{
return discovery::toNetworkByteStream(tempo.microsPerBeat(), std::move(out));
}

template <typename It>
static std::pair<Tempo, It> fromNetworkByteStream(It begin, It end)
{
auto result =
discovery::Deserialize<std::chrono::microseconds>::fromNetworkByteStream(
std::move(begin), std::move(end));
return std::make_pair(Tempo{std::move(result.first)}, std::move(result.second));
}
};

} // namespace link
} // namespace ableton

+ 99
- 0
source/modules/hylia/link/ableton/link/Timeline.hpp View File

@@ -0,0 +1,99 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/NetworkByteStreamSerializable.hpp>
#include <ableton/link/Beats.hpp>
#include <ableton/link/Tempo.hpp>
#include <cmath>
#include <cstdint>
#include <tuple>

namespace ableton
{
namespace link
{

// A tuple of (tempo, beats, time), with integral units
// based on microseconds. This type establishes a bijection between
// beats and wall time, given a valid tempo. It also serves as a
// payload entry.

struct Timeline
{
enum
{
key = 'tmln'
};

Beats toBeats(const std::chrono::microseconds time) const
{
return beatOrigin + tempo.microsToBeats(time - timeOrigin);
}

std::chrono::microseconds fromBeats(const Beats beats) const
{
return timeOrigin + tempo.beatsToMicros(beats - beatOrigin);
}

friend bool operator==(const Timeline& lhs, const Timeline& rhs)
{
return std::tie(lhs.tempo, lhs.beatOrigin, lhs.timeOrigin)
== std::tie(rhs.tempo, rhs.beatOrigin, rhs.timeOrigin);
}

friend bool operator!=(const Timeline& lhs, const Timeline& rhs)
{
return !(lhs == rhs);
}

// Model the NetworkByteStreamSerializable concept
friend std::uint32_t sizeInByteStream(const Timeline& tl)
{
return discovery::sizeInByteStream(std::tie(tl.tempo, tl.beatOrigin, tl.timeOrigin));
}

template <typename It>
friend It toNetworkByteStream(const Timeline& tl, It out)
{
return discovery::toNetworkByteStream(
std::tie(tl.tempo, tl.beatOrigin, tl.timeOrigin), std::move(out));
}

template <typename It>
static std::pair<Timeline, It> fromNetworkByteStream(It begin, It end)
{
using namespace std;
using namespace discovery;
Timeline timeline;
auto result =
Deserialize<tuple<Tempo, Beats, chrono::microseconds>>::fromNetworkByteStream(
move(begin), move(end));
tie(timeline.tempo, timeline.beatOrigin, timeline.timeOrigin) = move(result.first);
return make_pair(move(timeline), move(result.second));
}

Tempo tempo;
Beats beatOrigin;
std::chrono::microseconds timeOrigin;
};

} // namespace link
} // namespace ableton

+ 138
- 0
source/modules/hylia/link/ableton/link/v1/Messages.hpp View File

@@ -0,0 +1,138 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/Payload.hpp>
#include <array>

namespace ableton
{
namespace link
{
namespace v1
{

// The maximum size of a message, in bytes
const std::size_t kMaxMessageSize = 512;
// Utility typedef for an array of bytes of maximum message size
using MessageBuffer = std::array<uint8_t, v1::kMaxMessageSize>;

using MessageType = uint8_t;

const MessageType kPing = 1;
const MessageType kPong = 2;

struct MessageHeader
{
MessageType messageType;

friend std::uint32_t sizeInByteStream(const MessageHeader& header)
{
return discovery::sizeInByteStream(header.messageType);
}

template <typename It>
friend It toNetworkByteStream(const MessageHeader& header, It out)
{
return discovery::toNetworkByteStream(header.messageType, std::move(out));
}

template <typename It>
static std::pair<MessageHeader, It> fromNetworkByteStream(It begin, const It end)
{
using namespace discovery;

MessageHeader header;
std::tie(header.messageType, begin) =
Deserialize<decltype(header.messageType)>::fromNetworkByteStream(begin, end);

return std::make_pair(std::move(header), std::move(begin));
}
};

namespace detail
{

// Types that are only used in the sending/parsing of messages, not
// publicly exposed.
using ProtocolHeader = std::array<char, 8>;
const ProtocolHeader kProtocolHeader = {{'_', 'l', 'i', 'n', 'k', '_', 'v', 1}};

// Must have at least kMaxMessageSize bytes available in the output stream
template <typename Payload, typename It>
It encodeMessage(const MessageType messageType, const Payload& payload, It out)
{
using namespace std;
const MessageHeader header = {messageType};
const auto messageSize =
kProtocolHeader.size() + sizeInByteStream(header) + sizeInByteStream(payload);

if (messageSize < kMaxMessageSize)
{
return toNetworkByteStream(
payload, toNetworkByteStream(
header, copy(begin(kProtocolHeader), end(kProtocolHeader), move(out))));
}
else
{
throw range_error("Exceeded maximum message size");
}
}

} // namespace detail

template <typename Payload, typename It>
It pingMessage(const Payload& payload, It out)
{
return detail::encodeMessage(kPing, payload, std::move(out));
}

template <typename Payload, typename It>
It pongMessage(const Payload& payload, It out)
{
return detail::encodeMessage(kPong, payload, std::move(out));
}

template <typename It>
std::pair<MessageHeader, It> parseMessageHeader(It bytesBegin, const It bytesEnd)
{
using ItDiff = typename std::iterator_traits<It>::difference_type;

MessageHeader header = {};
const auto protocolHeaderSize = discovery::sizeInByteStream(detail::kProtocolHeader);
const auto minMessageSize =
static_cast<ItDiff>(protocolHeaderSize + sizeInByteStream(header));

// If there are enough bytes in the stream to make a header and if
// the first bytes in the stream are the protocol header, then
// proceed to parse the stream.
if (std::distance(bytesBegin, bytesEnd) >= minMessageSize
&& std::equal(
begin(detail::kProtocolHeader), end(detail::kProtocolHeader), bytesBegin))
{
std::tie(header, bytesBegin) =
MessageHeader::fromNetworkByteStream(bytesBegin + protocolHeaderSize, bytesEnd);
}
return std::make_pair(std::move(header), std::move(bytesBegin));
}

} // namespace v1
} // namespace link
} // namespace ableton

+ 66
- 0
source/modules/hylia/link/ableton/platforms/Config.hpp View File

@@ -0,0 +1,66 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/link/Controller.hpp>
#include <ableton/util/Log.hpp>

#if LINK_PLATFORM_WINDOWS
#include <ableton/platforms/asio/Context.hpp>
#include <ableton/platforms/windows/Clock.hpp>
#include <ableton/platforms/windows/ScanIpIfAddrs.hpp>
#elif LINK_PLATFORM_MACOSX
#include <ableton/platforms/asio/Context.hpp>
#include <ableton/platforms/darwin/Clock.hpp>
#include <ableton/platforms/posix/ScanIpIfAddrs.hpp>
#elif LINK_PLATFORM_LINUX
#include <ableton/platforms/asio/Context.hpp>
#include <ableton/platforms/posix/ScanIpIfAddrs.hpp>
#include <ableton/platforms/stl/Clock.hpp>
#endif

namespace ableton
{
namespace link
{
namespace platform
{

#if LINK_PLATFORM_WINDOWS
using Clock = platforms::windows::Clock;
using IoContext =
platforms::asio::Context<platforms::windows::ScanIpIfAddrs, util::NullLog>;

#elif LINK_PLATFORM_MACOSX
using Clock = platforms::darwin::Clock;
using IoContext =
platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>;

#elif LINK_PLATFORM_LINUX
using Clock = platforms::stl::Clock;
using IoContext =
platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>;
#endif

using Controller = Controller<PeerCountCallback, TempoCallback, Clock, IoContext>;

} // platform
} // link
} // ableton

+ 105
- 0
source/modules/hylia/link/ableton/platforms/asio/AsioService.hpp View File

@@ -0,0 +1,105 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioTimer.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <thread>

namespace ableton
{
namespace platforms
{
namespace asio
{

class AsioService
{
public:
using Timer = AsioTimer;

AsioService()
: AsioService(DefaultHandler{})
{
}

template <typename ExceptionHandler>
explicit AsioService(ExceptionHandler exceptHandler)
: mpWork(new ::asio::io_service::work(mService))
{
mThread =
std::thread{[](::asio::io_service& service, ExceptionHandler handler) {
for (;;)
{
try
{
service.run();
break;
}
catch (const typename ExceptionHandler::Exception& exception)
{
handler(exception);
}
}
},
std::ref(mService), std::move(exceptHandler)};
}

~AsioService()
{
mpWork.reset();
mThread.join();
}

AsioTimer makeTimer()
{
return {mService};
}

template <typename Handler>
void post(Handler handler)
{
mService.post(std::move(handler));
}

::asio::io_service mService;

private:
// Default handler is hidden and defines a hidden exception type
// that will never be thrown by other code, so it effectively does
// not catch.
struct DefaultHandler
{
struct Exception
{
};

void operator()(const Exception&)
{
}
};

std::unique_ptr<::asio::io_service::work> mpWork;
std::thread mThread;
};

} // namespace asio
} // namespace platforms
} // namespace ableton

+ 132
- 0
source/modules/hylia/link/ableton/platforms/asio/AsioTimer.hpp View File

@@ -0,0 +1,132 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <functional>

namespace ableton
{
namespace platforms
{
namespace asio
{

// This implementation is based on the boost::asio::system_timer concept.
// Since boost::system_timer doesn't support move semantics, we create a wrapper
// with a unique_ptr to get a movable type. It also handles an inconvenient
// aspect of asio timers, which is that you must explicitly guard against the
// handler firing after cancellation. We handle this by use of the SafeAsyncHandler
// utility. AsioTimer therefore guarantees that a handler will not be called after
// the destruction of the timer, or after the timer has been canceled.

class AsioTimer
{
public:
using ErrorCode = ::asio::error_code;
using TimePoint = std::chrono::system_clock::time_point;

AsioTimer(::asio::io_service& io)
: mpTimer(new ::asio::system_timer(io))
, mpAsyncHandler(std::make_shared<AsyncHandler>())
{
}

~AsioTimer()
{
// The timer may not be valid anymore if this instance was moved from
if (mpTimer != nullptr)
{
// Ignore errors during cancellation
cancel();
}
}

AsioTimer(const AsioTimer&) = delete;
AsioTimer& operator=(const AsioTimer&) = delete;

// Enable move construction but not move assignment. Move assignment
// would get weird - would have to handle outstanding handlers
AsioTimer(AsioTimer&& rhs)
: mpTimer(std::move(rhs.mpTimer))
, mpAsyncHandler(std::move(rhs.mpAsyncHandler))
{
}

void expires_at(std::chrono::system_clock::time_point tp)
{
mpTimer->expires_at(std::move(tp));
}

template <typename T>
void expires_from_now(T duration)
{
mpTimer->expires_from_now(std::move(duration));
}

ErrorCode cancel()
{
ErrorCode ec;
mpTimer->cancel(ec);
mpAsyncHandler->mpHandler = nullptr;
return ec;
}

template <typename Handler>
void async_wait(Handler handler)
{
*mpAsyncHandler = std::move(handler);
mpTimer->async_wait(util::makeAsyncSafe(mpAsyncHandler));
}

TimePoint now() const
{
return std::chrono::system_clock::now();
}

private:
struct AsyncHandler
{
template <typename Handler>
AsyncHandler& operator=(Handler handler)
{
mpHandler = [handler](ErrorCode ec) { handler(std::move(ec)); };
return *this;
}

void operator()(ErrorCode ec)
{
if (mpHandler)
{
mpHandler(std::move(ec));
}
}

std::function<void(const ErrorCode)> mpHandler;
};

std::unique_ptr<::asio::system_timer> mpTimer;
std::shared_ptr<AsyncHandler> mpAsyncHandler;
};

} // namespace asio
} // namespace platforms
} // namespace ableton

+ 76
- 0
source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp View File

@@ -0,0 +1,76 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

/*!
* \brief Wrapper file for AsioStandalone library
*
* This file includes all necessary headers from the AsioStandalone library which are used
* by Link.
*/

#pragma push_macro("ASIO_STANDALONE")
#define ASIO_STANDALONE 1

#pragma push_macro("ASIO_NO_TYPEID")
#define ASIO_NO_TYPEID 1

#if LINK_PLATFORM_WINDOWS
#pragma push_macro("INCL_EXTRA_HTON_FUNCTIONS")
#define INCL_EXTRA_HTON_FUNCTIONS 1
#endif

#if defined(__clang__)
#pragma clang diagnostic push
#if __has_warning("-Wcomma")
#pragma clang diagnostic ignored "-Wcomma"
#endif
#endif

#if defined(_MSC_VER)
#pragma warning(push)
// C4191: 'operator/operation': unsafe conversion from 'type of expression' to
// 'type required'
#pragma warning(disable : 4191)
// C4548: expression before comma has no effect; expected expression with side-effect
#pragma warning(disable : 4548)
// C4619: #pragma warning : there is no warning number 'number'
#pragma warning(disable : 4619)
// C4675: 'function' : resolved overload was found by argument-dependent lookup
#pragma warning(disable : 4675)
#endif

#include <asio.hpp>
#include <asio/system_timer.hpp>

#if LINK_PLATFORM_WINDOWS
#pragma pop_macro("INCL_EXTRA_HTON_FUNCTIONS")
#endif

#pragma pop_macro("ASIO_STANDALONE")
#pragma pop_macro("ASIO_NO_TYPEID")

#if defined(_MSC_VER)
#pragma warning(pop)
#endif

#if defined(__clang__)
#pragma clang diagnostic pop
#endif

+ 181
- 0
source/modules/hylia/link/ableton/platforms/asio/Context.hpp View File

@@ -0,0 +1,181 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/discovery/IpV4Interface.hpp>
#include <ableton/platforms/asio/AsioTimer.hpp>
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/platforms/asio/PooledHandlerContext.hpp>
#include <ableton/platforms/asio/Socket.hpp>
#include <thread>

namespace ableton
{
namespace platforms
{
namespace asio
{

template <typename ScanIpIfAddrs, typename LogT>
class Context
{
public:
using Timer = AsioTimer;
using Log = LogT;

template <std::size_t BufferSize>
using Socket = asio::Socket<BufferSize>;

template <typename IoContext>
using RealTimeContext = PooledHandlerContext<IoContext>;

Context()
: Context(DefaultHandler{})
{
}

template <typename ExceptionHandler>
explicit Context(ExceptionHandler exceptHandler)
: mpService(new ::asio::io_service())
, mpWork(new ::asio::io_service::work(*mpService))
{
mThread =
std::thread{[](::asio::io_service& service, ExceptionHandler handler) {
for (;;)
{
try
{
service.run();
break;
}
catch (const typename ExceptionHandler::Exception& exception)
{
handler(exception);
}
}
},
std::ref(*mpService), std::move(exceptHandler)};
}

Context(const Context&) = delete;

Context(Context&& rhs)
: mpService(std::move(rhs.mpService))
, mpWork(std::move(rhs.mpWork))
, mThread(std::move(rhs.mThread))
, mLog(std::move(rhs.mLog))
, mScanIpIfAddrs(std::move(rhs.mScanIpIfAddrs))
{
}

~Context()
{
if (mpService)
{
mpWork.reset();
mThread.join();
}
}

template <std::size_t BufferSize>
Socket<BufferSize> openUnicastSocket(const ::asio::ip::address_v4& addr)
{
auto socket = Socket<BufferSize>{*mpService};
socket.mpImpl->mSocket.set_option(
::asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::outbound_interface(addr));
socket.mpImpl->mSocket.bind(::asio::ip::udp::endpoint{addr, 0});
return socket;
}

template <std::size_t BufferSize>
Socket<BufferSize> openMulticastSocket(const ::asio::ip::address_v4& addr)
{
auto socket = Socket<BufferSize>{*mpService};
socket.mpImpl->mSocket.set_option(::asio::ip::udp::socket::reuse_address(true));
socket.mpImpl->mSocket.set_option(
::asio::socket_base::broadcast(!addr.is_loopback()));
socket.mpImpl->mSocket.set_option(
::asio::ip::multicast::enable_loopback(addr.is_loopback()));
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::outbound_interface(addr));
socket.mpImpl->mSocket.bind({::asio::ip::address::from_string("0.0.0.0"),
discovery::multicastEndpoint().port()});
socket.mpImpl->mSocket.set_option(::asio::ip::multicast::join_group(
discovery::multicastEndpoint().address().to_v4(), addr));
return socket;
}

std::vector<::asio::ip::address> scanNetworkInterfaces()
{
return mScanIpIfAddrs();
}

Timer makeTimer() const
{
return {*mpService};
}

Log& log()
{
return mLog;
}

template <typename Handler>
void async(Handler handler)
{
mpService->post(std::move(handler));
}

Context clone() const
{
return {};
}

template <typename ExceptionHandler>
Context clone(ExceptionHandler handler) const
{
return Context{std::move(handler)};
}

private:
// Default handler is hidden and defines a hidden exception type
// that will never be thrown by other code, so it effectively does
// not catch.
struct DefaultHandler
{
struct Exception
{
};

void operator()(const Exception&)
{
}
};

std::unique_ptr<::asio::io_service> mpService;
std::unique_ptr<::asio::io_service::work> mpWork;
std::thread mThread;
Log mLog;
ScanIpIfAddrs mScanIpIfAddrs;
};

} // namespace asio
} // namespace platforms
} // namespace ableton

+ 115
- 0
source/modules/hylia/link/ableton/platforms/asio/PooledHandlerContext.hpp View File

@@ -0,0 +1,115 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/Injected.hpp>

namespace ableton
{
namespace platforms
{
namespace asio
{

template <typename IoContext,
std::size_t MaxNumHandlers = 32,
std::size_t MaxHandlerSize = 128>
struct PooledHandlerContext
{
PooledHandlerContext(util::Injected<IoContext> io)
: mIo(std::move(io))
{
// initialize the handler free store
mFreeStack.reserve(MaxNumHandlers);
for (std::size_t i = 0; i < MaxNumHandlers; ++i)
{
mFreeStack.push_back(reinterpret_cast<void*>(mRaw + i));
}
}

template <typename Handler>
void async(Handler handler)
{
try
{
mIo->async(HandlerWrapper<Handler>{*this, std::move(handler)});
}
catch (std::bad_alloc)
{
warning(mIo->log()) << "Handler dropped due to low memory pool";
}
}

template <typename Handler>
struct HandlerWrapper
{
HandlerWrapper(PooledHandlerContext& context, Handler handler)
: mContext(context)
, mHandler(std::move(handler))
{
}

void operator()()
{
mHandler();
}

// Use pooled allocation so that posting handlers will not cause
// system allocation
friend void* asio_handler_allocate(
const std::size_t size, HandlerWrapper* const pHandler)
{
if (size > MaxHandlerSize || pHandler->mContext.mFreeStack.empty())
{
// Going over the max handler size is a programming error, as
// this is not a dynamically variable value.
assert(size <= MaxHandlerSize);
throw std::bad_alloc();
}
else
{
const auto p = pHandler->mContext.mFreeStack.back();
pHandler->mContext.mFreeStack.pop_back();
return p;
}
}

friend void asio_handler_deallocate(
void* const p, std::size_t, HandlerWrapper* const pHandler)
{
pHandler->mContext.mFreeStack.push_back(p);
}

PooledHandlerContext& mContext;
Handler mHandler;
};

using MemChunk = typename std::aligned_storage<MaxHandlerSize,
std::alignment_of<max_align_t>::value>::type;
MemChunk mRaw[MaxNumHandlers];
std::vector<void*> mFreeStack;

util::Injected<IoContext> mIo;
};

} // namespace asio
} // namespace platforms
} // namespace ableton

+ 110
- 0
source/modules/hylia/link/ableton/platforms/asio/Socket.hpp View File

@@ -0,0 +1,110 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <array>
#include <cassert>

namespace ableton
{
namespace platforms
{
namespace asio
{

template <std::size_t MaxPacketSize>
struct Socket
{
Socket(::asio::io_service& io)
: mpImpl(std::make_shared<Impl>(io))
{
}

Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;

Socket(Socket&& rhs)
: mpImpl(std::move(rhs.mpImpl))
{
}

std::size_t send(const uint8_t* const pData,
const size_t numBytes,
const ::asio::ip::udp::endpoint& to)
{
assert(numBytes < MaxPacketSize);
return mpImpl->mSocket.send_to(::asio::buffer(pData, numBytes), to);
}

template <typename Handler>
void receive(Handler handler)
{
mpImpl->mHandler = std::move(handler);
mpImpl->mSocket.async_receive_from(
::asio::buffer(mpImpl->mReceiveBuffer, MaxPacketSize), mpImpl->mSenderEndpoint,
util::makeAsyncSafe(mpImpl));
}

::asio::ip::udp::endpoint endpoint() const
{
return mpImpl->mSocket.local_endpoint();
}

struct Impl
{
Impl(::asio::io_service& io)
: mSocket(io, ::asio::ip::udp::v4())
{
}

~Impl()
{
// Ignore error codes in shutdown and close as the socket may
// have already been forcibly closed
::asio::error_code ec;
mSocket.shutdown(::asio::ip::udp::socket::shutdown_both, ec);
mSocket.close(ec);
}

void operator()(const ::asio::error_code& error, const std::size_t numBytes)
{
if (!error && numBytes > 0 && numBytes <= MaxPacketSize)
{
const auto bufBegin = begin(mReceiveBuffer);
mHandler(mSenderEndpoint, bufBegin, bufBegin + static_cast<ptrdiff_t>(numBytes));
}
}

::asio::ip::udp::socket mSocket;
::asio::ip::udp::endpoint mSenderEndpoint;
using Buffer = std::array<uint8_t, MaxPacketSize>;
Buffer mReceiveBuffer;
using ByteIt = typename Buffer::const_iterator;
std::function<void(const ::asio::ip::udp::endpoint&, ByteIt, ByteIt)> mHandler;
};

std::shared_ptr<Impl> mpImpl;
};

} // namespace asio
} // namespace platforms
} // namespace ableton

+ 43
- 0
source/modules/hylia/link/ableton/platforms/asio/Util.hpp View File

@@ -0,0 +1,43 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <algorithm>

namespace ableton
{
namespace platforms
{
namespace asio
{

// Utility for making v4 or v6 ip addresses from raw bytes in network byte-order
template <typename AsioAddrType>
AsioAddrType makeAddress(const char* pAddr)
{
using namespace std;
typename AsioAddrType::bytes_type bytes;
copy(pAddr, pAddr + bytes.size(), begin(bytes));
return AsioAddrType{bytes};
}

} // asio
} // platforms
} // ableton

+ 70
- 0
source/modules/hylia/link/ableton/platforms/darwin/Clock.hpp View File

@@ -0,0 +1,70 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>
#include <mach/mach_time.h>

namespace ableton
{
namespace platforms
{
namespace darwin
{

struct Clock
{
using Ticks = std::uint64_t;
using Micros = std::chrono::microseconds;

Clock()
{
mach_timebase_info_data_t timeInfo;
mach_timebase_info(&timeInfo);
// numer / denom gives nanoseconds, we want microseconds
mTicksToMicros = timeInfo.numer / (timeInfo.denom * 1000.);
}

Micros ticksToMicros(const Ticks ticks) const
{
return Micros{llround(mTicksToMicros * ticks)};
}

Ticks microsToTicks(const Micros micros) const
{
return static_cast<Ticks>(micros.count() / mTicksToMicros);
}

Ticks ticks() const
{
return mach_absolute_time();
}

std::chrono::microseconds micros() const
{
return ticksToMicros(ticks());
}

double mTicksToMicros;
};

} // namespace darwin
} // namespace platforms
} // namespace ableton

+ 30
- 0
source/modules/hylia/link/ableton/platforms/darwin/Darwin.hpp View File

@@ -0,0 +1,30 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

// ntohll and htonll are not defined in 10.7 SDK, so we provide a compatibility macro here

#ifndef ntohll
#define ntohll(x) __DARWIN_OSSwapInt64(x)
#endif

#ifndef htonll
#define htonll(x) __DARWIN_OSSwapInt64(x)
#endif

+ 30
- 0
source/modules/hylia/link/ableton/platforms/linux/Linux.hpp View File

@@ -0,0 +1,30 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <byteswap.h>

#ifndef ntohll
#define ntohll(x) bswap_64(x)
#endif

#ifndef htonll
#define htonll(x) bswap_64(x)
#endif

+ 110
- 0
source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp View File

@@ -0,0 +1,110 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/platforms/asio/Util.hpp>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <vector>

namespace ableton
{
namespace platforms
{
namespace posix
{
namespace detail
{

// RAII type to make [get,free]ifaddrs function pairs exception safe
class GetIfAddrs
{
public:
GetIfAddrs()
{
if (getifaddrs(&interfaces)) // returns 0 on success
{
interfaces = NULL;
}
}
~GetIfAddrs()
{
if (interfaces)
freeifaddrs(interfaces);
}

// RAII must not copy
GetIfAddrs(GetIfAddrs&) = delete;
GetIfAddrs& operator=(GetIfAddrs&) = delete;

template <typename Function>
void withIfAddrs(Function f)
{
if (interfaces)
f(*interfaces);
}

private:
struct ifaddrs* interfaces = NULL;
};

} // detail


// Posix implementation of ip interface address scanner
struct ScanIpIfAddrs
{
// Scan active network interfaces and return corresponding addresses
// for all ip-based interfaces.
std::vector<::asio::ip::address> operator()()
{
std::vector<::asio::ip::address> addrs;

detail::GetIfAddrs getIfAddrs;
getIfAddrs.withIfAddrs([&addrs](const struct ifaddrs& interfaces) {
const struct ifaddrs* interface;
for (interface = &interfaces; interface; interface = interface->ifa_next)
{
auto addr = reinterpret_cast<const struct sockaddr_in*>(interface->ifa_addr);
if (addr && interface->ifa_flags & IFF_UP)
{
if (addr->sin_family == AF_INET)
{
auto bytes = reinterpret_cast<const char*>(&addr->sin_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v4>(bytes));
}
else if (addr->sin_family == AF_INET6)
{
auto addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr);
auto bytes = reinterpret_cast<const char*>(&addr6->sin6_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v6>(bytes));
}
}
}
});
return addrs;
}
};

} // namespace posix
} // namespace platforms
} // namespace ableton

+ 51
- 0
source/modules/hylia/link/ableton/platforms/stl/Clock.hpp View File

@@ -0,0 +1,51 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>

namespace ableton
{
namespace platforms
{
namespace stl
{

struct Clock
{
using Ticks = std::uint64_t;

Clock()
{
mStartTime = std::chrono::high_resolution_clock::now();
}

std::chrono::microseconds micros() const
{
using namespace std::chrono;
return duration_cast<microseconds>(high_resolution_clock::now() - mStartTime);
}

std::chrono::high_resolution_clock::time_point mStartTime;
};

} // namespace stl
} // namespace platforms
} // namespace ableton

+ 71
- 0
source/modules/hylia/link/ableton/platforms/windows/Clock.hpp View File

@@ -0,0 +1,71 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>
#include <cstdint>

namespace ableton
{
namespace platforms
{
namespace windows
{

struct Clock
{
using Ticks = std::int64_t;
using Micros = std::chrono::microseconds;

Clock()
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
mTicksToMicros = 1.0e6 / frequency.QuadPart;
}

Micros ticksToMicros(const Ticks ticks) const
{
return Micros{llround(mTicksToMicros * ticks)};
}

Ticks microsToTicks(const Micros micros) const
{
return static_cast<Ticks>(micros.count() / mTicksToMicros);
}

Ticks ticks() const
{
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return count.QuadPart;
}

std::chrono::microseconds micros() const
{
return ticksToMicros(ticks());
}

double mTicksToMicros;
};

} // namespace windows
} // namespace platforms
} // namespace ableton

+ 140
- 0
source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp View File

@@ -0,0 +1,140 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/platforms/asio/Util.hpp>
#include <iphlpapi.h>
#include <stdio.h>
#include <vector>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

namespace ableton
{
namespace platforms
{
namespace windows
{
namespace detail
{
// RAII type to make [get,free]ifaddrs function pairs exception safe
class GetIfAddrs
{
public:
GetIfAddrs()
{
const int MAX_TRIES = 3; // MSFT recommendation
const int WORKING_BUFFER_SIZE = 15000; // MSFT recommendation

DWORD adapter_addrs_buffer_size = WORKING_BUFFER_SIZE;
for (int i = 0; i < MAX_TRIES; i++)
{
adapter_addrs = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addrs_buffer_size);
assert(adapter_addrs);

DWORD error = ::GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
| GAA_FLAG_SKIP_FRIENDLY_NAME,
NULL, adapter_addrs, &adapter_addrs_buffer_size);

if (error == ERROR_SUCCESS)
{
break;
}
// if buffer too small, use new buffer size in next iteration
if (error == ERROR_BUFFER_OVERFLOW)
{
free(adapter_addrs);
adapter_addrs = NULL;
continue;
}
}
}
~GetIfAddrs()
{
if (adapter_addrs)
free(adapter_addrs);
}

// RAII must not copy
GetIfAddrs(GetIfAddrs&) = delete;
GetIfAddrs& operator=(GetIfAddrs&) = delete;

template <typename Function>
void withIfAddrs(Function f)
{
if (adapter_addrs)
f(*adapter_addrs);
}

private:
IP_ADAPTER_ADDRESSES* adapter_addrs;
IP_ADAPTER_ADDRESSES* adapter;
};

} // detail

struct ScanIpIfAddrs
{
// Scan active network interfaces and return corresponding addresses
// for all ip-based interfaces.
std::vector<::asio::ip::address> operator()()
{
std::vector<::asio::ip::address> addrs;

detail::GetIfAddrs getIfAddrs;
getIfAddrs.withIfAddrs([&addrs](const IP_ADAPTER_ADDRESSES& interfaces) {
const IP_ADAPTER_ADDRESSES* networkInterface;
for (networkInterface = &interfaces; networkInterface;
networkInterface = networkInterface->Next)
{
for (IP_ADAPTER_UNICAST_ADDRESS* address = networkInterface->FirstUnicastAddress;
NULL != address; address = address->Next)
{
auto family = address->Address.lpSockaddr->sa_family;
if (AF_INET == family)
{
// IPv4
SOCKADDR_IN* addr4 =
reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
auto bytes = reinterpret_cast<const char*>(&addr4->sin_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v4>(bytes));
}
else if (AF_INET6 == family)
{
SOCKADDR_IN6* addr6 =
reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
auto bytes = reinterpret_cast<const char*>(&addr6->sin6_addr);
addrs.emplace_back(asio::makeAddress<::asio::ip::address_v6>(bytes));
}
}
}
});
return addrs;
}
};

} // namespace windows
} // namespace platforms
} // namespace ableton

+ 43
- 0
source/modules/hylia/link/ableton/test/CatchWrapper.hpp View File

@@ -0,0 +1,43 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

/*!
* \brief Wrapper file for Catch library
*
* This file includes the Catch header for Link, and also disables some compiler warnings
* which are specific to that library.
*/

// Visual Studio
#if defined(_MSC_VER)
#pragma warning(push)
// C4388: signed/unsigned mismatch
#pragma warning(disable : 4388)
// C4702: unreachable code
#pragma warning(disable : 4702)
#endif

#include <catch.hpp>

// Visual Studio
#if defined(_MSC_VER)
#pragma warning(pop)
#endif

+ 110
- 0
source/modules/hylia/link/ableton/test/serial_io/Context.hpp View File

@@ -0,0 +1,110 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/test/serial_io/SchedulerTree.hpp>
#include <ableton/test/serial_io/Timer.hpp>
#include <ableton/util/Log.hpp>
#include <chrono>
#include <memory>

namespace ableton
{
namespace test
{
namespace serial_io
{

class Context
{
public:
Context(const SchedulerTree::TimePoint& now,
const std::vector<::asio::ip::address>& ifAddrs,
std::shared_ptr<SchedulerTree> pScheduler)
: mNow(now)
, mIfAddrs(ifAddrs)
, mpScheduler(std::move(pScheduler))
, mNextTimerId(0)
{
}

~Context()
{
if (mpScheduler != nullptr)
{
// Finish any pending tasks before shutting down
mpScheduler->run();
}
}

Context(const Context&) = delete;
Context& operator=(const Context&) = delete;

Context(Context&& rhs)
: mNow(rhs.mNow)
, mIfAddrs(rhs.mIfAddrs)
, mpScheduler(std::move(rhs.mpScheduler))
, mLog(std::move(rhs.mLog))
, mNextTimerId(rhs.mNextTimerId)
{
}

template <typename Handler>
void async(Handler handler)
{
mpScheduler->async(std::move(handler));
}

Context clone()
{
return {mNow, mIfAddrs, mpScheduler->makeChild()};
}

using Timer = serial_io::Timer;

Timer makeTimer()
{
return {mNextTimerId++, mNow, mpScheduler};
}

using Log = util::NullLog;

Log& log()
{
return mLog;
}

std::vector<::asio::ip::address> scanNetworkInterfaces()
{
return mIfAddrs;
}

private:
const SchedulerTree::TimePoint& mNow;
const std::vector<::asio::ip::address>& mIfAddrs;
std::shared_ptr<SchedulerTree> mpScheduler;
Log mLog;
SchedulerTree::TimerId mNextTimerId;
};

} // namespace serial_io
} // namespace test
} // namespace ableton

+ 92
- 0
source/modules/hylia/link/ableton/test/serial_io/Fixture.hpp View File

@@ -0,0 +1,92 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/test/serial_io/Context.hpp>
#include <chrono>
#include <memory>

namespace ableton
{
namespace test
{
namespace serial_io
{

class Fixture
{
public:
Fixture()
: mpScheduler(std::make_shared<SchedulerTree>())
, mNow(std::chrono::milliseconds{123456789})
{
}

~Fixture()
{
flush();
}

Fixture(const Fixture&) = delete;
Fixture& operator=(const Fixture&) = delete;
Fixture(Fixture&&) = delete;
Fixture& operator=(Fixture&&) = delete;

void setNetworkInterfaces(std::vector<::asio::ip::address> ifAddrs)
{
mIfAddrs = std::move(ifAddrs);
}

Context makeIoContext()
{
return {mNow, mIfAddrs, mpScheduler};
}

void flush()
{
mpScheduler->run();
}

template <typename T, typename Rep>
void advanceTime(std::chrono::duration<T, Rep> duration)
{
const auto target = mNow + duration;
mpScheduler->run();
auto nextTimer = mpScheduler->nextTimerExpiration();
while (nextTimer <= target)
{
mNow = nextTimer;
mpScheduler->triggerTimersUntil(mNow);
mpScheduler->run();
nextTimer = mpScheduler->nextTimerExpiration();
}
mNow = target;
}

private:
std::shared_ptr<SchedulerTree> mpScheduler;
SchedulerTree::TimePoint mNow;
std::vector<::asio::ip::address> mIfAddrs;
};

} // namespace serial_io
} // namespace test
} // namespace ableton

+ 104
- 0
source/modules/hylia/link/ableton/test/serial_io/SchedulerTree.hpp View File

@@ -0,0 +1,104 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>
#include <functional>
#include <list>
#include <map>
#include <memory>

namespace ableton
{
namespace test
{
namespace serial_io
{

class SchedulerTree
{
public:
using TimePoint = std::chrono::system_clock::time_point;
using TimerId = std::size_t;
using TimerErrorCode = int;

void run();

std::shared_ptr<SchedulerTree> makeChild();

template <typename Handler>
void async(Handler handler)
{
mPendingHandlers.push_back(std::move(handler));
}

template <typename Handler>
void setTimer(const TimerId timerId, const TimePoint expiration, Handler handler)
{
using namespace std;
mTimers[make_pair(move(expiration), timerId)] = move(handler);
}

void cancelTimer(const TimerId timerId);

// returns the time that the next timer in the subtree expires
TimePoint nextTimerExpiration();

// triggers all timers in the subtree that expire at time t or before
void triggerTimersUntil(const TimePoint t);

private:
// returns true if some work was done, false if there was none to do
bool handlePending();

// returns the time that the next timer from this node expires
TimePoint nextOwnTimerExpiration();

// Traversal function over children that cleans up children that
// have been destroyed.
template <typename Fn>
void withChildren(Fn fn)
{
auto it = begin(mChildren);
while (it != end(mChildren))
{
const auto childIt = it++;
auto pChild = childIt->lock();
if (pChild)
{
fn(*pChild);
}
else
{
mChildren.erase(childIt);
}
}
}

using TimerHandler = std::function<void(TimerErrorCode)>;
using TimerMap = std::map<std::pair<TimePoint, TimerId>, TimerHandler>;
TimerMap mTimers;
std::list<std::function<void()>> mPendingHandlers;
std::list<std::weak_ptr<SchedulerTree>> mChildren;
};

} // namespace serial_io
} // namespace test
} // namespace ableton

+ 83
- 0
source/modules/hylia/link/ableton/test/serial_io/Socket.hpp View File

@@ -0,0 +1,83 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/platforms/AsioWrapper.hpp>
#include <functional>

namespace ableton
{
namespace test
{
namespace serial_io
{

struct Socket
{
using SendFn = std::function<std::size_t(
const uint8_t* const, const size_t, const asio::ip::udp::endpoint&)>;

struct ReceiveHandler
{
template <typename It>
void operator()(const asio::ip::udp::endpoint& from, const It begin, const It end)
{
std::vector<uint8_t> buffer{begin, end};
mReceive(from, buffer);
}

std::function<const asio::ip::udp::endpoint&, const std::vector<uint8_t>&> mReceive;
};

using ReceiveFn = std::function<void(ReceiveHandler)>;

Socket(SendFn sendFn, ReceiveFn receiveFn)
: mSendFn(std::move(sendFn))
, mReceiveFn(std::move(receiveFn))
{
}

std::size_t send(
const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to)
{
return mSendFn(pData, numBytes, to);
}

template <typename Handler>
void receive(Handler handler)
{
mReceiveFn(ReceiveHandler{
[handler](const asio::ip::udp::endpoint& from, const std::vector<uint8_t>& buffer) {
handler(from, begin(buffer), end(buffer));
}});
}

asio::ip::udp::endpoint endpoint() const
{
return asio::ip::udp::endpoint({}, 0);
}

SendFn mSendFn;
ReceiveFn mReceiveFn;
};

} // namespace test
} // namespace platforms
} // namespace ableton

+ 110
- 0
source/modules/hylia/link/ableton/test/serial_io/Timer.hpp View File

@@ -0,0 +1,110 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/test/serial_io/SchedulerTree.hpp>

namespace ableton
{
namespace test
{
namespace serial_io
{

struct Timer
{
using ErrorCode = SchedulerTree::TimerErrorCode;
using TimePoint = SchedulerTree::TimePoint;

Timer(const SchedulerTree::TimerId timerId,
const TimePoint& now,
std::shared_ptr<SchedulerTree> pScheduler)
: mId(timerId)
, mNow(now)
, mpScheduler(std::move(pScheduler))
{
}

~Timer()
{
if (!mbMovedFrom)
{
cancel();
}
}

Timer(const Timer&) = delete;

Timer(Timer&& rhs)
: mId(rhs.mId)
, mNow(rhs.mNow)
, mExpiration(std::move(rhs.mExpiration))
, mpScheduler(std::move(rhs.mpScheduler))
{
rhs.mbMovedFrom = true;
}

void expires_at(const TimePoint t)
{
if (t < mNow)
{
throw std::runtime_error("Setting timer in the past");
}
else
{
cancel();
mExpiration = t;
}
}

template <typename T, typename Rep>
void expires_from_now(std::chrono::duration<T, Rep> duration)
{
expires_at(mNow + duration);
}

void cancel()
{
auto pScheduler = mpScheduler.lock();
pScheduler->cancelTimer(mId);
}

template <typename Handler>
void async_wait(Handler handler)
{
auto pScheduler = mpScheduler.lock();
pScheduler->setTimer(mId, mExpiration, std::move(handler));
}

TimePoint now() const
{
return mNow;
}

const SchedulerTree::TimerId mId;
const TimePoint& mNow;
TimePoint mExpiration;
std::weak_ptr<SchedulerTree> mpScheduler;
bool mbMovedFrom = false;
};

} // namespace serial_io
} // namespace test
} // namespace ableton

+ 254
- 0
source/modules/hylia/link/ableton/util/Injected.hpp View File

@@ -0,0 +1,254 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <memory>

namespace ableton
{
namespace util
{

// Utility type for aiding in dependency injection.

// Base template and implementation for injected valued
template <typename T>
struct Injected
{
using type = T;

Injected() = default;

explicit Injected(T t)
: val(std::move(t))
{
}

Injected(const Injected&) = default;
Injected& operator=(const Injected&) = default;

Injected(Injected&& rhs)
: val(std::move(rhs.val))
{
}

Injected& operator=(Injected&& rhs)
{
val = std::move(rhs.val);
return *this;
}

T* operator->()
{
return &val;
}

const T* operator->() const
{
return &val;
}

T& operator*()
{
return val;
}

const T& operator*() const
{
return val;
}

T val;
};

// Utility function for injecting values
template <typename T>
Injected<T> injectVal(T t)
{
return Injected<T>(std::move(t));
}

// Specialization for injected references
template <typename T>
struct Injected<T&>
{
using type = T;

explicit Injected(T& t)
: ref(std::ref(t))
{
}

Injected(const Injected&) = default;
Injected& operator=(const Injected&) = default;

Injected(Injected&& rhs)
: ref(std::move(rhs.ref))
{
}

Injected& operator=(Injected&& rhs)
{
ref = std::move(rhs.ref);
return *this;
}

T* operator->()
{
return &ref.get();
}

const T* operator->() const
{
return &ref.get();
}

T& operator*()
{
return ref;
}

const T& operator*() const
{
return ref;
}

std::reference_wrapper<T> ref;
};

// Utility function for injecting references
template <typename T>
Injected<T&> injectRef(T& t)
{
return Injected<T&>(t);
}

// Specialization for injected shared_ptr
template <typename T>
struct Injected<std::shared_ptr<T>>
{
using type = T;

explicit Injected(std::shared_ptr<T> pT)
: shared(std::move(pT))
{
}

Injected(const Injected&) = default;
Injected& operator=(const Injected&) = default;

Injected(Injected&& rhs)
: shared(std::move(rhs.shared))
{
}

Injected& operator=(Injected&& rhs)
{
shared = std::move(rhs.shared);
return *this;
}

T* operator->()
{
return shared.get();
}

const T* operator->() const
{
return shared.get();
}

T& operator*()
{
return *shared;
}

const T& operator*() const
{
return *shared;
}

std::shared_ptr<T> shared;
};

// Utility function for injected shared_ptr
template <typename T>
Injected<std::shared_ptr<T>> injectShared(std::shared_ptr<T> shared)
{
return Injected<std::shared_ptr<T>>(std::move(shared));
}

// Specialization for injected unique_ptr
template <typename T>
struct Injected<std::unique_ptr<T>>
{
using type = T;

explicit Injected(std::unique_ptr<T> pT)
: unique(std::move(pT))
{
}

Injected(const Injected&) = default;
Injected& operator=(const Injected&) = default;

Injected(Injected&& rhs)
: unique(std::move(rhs.unique))
{
}

Injected& operator=(Injected&& rhs)
{
unique = std::move(rhs.unique);
return *this;
}

T* operator->()
{
return unique.get();
}

const T* operator->() const
{
return unique.get();
}

T& operator*()
{
return *unique;
}

const T& operator*() const
{
return *unique;
}

std::unique_ptr<T> unique;
};

// Utility function for injected unique_ptr
template <typename T>
Injected<std::unique_ptr<T>> injectUnique(std::unique_ptr<T> unique)
{
return Injected<std::unique_ptr<T>>(std::move(unique));
}

} // namespace util
} // namespace ableton

+ 176
- 0
source/modules/hylia/link/ableton/util/Log.hpp View File

@@ -0,0 +1,176 @@
// Copyright: 2014, Ableton AG, Berlin, all rights reserved

#pragma once

#include <ableton/util/Injected.hpp>
#include <iostream>
#include <string>

namespace ableton
{
namespace util
{

// Null object for the Log concept
struct NullLog
{
template <typename T>
friend const NullLog& operator<<(const NullLog& log, const T&)
{
return log;
}

friend const NullLog& debug(const NullLog& log)
{
return log;
}

friend const NullLog& info(const NullLog& log)
{
return log;
}

friend const NullLog& warning(const NullLog& log)
{
return log;
}

friend const NullLog& error(const NullLog& log)
{
return log;
}

friend NullLog channel(const NullLog&, std::string)
{
return {};
}
};

// std streams-based log
struct StdLog
{
StdLog(std::string channelName = "")
: mChannelName(std::move(channelName))
{
}

// Stream type used by std log to prepend the channel name to log messages
struct StdLogStream
{
StdLogStream(std::ostream& ioStream, const std::string& channelName)
: mpIoStream(&ioStream)
, mChannelName(channelName)
{
ioStream << "[" << mChannelName << "] ";
}

StdLogStream(StdLogStream&& rhs)
: mpIoStream(rhs.mpIoStream)
, mChannelName(rhs.mChannelName)
{
rhs.mpIoStream = nullptr;
}

~StdLogStream()
{
if (mpIoStream)
{
(*mpIoStream) << "\n";
}
}

template <typename T>
std::ostream& operator<<(const T& rhs)
{
(*mpIoStream) << rhs;
return *mpIoStream;
}

std::ostream* mpIoStream;
const std::string& mChannelName;
};

friend StdLogStream debug(const StdLog& log)
{
return {std::clog, log.mChannelName};
}

friend StdLogStream info(const StdLog& log)
{
return {std::clog, log.mChannelName};
}

friend StdLogStream warning(const StdLog& log)
{
return {std::clog, log.mChannelName};
}

friend StdLogStream error(const StdLog& log)
{
return {std::cerr, log.mChannelName};
}

friend StdLog channel(const StdLog& log, const std::string& channelName)
{
auto compositeName =
log.mChannelName.empty() ? channelName : log.mChannelName + "::" + channelName;
return {std::move(compositeName)};
}

std::string mChannelName;
};

// Log adapter that adds timestamps
template <typename Log>
struct Timestamped
{
using InnerLog = typename util::Injected<Log>::type;

Timestamped() = default;

Timestamped(util::Injected<Log> log)
: mLog(std::move(log))
{
}

util::Injected<Log> mLog;

friend decltype(debug(std::declval<InnerLog>())) debug(const Timestamped& log)
{
return log.logTimestamp(debug(*log.mLog));
}

friend decltype(info(std::declval<InnerLog>())) info(const Timestamped& log)
{
return log.logTimestamp(info(*log.mLog));
}

friend decltype(warning(std::declval<InnerLog>())) warning(const Timestamped& log)
{
return log.logTimestamp(warning(*log.mLog));
}

friend decltype(error(std::declval<InnerLog>())) error(const Timestamped& log)
{
return log.logTimestamp(error(*log.mLog));
}

friend Timestamped channel(const Timestamped& log, const std::string& channelName)
{
return {channel(*log.mLog, channelName)};
}

template <typename Stream>
Stream logTimestamp(Stream&& streamRef) const
{
using namespace std::chrono;
Stream stream = std::forward<Stream>(streamRef);
stream << "|"
<< duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count()
<< "ms| ";
return stream;
}
};

} // namespace util
} // namespace ableton

+ 66
- 0
source/modules/hylia/link/ableton/util/SafeAsyncHandler.hpp View File

@@ -0,0 +1,66 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <memory>

namespace ableton
{
namespace util
{

// A utility handler for passing to async functions that may call the
// handler past the lifetime of the wrapped delegate object.
// The need for this is particularly driven by boost::asio timer
// objects, which explicitly document that they may be called without
// an error code after they have been cancelled. This has led to
// several crashes. This handler wrapper implements a useful idiom for
// avoiding this problem.

template <typename Delegate>
struct SafeAsyncHandler
{
SafeAsyncHandler(const std::shared_ptr<Delegate>& pDelegate)
: mpDelegate(pDelegate)
{
}

template <typename... T>
void operator()(T&&... t) const
{
std::shared_ptr<Delegate> pDelegate = mpDelegate.lock();
if (pDelegate)
{
(*pDelegate)(std::forward<T>(t)...);
}
}

std::weak_ptr<Delegate> mpDelegate;
};

// Factory function for easily wrapping a shared_ptr to a handler
template <typename Delegate>
SafeAsyncHandler<Delegate> makeAsyncSafe(const std::shared_ptr<Delegate>& pDelegate)
{
return {pDelegate};
}

} // namespace util
} // namespace ableton

+ 52
- 0
source/modules/hylia/link/ableton/util/SampleTiming.hpp View File

@@ -0,0 +1,52 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>

namespace ableton
{
namespace util
{

/*! Utility type to convert between time and sample index given the
* time at the beginning of a buffer and the sample rate.
*/
struct SampleTiming
{
double sampleAtTime(std::chrono::microseconds time) const
{
using namespace std::chrono;
return duration_cast<duration<double>>(time - mBufferBegin).count() * mSampleRate;
}

std::chrono::microseconds timeAtSample(const double sample) const
{
using namespace std::chrono;
return mBufferBegin
+ duration_cast<microseconds>(duration<double>{sample / mSampleRate});
}

std::chrono::microseconds mBufferBegin;
double mSampleRate;
};

} // util
} // ableton

+ 114
- 0
source/modules/hylia/link/ableton/util/test/IoService.hpp View File

@@ -0,0 +1,114 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <ableton/util/test/Timer.hpp>

namespace ableton
{
namespace util
{
namespace test
{

struct IoService
{
// Wrapper around the internal util::test::Timer in the list
struct Timer
{
using ErrorCode = test::Timer::ErrorCode;
using TimePoint = test::Timer::TimePoint;

Timer(util::test::Timer* pTimer)
: mpTimer(pTimer)
{
}

void expires_at(std::chrono::system_clock::time_point t)
{
mpTimer->expires_at(t);
}

template <typename T, typename Rep>
void expires_from_now(std::chrono::duration<T, Rep> duration)
{
mpTimer->expires_from_now(duration);
}

ErrorCode cancel()
{
return mpTimer->cancel();
}

template <typename Handler>
void async_wait(Handler handler)
{
mpTimer->async_wait(std::move(handler));
}

TimePoint now() const
{
return mpTimer->now();
}

util::test::Timer* mpTimer;
};

IoService() = default;

Timer makeTimer()
{
mTimers.emplace_back();
return Timer{&mTimers.back()};
}

template <typename Handler>
void post(Handler handler)
{
mHandlers.emplace_back(std::move(handler));
}

template <typename T, typename Rep>
void advance(std::chrono::duration<T, Rep> duration)
{
runHandlers();

for (auto& timer : mTimers)
{
timer.advance(duration);
}
}

void runHandlers()
{
for (auto& handler : mHandlers)
{
handler();
}
mHandlers.clear();
}

std::vector<std::function<void()>> mHandlers;
std::vector<util::test::Timer> mTimers;
};

} // namespace test
} // namespace util
} // namespace ableton

+ 96
- 0
source/modules/hylia/link/ableton/util/test/Timer.hpp View File

@@ -0,0 +1,96 @@
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/

#pragma once

#include <chrono>
#include <functional>

namespace ableton
{
namespace util
{
namespace test
{

struct Timer
{
using ErrorCode = int;
using TimePoint = std::chrono::system_clock::time_point;

// Initialize timer with an arbitrary large value to simulate the
// time_since_epoch of a real clock.
Timer()
: mNow{std::chrono::milliseconds{123456789}}
{
}

void expires_at(std::chrono::system_clock::time_point t)
{
cancel();
mFireAt = std::move(t);
}

template <typename T, typename Rep>
void expires_from_now(std::chrono::duration<T, Rep> duration)
{
cancel();
mFireAt = now() + duration;
}

ErrorCode cancel()
{
if (mHandler)
{
mHandler(1); // call existing handler with truthy error code
}
mHandler = nullptr;
return 0;
}

template <typename Handler>
void async_wait(Handler handler)
{
mHandler = [handler](ErrorCode ec) { handler(ec); };
}

std::chrono::system_clock::time_point now() const
{
return mNow;
}

template <typename T, typename Rep>
void advance(std::chrono::duration<T, Rep> duration)
{
mNow += duration;
if (mHandler && mFireAt < mNow)
{
mHandler(0);
mHandler = nullptr;
}
}

std::function<void(ErrorCode)> mHandler;
std::chrono::system_clock::time_point mFireAt;
std::chrono::system_clock::time_point mNow;
};

} // namespace test
} // namespace util
} // namespace ableton

+ 144
- 0
source/modules/hylia/link/asio.hpp View File

@@ -0,0 +1,144 @@
//
// asio.hpp
// ~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_HPP
#define ASIO_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/associated_allocator.hpp"
#include "asio/associated_executor.hpp"
#include "asio/async_result.hpp"
#include "asio/basic_datagram_socket.hpp"
#include "asio/basic_deadline_timer.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/basic_raw_socket.hpp"
#include "asio/basic_seq_packet_socket.hpp"
#include "asio/basic_serial_port.hpp"
#include "asio/basic_signal_set.hpp"
#include "asio/basic_socket_acceptor.hpp"
#include "asio/basic_socket_iostream.hpp"
#include "asio/basic_socket_streambuf.hpp"
#include "asio/basic_stream_socket.hpp"
#include "asio/basic_streambuf.hpp"
#include "asio/basic_waitable_timer.hpp"
#include "asio/bind_executor.hpp"
#include "asio/buffer.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/buffered_stream.hpp"
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/buffers_iterator.hpp"
#include "asio/completion_condition.hpp"
#include "asio/connect.hpp"
#include "asio/coroutine.hpp"
#include "asio/datagram_socket_service.hpp"
#include "asio/deadline_timer_service.hpp"
#include "asio/deadline_timer.hpp"
#include "asio/defer.hpp"
#include "asio/dispatch.hpp"
#include "asio/error.hpp"
#include "asio/error_code.hpp"
#include "asio/execution_context.hpp"
#include "asio/executor.hpp"
#include "asio/executor_work_guard.hpp"
#include "asio/generic/basic_endpoint.hpp"
#include "asio/generic/datagram_protocol.hpp"
#include "asio/generic/raw_protocol.hpp"
#include "asio/generic/seq_packet_protocol.hpp"
#include "asio/generic/stream_protocol.hpp"
#include "asio/handler_alloc_hook.hpp"
#include "asio/handler_continuation_hook.hpp"
#include "asio/handler_invoke_hook.hpp"
#include "asio/handler_type.hpp"
#include "asio/io_context.hpp"
#include "asio/io_context_strand.hpp"
#include "asio/io_service.hpp"
#include "asio/io_service_strand.hpp"
#include "asio/ip/address.hpp"
#include "asio/ip/address_v4.hpp"
#include "asio/ip/address_v4_iterator.hpp"
#include "asio/ip/address_v4_range.hpp"
#include "asio/ip/address_v6.hpp"
#include "asio/ip/address_v6_iterator.hpp"
#include "asio/ip/address_v6_range.hpp"
#include "asio/ip/bad_address_cast.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver.hpp"
#include "asio/ip/basic_resolver_entry.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
#include "asio/ip/host_name.hpp"
#include "asio/ip/icmp.hpp"
#include "asio/ip/multicast.hpp"
#include "asio/ip/resolver_base.hpp"
#include "asio/ip/resolver_query_base.hpp"
#include "asio/ip/resolver_service.hpp"
#include "asio/ip/tcp.hpp"
#include "asio/ip/udp.hpp"
#include "asio/ip/unicast.hpp"
#include "asio/ip/v6_only.hpp"
#include "asio/is_executor.hpp"
#include "asio/is_read_buffered.hpp"
#include "asio/is_write_buffered.hpp"
#include "asio/local/basic_endpoint.hpp"
#include "asio/local/connect_pair.hpp"
#include "asio/local/datagram_protocol.hpp"
#include "asio/local/stream_protocol.hpp"
#include "asio/placeholders.hpp"
#include "asio/posix/basic_descriptor.hpp"
#include "asio/posix/basic_stream_descriptor.hpp"
#include "asio/posix/descriptor_base.hpp"
#include "asio/posix/stream_descriptor.hpp"
#include "asio/posix/stream_descriptor_service.hpp"
#include "asio/post.hpp"
#include "asio/raw_socket_service.hpp"
#include "asio/read.hpp"
#include "asio/read_at.hpp"
#include "asio/read_until.hpp"
#include "asio/seq_packet_socket_service.hpp"
#include "asio/serial_port.hpp"
#include "asio/serial_port_base.hpp"
#include "asio/serial_port_service.hpp"
#include "asio/signal_set.hpp"
#include "asio/signal_set_service.hpp"
#include "asio/socket_acceptor_service.hpp"
#include "asio/socket_base.hpp"
#include "asio/strand.hpp"
#include "asio/stream_socket_service.hpp"
#include "asio/streambuf.hpp"
#include "asio/system_error.hpp"
#include "asio/system_executor.hpp"
#include "asio/thread.hpp"
#include "asio/thread_pool.hpp"
#include "asio/time_traits.hpp"
#include "asio/uses_executor.hpp"
#include "asio/version.hpp"
#include "asio/wait_traits.hpp"
#include "asio/waitable_timer_service.hpp"
#include "asio/windows/basic_handle.hpp"
#include "asio/windows/basic_object_handle.hpp"
#include "asio/windows/basic_random_access_handle.hpp"
#include "asio/windows/basic_stream_handle.hpp"
#include "asio/windows/object_handle.hpp"
#include "asio/windows/object_handle_service.hpp"
#include "asio/windows/overlapped_ptr.hpp"
#include "asio/windows/random_access_handle.hpp"
#include "asio/windows/random_access_handle_service.hpp"
#include "asio/windows/stream_handle.hpp"
#include "asio/windows/stream_handle_service.hpp"
#include "asio/write.hpp"
#include "asio/write_at.hpp"

#endif // ASIO_HPP

+ 123
- 0
source/modules/hylia/link/asio/associated_allocator.hpp View File

@@ -0,0 +1,123 @@
//
// associated_allocator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_ASSOCIATED_ALLOCATOR_HPP
#define ASIO_ASSOCIATED_ALLOCATOR_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <memory>
#include "asio/detail/type_traits.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
namespace detail {

template <typename>
struct associated_allocator_check
{
typedef void type;
};

template <typename T, typename E, typename = void>
struct associated_allocator_impl
{
typedef E type;

static type get(const T&, const E& e) ASIO_NOEXCEPT
{
return e;
}
};

template <typename T, typename E>
struct associated_allocator_impl<T, E,
typename associated_allocator_check<typename T::allocator_type>::type>
{
typedef typename T::allocator_type type;

static type get(const T& t, const E&) ASIO_NOEXCEPT
{
return t.get_allocator();
}
};

} // namespace detail

/// Traits type used to obtain the allocator associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* Allocator shall be a type meeting the Allocator requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c a is an object of type @c
* Allocator.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* Allocator requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,a) and with return type @c type.
*/
template <typename T, typename Allocator = std::allocator<void> >
struct associated_allocator
{
/// If @c T has a nested type @c allocator_type, <tt>T::allocator_type</tt>.
/// Otherwise @c Allocator.
#if defined(GENERATING_DOCUMENTATION)
typedef see_below type;
#else // defined(GENERATING_DOCUMENTATION)
typedef typename detail::associated_allocator_impl<T, Allocator>::type type;
#endif // defined(GENERATING_DOCUMENTATION)

/// If @c T has a nested type @c allocator_type, returns
/// <tt>t.get_allocator()</tt>. Otherwise returns @c a.
static type get(const T& t,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return detail::associated_allocator_impl<T, Allocator>::get(t, a);
}
};

/// Helper function to obtain an object's associated allocator.
/**
* @returns <tt>associated_allocator<T>::get(t)</tt>
*/
template <typename T>
inline typename associated_allocator<T>::type
get_associated_allocator(const T& t) ASIO_NOEXCEPT
{
return associated_allocator<T>::get(t);
}

/// Helper function to obtain an object's associated allocator.
/**
* @returns <tt>associated_allocator<T, Allocator>::get(t, a)</tt>
*/
template <typename T, typename Allocator>
inline typename associated_allocator<T, Allocator>::type
get_associated_allocator(const T& t, const Allocator& a) ASIO_NOEXCEPT
{
return associated_allocator<T, Allocator>::get(t, a);
}

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_ASSOCIATED_ALLOCATOR_HPP

+ 142
- 0
source/modules/hylia/link/asio/associated_executor.hpp View File

@@ -0,0 +1,142 @@
//
// associated_executor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_ASSOCIATED_EXECUTOR_HPP
#define ASIO_ASSOCIATED_EXECUTOR_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/is_executor.hpp"
#include "asio/system_executor.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
namespace detail {

template <typename>
struct associated_executor_check
{
typedef void type;
};

template <typename T, typename E, typename = void>
struct associated_executor_impl
{
typedef E type;

static type get(const T&, const E& e) ASIO_NOEXCEPT
{
return e;
}
};

template <typename T, typename E>
struct associated_executor_impl<T, E,
typename associated_executor_check<typename T::executor_type>::type>
{
typedef typename T::executor_type type;

static type get(const T& t, const E&) ASIO_NOEXCEPT
{
return t.get_executor();
}
};

} // namespace detail

/// Traits type used to obtain the executor associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* Executor shall be a type meeting the Executor requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c e is an object of type @c
* Executor.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* Executor requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,e) and with return type @c type.
*/
template <typename T, typename Executor = system_executor>
struct associated_executor
{
/// If @c T has a nested type @c executor_type, <tt>T::executor_type</tt>.
/// Otherwise @c Executor.
#if defined(GENERATING_DOCUMENTATION)
typedef see_below type;
#else // defined(GENERATING_DOCUMENTATION)
typedef typename detail::associated_executor_impl<T, Executor>::type type;
#endif // defined(GENERATING_DOCUMENTATION)

/// If @c T has a nested type @c executor_type, returns
/// <tt>t.get_executor()</tt>. Otherwise returns @c ex.
static type get(const T& t,
const Executor& ex = Executor()) ASIO_NOEXCEPT
{
return detail::associated_executor_impl<T, Executor>::get(t, ex);
}
};

/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T>::get(t)</tt>
*/
template <typename T>
inline typename associated_executor<T>::type
get_associated_executor(const T& t) ASIO_NOEXCEPT
{
return associated_executor<T>::get(t);
}

/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T, Executor>::get(t, ex)</tt>
*/
template <typename T, typename Executor>
inline typename associated_executor<T, Executor>::type
get_associated_executor(const T& t, const Executor& ex,
typename enable_if<is_executor<
Executor>::value>::type* = 0) ASIO_NOEXCEPT
{
return associated_executor<T, Executor>::get(t, ex);
}

/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T, typename
* ExecutionContext::executor_type>::get(t, ctx.get_executor())</tt>
*/
template <typename T, typename ExecutionContext>
inline typename associated_executor<T,
typename ExecutionContext::executor_type>::type
get_associated_executor(const T& t, ExecutionContext& ctx,
typename enable_if<is_convertible<ExecutionContext&,
execution_context&>::value>::type* = 0) ASIO_NOEXCEPT
{
return associated_executor<T,
typename ExecutionContext::executor_type>::get(t, ctx.get_executor());
}

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_ASSOCIATED_EXECUTOR_HPP

+ 125
- 0
source/modules/hylia/link/asio/async_result.hpp View File

@@ -0,0 +1,125 @@
//
// async_result.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_ASYNC_RESULT_HPP
#define ASIO_ASYNC_RESULT_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/handler_type.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// An interface for customising the behaviour of an initiating function.
/**
* This template may be specialised for user-defined handler types.
*/
template <typename Handler>
class async_result
{
public:
/// The return type of the initiating function.
typedef void type;

/// Construct an async result from a given handler.
/**
* When using a specalised async_result, the constructor has an opportunity
* to initialise some state associated with the handler, which is then
* returned from the initiating function.
*/
explicit async_result(Handler&)
{
}

/// Obtain the value to be returned from the initiating function.
type get()
{
}
};

/// Helper template to deduce the real type of a handler, capture a local copy
/// of the handler, and then create an async_result for the handler.
template <typename Handler, typename Signature>
struct async_completion
{
/// The real handler type to be used for the asynchronous operation.
typedef typename asio::handler_type<
Handler, Signature>::type handler_type;

/// Constructor.
/**
* The constructor creates the concrete handler and makes the link between
* the handler and the asynchronous result.
*/
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
explicit async_completion(
Handler& orig_handler)
: handler(static_cast<typename conditional<
is_same<Handler, handler_type>::value,
handler_type&, Handler&&>::type>(orig_handler)),
result(handler)
{
}
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
explicit async_completion(const Handler& orig_handler)
: handler(orig_handler),
result(handler)
{
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// A copy of, or reference to, a real handler object.
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
typename conditional<
is_same<Handler, handler_type>::value,
handler_type&, handler_type>::type handler;
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
typename asio::handler_type<Handler, Signature>::type handler;
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// The result of the asynchronous operation's initiating function.
async_result<typename asio::handler_type<
Handler, Signature>::type> result;
};

namespace detail {

template <typename Handler, typename Signature>
struct async_result_type_helper
{
typedef typename async_result<
typename handler_type<Handler, Signature>::type
>::type type;
};

} // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#if defined(GENERATING_DOCUMENTATION)
# define ASIO_INITFN_RESULT_TYPE(h, sig) \
void_or_deduced
#elif defined(_MSC_VER) && (_MSC_VER < 1500)
# define ASIO_INITFN_RESULT_TYPE(h, sig) \
typename ::asio::detail::async_result_type_helper<h, sig>::type
#else
# define ASIO_INITFN_RESULT_TYPE(h, sig) \
typename ::asio::async_result< \
typename ::asio::handler_type<h, sig>::type>::type
#endif

#endif // ASIO_ASYNC_RESULT_HPP

+ 945
- 0
source/modules/hylia/link/asio/basic_datagram_socket.hpp View File

@@ -0,0 +1,945 @@
//
// basic_datagram_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP
#define ASIO_BASIC_DATAGRAM_SOCKET_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/basic_socket.hpp"
#include "asio/datagram_socket_service.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides datagram-oriented socket functionality.
/**
* The basic_datagram_socket class template provides asynchronous and blocking
* datagram-oriented socket functionality.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename Protocol,
typename DatagramSocketService = datagram_socket_service<Protocol> >
class basic_datagram_socket
: public basic_socket<Protocol, DatagramSocketService>
{
public:
/// The native representation of a socket.
typedef typename DatagramSocketService::native_handle_type native_handle_type;

/// The protocol type.
typedef Protocol protocol_type;

/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

/// Construct a basic_datagram_socket without opening it.
/**
* This constructor creates a datagram socket without opening it. The open()
* function must be called before data can be sent or received on the socket.
*
* @param io_context The io_context object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*/
explicit basic_datagram_socket(asio::io_context& io_context)
: basic_socket<Protocol, DatagramSocketService>(io_context)
{
}

/// Construct and open a basic_datagram_socket.
/**
* This constructor creates and opens a datagram socket.
*
* @param io_context The io_context object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::system_error Thrown on failure.
*/
basic_datagram_socket(asio::io_context& io_context,
const protocol_type& protocol)
: basic_socket<Protocol, DatagramSocketService>(io_context, protocol)
{
}

/// Construct a basic_datagram_socket, opening it and binding it to the given
/// local endpoint.
/**
* This constructor creates a datagram socket and automatically opens it bound
* to the specified endpoint on the local machine. The protocol used is the
* protocol associated with the given endpoint.
*
* @param io_context The io_context object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param endpoint An endpoint on the local machine to which the datagram
* socket will be bound.
*
* @throws asio::system_error Thrown on failure.
*/
basic_datagram_socket(asio::io_context& io_context,
const endpoint_type& endpoint)
: basic_socket<Protocol, DatagramSocketService>(io_context, endpoint)
{
}

/// Construct a basic_datagram_socket on an existing native socket.
/**
* This constructor creates a datagram socket object to hold an existing
* native socket.
*
* @param io_context The io_context object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::system_error Thrown on failure.
*/
basic_datagram_socket(asio::io_context& io_context,
const protocol_type& protocol, const native_handle_type& native_socket)
: basic_socket<Protocol, DatagramSocketService>(
io_context, protocol, native_socket)
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_datagram_socket from another.
/**
* This constructor moves a datagram socket from one object to another.
*
* @param other The other basic_datagram_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_datagram_socket(io_context&) constructor.
*/
basic_datagram_socket(basic_datagram_socket&& other)
: basic_socket<Protocol, DatagramSocketService>(
ASIO_MOVE_CAST(basic_datagram_socket)(other))
{
}

/// Move-assign a basic_datagram_socket from another.
/**
* This assignment operator moves a datagram socket from one object to
* another.
*
* @param other The other basic_datagram_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_datagram_socket(io_context&) constructor.
*/
basic_datagram_socket& operator=(basic_datagram_socket&& other)
{
basic_socket<Protocol, DatagramSocketService>::operator=(
ASIO_MOVE_CAST(basic_datagram_socket)(other));
return *this;
}

/// Move-construct a basic_datagram_socket from a socket of another protocol
/// type.
/**
* This constructor moves a datagram socket from one object to another.
*
* @param other The other basic_datagram_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_datagram_socket(io_context&) constructor.
*/
template <typename Protocol1, typename DatagramSocketService1>
basic_datagram_socket(
basic_datagram_socket<Protocol1, DatagramSocketService1>&& other,
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
: basic_socket<Protocol, DatagramSocketService>(
ASIO_MOVE_CAST2(basic_datagram_socket<
Protocol1, DatagramSocketService1>)(other))
{
}

/// Move-assign a basic_datagram_socket from a socket of another protocol
/// type.
/**
* This assignment operator moves a datagram socket from one object to
* another.
*
* @param other The other basic_datagram_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_datagram_socket(io_context&) constructor.
*/
template <typename Protocol1, typename DatagramSocketService1>
typename enable_if<is_convertible<Protocol1, Protocol>::value,
basic_datagram_socket>::type& operator=(
basic_datagram_socket<Protocol1, DatagramSocketService1>&& other)
{
basic_socket<Protocol, DatagramSocketService>::operator=(
ASIO_MOVE_CAST2(basic_datagram_socket<
Protocol1, DatagramSocketService1>)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code socket.send(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().send(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous send on a connected socket.
/**
* This function is used to asynchronously send data on the datagram socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected datagram
* socket.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Start an asynchronous send on a connected socket.
/**
* This function is used to asynchronously send data on the datagram socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected datagram
* socket.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.send_to(asio::buffer(data, size), destination);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination)
{
asio::error_code ec;
std::size_t s = this->get_service().send_to(
this->get_implementation(), buffers, destination, 0, ec);
asio::detail::throw_error(ec, "send_to");
return s;
}

/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send_to(
this->get_implementation(), buffers, destination, flags, ec);
asio::detail::throw_error(ec, "send_to");
return s;
}

/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
asio::error_code& ec)
{
return this->get_service().send_to(this->get_implementation(),
buffers, destination, flags, ec);
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send a datagram to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.async_send_to(
* asio::buffer(data, size), destination, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send_to(
this->get_implementation(), buffers, destination, 0,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send a datagram to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send_to(
this->get_implementation(), buffers, destination, flags,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.receive(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the datagram
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* datagram socket.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the datagram
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* datagram socket.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* asio::ip::udp::endpoint sender_endpoint;
* socket.receive_from(
* asio::buffer(data, size), sender_endpoint);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint)
{
asio::error_code ec;
std::size_t s = this->get_service().receive_from(
this->get_implementation(), buffers, sender_endpoint, 0, ec);
asio::detail::throw_error(ec, "receive_from");
return s;
}
/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive_from(
this->get_implementation(), buffers, sender_endpoint, flags, ec);
asio::detail::throw_error(ec, "receive_from");
return s;
}
/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
asio::error_code& ec)
{
return this->get_service().receive_from(this->get_implementation(),
buffers, sender_endpoint, flags, ec);
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive a datagram. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.async_receive_from(
* asio::buffer(data, size), sender_endpoint, handler); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive_from(
this->get_implementation(), buffers, sender_endpoint, 0,
ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive a datagram. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive_from(
this->get_implementation(), buffers, sender_endpoint, flags,
ASIO_MOVE_CAST(ReadHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP

+ 521
- 0
source/modules/hylia/link/asio/basic_deadline_timer.hpp View File

@@ -0,0 +1,521 @@
//
// basic_deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
#define ASIO_BASIC_DEADLINE_TIMER_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|| defined(GENERATING_DOCUMENTATION)

#include <cstddef>
#include "asio/basic_io_object.hpp"
#include "asio/deadline_timer_service.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides waitable timer functionality.
/**
* The basic_deadline_timer class template provides the ability to perform a
* blocking or asynchronous wait for a timer to expire.
*
* A deadline timer is always in one of two states: "expired" or "not expired".
* If the wait() or async_wait() function is called on an expired timer, the
* wait operation will complete immediately.
*
* Most applications will use the asio::deadline_timer typedef.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Examples
* Performing a blocking wait:
* @code
* // Construct a timer without setting an expiry time.
* asio::deadline_timer timer(io_context);
*
* // Set an expiry time relative to now.
* timer.expires_from_now(boost::posix_time::seconds(5));
*
* // Wait for the timer to expire.
* timer.wait();
* @endcode
*
* @par
* Performing an asynchronous wait:
* @code
* void handler(const asio::error_code& error)
* {
* if (!error)
* {
* // Timer expired.
* }
* }
*
* ...
*
* // Construct a timer with an absolute expiry time.
* asio::deadline_timer timer(io_context,
* boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
*
* // Start an asynchronous wait.
* timer.async_wait(handler);
* @endcode
*
* @par Changing an active deadline_timer's expiry time
*
* Changing the expiry time of a timer while there are pending asynchronous
* waits causes those wait operations to be cancelled. To ensure that the action
* associated with the timer is performed only once, use something like this:
* used:
*
* @code
* void on_some_event()
* {
* if (my_timer.expires_from_now(seconds(5)) > 0)
* {
* // We managed to cancel the timer. Start new asynchronous wait.
* my_timer.async_wait(on_timeout);
* }
* else
* {
* // Too late, timer has already expired!
* }
* }
*
* void on_timeout(const asio::error_code& e)
* {
* if (e != asio::error::operation_aborted)
* {
* // Timer was not cancelled, take necessary action.
* }
* }
* @endcode
*
* @li The asio::basic_deadline_timer::expires_from_now() function
* cancels any pending asynchronous waits, and returns the number of
* asynchronous waits that were cancelled. If it returns 0 then you were too
* late and the wait handler has already been executed, or will soon be
* executed. If it returns 1 then the wait handler was successfully cancelled.
*
* @li If a wait handler is cancelled, the asio::error_code passed to
* it contains the value asio::error::operation_aborted.
*/
template <typename Time,
typename TimeTraits = asio::time_traits<Time>,
typename TimerService = deadline_timer_service<Time, TimeTraits> >
class basic_deadline_timer
: public basic_io_object<TimerService>
{
public:
/// The time traits type.
typedef TimeTraits traits_type;

/// The time type.
typedef typename traits_type::time_type time_type;

/// The duration type.
typedef typename traits_type::duration_type duration_type;

/// Constructor.
/**
* This constructor creates a timer without setting an expiry time. The
* expires_at() or expires_from_now() functions must be called to set an
* expiry time before the timer can be waited on.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*/
explicit basic_deadline_timer(asio::io_context& io_context)
: basic_io_object<TimerService>(io_context)
{
}

/// Constructor to set a particular expiry time as an absolute time.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
basic_deadline_timer(asio::io_context& io_context,
const time_type& expiry_time)
: basic_io_object<TimerService>(io_context)
{
asio::error_code ec;
this->get_service().expires_at(this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
}

/// Constructor to set a particular expiry time relative to now.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
basic_deadline_timer(asio::io_context& io_context,
const duration_type& expiry_time)
: basic_io_object<TimerService>(io_context)
{
asio::error_code ec;
this->get_service().expires_from_now(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
}

/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel()
{
asio::error_code ec;
std::size_t s = this->get_service().cancel(this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
return s;
}

/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel(asio::error_code& ec)
{
return this->get_service().cancel(this->get_implementation(), ec);
}

/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one()
{
asio::error_code ec;
std::size_t s = this->get_service().cancel_one(
this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel_one");
return s;
}

/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one(asio::error_code& ec)
{
return this->get_service().cancel_one(this->get_implementation(), ec);
}

/// Get the timer's expiry time as an absolute time.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
time_type expires_at() const
{
return this->get_service().expires_at(this->get_implementation());
}

/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_type& expiry_time)
{
asio::error_code ec;
std::size_t s = this->get_service().expires_at(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
return s;
}

/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_type& expiry_time,
asio::error_code& ec)
{
return this->get_service().expires_at(
this->get_implementation(), expiry_time, ec);
}

/// Get the timer's expiry time relative to now.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
duration_type expires_from_now() const
{
return this->get_service().expires_from_now(this->get_implementation());
}

/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration_type& expiry_time)
{
asio::error_code ec;
std::size_t s = this->get_service().expires_from_now(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
return s;
}

/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration_type& expiry_time,
asio::error_code& ec)
{
return this->get_service().expires_from_now(
this->get_implementation(), expiry_time, ec);
}

/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @throws asio::system_error Thrown on failure.
*/
void wait()
{
asio::error_code ec;
this->get_service().wait(this->get_implementation(), ec);
asio::detail::throw_error(ec, "wait");
}

/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @param ec Set to indicate what error occurred, if any.
*/
void wait(asio::error_code& ec)
{
this->get_service().wait(this->get_implementation(), ec);
}

/// Start an asynchronous wait on the timer.
/**
* This function may be used to initiate an asynchronous wait against the
* timer. It always returns immediately.
*
* For each call to async_wait(), the supplied handler will be called exactly
* once. The handler will be called when:
*
* @li The timer has expired.
*
* @li The timer was cancelled, in which case the handler is passed the error
* code asio::error::operation_aborted.
*
* @param handler The handler to be called when the timer expires. Copies
* will be made of the handler as required. The function signature of the
* handler must be:
* @code void handler(
* const asio::error_code& error // Result of operation.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename WaitHandler>
ASIO_INITFN_RESULT_TYPE(WaitHandler,
void (asio::error_code))
async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WaitHandler.
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;

return this->get_service().async_wait(this->get_implementation(),
ASIO_MOVE_CAST(WaitHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
// || defined(GENERATING_DOCUMENTATION)

#endif // ASIO_BASIC_DEADLINE_TIMER_HPP

+ 274
- 0
source/modules/hylia/link/asio/basic_io_object.hpp View File

@@ -0,0 +1,274 @@
//
// basic_io_object.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_IO_OBJECT_HPP
#define ASIO_BASIC_IO_OBJECT_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/io_context.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

#if defined(ASIO_HAS_MOVE)
namespace detail
{
// Type trait used to determine whether a service supports move.
template <typename IoObjectService>
class service_has_move
{
private:
typedef IoObjectService service_type;
typedef typename service_type::implementation_type implementation_type;

template <typename T, typename U>
static auto eval(T* t, U* u) -> decltype(t->move_construct(*u, *u), char());
static char (&eval(...))[2];

public:
static const bool value =
sizeof(service_has_move::eval(
static_cast<service_type*>(0),
static_cast<implementation_type*>(0))) == 1;
};
}
#endif // defined(ASIO_HAS_MOVE)

/// Base class for all I/O objects.
/**
* @note All I/O objects are non-copyable. However, when using C++0x, certain
* I/O objects do support move construction and move assignment.
*/
#if !defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
template <typename IoObjectService>
#else
template <typename IoObjectService,
bool Movable = detail::service_has_move<IoObjectService>::value>
#endif
class basic_io_object
{
public:
/// The type of the service that will be used to provide I/O operations.
typedef IoObjectService service_type;

/// The underlying implementation type of I/O object.
typedef typename service_type::implementation_type implementation_type;

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
/**
* This function may be used to obtain the io_context object that the I/O
* object uses to dispatch handlers for asynchronous operations.
*
* @return A reference to the io_context object that the I/O object will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
asio::io_context& get_io_context()
{
return service_.get_io_context();
}

/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
/**
* This function may be used to obtain the io_context object that the I/O
* object uses to dispatch handlers for asynchronous operations.
*
* @return A reference to the io_context object that the I/O object will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
asio::io_context& get_io_service()
{
return service_.get_io_context();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// The type of the executor associated with the object.
typedef asio::io_context::executor_type executor_type;

/// Get the executor associated with the object.
executor_type get_executor() ASIO_NOEXCEPT
{
return service_.get_io_context().get_executor();
}

protected:
/// Construct a basic_io_object.
/**
* Performs:
* @code get_service().construct(get_implementation()); @endcode
*/
explicit basic_io_object(asio::io_context& io_context)
: service_(asio::use_service<IoObjectService>(io_context))
{
service_.construct(implementation_);
}

#if defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_io_object.
/**
* Performs:
* @code get_service().move_construct(
* get_implementation(), other.get_implementation()); @endcode
*
* @note Available only for services that support movability,
*/
basic_io_object(basic_io_object&& other);

/// Move-assign a basic_io_object.
/**
* Performs:
* @code get_service().move_assign(get_implementation(),
* other.get_service(), other.get_implementation()); @endcode
*
* @note Available only for services that support movability,
*/
basic_io_object& operator=(basic_io_object&& other);
#endif // defined(GENERATING_DOCUMENTATION)

/// Protected destructor to prevent deletion through this type.
/**
* Performs:
* @code get_service().destroy(get_implementation()); @endcode
*/
~basic_io_object()
{
service_.destroy(implementation_);
}

/// Get the service associated with the I/O object.
service_type& get_service()
{
return service_;
}

/// Get the service associated with the I/O object.
const service_type& get_service() const
{
return service_;
}

/// Get the underlying implementation of the I/O object.
implementation_type& get_implementation()
{
return implementation_;
}

/// Get the underlying implementation of the I/O object.
const implementation_type& get_implementation() const
{
return implementation_;
}

private:
basic_io_object(const basic_io_object&);
basic_io_object& operator=(const basic_io_object&);

// The service associated with the I/O object.
service_type& service_;

/// The underlying implementation of the I/O object.
implementation_type implementation_;
};

#if defined(ASIO_HAS_MOVE)
// Specialisation for movable objects.
template <typename IoObjectService>
class basic_io_object<IoObjectService, true>
{
public:
typedef IoObjectService service_type;
typedef typename service_type::implementation_type implementation_type;

#if !defined(ASIO_NO_DEPRECATED)
asio::io_context& get_io_context()
{
return service_->get_io_context();
}

asio::io_context& get_io_service()
{
return service_->get_io_context();
}
#endif // !defined(ASIO_NO_DEPRECATED)

typedef asio::io_context::executor_type executor_type;

executor_type get_executor() ASIO_NOEXCEPT
{
return service_->get_io_context().get_executor();
}

protected:
explicit basic_io_object(asio::io_context& io_context)
: service_(&asio::use_service<IoObjectService>(io_context))
{
service_->construct(implementation_);
}

basic_io_object(basic_io_object&& other)
: service_(&other.get_service())
{
service_->move_construct(implementation_, other.implementation_);
}

~basic_io_object()
{
service_->destroy(implementation_);
}

basic_io_object& operator=(basic_io_object&& other)
{
service_->move_assign(implementation_,
*other.service_, other.implementation_);
service_ = other.service_;
return *this;
}

service_type& get_service()
{
return *service_;
}

const service_type& get_service() const
{
return *service_;
}

implementation_type& get_implementation()
{
return implementation_;
}

const implementation_type& get_implementation() const
{
return implementation_;
}

private:
basic_io_object(const basic_io_object&);
void operator=(const basic_io_object&);

IoObjectService* service_;
implementation_type implementation_;
};
#endif // defined(ASIO_HAS_MOVE)

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_IO_OBJECT_HPP

+ 936
- 0
source/modules/hylia/link/asio/basic_raw_socket.hpp View File

@@ -0,0 +1,936 @@
//
// basic_raw_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_RAW_SOCKET_HPP
#define ASIO_BASIC_RAW_SOCKET_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/basic_socket.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"
#include "asio/raw_socket_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides raw-oriented socket functionality.
/**
* The basic_raw_socket class template provides asynchronous and blocking
* raw-oriented socket functionality.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename Protocol,
typename RawSocketService = raw_socket_service<Protocol> >
class basic_raw_socket
: public basic_socket<Protocol, RawSocketService>
{
public:
/// The native representation of a socket.
typedef typename RawSocketService::native_handle_type native_handle_type;

/// The protocol type.
typedef Protocol protocol_type;

/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

/// Construct a basic_raw_socket without opening it.
/**
* This constructor creates a raw socket without opening it. The open()
* function must be called before data can be sent or received on the socket.
*
* @param io_context The io_context object that the raw socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*/
explicit basic_raw_socket(asio::io_context& io_context)
: basic_socket<Protocol, RawSocketService>(io_context)
{
}

/// Construct and open a basic_raw_socket.
/**
* This constructor creates and opens a raw socket.
*
* @param io_context The io_context object that the raw socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::system_error Thrown on failure.
*/
basic_raw_socket(asio::io_context& io_context,
const protocol_type& protocol)
: basic_socket<Protocol, RawSocketService>(io_context, protocol)
{
}

/// Construct a basic_raw_socket, opening it and binding it to the given
/// local endpoint.
/**
* This constructor creates a raw socket and automatically opens it bound
* to the specified endpoint on the local machine. The protocol used is the
* protocol associated with the given endpoint.
*
* @param io_context The io_context object that the raw socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param endpoint An endpoint on the local machine to which the raw
* socket will be bound.
*
* @throws asio::system_error Thrown on failure.
*/
basic_raw_socket(asio::io_context& io_context,
const endpoint_type& endpoint)
: basic_socket<Protocol, RawSocketService>(io_context, endpoint)
{
}

/// Construct a basic_raw_socket on an existing native socket.
/**
* This constructor creates a raw socket object to hold an existing
* native socket.
*
* @param io_context The io_context object that the raw socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::system_error Thrown on failure.
*/
basic_raw_socket(asio::io_context& io_context,
const protocol_type& protocol, const native_handle_type& native_socket)
: basic_socket<Protocol, RawSocketService>(
io_context, protocol, native_socket)
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_raw_socket from another.
/**
* This constructor moves a raw socket from one object to another.
*
* @param other The other basic_raw_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_raw_socket(io_context&) constructor.
*/
basic_raw_socket(basic_raw_socket&& other)
: basic_socket<Protocol, RawSocketService>(
ASIO_MOVE_CAST(basic_raw_socket)(other))
{
}

/// Move-assign a basic_raw_socket from another.
/**
* This assignment operator moves a raw socket from one object to another.
*
* @param other The other basic_raw_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_raw_socket(io_context&) constructor.
*/
basic_raw_socket& operator=(basic_raw_socket&& other)
{
basic_socket<Protocol, RawSocketService>::operator=(
ASIO_MOVE_CAST(basic_raw_socket)(other));
return *this;
}

/// Move-construct a basic_raw_socket from a socket of another protocol type.
/**
* This constructor moves a raw socket from one object to another.
*
* @param other The other basic_raw_socket object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_raw_socket(io_context&) constructor.
*/
template <typename Protocol1, typename RawSocketService1>
basic_raw_socket(basic_raw_socket<Protocol1, RawSocketService1>&& other,
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
: basic_socket<Protocol, RawSocketService>(
ASIO_MOVE_CAST2(basic_raw_socket<
Protocol1, RawSocketService1>)(other))
{
}

/// Move-assign a basic_raw_socket from a socket of another protocol type.
/**
* This assignment operator moves a raw socket from one object to another.
*
* @param other The other basic_raw_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_raw_socket(io_context&) constructor.
*/
template <typename Protocol1, typename RawSocketService1>
typename enable_if<is_convertible<Protocol1, Protocol>::value,
basic_raw_socket>::type& operator=(
basic_raw_socket<Protocol1, RawSocketService1>&& other)
{
basic_socket<Protocol, RawSocketService>::operator=(
ASIO_MOVE_CAST2(basic_raw_socket<
Protocol1, RawSocketService1>)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Send some data on a connected socket.
/**
* This function is used to send data on the raw socket. The function call
* will block until the data has been sent successfully or an error occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected raw socket.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code socket.send(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on a connected socket.
/**
* This function is used to send data on the raw socket. The function call
* will block until the data has been sent successfully or an error occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected raw socket.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on a connected socket.
/**
* This function is used to send data on the raw socket. The function call
* will block until the data has been sent successfully or an error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected raw socket.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().send(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous send on a connected socket.
/**
* This function is used to send data on the raw socket. The function call
* will block until the data has been sent successfully or an error occurs.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected raw
* socket.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Start an asynchronous send on a connected socket.
/**
* This function is used to send data on the raw socket. The function call
* will block until the data has been sent successfully or an error occurs.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected raw
* socket.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Send raw data to the specified endpoint.
/**
* This function is used to send raw data to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.send_to(asio::buffer(data, size), destination);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination)
{
asio::error_code ec;
std::size_t s = this->get_service().send_to(
this->get_implementation(), buffers, destination, 0, ec);
asio::detail::throw_error(ec, "send_to");
return s;
}

/// Send raw data to the specified endpoint.
/**
* This function is used to send raw data to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send_to(
this->get_implementation(), buffers, destination, flags, ec);
asio::detail::throw_error(ec, "send_to");
return s;
}

/// Send raw data to the specified endpoint.
/**
* This function is used to send raw data to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent.
*/
template <typename ConstBufferSequence>
std::size_t send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
asio::error_code& ec)
{
return this->get_service().send_to(this->get_implementation(),
buffers, destination, flags, ec);
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send raw data to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.async_send_to(
* asio::buffer(data, size), destination, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send_to(this->get_implementation(),
buffers, destination, 0, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send raw data to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send_to(const ConstBufferSequence& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send_to(
this->get_implementation(), buffers, destination, flags,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the raw socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected raw
* socket.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.receive(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the raw socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected raw
* socket.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the raw socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected raw
* socket.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the raw
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* raw socket.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the raw
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* raw socket.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Receive raw data with the endpoint of the sender.
/**
* This function is used to receive raw data. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the data.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* asio::ip::udp::endpoint sender_endpoint;
* socket.receive_from(
* asio::buffer(data, size), sender_endpoint);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint)
{
asio::error_code ec;
std::size_t s = this->get_service().receive_from(
this->get_implementation(), buffers, sender_endpoint, 0, ec);
asio::detail::throw_error(ec, "receive_from");
return s;
}
/// Receive raw data with the endpoint of the sender.
/**
* This function is used to receive raw data. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the data.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive_from(
this->get_implementation(), buffers, sender_endpoint, flags, ec);
asio::detail::throw_error(ec, "receive_from");
return s;
}
/// Receive raw data with the endpoint of the sender.
/**
* This function is used to receive raw data. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the data.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received.
*/
template <typename MutableBufferSequence>
std::size_t receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
asio::error_code& ec)
{
return this->get_service().receive_from(this->get_implementation(),
buffers, sender_endpoint, flags, ec);
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive raw data. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the data. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.async_receive_from(
* asio::buffer(data, size), 0, sender_endpoint, handler); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive_from(
this->get_implementation(), buffers, sender_endpoint, 0,
ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive raw data. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the data. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive_from(const MutableBufferSequence& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive_from(
this->get_implementation(), buffers, sender_endpoint, flags,
ASIO_MOVE_CAST(ReadHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_RAW_SOCKET_HPP

+ 561
- 0
source/modules/hylia/link/asio/basic_seq_packet_socket.hpp View File

@@ -0,0 +1,561 @@
//
// basic_seq_packet_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_SEQ_PACKET_SOCKET_HPP
#define ASIO_BASIC_SEQ_PACKET_SOCKET_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/basic_socket.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/seq_packet_socket_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides sequenced packet socket functionality.
/**
* The basic_seq_packet_socket class template provides asynchronous and blocking
* sequenced packet socket functionality.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename Protocol,
typename SeqPacketSocketService = seq_packet_socket_service<Protocol> >
class basic_seq_packet_socket
: public basic_socket<Protocol, SeqPacketSocketService>
{
public:
/// The native representation of a socket.
typedef typename SeqPacketSocketService::native_handle_type
native_handle_type;

/// The protocol type.
typedef Protocol protocol_type;

/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

/// Construct a basic_seq_packet_socket without opening it.
/**
* This constructor creates a sequenced packet socket without opening it. The
* socket needs to be opened and then connected or accepted before data can
* be sent or received on it.
*
* @param io_context The io_context object that the sequenced packet socket
* will use to dispatch handlers for any asynchronous operations performed on
* the socket.
*/
explicit basic_seq_packet_socket(asio::io_context& io_context)
: basic_socket<Protocol, SeqPacketSocketService>(io_context)
{
}

/// Construct and open a basic_seq_packet_socket.
/**
* This constructor creates and opens a sequenced_packet socket. The socket
* needs to be connected or accepted before data can be sent or received on
* it.
*
* @param io_context The io_context object that the sequenced packet socket
* will use to dispatch handlers for any asynchronous operations performed on
* the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::system_error Thrown on failure.
*/
basic_seq_packet_socket(asio::io_context& io_context,
const protocol_type& protocol)
: basic_socket<Protocol, SeqPacketSocketService>(io_context, protocol)
{
}

/// Construct a basic_seq_packet_socket, opening it and binding it to the
/// given local endpoint.
/**
* This constructor creates a sequenced packet socket and automatically opens
* it bound to the specified endpoint on the local machine. The protocol used
* is the protocol associated with the given endpoint.
*
* @param io_context The io_context object that the sequenced packet socket
* will use to dispatch handlers for any asynchronous operations performed on
* the socket.
*
* @param endpoint An endpoint on the local machine to which the sequenced
* packet socket will be bound.
*
* @throws asio::system_error Thrown on failure.
*/
basic_seq_packet_socket(asio::io_context& io_context,
const endpoint_type& endpoint)
: basic_socket<Protocol, SeqPacketSocketService>(io_context, endpoint)
{
}

/// Construct a basic_seq_packet_socket on an existing native socket.
/**
* This constructor creates a sequenced packet socket object to hold an
* existing native socket.
*
* @param io_context The io_context object that the sequenced packet socket
* will use to dispatch handlers for any asynchronous operations performed on
* the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::system_error Thrown on failure.
*/
basic_seq_packet_socket(asio::io_context& io_context,
const protocol_type& protocol, const native_handle_type& native_socket)
: basic_socket<Protocol, SeqPacketSocketService>(
io_context, protocol, native_socket)
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_seq_packet_socket from another.
/**
* This constructor moves a sequenced packet socket from one object to
* another.
*
* @param other The other basic_seq_packet_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
*/
basic_seq_packet_socket(basic_seq_packet_socket&& other)
: basic_socket<Protocol, SeqPacketSocketService>(
ASIO_MOVE_CAST(basic_seq_packet_socket)(other))
{
}

/// Move-assign a basic_seq_packet_socket from another.
/**
* This assignment operator moves a sequenced packet socket from one object to
* another.
*
* @param other The other basic_seq_packet_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
*/
basic_seq_packet_socket& operator=(basic_seq_packet_socket&& other)
{
basic_socket<Protocol, SeqPacketSocketService>::operator=(
ASIO_MOVE_CAST(basic_seq_packet_socket)(other));
return *this;
}

/// Move-construct a basic_seq_packet_socket from a socket of another protocol
/// type.
/**
* This constructor moves a sequenced packet socket from one object to
* another.
*
* @param other The other basic_seq_packet_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
*/
template <typename Protocol1, typename SeqPacketSocketService1>
basic_seq_packet_socket(
basic_seq_packet_socket<Protocol1, SeqPacketSocketService1>&& other,
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
: basic_socket<Protocol, SeqPacketSocketService>(
ASIO_MOVE_CAST2(basic_seq_packet_socket<
Protocol1, SeqPacketSocketService1>)(other))
{
}

/// Move-assign a basic_seq_packet_socket from a socket of another protocol
/// type.
/**
* This assignment operator moves a sequenced packet socket from one object to
* another.
*
* @param other The other basic_seq_packet_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
*/
template <typename Protocol1, typename SeqPacketSocketService1>
typename enable_if<is_convertible<Protocol1, Protocol>::value,
basic_seq_packet_socket>::type& operator=(
basic_seq_packet_socket<Protocol1, SeqPacketSocketService1>&& other)
{
basic_socket<Protocol, SeqPacketSocketService>::operator=(
ASIO_MOVE_CAST2(basic_seq_packet_socket<
Protocol1, SeqPacketSocketService1>)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Send some data on the socket.
/**
* This function is used to send data on the sequenced packet socket. The
* function call will block until the data has been sent successfully, or an
* until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.send(asio::buffer(data, size), 0);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on the socket.
/**
* This function is used to send data on the sequenced packet socket. The
* function call will block the data has been sent successfully, or an until
* error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent. Returns 0 if an error occurred.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().send(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on the sequenced packet
* socket. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), 0, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Receive some data on the socket.
/**
* This function is used to receive data on the sequenced packet socket. The
* function call will block until data has been received successfully, or
* until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param out_flags After the receive call completes, contains flags
* associated with the received data. For example, if the
* socket_base::message_end_of_record bit is set then the received data marks
* the end of a record.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size), out_flags);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags& out_flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, 0, out_flags, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on the socket.
/**
* This function is used to receive data on the sequenced packet socket. The
* function call will block until data has been received successfully, or
* until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param in_flags Flags specifying how the receive call is to be made.
*
* @param out_flags After the receive call completes, contains flags
* associated with the received data. For example, if the
* socket_base::message_end_of_record bit is set then the received data marks
* the end of a record.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size), 0, out_flags);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags in_flags,
socket_base::message_flags& out_flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, in_flags, out_flags, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the sequenced packet socket. The
* function call will block until data has been received successfully, or
* until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param in_flags Flags specifying how the receive call is to be made.
*
* @param out_flags After the receive call completes, contains flags
* associated with the received data. For example, if the
* socket_base::message_end_of_record bit is set then the received data marks
* the end of a record.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received. Returns 0 if an error occurred.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags in_flags,
socket_base::message_flags& out_flags, asio::error_code& ec)
{
return this->get_service().receive(this->get_implementation(),
buffers, in_flags, out_flags, ec);
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the sequenced
* packet socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param out_flags Once the asynchronous operation completes, contains flags
* associated with the received data. For example, if the
* socket_base::message_end_of_record bit is set then the received data marks
* the end of a record. The caller must guarantee that the referenced
* variable remains valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), out_flags, handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
socket_base::message_flags& out_flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(
this->get_implementation(), buffers, 0, out_flags,
ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the sequenced
* data socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param in_flags Flags specifying how the receive call is to be made.
*
* @param out_flags Once the asynchronous operation completes, contains flags
* associated with the received data. For example, if the
* socket_base::message_end_of_record bit is set then the received data marks
* the end of a record. The caller must guarantee that the referenced
* variable remains valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(
* asio::buffer(data, size),
* 0, out_flags, handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
socket_base::message_flags in_flags,
socket_base::message_flags& out_flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(
this->get_implementation(), buffers, in_flags, out_flags,
ASIO_MOVE_CAST(ReadHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_SEQ_PACKET_SOCKET_HPP

+ 679
- 0
source/modules/hylia/link/asio/basic_serial_port.hpp View File

@@ -0,0 +1,679 @@
//
// basic_serial_port.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.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)
//

#ifndef ASIO_BASIC_SERIAL_PORT_HPP
#define ASIO_BASIC_SERIAL_PORT_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if defined(ASIO_HAS_SERIAL_PORT) \
|| defined(GENERATING_DOCUMENTATION)

#include <string>
#include "asio/basic_io_object.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/serial_port_base.hpp"
#include "asio/serial_port_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides serial port functionality.
/**
* The basic_serial_port class template provides functionality that is common
* to all serial ports.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename SerialPortService = serial_port_service>
class basic_serial_port
: public basic_io_object<SerialPortService>,
public serial_port_base
{
public:
/// The native representation of a serial port.
typedef typename SerialPortService::native_handle_type native_handle_type;

/// A basic_serial_port is always the lowest layer.
typedef basic_serial_port<SerialPortService> lowest_layer_type;

/// Construct a basic_serial_port without opening it.
/**
* This constructor creates a serial port without opening it.
*
* @param io_context The io_context object that the serial port will use to
* dispatch handlers for any asynchronous operations performed on the port.
*/
explicit basic_serial_port(asio::io_context& io_context)
: basic_io_object<SerialPortService>(io_context)
{
}

/// Construct and open a basic_serial_port.
/**
* This constructor creates and opens a serial port for the specified device
* name.
*
* @param io_context The io_context object that the serial port will use to
* dispatch handlers for any asynchronous operations performed on the port.
*
* @param device The platform-specific device name for this serial
* port.
*/
explicit basic_serial_port(asio::io_context& io_context,
const char* device)
: basic_io_object<SerialPortService>(io_context)
{
asio::error_code ec;
this->get_service().open(this->get_implementation(), device, ec);
asio::detail::throw_error(ec, "open");
}

/// Construct and open a basic_serial_port.
/**
* This constructor creates and opens a serial port for the specified device
* name.
*
* @param io_context The io_context object that the serial port will use to
* dispatch handlers for any asynchronous operations performed on the port.
*
* @param device The platform-specific device name for this serial
* port.
*/
explicit basic_serial_port(asio::io_context& io_context,
const std::string& device)
: basic_io_object<SerialPortService>(io_context)
{
asio::error_code ec;
this->get_service().open(this->get_implementation(), device, ec);
asio::detail::throw_error(ec, "open");
}

/// Construct a basic_serial_port on an existing native serial port.
/**
* This constructor creates a serial port object to hold an existing native
* serial port.
*
* @param io_context The io_context object that the serial port will use to
* dispatch handlers for any asynchronous operations performed on the port.
*
* @param native_serial_port A native serial port.
*
* @throws asio::system_error Thrown on failure.
*/
basic_serial_port(asio::io_context& io_context,
const native_handle_type& native_serial_port)
: basic_io_object<SerialPortService>(io_context)
{
asio::error_code ec;
this->get_service().assign(this->get_implementation(),
native_serial_port, ec);
asio::detail::throw_error(ec, "assign");
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_serial_port from another.
/**
* This constructor moves a serial port from one object to another.
*
* @param other The other basic_serial_port object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_serial_port(io_context&) constructor.
*/
basic_serial_port(basic_serial_port&& other)
: basic_io_object<SerialPortService>(
ASIO_MOVE_CAST(basic_serial_port)(other))
{
}

/// Move-assign a basic_serial_port from another.
/**
* This assignment operator moves a serial port from one object to another.
*
* @param other The other basic_serial_port object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_serial_port(io_context&) constructor.
*/
basic_serial_port& operator=(basic_serial_port&& other)
{
basic_io_object<SerialPortService>::operator=(
ASIO_MOVE_CAST(basic_serial_port)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Get a reference to the lowest layer.
/**
* This function returns a reference to the lowest layer in a stack of
* layers. Since a basic_serial_port cannot contain any further layers, it
* simply returns a reference to itself.
*
* @return A reference to the lowest layer in the stack of layers. Ownership
* is not transferred to the caller.
*/
lowest_layer_type& lowest_layer()
{
return *this;
}

/// Get a const reference to the lowest layer.
/**
* This function returns a const reference to the lowest layer in a stack of
* layers. Since a basic_serial_port cannot contain any further layers, it
* simply returns a reference to itself.
*
* @return A const reference to the lowest layer in the stack of layers.
* Ownership is not transferred to the caller.
*/
const lowest_layer_type& lowest_layer() const
{
return *this;
}

/// Open the serial port using the specified device name.
/**
* This function opens the serial port for the specified device name.
*
* @param device The platform-specific device name.
*
* @throws asio::system_error Thrown on failure.
*/
void open(const std::string& device)
{
asio::error_code ec;
this->get_service().open(this->get_implementation(), device, ec);
asio::detail::throw_error(ec, "open");
}

/// Open the serial port using the specified device name.
/**
* This function opens the serial port using the given platform-specific
* device name.
*
* @param device The platform-specific device name.
*
* @param ec Set the indicate what error occurred, if any.
*/
asio::error_code open(const std::string& device,
asio::error_code& ec)
{
return this->get_service().open(this->get_implementation(), device, ec);
}

/// Assign an existing native serial port to the serial port.
/*
* This function opens the serial port to hold an existing native serial port.
*
* @param native_serial_port A native serial port.
*
* @throws asio::system_error Thrown on failure.
*/
void assign(const native_handle_type& native_serial_port)
{
asio::error_code ec;
this->get_service().assign(this->get_implementation(),
native_serial_port, ec);
asio::detail::throw_error(ec, "assign");
}

/// Assign an existing native serial port to the serial port.
/*
* This function opens the serial port to hold an existing native serial port.
*
* @param native_serial_port A native serial port.
*
* @param ec Set to indicate what error occurred, if any.
*/
asio::error_code assign(const native_handle_type& native_serial_port,
asio::error_code& ec)
{
return this->get_service().assign(this->get_implementation(),
native_serial_port, ec);
}

/// Determine whether the serial port is open.
bool is_open() const
{
return this->get_service().is_open(this->get_implementation());
}

/// Close the serial port.
/**
* This function is used to close the serial port. Any asynchronous read or
* write operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure.
*/
void close()
{
asio::error_code ec;
this->get_service().close(this->get_implementation(), ec);
asio::detail::throw_error(ec, "close");
}

/// Close the serial port.
/**
* This function is used to close the serial port. Any asynchronous read or
* write operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any.
*/
asio::error_code close(asio::error_code& ec)
{
return this->get_service().close(this->get_implementation(), ec);
}

/// Get the native serial port representation.
/**
* This function may be used to obtain the underlying representation of the
* serial port. This is intended to allow access to native serial port
* functionality that is not otherwise provided.
*/
native_handle_type native_handle()
{
return this->get_service().native_handle(this->get_implementation());
}

/// Cancel all asynchronous operations associated with the serial port.
/**
* This function causes all outstanding asynchronous read or write operations
* to finish immediately, and the handlers for cancelled operations will be
* passed the asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure.
*/
void cancel()
{
asio::error_code ec;
this->get_service().cancel(this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
}

/// Cancel all asynchronous operations associated with the serial port.
/**
* This function causes all outstanding asynchronous read or write operations
* to finish immediately, and the handlers for cancelled operations will be
* passed the asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any.
*/
asio::error_code cancel(asio::error_code& ec)
{
return this->get_service().cancel(this->get_implementation(), ec);
}

/// Send a break sequence to the serial port.
/**
* This function causes a break sequence of platform-specific duration to be
* sent out the serial port.
*
* @throws asio::system_error Thrown on failure.
*/
void send_break()
{
asio::error_code ec;
this->get_service().send_break(this->get_implementation(), ec);
asio::detail::throw_error(ec, "send_break");
}

/// Send a break sequence to the serial port.
/**
* This function causes a break sequence of platform-specific duration to be
* sent out the serial port.
*
* @param ec Set to indicate what error occurred, if any.
*/
asio::error_code send_break(asio::error_code& ec)
{
return this->get_service().send_break(this->get_implementation(), ec);
}

/// Set an option on the serial port.
/**
* This function is used to set an option on the serial port.
*
* @param option The option value to be set on the serial port.
*
* @throws asio::system_error Thrown on failure.
*
* @sa SettableSerialPortOption @n
* asio::serial_port_base::baud_rate @n
* asio::serial_port_base::flow_control @n
* asio::serial_port_base::parity @n
* asio::serial_port_base::stop_bits @n
* asio::serial_port_base::character_size
*/
template <typename SettableSerialPortOption>
void set_option(const SettableSerialPortOption& option)
{
asio::error_code ec;
this->get_service().set_option(this->get_implementation(), option, ec);
asio::detail::throw_error(ec, "set_option");
}

/// Set an option on the serial port.
/**
* This function is used to set an option on the serial port.
*
* @param option The option value to be set on the serial port.
*
* @param ec Set to indicate what error occurred, if any.
*
* @sa SettableSerialPortOption @n
* asio::serial_port_base::baud_rate @n
* asio::serial_port_base::flow_control @n
* asio::serial_port_base::parity @n
* asio::serial_port_base::stop_bits @n
* asio::serial_port_base::character_size
*/
template <typename SettableSerialPortOption>
asio::error_code set_option(const SettableSerialPortOption& option,
asio::error_code& ec)
{
return this->get_service().set_option(
this->get_implementation(), option, ec);
}

/// Get an option from the serial port.
/**
* This function is used to get the current value of an option on the serial
* port.
*
* @param option The option value to be obtained from the serial port.
*
* @throws asio::system_error Thrown on failure.
*
* @sa GettableSerialPortOption @n
* asio::serial_port_base::baud_rate @n
* asio::serial_port_base::flow_control @n
* asio::serial_port_base::parity @n
* asio::serial_port_base::stop_bits @n
* asio::serial_port_base::character_size
*/
template <typename GettableSerialPortOption>
void get_option(GettableSerialPortOption& option)
{
asio::error_code ec;
this->get_service().get_option(this->get_implementation(), option, ec);
asio::detail::throw_error(ec, "get_option");
}

/// Get an option from the serial port.
/**
* This function is used to get the current value of an option on the serial
* port.
*
* @param option The option value to be obtained from the serial port.
*
* @param ec Set to indicate what error occurred, if any.
*
* @sa GettableSerialPortOption @n
* asio::serial_port_base::baud_rate @n
* asio::serial_port_base::flow_control @n
* asio::serial_port_base::parity @n
* asio::serial_port_base::stop_bits @n
* asio::serial_port_base::character_size
*/
template <typename GettableSerialPortOption>
asio::error_code get_option(GettableSerialPortOption& option,
asio::error_code& ec)
{
return this->get_service().get_option(
this->get_implementation(), option, ec);
}

/// Write some data to the serial port.
/**
* This function is used to write data to the serial port. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the serial port.
*
* @returns The number of bytes written.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* serial_port.write_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().write_some(
this->get_implementation(), buffers, ec);
asio::detail::throw_error(ec, "write_some");
return s;
}

/// Write some data to the serial port.
/**
* This function is used to write data to the serial port. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the serial port.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes written. Returns 0 if an error occurred.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
asio::error_code& ec)
{
return this->get_service().write_some(
this->get_implementation(), buffers, ec);
}

/// Start an asynchronous write.
/**
* This function is used to asynchronously write data to the serial port.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be written to the serial port.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the write operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The write operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* serial_port.async_write_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_write_some(this->get_implementation(),
buffers, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Read some data from the serial port.
/**
* This function is used to read data from the serial port. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* serial_port.read_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().read_some(
this->get_implementation(), buffers, ec);
asio::detail::throw_error(ec, "read_some");
return s;
}

/// Read some data from the serial port.
/**
* This function is used to read data from the serial port. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes read. Returns 0 if an error occurred.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return this->get_service().read_some(
this->get_implementation(), buffers, ec);
}

/// Start an asynchronous read.
/**
* This function is used to asynchronously read data from the serial port.
* The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be read.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the read operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The read operation may not read all of the requested number of bytes.
* Consider using the @ref async_read function if you need to ensure that the
* requested amount of data is read before the asynchronous operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* serial_port.async_read_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_read_some(this->get_implementation(),
buffers, ASIO_MOVE_CAST(ReadHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // defined(ASIO_HAS_SERIAL_PORT)
// || defined(GENERATING_DOCUMENTATION)

#endif // ASIO_BASIC_SERIAL_PORT_HPP

+ 386
- 0
source/modules/hylia/link/asio/basic_signal_set.hpp View File

@@ -0,0 +1,386 @@
//
// basic_signal_set.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_SIGNAL_SET_HPP
#define ASIO_BASIC_SIGNAL_SET_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#include "asio/basic_io_object.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/signal_set_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides signal functionality.
/**
* The basic_signal_set class template provides the ability to perform an
* asynchronous wait for one or more signals to occur.
*
* Most applications will use the asio::signal_set typedef.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Example
* Performing an asynchronous wait:
* @code
* void handler(
* const asio::error_code& error,
* int signal_number)
* {
* if (!error)
* {
* // A signal occurred.
* }
* }
*
* ...
*
* // Construct a signal set registered for process termination.
* asio::signal_set signals(io_context, SIGINT, SIGTERM);
*
* // Start an asynchronous wait for one of the signals to occur.
* signals.async_wait(handler);
* @endcode
*
* @par Queueing of signal notifications
*
* If a signal is registered with a signal_set, and the signal occurs when
* there are no waiting handlers, then the signal notification is queued. The
* next async_wait operation on that signal_set will dequeue the notification.
* If multiple notifications are queued, subsequent async_wait operations
* dequeue them one at a time. Signal notifications are dequeued in order of
* ascending signal number.
*
* If a signal number is removed from a signal_set (using the @c remove or @c
* erase member functions) then any queued notifications for that signal are
* discarded.
*
* @par Multiple registration of signals
*
* The same signal number may be registered with different signal_set objects.
* When the signal occurs, one handler is called for each signal_set object.
*
* Note that multiple registration only works for signals that are registered
* using Asio. The application must not also register a signal handler using
* functions such as @c signal() or @c sigaction().
*
* @par Signal masking on POSIX platforms
*
* POSIX allows signals to be blocked using functions such as @c sigprocmask()
* and @c pthread_sigmask(). For signals to be delivered, programs must ensure
* that any signals registered using signal_set objects are unblocked in at
* least one thread.
*/
template <typename SignalSetService = signal_set_service>
class basic_signal_set
: public basic_io_object<SignalSetService>
{
public:
/// Construct a signal set without adding any signals.
/**
* This constructor creates a signal set without registering for any signals.
*
* @param io_context The io_context object that the signal set will use to
* dispatch handlers for any asynchronous operations performed on the set.
*/
explicit basic_signal_set(asio::io_context& io_context)
: basic_io_object<SignalSetService>(io_context)
{
}

/// Construct a signal set and add one signal.
/**
* This constructor creates a signal set and registers for one signal.
*
* @param io_context The io_context object that the signal set will use to
* dispatch handlers for any asynchronous operations performed on the set.
*
* @param signal_number_1 The signal number to be added.
*
* @note This constructor is equivalent to performing:
* @code asio::signal_set signals(io_context);
* signals.add(signal_number_1); @endcode
*/
basic_signal_set(asio::io_context& io_context, int signal_number_1)
: basic_io_object<SignalSetService>(io_context)
{
asio::error_code ec;
this->get_service().add(this->get_implementation(), signal_number_1, ec);
asio::detail::throw_error(ec, "add");
}

/// Construct a signal set and add two signals.
/**
* This constructor creates a signal set and registers for two signals.
*
* @param io_context The io_context object that the signal set will use to
* dispatch handlers for any asynchronous operations performed on the set.
*
* @param signal_number_1 The first signal number to be added.
*
* @param signal_number_2 The second signal number to be added.
*
* @note This constructor is equivalent to performing:
* @code asio::signal_set signals(io_context);
* signals.add(signal_number_1);
* signals.add(signal_number_2); @endcode
*/
basic_signal_set(asio::io_context& io_context, int signal_number_1,
int signal_number_2)
: basic_io_object<SignalSetService>(io_context)
{
asio::error_code ec;
this->get_service().add(this->get_implementation(), signal_number_1, ec);
asio::detail::throw_error(ec, "add");
this->get_service().add(this->get_implementation(), signal_number_2, ec);
asio::detail::throw_error(ec, "add");
}

/// Construct a signal set and add three signals.
/**
* This constructor creates a signal set and registers for three signals.
*
* @param io_context The io_context object that the signal set will use to
* dispatch handlers for any asynchronous operations performed on the set.
*
* @param signal_number_1 The first signal number to be added.
*
* @param signal_number_2 The second signal number to be added.
*
* @param signal_number_3 The third signal number to be added.
*
* @note This constructor is equivalent to performing:
* @code asio::signal_set signals(io_context);
* signals.add(signal_number_1);
* signals.add(signal_number_2);
* signals.add(signal_number_3); @endcode
*/
basic_signal_set(asio::io_context& io_context, int signal_number_1,
int signal_number_2, int signal_number_3)
: basic_io_object<SignalSetService>(io_context)
{
asio::error_code ec;
this->get_service().add(this->get_implementation(), signal_number_1, ec);
asio::detail::throw_error(ec, "add");
this->get_service().add(this->get_implementation(), signal_number_2, ec);
asio::detail::throw_error(ec, "add");
this->get_service().add(this->get_implementation(), signal_number_3, ec);
asio::detail::throw_error(ec, "add");
}

/// Add a signal to a signal_set.
/**
* This function adds the specified signal to the set. It has no effect if the
* signal is already in the set.
*
* @param signal_number The signal to be added to the set.
*
* @throws asio::system_error Thrown on failure.
*/
void add(int signal_number)
{
asio::error_code ec;
this->get_service().add(this->get_implementation(), signal_number, ec);
asio::detail::throw_error(ec, "add");
}

/// Add a signal to a signal_set.
/**
* This function adds the specified signal to the set. It has no effect if the
* signal is already in the set.
*
* @param signal_number The signal to be added to the set.
*
* @param ec Set to indicate what error occurred, if any.
*/
asio::error_code add(int signal_number,
asio::error_code& ec)
{
return this->get_service().add(
this->get_implementation(), signal_number, ec);
}

/// Remove a signal from a signal_set.
/**
* This function removes the specified signal from the set. It has no effect
* if the signal is not in the set.
*
* @param signal_number The signal to be removed from the set.
*
* @throws asio::system_error Thrown on failure.
*
* @note Removes any notifications that have been queued for the specified
* signal number.
*/
void remove(int signal_number)
{
asio::error_code ec;
this->get_service().remove(this->get_implementation(), signal_number, ec);
asio::detail::throw_error(ec, "remove");
}

/// Remove a signal from a signal_set.
/**
* This function removes the specified signal from the set. It has no effect
* if the signal is not in the set.
*
* @param signal_number The signal to be removed from the set.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Removes any notifications that have been queued for the specified
* signal number.
*/
asio::error_code remove(int signal_number,
asio::error_code& ec)
{
return this->get_service().remove(
this->get_implementation(), signal_number, ec);
}

/// Remove all signals from a signal_set.
/**
* This function removes all signals from the set. It has no effect if the set
* is already empty.
*
* @throws asio::system_error Thrown on failure.
*
* @note Removes all queued notifications.
*/
void clear()
{
asio::error_code ec;
this->get_service().clear(this->get_implementation(), ec);
asio::detail::throw_error(ec, "clear");
}

/// Remove all signals from a signal_set.
/**
* This function removes all signals from the set. It has no effect if the set
* is already empty.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Removes all queued notifications.
*/
asio::error_code clear(asio::error_code& ec)
{
return this->get_service().clear(this->get_implementation(), ec);
}

/// Cancel all operations associated with the signal set.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the signal set. The handler for each cancelled
* operation will be invoked with the asio::error::operation_aborted
* error code.
*
* Cancellation does not alter the set of registered signals.
*
* @throws asio::system_error Thrown on failure.
*
* @note If a registered signal occurred before cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
void cancel()
{
asio::error_code ec;
this->get_service().cancel(this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
}

/// Cancel all operations associated with the signal set.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the signal set. The handler for each cancelled
* operation will be invoked with the asio::error::operation_aborted
* error code.
*
* Cancellation does not alter the set of registered signals.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note If a registered signal occurred before cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
asio::error_code cancel(asio::error_code& ec)
{
return this->get_service().cancel(this->get_implementation(), ec);
}

/// Start an asynchronous operation to wait for a signal to be delivered.
/**
* This function may be used to initiate an asynchronous wait against the
* signal set. It always returns immediately.
*
* For each call to async_wait(), the supplied handler will be called exactly
* once. The handler will be called when:
*
* @li One of the registered signals in the signal set occurs; or
*
* @li The signal set was cancelled, in which case the handler is passed the
* error code asio::error::operation_aborted.
*
* @param handler The handler to be called when the signal occurs. Copies
* will be made of the handler as required. The function signature of the
* handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* int signal_number // Indicates which signal occurred.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename SignalHandler>
ASIO_INITFN_RESULT_TYPE(SignalHandler,
void (asio::error_code, int))
async_wait(ASIO_MOVE_ARG(SignalHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a SignalHandler.
ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check;

return this->get_service().async_wait(this->get_implementation(),
ASIO_MOVE_CAST(SignalHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_SIGNAL_SET_HPP

+ 1599
- 0
source/modules/hylia/link/asio/basic_socket.hpp
File diff suppressed because it is too large
View File


+ 1734
- 0
source/modules/hylia/link/asio/basic_socket_acceptor.hpp
File diff suppressed because it is too large
View File


+ 325
- 0
source/modules/hylia/link/asio/basic_socket_iostream.hpp View File

@@ -0,0 +1,325 @@
//
// basic_socket_iostream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP
#define ASIO_BASIC_SOCKET_IOSTREAM_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if !defined(ASIO_NO_IOSTREAM)

#include <istream>
#include <ostream>
#include "asio/basic_socket_streambuf.hpp"
#include "asio/stream_socket_service.hpp"

#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)

# include "asio/detail/variadic_templates.hpp"

// A macro that should expand to:
// template <typename T1, ..., typename Tn>
// explicit basic_socket_iostream(T1 x1, ..., Tn xn)
// : std::basic_iostream<char>(
// &this->detail::socket_iostream_base<
// Protocol, StreamSocketService, Time,
// TimeTraits, TimerService>::streambuf_)
// {
// if (rdbuf()->connect(x1, ..., xn) == 0)
// this->setstate(std::ios_base::failbit);
// }
// This macro should only persist within this file.

# define ASIO_PRIVATE_CTR_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
: std::basic_iostream<char>( \
&this->detail::socket_iostream_base< \
Protocol, StreamSocketService, Time, \
TimeTraits, TimerService>::streambuf_) \
{ \
this->setf(std::ios_base::unitbuf); \
if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \
this->setstate(std::ios_base::failbit); \
} \
/**/

// A macro that should expand to:
// template <typename T1, ..., typename Tn>
// void connect(T1 x1, ..., Tn xn)
// {
// if (rdbuf()->connect(x1, ..., xn) == 0)
// this->setstate(std::ios_base::failbit);
// }
// This macro should only persist within this file.

# define ASIO_PRIVATE_CONNECT_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
void connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
{ \
if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \
this->setstate(std::ios_base::failbit); \
} \
/**/

#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)

#include "asio/detail/push_options.hpp"

namespace asio {
namespace detail {

// A separate base class is used to ensure that the streambuf is initialised
// prior to the basic_socket_iostream's basic_iostream base class.
template <typename Protocol, typename StreamSocketService,
typename Time, typename TimeTraits, typename TimerService>
class socket_iostream_base
{
protected:
basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService> streambuf_;
};

}

/// Iostream interface for a socket.
template <typename Protocol,
typename StreamSocketService = stream_socket_service<Protocol>,
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|| defined(GENERATING_DOCUMENTATION)
typename Time = boost::posix_time::ptime,
typename TimeTraits = asio::time_traits<Time>,
typename TimerService = deadline_timer_service<Time, TimeTraits> >
#else
typename Time = steady_timer::clock_type,
typename TimeTraits = steady_timer::traits_type,
typename TimerService = steady_timer::service_type>
#endif
class basic_socket_iostream
: private detail::socket_iostream_base<Protocol,
StreamSocketService, Time, TimeTraits, TimerService>,
public std::basic_iostream<char>
{
private:
// These typedefs are intended keep this class's implementation independent
// of whether it's using Boost.DateTime, Boost.Chrono or std::chrono.
#if defined(ASIO_HAS_BOOST_DATE_TIME)
typedef TimeTraits traits_helper;
#else
typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper;
#endif

public:
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

#if defined(GENERATING_DOCUMENTATION)
/// (Deprecated: Use time_point.) The time type.
typedef typename TimeTraits::time_type time_type;

/// The time type.
typedef typename TimeTraits::time_point time_point;

/// (Deprecated: Use duration.) The duration type.
typedef typename TimeTraits::duration_type duration_type;

/// The duration type.
typedef typename TimeTraits::duration duration;
#else
# if !defined(ASIO_NO_DEPRECATED)
typedef typename traits_helper::time_type time_type;
typedef typename traits_helper::duration_type duration_type;
# endif // !defined(ASIO_NO_DEPRECATED)
typedef typename traits_helper::time_type time_point;
typedef typename traits_helper::duration_type duration;
#endif

/// Construct a basic_socket_iostream without establishing a connection.
basic_socket_iostream()
: std::basic_iostream<char>(
&this->detail::socket_iostream_base<
Protocol, StreamSocketService, Time,
TimeTraits, TimerService>::streambuf_)
{
this->setf(std::ios_base::unitbuf);
}

#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This constructor automatically establishes a connection based on the
* supplied resolver query parameters. The arguments are used to construct
* a resolver query object.
*/
template <typename T1, ..., typename TN>
explicit basic_socket_iostream(T1 t1, ..., TN tn);
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
template <typename... T>
explicit basic_socket_iostream(T... x)
: std::basic_iostream<char>(
&this->detail::socket_iostream_base<
Protocol, StreamSocketService, Time,
TimeTraits, TimerService>::streambuf_)
{
this->setf(std::ios_base::unitbuf);
if (rdbuf()->connect(x...) == 0)
this->setstate(std::ios_base::failbit);
}
#else
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CTR_DEF)
#endif

#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This function automatically establishes a connection based on the supplied
* resolver query parameters. The arguments are used to construct a resolver
* query object.
*/
template <typename T1, ..., typename TN>
void connect(T1 t1, ..., TN tn);
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
template <typename... T>
void connect(T... x)
{
if (rdbuf()->connect(x...) == 0)
this->setstate(std::ios_base::failbit);
}
#else
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
#endif

/// Close the connection.
void close()
{
if (rdbuf()->close() == 0)
this->setstate(std::ios_base::failbit);
}

/// Return a pointer to the underlying streambuf.
basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService>* rdbuf() const
{
return const_cast<basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService>*>(
&this->detail::socket_iostream_base<
Protocol, StreamSocketService, Time,
TimeTraits, TimerService>::streambuf_);
}

/// Get the last error associated with the stream.
/**
* @return An \c error_code corresponding to the last error from the stream.
*
* @par Example
* To print the error associated with a failure to establish a connection:
* @code tcp::iostream s("www.boost.org", "http");
* if (!s)
* {
* std::cout << "Error: " << s.error().message() << std::endl;
* } @endcode
*/
const asio::error_code& error() const
{
return rdbuf()->puberror();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the stream's expiry time as an absolute
/// time.
/**
* @return An absolute time value representing the stream's expiry time.
*/
time_point expires_at() const
{
return rdbuf()->expires_at();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Get the stream's expiry time as an absolute time.
/**
* @return An absolute time value representing the stream's expiry time.
*/
time_point expiry() const
{
return rdbuf()->expiry();
}

/// Set the stream's expiry time as an absolute time.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the stream.
*/
void expires_at(const time_point& expiry_time)
{
rdbuf()->expires_at(expiry_time);
}

/// Set the stream's expiry time relative to now.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the timer.
*/
void expires_after(const duration& expiry_time)
{
rdbuf()->expires_after(expiry_time);
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the stream's expiry time relative to now.
/**
* @return A relative time value representing the stream's expiry time.
*/
duration expires_from_now() const
{
return rdbuf()->expires_from_now();
}

/// (Deprecated: Use expires_after().) Set the stream's expiry time relative
/// to now.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the timer.
*/
void expires_from_now(const duration& expiry_time)
{
rdbuf()->expires_from_now(expiry_time);
}
#endif // !defined(ASIO_NO_DEPRECATED)
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
# undef ASIO_PRIVATE_CTR_DEF
# undef ASIO_PRIVATE_CONNECT_DEF
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)

#endif // !defined(ASIO_NO_IOSTREAM)

#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP

+ 646
- 0
source/modules/hylia/link/asio/basic_socket_streambuf.hpp View File

@@ -0,0 +1,646 @@
//
// basic_socket_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
#define ASIO_BASIC_SOCKET_STREAMBUF_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if !defined(ASIO_NO_IOSTREAM)

#include <streambuf>
#include "asio/basic_socket.hpp"
#include "asio/deadline_timer_service.hpp"
#include "asio/detail/array.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/io_context.hpp"
#include "asio/stream_socket_service.hpp"

#if defined(ASIO_HAS_BOOST_DATE_TIME)
# include "asio/deadline_timer.hpp"
#else
# include "asio/steady_timer.hpp"
#endif

#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)

# include "asio/detail/variadic_templates.hpp"

// A macro that should expand to:
// template <typename T1, ..., typename Tn>
// basic_socket_streambuf<Protocol, StreamSocketService,
// Time, TimeTraits, TimerService>* connect(
// T1 x1, ..., Tn xn)
// {
// init_buffers();
// this->basic_socket<Protocol, StreamSocketService>::close(ec_);
// typedef typename Protocol::resolver resolver_type;
// resolver_type resolver(detail::socket_streambuf_base::io_context_);
// connect_to_endpoints(
// resolver.resolve(x1, ..., xn, ec_));
// return !ec_ ? this : 0;
// }
// This macro should only persist within this file.

# define ASIO_PRIVATE_CONNECT_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
basic_socket_streambuf<Protocol, StreamSocketService, \
Time, TimeTraits, TimerService>* connect( \
ASIO_VARIADIC_BYVAL_PARAMS(n)) \
{ \
init_buffers(); \
this->basic_socket<Protocol, StreamSocketService>::close(ec_); \
typedef typename Protocol::resolver resolver_type; \
resolver_type resolver(detail::socket_streambuf_base::io_context_); \
connect_to_endpoints( \
resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \
return !ec_ ? this : 0; \
} \
/**/

#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)

#include "asio/detail/push_options.hpp"

namespace asio {
namespace detail {

// A separate base class is used to ensure that the io_context is initialised
// prior to the basic_socket_streambuf's basic_socket base class.
class socket_streambuf_base
{
protected:
io_context io_context_;
};

} // namespace detail

/// Iostream streambuf for a socket.
template <typename Protocol,
typename StreamSocketService = stream_socket_service<Protocol>,
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|| defined(GENERATING_DOCUMENTATION)
typename Time = boost::posix_time::ptime,
typename TimeTraits = asio::time_traits<Time>,
typename TimerService = deadline_timer_service<Time, TimeTraits> >
#else
typename Time = steady_timer::clock_type,
typename TimeTraits = steady_timer::traits_type,
typename TimerService = steady_timer::service_type>
#endif
class basic_socket_streambuf
: public std::streambuf,
private detail::socket_streambuf_base,
public basic_socket<Protocol, StreamSocketService>
{
private:
// These typedefs are intended keep this class's implementation independent
// of whether it's using Boost.DateTime, Boost.Chrono or std::chrono.
#if defined(ASIO_HAS_BOOST_DATE_TIME)
typedef TimeTraits traits_helper;
#else
typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper;
#endif

public:
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

#if defined(GENERATING_DOCUMENTATION)
/// (Deprecated: Use time_point.) The time type.
typedef typename TimeTraits::time_type time_type;

/// The time type.
typedef typename TimeTraits::time_point time_point;

/// (Deprecated: Use duration.) The duration type.
typedef typename TimeTraits::duration_type duration_type;

/// The duration type.
typedef typename TimeTraits::duration duration;
#else
# if !defined(ASIO_NO_DEPRECATED)
typedef typename traits_helper::time_type time_type;
typedef typename traits_helper::duration_type duration_type;
# endif // !defined(ASIO_NO_DEPRECATED)
typedef typename traits_helper::time_type time_point;
typedef typename traits_helper::duration_type duration;
#endif

/// Construct a basic_socket_streambuf without establishing a connection.
basic_socket_streambuf()
: basic_socket<Protocol, StreamSocketService>(
this->detail::socket_streambuf_base::io_context_),
unbuffered_(false),
timer_service_(0),
timer_state_(no_timer)
{
init_buffers();
}

/// Destructor flushes buffered data.
virtual ~basic_socket_streambuf()
{
if (pptr() != pbase())
overflow(traits_type::eof());

destroy_timer();
}

/// Establish a connection.
/**
* This function establishes a connection to the specified endpoint.
*
* @return \c this if a connection was successfully established, a null
* pointer otherwise.
*/
basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService>* connect(
const endpoint_type& endpoint)
{
init_buffers();

this->basic_socket<Protocol, StreamSocketService>::close(ec_);

if (timer_state_ == timer_has_expired)
{
ec_ = asio::error::operation_aborted;
return 0;
}

io_handler handler = { this };
this->basic_socket<Protocol, StreamSocketService>::async_connect(
endpoint, handler);

ec_ = asio::error::would_block;
this->get_service().get_io_context().restart();
do this->get_service().get_io_context().run_one();
while (ec_ == asio::error::would_block);

return !ec_ ? this : 0;
}

#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection.
/**
* This function automatically establishes a connection based on the supplied
* resolver query parameters. The arguments are used to construct a resolver
* query object.
*
* @return \c this if a connection was successfully established, a null
* pointer otherwise.
*/
template <typename T1, ..., typename TN>
basic_socket_streambuf<Protocol, StreamSocketService>* connect(
T1 t1, ..., TN tn);
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
template <typename... T>
basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService>* connect(T... x)
{
init_buffers();
this->basic_socket<Protocol, StreamSocketService>::close(ec_);
typedef typename Protocol::resolver resolver_type;
resolver_type resolver(detail::socket_streambuf_base::io_context_);
connect_to_endpoints(resolver.resolve(x..., ec_));
return !ec_ ? this : 0;
}
#else
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
#endif

/// Close the connection.
/**
* @return \c this if a connection was successfully established, a null
* pointer otherwise.
*/
basic_socket_streambuf<Protocol, StreamSocketService,
Time, TimeTraits, TimerService>* close()
{
sync();
this->basic_socket<Protocol, StreamSocketService>::close(ec_);
if (!ec_)
init_buffers();
return !ec_ ? this : 0;
}

/// Get the last error associated with the stream buffer.
/**
* @return An \c error_code corresponding to the last error from the stream
* buffer.
*/
const asio::error_code& puberror() const
{
return error();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an
/// absolute time.
/**
* @return An absolute time value representing the stream buffer's expiry
* time.
*/
time_point expires_at() const
{
return timer_service_
? timer_service_->expires_at(timer_implementation_)
: time_point();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Get the stream buffer's expiry time as an absolute time.
/**
* @return An absolute time value representing the stream buffer's expiry
* time.
*/
time_point expiry() const
{
return timer_service_
#if defined(ASIO_HAS_BOOST_DATE_TIME)
? timer_service_->expires_at(timer_implementation_)
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
? timer_service_->expiry(timer_implementation_)
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
: time_point();
}

/// Set the stream buffer's expiry time as an absolute time.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the stream.
*/
void expires_at(const time_point& expiry_time)
{
construct_timer();

asio::error_code ec;
timer_service_->expires_at(timer_implementation_, expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");

start_timer();
}

/// Set the stream buffer's expiry time relative to now.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the timer.
*/
void expires_at(const duration& expiry_time)
{
construct_timer();

asio::error_code ec;
timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");

start_timer();
}

/// Set the stream buffer's expiry time relative to now.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the timer.
*/
void expires_after(const duration& expiry_time)
{
construct_timer();

asio::error_code ec;
#if defined(ASIO_HAS_BOOST_DATE_TIME)
timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
timer_service_->expires_after(timer_implementation_, expiry_time, ec);
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
asio::detail::throw_error(ec, "after");

start_timer();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative
/// to now.
/**
* @return A relative time value representing the stream buffer's expiry time.
*/
duration expires_from_now() const
{
return traits_helper::subtract(expires_at(), traits_helper::now());
}

/// (Deprecated: Use expires_after().) Set the stream buffer's expiry time
/// relative to now.
/**
* This function sets the expiry time associated with the stream. Stream
* operations performed after this time (where the operations cannot be
* completed using the internal buffers) will fail with the error
* asio::error::operation_aborted.
*
* @param expiry_time The expiry time to be used for the timer.
*/
void expires_from_now(const duration& expiry_time)
{
construct_timer();

asio::error_code ec;
timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");

start_timer();
}
#endif // !defined(ASIO_NO_DEPRECATED)

protected:
int_type underflow()
{
if (gptr() == egptr())
{
if (timer_state_ == timer_has_expired)
{
ec_ = asio::error::operation_aborted;
return traits_type::eof();
}

io_handler handler = { this };
this->get_service().async_receive(this->get_implementation(),
asio::buffer(asio::buffer(get_buffer_) + putback_max),
0, handler);

ec_ = asio::error::would_block;
this->get_service().get_io_context().restart();
do this->get_service().get_io_context().run_one();
while (ec_ == asio::error::would_block);
if (ec_)
return traits_type::eof();

setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
&get_buffer_[0] + putback_max + bytes_transferred_);
return traits_type::to_int_type(*gptr());
}
else
{
return traits_type::eof();
}
}

int_type overflow(int_type c)
{
if (unbuffered_)
{
if (traits_type::eq_int_type(c, traits_type::eof()))
{
// Nothing to do.
return traits_type::not_eof(c);
}
else
{
if (timer_state_ == timer_has_expired)
{
ec_ = asio::error::operation_aborted;
return traits_type::eof();
}

// Send the single character immediately.
char_type ch = traits_type::to_char_type(c);
io_handler handler = { this };
this->get_service().async_send(this->get_implementation(),
asio::buffer(&ch, sizeof(char_type)), 0, handler);

ec_ = asio::error::would_block;
this->get_service().get_io_context().restart();
do this->get_service().get_io_context().run_one();
while (ec_ == asio::error::would_block);
if (ec_)
return traits_type::eof();

return c;
}
}
else
{
// Send all data in the output buffer.
asio::const_buffer buffer =
asio::buffer(pbase(), pptr() - pbase());
while (buffer.size() > 0)
{
if (timer_state_ == timer_has_expired)
{
ec_ = asio::error::operation_aborted;
return traits_type::eof();
}

io_handler handler = { this };
this->get_service().async_send(this->get_implementation(),
asio::buffer(buffer), 0, handler);

ec_ = asio::error::would_block;
this->get_service().get_io_context().restart();
do this->get_service().get_io_context().run_one();
while (ec_ == asio::error::would_block);
if (ec_)
return traits_type::eof();

buffer = buffer + bytes_transferred_;
}
setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());

// If the new character is eof then our work here is done.
if (traits_type::eq_int_type(c, traits_type::eof()))
return traits_type::not_eof(c);

// Add the new character to the output buffer.
*pptr() = traits_type::to_char_type(c);
pbump(1);
return c;
}
}

int sync()
{
return overflow(traits_type::eof());
}

std::streambuf* setbuf(char_type* s, std::streamsize n)
{
if (pptr() == pbase() && s == 0 && n == 0)
{
unbuffered_ = true;
setp(0, 0);
return this;
}

return 0;
}

/// Get the last error associated with the stream buffer.
/**
* @return An \c error_code corresponding to the last error from the stream
* buffer.
*/
virtual const asio::error_code& error() const
{
return ec_;
}

private:
void init_buffers()
{
setg(&get_buffer_[0],
&get_buffer_[0] + putback_max,
&get_buffer_[0] + putback_max);
if (unbuffered_)
setp(0, 0);
else
setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
}

template <typename EndpointSequence>
void connect_to_endpoints(const EndpointSequence& endpoints)
{
if (!ec_)
{
typename EndpointSequence::iterator i = endpoints.begin();
typename EndpointSequence::iterator end = endpoints.end();
ec_ = asio::error::host_not_found;
while (ec_ && i != end)
{
this->basic_socket<Protocol, StreamSocketService>::close(ec_);

if (timer_state_ == timer_has_expired)
{
ec_ = asio::error::operation_aborted;
return;
}

io_handler handler = { this };
this->basic_socket<Protocol, StreamSocketService>::async_connect(
*i, handler);

ec_ = asio::error::would_block;
this->get_service().get_io_context().restart();
do this->get_service().get_io_context().run_one();
while (ec_ == asio::error::would_block);

++i;
}
}
}

struct io_handler;
friend struct io_handler;
struct io_handler
{
basic_socket_streambuf* this_;

void operator()(const asio::error_code& ec,
std::size_t bytes_transferred = 0)
{
this_->ec_ = ec;
this_->bytes_transferred_ = bytes_transferred;
}
};

struct timer_handler;
friend struct timer_handler;
struct timer_handler
{
basic_socket_streambuf* this_;

void operator()(const asio::error_code&)
{
time_point now = traits_helper::now();

#if defined(ASIO_HAS_BOOST_DATE_TIME)
time_point expiry_time = this_->timer_service_->expires_at(
this_->timer_implementation_);
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
time_point expiry_time = this_->timer_service_->expiry(
this_->timer_implementation_);
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)

if (traits_helper::less_than(now, expiry_time))
{
this_->timer_state_ = timer_is_pending;
this_->timer_service_->async_wait(this_->timer_implementation_, *this);
}
else
{
this_->timer_state_ = timer_has_expired;
asio::error_code ec;
this_->basic_socket<Protocol, StreamSocketService>::close(ec);
}
}
};

void construct_timer()
{
if (timer_service_ == 0)
{
TimerService& timer_service = use_service<TimerService>(
detail::socket_streambuf_base::io_context_);
timer_service.construct(timer_implementation_);
timer_service_ = &timer_service;
}
}

void destroy_timer()
{
if (timer_service_)
timer_service_->destroy(timer_implementation_);
}

void start_timer()
{
if (timer_state_ != timer_is_pending)
{
timer_handler handler = { this };
handler(asio::error_code());
}
}

enum { putback_max = 8 };
enum { buffer_size = 512 };
asio::detail::array<char, buffer_size> get_buffer_;
asio::detail::array<char, buffer_size> put_buffer_;
bool unbuffered_;
asio::error_code ec_;
std::size_t bytes_transferred_;
TimerService* timer_service_;
typename TimerService::implementation_type timer_implementation_;
enum state { no_timer, timer_is_pending, timer_has_expired } timer_state_;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
# undef ASIO_PRIVATE_CONNECT_DEF
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)

#endif // !defined(ASIO_NO_IOSTREAM)

#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP

+ 848
- 0
source/modules/hylia/link/asio/basic_stream_socket.hpp View File

@@ -0,0 +1,848 @@
//
// basic_stream_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_STREAM_SOCKET_HPP
#define ASIO_BASIC_STREAM_SOCKET_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/async_result.hpp"
#include "asio/basic_socket.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/stream_socket_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides stream-oriented socket functionality.
/**
* The basic_stream_socket class template provides asynchronous and blocking
* stream-oriented socket functionality.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template <typename Protocol,
typename StreamSocketService = stream_socket_service<Protocol> >
class basic_stream_socket
: public basic_socket<Protocol, StreamSocketService>
{
public:
/// The native representation of a socket.
typedef typename StreamSocketService::native_handle_type native_handle_type;

/// The protocol type.
typedef Protocol protocol_type;

/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;

/// Construct a basic_stream_socket without opening it.
/**
* This constructor creates a stream socket without opening it. The socket
* needs to be opened and then connected or accepted before data can be sent
* or received on it.
*
* @param io_context The io_context object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*/
explicit basic_stream_socket(asio::io_context& io_context)
: basic_socket<Protocol, StreamSocketService>(io_context)
{
}

/// Construct and open a basic_stream_socket.
/**
* This constructor creates and opens a stream socket. The socket needs to be
* connected or accepted before data can be sent or received on it.
*
* @param io_context The io_context object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::system_error Thrown on failure.
*/
basic_stream_socket(asio::io_context& io_context,
const protocol_type& protocol)
: basic_socket<Protocol, StreamSocketService>(io_context, protocol)
{
}

/// Construct a basic_stream_socket, opening it and binding it to the given
/// local endpoint.
/**
* This constructor creates a stream socket and automatically opens it bound
* to the specified endpoint on the local machine. The protocol used is the
* protocol associated with the given endpoint.
*
* @param io_context The io_context object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param endpoint An endpoint on the local machine to which the stream
* socket will be bound.
*
* @throws asio::system_error Thrown on failure.
*/
basic_stream_socket(asio::io_context& io_context,
const endpoint_type& endpoint)
: basic_socket<Protocol, StreamSocketService>(io_context, endpoint)
{
}

/// Construct a basic_stream_socket on an existing native socket.
/**
* This constructor creates a stream socket object to hold an existing native
* socket.
*
* @param io_context The io_context object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::system_error Thrown on failure.
*/
basic_stream_socket(asio::io_context& io_context,
const protocol_type& protocol, const native_handle_type& native_socket)
: basic_socket<Protocol, StreamSocketService>(
io_context, protocol, native_socket)
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_stream_socket from another.
/**
* This constructor moves a stream socket from one object to another.
*
* @param other The other basic_stream_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_stream_socket(io_context&) constructor.
*/
basic_stream_socket(basic_stream_socket&& other)
: basic_socket<Protocol, StreamSocketService>(
ASIO_MOVE_CAST(basic_stream_socket)(other))
{
}

/// Move-assign a basic_stream_socket from another.
/**
* This assignment operator moves a stream socket from one object to another.
*
* @param other The other basic_stream_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_stream_socket(io_context&) constructor.
*/
basic_stream_socket& operator=(basic_stream_socket&& other)
{
basic_socket<Protocol, StreamSocketService>::operator=(
ASIO_MOVE_CAST(basic_stream_socket)(other));
return *this;
}

/// Move-construct a basic_stream_socket from a socket of another protocol
/// type.
/**
* This constructor moves a stream socket from one object to another.
*
* @param other The other basic_stream_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_stream_socket(io_context&) constructor.
*/
template <typename Protocol1, typename StreamSocketService1>
basic_stream_socket(
basic_stream_socket<Protocol1, StreamSocketService1>&& other,
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
: basic_socket<Protocol, StreamSocketService>(
ASIO_MOVE_CAST2(basic_stream_socket<
Protocol1, StreamSocketService1>)(other))
{
}

/// Move-assign a basic_stream_socket from a socket of another protocol type.
/**
* This assignment operator moves a stream socket from one object to another.
*
* @param other The other basic_stream_socket object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_stream_socket(io_context&) constructor.
*/
template <typename Protocol1, typename StreamSocketService1>
typename enable_if<is_convertible<Protocol1, Protocol>::value,
basic_stream_socket>::type& operator=(
basic_stream_socket<Protocol1, StreamSocketService1>&& other)
{
basic_socket<Protocol, StreamSocketService>::operator=(
ASIO_MOVE_CAST2(basic_stream_socket<
Protocol1, StreamSocketService1>)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.send(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::system_error Thrown on failure.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.send(asio::buffer(data, size), 0);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "send");
return s;
}

/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes sent. Returns 0 if an error occurred.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().send(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(
this->get_implementation(), buffers, 0,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), 0, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_send(const ConstBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(
this->get_implementation(), buffers, flags,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Receive some data on the socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on the socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size), 0);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
asio::detail::throw_error(ec, "receive");
return s;
}

/// Receive some data on a connected socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes received. Returns 0 if an error occurred.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*/
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags, asio::error_code& ec)
{
return this->get_service().receive(
this->get_implementation(), buffers, flags, ec);
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the stream
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref async_read function if you need to ensure
* that the requested amount of data is received before the asynchronous
* operation completes.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the stream
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref async_read function if you need to ensure
* that the requested amount of data is received before the asynchronous
* operation completes.
*
* @par Example
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), 0, handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_receive(const MutableBufferSequence& buffers,
socket_base::message_flags flags,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Write some data to the socket.
/**
* This function is used to write data to the stream socket. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the socket.
*
* @returns The number of bytes written.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* socket.write_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().send(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "write_some");
return s;
}

/// Write some data to the socket.
/**
* This function is used to write data to the stream socket. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the socket.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes written. Returns 0 if an error occurred.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
asio::error_code& ec)
{
return this->get_service().send(this->get_implementation(), buffers, 0, ec);
}

/// Start an asynchronous write.
/**
* This function is used to asynchronously write data to the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be written to the socket.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the write operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The write operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_write_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;

return this->get_service().async_send(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Read some data from the socket.
/**
* This function is used to read data from the stream socket. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* socket.read_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->get_service().receive(
this->get_implementation(), buffers, 0, ec);
asio::detail::throw_error(ec, "read_some");
return s;
}

/// Read some data from the socket.
/**
* This function is used to read data from the stream socket. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes read. Returns 0 if an error occurred.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return this->get_service().receive(
this->get_implementation(), buffers, 0, ec);
}

/// Start an asynchronous read.
/**
* This function is used to asynchronously read data from the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be read.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the read operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*
* @note The read operation may not read all of the requested number of bytes.
* Consider using the @ref async_read function if you need to ensure that the
* requested amount of data is read before the asynchronous operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_read_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

return this->get_service().async_receive(this->get_implementation(),
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_STREAM_SOCKET_HPP

+ 453
- 0
source/modules/hylia/link/asio/basic_streambuf.hpp View File

@@ -0,0 +1,453 @@
//
// basic_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_STREAMBUF_HPP
#define ASIO_BASIC_STREAMBUF_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if !defined(ASIO_NO_IOSTREAM)

#include <algorithm>
#include <cstring>
#include <stdexcept>
#include <streambuf>
#include <vector>
#include "asio/basic_streambuf_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/detail/limits.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/throw_exception.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Automatically resizable buffer class based on std::streambuf.
/**
* The @c basic_streambuf class is derived from @c std::streambuf to associate
* the streambuf's input and output sequences with one or more character
* arrays. These character arrays are internal to the @c basic_streambuf
* object, but direct access to the array elements is provided to permit them
* to be used efficiently with I/O operations. Characters written to the output
* sequence of a @c basic_streambuf object are appended to the input sequence
* of the same object.
*
* The @c basic_streambuf class's public interface is intended to permit the
* following implementation strategies:
*
* @li A single contiguous character array, which is reallocated as necessary
* to accommodate changes in the size of the character sequence. This is the
* implementation approach currently used in Asio.
*
* @li A sequence of one or more character arrays, where each array is of the
* same size. Additional character array objects are appended to the sequence
* to accommodate changes in the size of the character sequence.
*
* @li A sequence of one or more character arrays of varying sizes. Additional
* character array objects are appended to the sequence to accommodate changes
* in the size of the character sequence.
*
* The constructor for basic_streambuf accepts a @c size_t argument specifying
* the maximum of the sum of the sizes of the input sequence and output
* sequence. During the lifetime of the @c basic_streambuf object, the following
* invariant holds:
* @code size() <= max_size()@endcode
* Any member function that would, if successful, cause the invariant to be
* violated shall throw an exception of class @c std::length_error.
*
* The constructor for @c basic_streambuf takes an Allocator argument. A copy
* of this argument is used for any memory allocation performed, by the
* constructor and by all member functions, during the lifetime of each @c
* basic_streambuf object.
*
* @par Examples
* Writing directly from an streambuf to a socket:
* @code
* asio::streambuf b;
* std::ostream os(&b);
* os << "Hello, World!\n";
*
* // try sending some data in input sequence
* size_t n = sock.send(b.data());
*
* b.consume(n); // sent data is removed from input sequence
* @endcode
*
* Reading from a socket directly into a streambuf:
* @code
* asio::streambuf b;
*
* // reserve 512 bytes in output sequence
* asio::streambuf::mutable_buffers_type bufs = b.prepare(512);
*
* size_t n = sock.receive(bufs);
*
* // received data is "committed" from output sequence to input sequence
* b.commit(n);
*
* std::istream is(&b);
* std::string s;
* is >> s;
* @endcode
*/
#if defined(GENERATING_DOCUMENTATION)
template <typename Allocator = std::allocator<char> >
#else
template <typename Allocator>
#endif
class basic_streambuf
: public std::streambuf,
private noncopyable
{
public:
#if defined(GENERATING_DOCUMENTATION)
/// The type used to represent the input sequence as a list of buffers.
typedef implementation_defined const_buffers_type;

/// The type used to represent the output sequence as a list of buffers.
typedef implementation_defined mutable_buffers_type;
#else
typedef asio::const_buffers_1 const_buffers_type;
typedef asio::mutable_buffers_1 mutable_buffers_type;
#endif

/// Construct a basic_streambuf object.
/**
* Constructs a streambuf with the specified maximum size. The initial size
* of the streambuf's input sequence is 0.
*/
explicit basic_streambuf(
std::size_t maximum_size = (std::numeric_limits<std::size_t>::max)(),
const Allocator& allocator = Allocator())
: max_size_(maximum_size),
buffer_(allocator)
{
std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta);
buffer_.resize((std::max<std::size_t>)(pend, 1));
setg(&buffer_[0], &buffer_[0], &buffer_[0]);
setp(&buffer_[0], &buffer_[0] + pend);
}

/// Get the size of the input sequence.
/**
* @returns The size of the input sequence. The value is equal to that
* calculated for @c s in the following code:
* @code
* size_t s = 0;
* const_buffers_type bufs = data();
* const_buffers_type::const_iterator i = bufs.begin();
* while (i != bufs.end())
* {
* const_buffer buf(*i++);
* s += buf.size();
* }
* @endcode
*/
std::size_t size() const ASIO_NOEXCEPT
{
return pptr() - gptr();
}

/// Get the maximum size of the basic_streambuf.
/**
* @returns The allowed maximum of the sum of the sizes of the input sequence
* and output sequence.
*/
std::size_t max_size() const ASIO_NOEXCEPT
{
return max_size_;
}

/// Get the current capacity of the basic_streambuf.
/**
* @returns The current total capacity of the streambuf, i.e. for both the
* input sequence and output sequence.
*/
std::size_t capacity() const ASIO_NOEXCEPT
{
return buffer_.capacity();
}

/// Get a list of buffers that represents the input sequence.
/**
* @returns An object of type @c const_buffers_type that satisfies
* ConstBufferSequence requirements, representing all character arrays in the
* input sequence.
*
* @note The returned object is invalidated by any @c basic_streambuf member
* function that modifies the input sequence or output sequence.
*/
const_buffers_type data() const ASIO_NOEXCEPT
{
return asio::buffer(asio::const_buffer(gptr(),
(pptr() - gptr()) * sizeof(char_type)));
}

/// Get a list of buffers that represents the output sequence, with the given
/// size.
/**
* Ensures that the output sequence can accommodate @c n characters,
* reallocating character array objects as necessary.
*
* @returns An object of type @c mutable_buffers_type that satisfies
* MutableBufferSequence requirements, representing character array objects
* at the start of the output sequence such that the sum of the buffer sizes
* is @c n.
*
* @throws std::length_error If <tt>size() + n > max_size()</tt>.
*
* @note The returned object is invalidated by any @c basic_streambuf member
* function that modifies the input sequence or output sequence.
*/
mutable_buffers_type prepare(std::size_t n)
{
reserve(n);
return asio::buffer(asio::mutable_buffer(
pptr(), n * sizeof(char_type)));
}

/// Move characters from the output sequence to the input sequence.
/**
* Appends @c n characters from the start of the output sequence to the input
* sequence. The beginning of the output sequence is advanced by @c n
* characters.
*
* Requires a preceding call <tt>prepare(x)</tt> where <tt>x >= n</tt>, and
* no intervening operations that modify the input or output sequence.
*
* @note If @c n is greater than the size of the output sequence, the entire
* output sequence is moved to the input sequence and no error is issued.
*/
void commit(std::size_t n)
{
if (pptr() + n > epptr())
n = epptr() - pptr();
pbump(static_cast<int>(n));
setg(eback(), gptr(), pptr());
}

/// Remove characters from the input sequence.
/**
* Removes @c n characters from the beginning of the input sequence.
*
* @note If @c n is greater than the size of the input sequence, the entire
* input sequence is consumed and no error is issued.
*/
void consume(std::size_t n)
{
if (egptr() < pptr())
setg(&buffer_[0], gptr(), pptr());
if (gptr() + n > pptr())
n = pptr() - gptr();
gbump(static_cast<int>(n));
}

protected:
enum { buffer_delta = 128 };

/// Override std::streambuf behaviour.
/**
* Behaves according to the specification of @c std::streambuf::underflow().
*/
int_type underflow()
{
if (gptr() < pptr())
{
setg(&buffer_[0], gptr(), pptr());
return traits_type::to_int_type(*gptr());
}
else
{
return traits_type::eof();
}
}

/// Override std::streambuf behaviour.
/**
* Behaves according to the specification of @c std::streambuf::overflow(),
* with the specialisation that @c std::length_error is thrown if appending
* the character to the input sequence would require the condition
* <tt>size() > max_size()</tt> to be true.
*/
int_type overflow(int_type c)
{
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
if (pptr() == epptr())
{
std::size_t buffer_size = pptr() - gptr();
if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta)
{
reserve(max_size_ - buffer_size);
}
else
{
reserve(buffer_delta);
}
}

*pptr() = traits_type::to_char_type(c);
pbump(1);
return c;
}

return traits_type::not_eof(c);
}

void reserve(std::size_t n)
{
// Get current stream positions as offsets.
std::size_t gnext = gptr() - &buffer_[0];
std::size_t pnext = pptr() - &buffer_[0];
std::size_t pend = epptr() - &buffer_[0];

// Check if there is already enough space in the put area.
if (n <= pend - pnext)
{
return;
}

// Shift existing contents of get area to start of buffer.
if (gnext > 0)
{
pnext -= gnext;
std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext);
}

// Ensure buffer is large enough to hold at least the specified size.
if (n > pend - pnext)
{
if (n <= max_size_ && pnext <= max_size_ - n)
{
pend = pnext + n;
buffer_.resize((std::max<std::size_t>)(pend, 1));
}
else
{
std::length_error ex("asio::streambuf too long");
asio::detail::throw_exception(ex);
}
}

// Update stream positions.
setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext);
setp(&buffer_[0] + pnext, &buffer_[0] + pend);
}

private:
std::size_t max_size_;
std::vector<char_type, Allocator> buffer_;

// Helper function to get the preferred size for reading data.
friend std::size_t read_size_helper(
basic_streambuf& sb, std::size_t max_size)
{
return std::min<std::size_t>(
std::max<std::size_t>(512, sb.buffer_.capacity() - sb.size()),
std::min<std::size_t>(max_size, sb.max_size() - sb.size()));
}
};

/// Adapts basic_streambuf to the dynamic buffer sequence type requirements.
#if defined(GENERATING_DOCUMENTATION)
template <typename Allocator = std::allocator<char> >
#else
template <typename Allocator>
#endif
class basic_streambuf_ref
{
public:
/// The type used to represent the input sequence as a list of buffers.
typedef typename basic_streambuf<Allocator>::const_buffers_type
const_buffers_type;

/// The type used to represent the output sequence as a list of buffers.
typedef typename basic_streambuf<Allocator>::mutable_buffers_type
mutable_buffers_type;

/// Construct a basic_streambuf_ref for the given basic_streambuf object.
explicit basic_streambuf_ref(basic_streambuf<Allocator>& sb)
: sb_(sb)
{
}

/// Copy construct a basic_streambuf_ref.
basic_streambuf_ref(const basic_streambuf_ref& other) ASIO_NOEXCEPT
: sb_(other.sb_)
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move construct a basic_streambuf_ref.
basic_streambuf_ref(basic_streambuf_ref&& other) ASIO_NOEXCEPT
: sb_(other.sb_)
{
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Get the size of the input sequence.
std::size_t size() const ASIO_NOEXCEPT
{
return sb_.size();
}

/// Get the maximum size of the dynamic buffer.
std::size_t max_size() const ASIO_NOEXCEPT
{
return sb_.max_size();
}

/// Get the current capacity of the dynamic buffer.
std::size_t capacity() const ASIO_NOEXCEPT
{
return sb_.capacity();
}

/// Get a list of buffers that represents the input sequence.
const_buffers_type data() const ASIO_NOEXCEPT
{
return sb_.data();
}

/// Get a list of buffers that represents the output sequence, with the given
/// size.
mutable_buffers_type prepare(std::size_t n)
{
return sb_.prepare(n);
}

/// Move bytes from the output sequence to the input sequence.
void commit(std::size_t n)
{
return sb_.commit(n);
}

/// Remove characters from the input sequence.
void consume(std::size_t n)
{
return sb_.consume(n);
}

private:
basic_streambuf<Allocator>& sb_;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // !defined(ASIO_NO_IOSTREAM)

#endif // ASIO_BASIC_STREAMBUF_HPP

+ 36
- 0
source/modules/hylia/link/asio/basic_streambuf_fwd.hpp View File

@@ -0,0 +1,36 @@
//
// basic_streambuf_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP
#define ASIO_BASIC_STREAMBUF_FWD_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"

#if !defined(ASIO_NO_IOSTREAM)

#include <memory>

namespace asio {

template <typename Allocator = std::allocator<char> >
class basic_streambuf;

template <typename Allocator = std::allocator<char> >
class basic_streambuf_ref;

} // namespace asio

#endif // !defined(ASIO_NO_IOSTREAM)

#endif // ASIO_BASIC_STREAMBUF_FWD_HPP

+ 634
- 0
source/modules/hylia/link/asio/basic_waitable_timer.hpp View File

@@ -0,0 +1,634 @@
//
// basic_waitable_timer.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BASIC_WAITABLE_TIMER_HPP
#define ASIO_BASIC_WAITABLE_TIMER_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/basic_io_object.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/wait_traits.hpp"
#include "asio/waitable_timer_service.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Provides waitable timer functionality.
/**
* The basic_waitable_timer class template provides the ability to perform a
* blocking or asynchronous wait for a timer to expire.
*
* A waitable timer is always in one of two states: "expired" or "not expired".
* If the wait() or async_wait() function is called on an expired timer, the
* wait operation will complete immediately.
*
* Most applications will use one of the asio::steady_timer,
* asio::system_timer or asio::high_resolution_timer typedefs.
*
* @note This waitable timer functionality is for use with the C++11 standard
* library's @c &lt;chrono&gt; facility, or with the Boost.Chrono library.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Examples
* Performing a blocking wait (C++11):
* @code
* // Construct a timer without setting an expiry time.
* asio::steady_timer timer(io_context);
*
* // Set an expiry time relative to now.
* timer.expires_after(std::chrono::seconds(5));
*
* // Wait for the timer to expire.
* timer.wait();
* @endcode
*
* @par
* Performing an asynchronous wait (C++11):
* @code
* void handler(const asio::error_code& error)
* {
* if (!error)
* {
* // Timer expired.
* }
* }
*
* ...
*
* // Construct a timer with an absolute expiry time.
* asio::steady_timer timer(io_context,
* std::chrono::steady_clock::now() + std::chrono::seconds(60));
*
* // Start an asynchronous wait.
* timer.async_wait(handler);
* @endcode
*
* @par Changing an active waitable timer's expiry time
*
* Changing the expiry time of a timer while there are pending asynchronous
* waits causes those wait operations to be cancelled. To ensure that the action
* associated with the timer is performed only once, use something like this:
* used:
*
* @code
* void on_some_event()
* {
* if (my_timer.expires_after(seconds(5)) > 0)
* {
* // We managed to cancel the timer. Start new asynchronous wait.
* my_timer.async_wait(on_timeout);
* }
* else
* {
* // Too late, timer has already expired!
* }
* }
*
* void on_timeout(const asio::error_code& e)
* {
* if (e != asio::error::operation_aborted)
* {
* // Timer was not cancelled, take necessary action.
* }
* }
* @endcode
*
* @li The asio::basic_waitable_timer::expires_after() function
* cancels any pending asynchronous waits, and returns the number of
* asynchronous waits that were cancelled. If it returns 0 then you were too
* late and the wait handler has already been executed, or will soon be
* executed. If it returns 1 then the wait handler was successfully cancelled.
*
* @li If a wait handler is cancelled, the asio::error_code passed to
* it contains the value asio::error::operation_aborted.
*/
template <typename Clock,
typename WaitTraits = asio::wait_traits<Clock>,
typename WaitableTimerService = waitable_timer_service<Clock, WaitTraits> >
class basic_waitable_timer
: public basic_io_object<WaitableTimerService>
{
public:
/// The clock type.
typedef Clock clock_type;

/// The duration type of the clock.
typedef typename clock_type::duration duration;

/// The time point type of the clock.
typedef typename clock_type::time_point time_point;

/// The wait traits type.
typedef WaitTraits traits_type;

/// Constructor.
/**
* This constructor creates a timer without setting an expiry time. The
* expires_at() or expires_after() functions must be called to set an expiry
* time before the timer can be waited on.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*/
explicit basic_waitable_timer(asio::io_context& io_context)
: basic_io_object<WaitableTimerService>(io_context)
{
}

/// Constructor to set a particular expiry time as an absolute time.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
basic_waitable_timer(asio::io_context& io_context,
const time_point& expiry_time)
: basic_io_object<WaitableTimerService>(io_context)
{
asio::error_code ec;
this->get_service().expires_at(this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
}

/// Constructor to set a particular expiry time relative to now.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_context The io_context object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
basic_waitable_timer(asio::io_context& io_context,
const duration& expiry_time)
: basic_io_object<WaitableTimerService>(io_context)
{
asio::error_code ec;
this->get_service().expires_after(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_after");
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_waitable_timer from another.
/**
* This constructor moves a timer from one object to another.
*
* @param other The other basic_waitable_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_waitable_timer(io_context&) constructor.
*/
basic_waitable_timer(basic_waitable_timer&& other)
: basic_io_object<WaitableTimerService>(
ASIO_MOVE_CAST(basic_waitable_timer)(other))
{
}

/// Move-assign a basic_waitable_timer from another.
/**
* This assignment operator moves a timer from one object to another.
*
* @param other The other basic_waitable_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_waitable_timer(io_context&) constructor.
*/
basic_waitable_timer& operator=(basic_waitable_timer&& other)
{
basic_io_object<WaitableTimerService>::operator=(
ASIO_MOVE_CAST(basic_waitable_timer)(other));
return *this;
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel()
{
asio::error_code ec;
std::size_t s = this->get_service().cancel(this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
return s;
}

/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel(asio::error_code& ec)
{
return this->get_service().cancel(this->get_implementation(), ec);
}

/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one()
{
asio::error_code ec;
std::size_t s = this->get_service().cancel_one(
this->get_implementation(), ec);
asio::detail::throw_error(ec, "cancel_one");
return s;
}

/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one(asio::error_code& ec)
{
return this->get_service().cancel_one(this->get_implementation(), ec);
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute
/// time.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
time_point expires_at() const
{
return this->get_service().expires_at(this->get_implementation());
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Get the timer's expiry time as an absolute time.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
time_point expiry() const
{
return this->get_service().expiry(this->get_implementation());
}

/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_point& expiry_time)
{
asio::error_code ec;
std::size_t s = this->get_service().expires_at(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
return s;
}

/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_point& expiry_time,
asio::error_code& ec)
{
return this->get_service().expires_at(
this->get_implementation(), expiry_time, ec);
}

/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_after() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_after(const duration& expiry_time)
{
asio::error_code ec;
std::size_t s = this->get_service().expires_after(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_after");
return s;
}

/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_after() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_after(const duration& expiry_time,
asio::error_code& ec)
{
return this->get_service().expires_after(
this->get_implementation(), expiry_time, ec);
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use expiry().) Get the timer's expiry time relative to now.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
duration expires_from_now() const
{
return this->get_service().expires_from_now(this->get_implementation());
}

/// (Deprecated: Use expires_after().) Set the timer's expiry time relative
/// to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration& expiry_time)
{
asio::error_code ec;
std::size_t s = this->get_service().expires_from_now(
this->get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
return s;
}

/// (Deprecated: Use expires_after().) Set the timer's expiry time relative
/// to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration& expiry_time,
asio::error_code& ec)
{
return this->get_service().expires_from_now(
this->get_implementation(), expiry_time, ec);
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @throws asio::system_error Thrown on failure.
*/
void wait()
{
asio::error_code ec;
this->get_service().wait(this->get_implementation(), ec);
asio::detail::throw_error(ec, "wait");
}

/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @param ec Set to indicate what error occurred, if any.
*/
void wait(asio::error_code& ec)
{
this->get_service().wait(this->get_implementation(), ec);
}

/// Start an asynchronous wait on the timer.
/**
* This function may be used to initiate an asynchronous wait against the
* timer. It always returns immediately.
*
* For each call to async_wait(), the supplied handler will be called exactly
* once. The handler will be called when:
*
* @li The timer has expired.
*
* @li The timer was cancelled, in which case the handler is passed the error
* code asio::error::operation_aborted.
*
* @param handler The handler to be called when the timer expires. Copies
* will be made of the handler as required. The function signature of the
* handler must be:
* @code void handler(
* const asio::error_code& error // Result of operation.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_context::post().
*/
template <typename WaitHandler>
ASIO_INITFN_RESULT_TYPE(WaitHandler,
void (asio::error_code))
async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a WaitHandler.
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;

return this->get_service().async_wait(this->get_implementation(),
ASIO_MOVE_CAST(WaitHandler)(handler));
}
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BASIC_WAITABLE_TIMER_HPP

+ 578
- 0
source/modules/hylia/link/asio/bind_executor.hpp View File

@@ -0,0 +1,578 @@
//
// bind_executor.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BIND_EXECUTOR_HPP
#define ASIO_BIND_EXECUTOR_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/variadic_templates.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/async_result.hpp"
#include "asio/handler_type.hpp"
#include "asio/uses_executor.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
namespace detail {

template <typename T>
struct executor_binder_check
{
typedef void type;
};

// Helper to automatically define nested typedef result_type.

template <typename T, typename = void>
struct executor_binder_result_type
{
protected:
typedef void result_type_or_void;
};

template <typename T>
struct executor_binder_result_type<T,
typename executor_binder_check<typename T::result_type>::type>
{
typedef typename T::result_type result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R>
struct executor_binder_result_type<R(*)()>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R>
struct executor_binder_result_type<R(&)()>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R, typename A1>
struct executor_binder_result_type<R(*)(A1)>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R, typename A1>
struct executor_binder_result_type<R(&)(A1)>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R, typename A1, typename A2>
struct executor_binder_result_type<R(*)(A1, A2)>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

template <typename R, typename A1, typename A2>
struct executor_binder_result_type<R(&)(A1, A2)>
{
typedef R result_type;
protected:
typedef result_type result_type_or_void;
};

// Helper to automatically define nested typedef argument_type.

template <typename T, typename = void>
struct executor_binder_argument_type {};

template <typename T>
struct executor_binder_argument_type<T,
typename executor_binder_check<typename T::argument_type>::type>
{
typedef typename T::argument_type argument_type;
};

template <typename R, typename A1>
struct executor_binder_argument_type<R(*)(A1)>
{
typedef A1 argument_type;
};

template <typename R, typename A1>
struct executor_binder_argument_type<R(&)(A1)>
{
typedef A1 argument_type;
};

// Helper to automatically define nested typedefs first_argument_type and
// second_argument_type.

template <typename T, typename = void>
struct executor_binder_argument_types {};

template <typename T>
struct executor_binder_argument_types<T,
typename executor_binder_check<typename T::first_argument_type>::type>
{
typedef typename T::first_argument_type first_argument_type;
typedef typename T::second_argument_type second_argument_type;
};

template <typename R, typename A1, typename A2>
struct executor_binder_argument_type<R(*)(A1, A2)>
{
typedef A1 first_argument_type;
typedef A2 second_argument_type;
};

template <typename R, typename A1, typename A2>
struct executor_binder_argument_type<R(&)(A1, A2)>
{
typedef A1 first_argument_type;
typedef A2 second_argument_type;
};

// Helper to:
// - Apply the empty base optimisation to the executor.
// - Perform uses_executor construction of the target type, if required.

template <typename T, typename Executor, bool UsesExecutor>
class executor_binder_base;

template <typename T, typename Executor>
class executor_binder_base<T, Executor, true>
: protected Executor
{
protected:
template <typename E, typename U>
executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u)
: Executor(ASIO_MOVE_CAST(E)(e)),
target_(executor_arg_t(), static_cast<const Executor&>(*this),
ASIO_MOVE_CAST(U)(u))
{
}

T target_;
};

template <typename T, typename Executor>
class executor_binder_base<T, Executor, false>
: protected Executor
{
protected:
template <typename E, typename U>
executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u)
: Executor(ASIO_MOVE_CAST(E)(e)),
target_(ASIO_MOVE_CAST(U)(u))
{
}

T target_;
};

// Helper to enable SFINAE on zero-argument operator() below.

template <typename T, typename = void>
struct executor_binder_result_of0
{
typedef void type;
};

template <typename T>
struct executor_binder_result_of0<T,
typename executor_binder_check<typename result_of<T()>::type>::type>
{
typedef typename result_of<T()>::type type;
};

} // namespace detail

/// A call wrapper type to bind an executor of type @c Executor to an object of
/// type @c T.
template <typename T, typename Executor>
class executor_binder
#if !defined(GENERATING_DOCUMENTATION)
: public detail::executor_binder_result_type<T>,
public detail::executor_binder_argument_type<T>,
public detail::executor_binder_argument_types<T>,
private detail::executor_binder_base<
T, Executor, uses_executor<T, Executor>::value>
#endif // !defined(GENERATING_DOCUMENTATION)
{
public:
/// The type of the target object.
typedef T target_type;

/// The type of the associated executor.
typedef Executor executor_type;

#if defined(GENERATING_DOCUMENTATION)
/// The return type if a function.
/**
* The type of @c result_type is based on the type @c T of the wrapper's
* target object:
*
* @li if @c T is a pointer to function type, @c result_type is a synonym for
* the return type of @c T;
*
* @li if @c T is a class type with a member type @c result_type, then @c
* result_type is a synonym for @c T::result_type;
*
* @li otherwise @c result_type is not defined.
*/
typedef see_below result_type;

/// The type of the function's argument.
/**
* The type of @c argument_type is based on the type @c T of the wrapper's
* target object:
*
* @li if @c T is a pointer to a function type accepting a single argument,
* @c argument_type is a synonym for the return type of @c T;
*
* @li if @c T is a class type with a member type @c argument_type, then @c
* argument_type is a synonym for @c T::argument_type;
*
* @li otherwise @c argument_type is not defined.
*/
typedef see_below argument_type;

/// The type of the function's first argument.
/**
* The type of @c first_argument_type is based on the type @c T of the
* wrapper's target object:
*
* @li if @c T is a pointer to a function type accepting two arguments, @c
* first_argument_type is a synonym for the return type of @c T;
*
* @li if @c T is a class type with a member type @c first_argument_type,
* then @c first_argument_type is a synonym for @c T::first_argument_type;
*
* @li otherwise @c first_argument_type is not defined.
*/
typedef see_below first_argument_type;

/// The type of the function's second argument.
/**
* The type of @c second_argument_type is based on the type @c T of the
* wrapper's target object:
*
* @li if @c T is a pointer to a function type accepting two arguments, @c
* second_argument_type is a synonym for the return type of @c T;
*
* @li if @c T is a class type with a member type @c first_argument_type,
* then @c second_argument_type is a synonym for @c T::second_argument_type;
*
* @li otherwise @c second_argument_type is not defined.
*/
typedef see_below second_argument_type;
#endif // defined(GENERATING_DOCUMENTATION)

/// Construct an executor wrapper for the specified object.
/**
* This constructor is only valid if the type @c T is constructible from type
* @c U.
*/
template <typename U>
executor_binder(executor_arg_t, const executor_type& e,
ASIO_MOVE_ARG(U) u)
: base_type(e, ASIO_MOVE_CAST(U)(u))
{
}

/// Copy constructor.
executor_binder(const executor_binder& other)
: base_type(other.get_executor(), other.get())
{
}

/// Construct a copy, but specify a different executor.
executor_binder(executor_arg_t, const executor_type& e,
const executor_binder& other)
: base_type(e, other.get())
{
}

/// Construct a copy of a different executor wrapper type.
/**
* This constructor is only valid if the @c Executor type is constructible
* from type @c OtherExecutor, and the type @c T is constructible from type
* @c U.
*/
template <typename U, typename OtherExecutor>
executor_binder(const executor_binder<U, OtherExecutor>& other)
: base_type(other.get_executor(), other.get())
{
}

/// Construct a copy of a different executor wrapper type, but specify a
/// different executor.
/**
* This constructor is only valid if the type @c T is constructible from type
* @c U.
*/
template <typename U, typename OtherExecutor>
executor_binder(executor_arg_t, const executor_type& e,
const executor_binder<U, OtherExecutor>& other)
: base_type(e, other.get())
{
}

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Move constructor.
executor_binder(executor_binder&& other)
: base_type(ASIO_MOVE_CAST(executor_type)(other.get_executor()),
ASIO_MOVE_CAST(T)(other.get()))
{
}

/// Move construct the target object, but specify a different executor.
executor_binder(executor_arg_t, const executor_type& e,
executor_binder&& other)
: base_type(e, ASIO_MOVE_CAST(T)(other.get()))
{
}

/// Move construct from a different executor wrapper type.
template <typename U, typename OtherExecutor>
executor_binder(executor_binder<U, OtherExecutor>&& other)
: base_type(ASIO_MOVE_CAST(OtherExecutor)(other.get_executor()),
ASIO_MOVE_CAST(U)(other.get()))
{
}

/// Move construct from a different executor wrapper type, but specify a
/// different executor.
template <typename U, typename OtherExecutor>
executor_binder(executor_arg_t, const executor_type& e,
executor_binder<U, OtherExecutor>&& other)
: base_type(e, ASIO_MOVE_CAST(U)(other.get()))
{
}

#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

/// Destructor.
~executor_binder()
{
}

/// Obtain a reference to the target object.
target_type& get() ASIO_NOEXCEPT
{
return this->target_;
}

/// Obtain a reference to the target object.
const target_type& get() const ASIO_NOEXCEPT
{
return this->target_;
}

/// Obtain the associated executor.
executor_type get_executor() const ASIO_NOEXCEPT
{
return static_cast<const Executor&>(*this);
}

#if defined(GENERATING_DOCUMENTATION)

template <typename... Args> auto operator()(Args&& ...);
template <typename... Args> auto operator()(Args&& ...) const;

#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)

/// Forwarding function call operator.
template <typename... Args>
typename result_of<T(Args...)>::type operator()(
ASIO_MOVE_ARG(Args)... args)
{
return this->target_(ASIO_MOVE_CAST(Args)(args)...);
}

/// Forwarding function call operator.
template <typename... Args>
typename result_of<T(Args...)>::type operator()(
ASIO_MOVE_ARG(Args)... args) const
{
return this->target_(ASIO_MOVE_CAST(Args)(args)...);
}

#elif defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)

typename detail::executor_binder_result_of0<T>::type operator()()
{
return this->target_();
}

typename detail::executor_binder_result_of0<T>::type operator()() const
{
return this->target_();
}

#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
typename result_of<T(ASIO_VARIADIC_TARGS(n))>::type operator()( \
ASIO_VARIADIC_MOVE_PARAMS(n)) \
{ \
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
} \
\
template <ASIO_VARIADIC_TPARAMS(n)> \
typename result_of<T(ASIO_VARIADIC_TARGS(n))>::type operator()( \
ASIO_VARIADIC_MOVE_PARAMS(n)) const \
{ \
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
} \
/**/
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF

#else // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)

typedef typename detail::executor_binder_result_type<T>::result_type_or_void
result_type_or_void;

result_type_or_void operator()()
{
return this->target_();
}

result_type_or_void operator()() const
{
return this->target_();
}

#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
result_type_or_void operator()( \
ASIO_VARIADIC_MOVE_PARAMS(n)) \
{ \
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
} \
\
template <ASIO_VARIADIC_TPARAMS(n)> \
result_type_or_void operator()( \
ASIO_VARIADIC_MOVE_PARAMS(n)) const \
{ \
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
} \
/**/
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF

#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)

private:
typedef detail::executor_binder_base<T, Executor,
uses_executor<T, Executor>::value> base_type;
};

/// Associate an object of type @c T with an executor of type @c Executor.
template <typename Executor, typename T>
inline executor_binder<typename decay<T>::type, Executor>
bind_executor(const Executor& ex, ASIO_MOVE_ARG(T) t,
typename enable_if<is_executor<Executor>::value>::type* = 0)
{
return executor_binder<typename decay<T>::type, Executor>(
executor_arg_t(), ex, ASIO_MOVE_CAST(T)(t));
}

/// Associate an object of type @c T with an execution context's executor.
template <typename ExecutionContext, typename T>
inline executor_binder<typename decay<T>::type,
typename ExecutionContext::executor_type>
bind_executor(ExecutionContext& ctx, ASIO_MOVE_ARG(T) t,
typename enable_if<is_convertible<
ExecutionContext&, execution_context&>::value>::type* = 0)
{
return executor_binder<typename decay<T>::type,
typename ExecutionContext::executor_type>(
executor_arg_t(), ctx.get_executor(), ASIO_MOVE_CAST(T)(t));
}

#if !defined(GENERATING_DOCUMENTATION)

template <typename T, typename Executor>
struct uses_executor<executor_binder<T, Executor>, Executor>
: true_type {};

template <typename T, typename Executor, typename Signature>
struct handler_type<executor_binder<T, Executor>, Signature>
{
typedef executor_binder<
typename handler_type<T, Signature>::type, Executor> type;
};

template <typename T, typename Executor>
class async_result<executor_binder<T, Executor> >
{
public:
typedef typename async_result<T>::type type;

explicit async_result(executor_binder<T, Executor>& b)
: target_(b.get())
{
}

type get()
{
return target_.get();
}

private:
async_result<T> target_;
};

template <typename T, typename Executor, typename Allocator>
struct associated_allocator<executor_binder<T, Executor>, Allocator>
{
typedef typename associated_allocator<T, Allocator>::type type;

static type get(const executor_binder<T, Executor>& b,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return associated_allocator<T, Allocator>::get(b.get(), a);
}
};

template <typename T, typename Executor, typename Executor1>
struct associated_executor<executor_binder<T, Executor>, Executor1>
{
typedef Executor type;

static type get(const executor_binder<T, Executor>& b,
const Executor1& = Executor1()) ASIO_NOEXCEPT
{
return b.get_executor();
}
};

#endif // !defined(GENERATING_DOCUMENTATION)

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BIND_EXECUTOR_HPP

+ 2725
- 0
source/modules/hylia/link/asio/buffer.hpp
File diff suppressed because it is too large
View File


+ 262
- 0
source/modules/hylia/link/asio/buffered_read_stream.hpp View File

@@ -0,0 +1,262 @@
//
// buffered_read_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_READ_STREAM_HPP
#define ASIO_BUFFERED_READ_STREAM_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/async_result.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_resize_guard.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"
#include "asio/io_context.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Adds buffering to the read-related operations of a stream.
/**
* The buffered_read_stream class template can be used to add buffering to the
* synchronous and asynchronous read operations of a stream.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template <typename Stream>
class buffered_read_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename remove_reference<Stream>::type next_layer_type;

/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;

/// The type of the executor associated with the object.
typedef typename lowest_layer_type::executor_type executor_type;

#if defined(GENERATING_DOCUMENTATION)
/// The default buffer size.
static const std::size_t default_buffer_size = implementation_defined;
#else
ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
#endif

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_read_stream(Arg& a)
: next_layer_(a),
storage_(default_buffer_size)
{
}

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
buffered_read_stream(Arg& a, std::size_t buffer_size)
: next_layer_(a),
storage_(buffer_size)
{
}

/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return next_layer_;
}

/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return next_layer_.lowest_layer();
}

/// Get a const reference to the lowest layer.
const lowest_layer_type& lowest_layer() const
{
return next_layer_.lowest_layer();
}

/// Get the executor associated with the object.
executor_type get_executor() ASIO_NOEXCEPT
{
return next_layer_.lowest_layer().get_executor();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_context()
{
return next_layer_.get_io_context();
}

/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_service()
{
return next_layer_.get_io_service();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Close the stream.
void close()
{
next_layer_.close();
}

/// Close the stream.
asio::error_code close(asio::error_code& ec)
{
return next_layer_.close(ec);
}

/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
return next_layer_.write_some(buffers);
}

/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
asio::error_code& ec)
{
return next_layer_.write_some(buffers, ec);
}

/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
async_completion<WriteHandler,
void (asio::error_code, std::size_t)> init(handler);

next_layer_.async_write_some(buffers,
ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(WriteHandler,
void (asio::error_code, std::size_t)))(init.handler));

return init.result.get();
}

/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation. Throws an exception on failure.
std::size_t fill();

/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if an error occurred.
std::size_t fill(asio::error_code& ec);

/// Start an asynchronous fill.
template <typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_fill(ASIO_MOVE_ARG(ReadHandler) handler);

/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers);

/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
asio::error_code& ec);

/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler);

/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers);

/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers,
asio::error_code& ec);

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return storage_.size();
}

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail(asio::error_code& ec)
{
ec = asio::error_code();
return storage_.size();
}

private:
/// Copy data out of the internal buffer to the specified target buffer.
/// Returns the number of bytes copied.
template <typename MutableBufferSequence>
std::size_t copy(const MutableBufferSequence& buffers)
{
std::size_t bytes_copied = asio::buffer_copy(
buffers, storage_.data(), storage_.size());
storage_.consume(bytes_copied);
return bytes_copied;
}

/// Copy data from the internal buffer to the specified target buffer, without
/// removing the data from the internal buffer. Returns the number of bytes
/// copied.
template <typename MutableBufferSequence>
std::size_t peek_copy(const MutableBufferSequence& buffers)
{
return asio::buffer_copy(buffers, storage_.data(), storage_.size());
}

/// The next layer.
Stream next_layer_;

// The data in the buffer.
detail::buffered_stream_storage storage_;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#include "asio/impl/buffered_read_stream.hpp"

#endif // ASIO_BUFFERED_READ_STREAM_HPP

+ 25
- 0
source/modules/hylia/link/asio/buffered_read_stream_fwd.hpp View File

@@ -0,0 +1,25 @@
//
// buffered_read_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP
#define ASIO_BUFFERED_READ_STREAM_FWD_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

namespace asio {

template <typename Stream>
class buffered_read_stream;

} // namespace asio

#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP

+ 277
- 0
source/modules/hylia/link/asio/buffered_stream.hpp View File

@@ -0,0 +1,277 @@
//
// buffered_stream.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_STREAM_HPP
#define ASIO_BUFFERED_STREAM_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/async_result.hpp"
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/error.hpp"
#include "asio/io_context.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Adds buffering to the read- and write-related operations of a stream.
/**
* The buffered_stream class template can be used to add buffering to the
* synchronous and asynchronous read and write operations of a stream.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template <typename Stream>
class buffered_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename remove_reference<Stream>::type next_layer_type;

/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;

/// The type of the executor associated with the object.
typedef typename lowest_layer_type::executor_type executor_type;

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_stream(Arg& a)
: inner_stream_impl_(a),
stream_impl_(inner_stream_impl_)
{
}

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_stream(Arg& a, std::size_t read_buffer_size,
std::size_t write_buffer_size)
: inner_stream_impl_(a, write_buffer_size),
stream_impl_(inner_stream_impl_, read_buffer_size)
{
}

/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return stream_impl_.next_layer().next_layer();
}

/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return stream_impl_.lowest_layer();
}

/// Get a const reference to the lowest layer.
const lowest_layer_type& lowest_layer() const
{
return stream_impl_.lowest_layer();
}

/// Get the executor associated with the object.
executor_type get_executor() ASIO_NOEXCEPT
{
return stream_impl_.lowest_layer().get_executor();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_context()
{
return stream_impl_.get_io_context();
}

/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_service()
{
return stream_impl_.get_io_service();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Close the stream.
void close()
{
stream_impl_.close();
}

/// Close the stream.
asio::error_code close(asio::error_code& ec)
{
return stream_impl_.close(ec);
}

/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation. Throws an
/// exception on failure.
std::size_t flush()
{
return stream_impl_.next_layer().flush();
}

/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation, or 0 if an
/// error occurred.
std::size_t flush(asio::error_code& ec)
{
return stream_impl_.next_layer().flush(ec);
}

/// Start an asynchronous flush.
template <typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_flush(ASIO_MOVE_ARG(WriteHandler) handler)
{
return stream_impl_.next_layer().async_flush(
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
return stream_impl_.write_some(buffers);
}

/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
asio::error_code& ec)
{
return stream_impl_.write_some(buffers, ec);
}

/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler)
{
return stream_impl_.async_write_some(buffers,
ASIO_MOVE_CAST(WriteHandler)(handler));
}

/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation. Throws an exception on failure.
std::size_t fill()
{
return stream_impl_.fill();
}

/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if an error occurred.
std::size_t fill(asio::error_code& ec)
{
return stream_impl_.fill(ec);
}

/// Start an asynchronous fill.
template <typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_fill(ASIO_MOVE_ARG(ReadHandler) handler)
{
return stream_impl_.async_fill(ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
return stream_impl_.read_some(buffers);
}

/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return stream_impl_.read_some(buffers, ec);
}

/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
return stream_impl_.async_read_some(buffers,
ASIO_MOVE_CAST(ReadHandler)(handler));
}

/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers)
{
return stream_impl_.peek(buffers);
}

/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return stream_impl_.peek(buffers, ec);
}

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return stream_impl_.in_avail();
}

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail(asio::error_code& ec)
{
return stream_impl_.in_avail(ec);
}

private:
// The buffered write stream.
typedef buffered_write_stream<Stream> write_stream_type;
write_stream_type inner_stream_impl_;

// The buffered read stream.
typedef buffered_read_stream<write_stream_type&> read_stream_type;
read_stream_type stream_impl_;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BUFFERED_STREAM_HPP

+ 25
- 0
source/modules/hylia/link/asio/buffered_stream_fwd.hpp View File

@@ -0,0 +1,25 @@
//
// buffered_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_STREAM_FWD_HPP
#define ASIO_BUFFERED_STREAM_FWD_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

namespace asio {

template <typename Stream>
class buffered_stream;

} // namespace asio

#endif // ASIO_BUFFERED_STREAM_FWD_HPP

+ 254
- 0
source/modules/hylia/link/asio/buffered_write_stream.hpp View File

@@ -0,0 +1,254 @@
//
// buffered_write_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP
#define ASIO_BUFFERED_WRITE_STREAM_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/completion_condition.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"
#include "asio/io_context.hpp"
#include "asio/write.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

/// Adds buffering to the write-related operations of a stream.
/**
* The buffered_write_stream class template can be used to add buffering to the
* synchronous and asynchronous write operations of a stream.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template <typename Stream>
class buffered_write_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename remove_reference<Stream>::type next_layer_type;

/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;

/// The type of the executor associated with the object.
typedef typename lowest_layer_type::executor_type executor_type;

#if defined(GENERATING_DOCUMENTATION)
/// The default buffer size.
static const std::size_t default_buffer_size = implementation_defined;
#else
ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
#endif

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_write_stream(Arg& a)
: next_layer_(a),
storage_(default_buffer_size)
{
}

/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
buffered_write_stream(Arg& a, std::size_t buffer_size)
: next_layer_(a),
storage_(buffer_size)
{
}

/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return next_layer_;
}

/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return next_layer_.lowest_layer();
}

/// Get a const reference to the lowest layer.
const lowest_layer_type& lowest_layer() const
{
return next_layer_.lowest_layer();
}

/// Get the executor associated with the object.
executor_type get_executor() ASIO_NOEXCEPT
{
return next_layer_.lowest_layer().get_executor();
}

#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_context()
{
return next_layer_.get_io_context();
}

/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
asio::io_context& get_io_service()
{
return next_layer_.get_io_service();
}
#endif // !defined(ASIO_NO_DEPRECATED)

/// Close the stream.
void close()
{
next_layer_.close();
}

/// Close the stream.
asio::error_code close(asio::error_code& ec)
{
return next_layer_.close(ec);
}

/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation. Throws an
/// exception on failure.
std::size_t flush();

/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation, or 0 if an
/// error occurred.
std::size_t flush(asio::error_code& ec);

/// Start an asynchronous flush.
template <typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_flush(ASIO_MOVE_ARG(WriteHandler) handler);

/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers);

/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred and the error handler did not throw.
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
asio::error_code& ec);

/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename WriteHandler>
ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (asio::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(WriteHandler) handler);

/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
return next_layer_.read_some(buffers);
}

/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred.
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return next_layer_.read_some(buffers, ec);
}

/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename MutableBufferSequence, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (asio::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{
async_completion<ReadHandler,
void (asio::error_code, std::size_t)> init(handler);

next_layer_.async_read_some(buffers,
ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(ReadHandler,
void (asio::error_code, std::size_t)))(init.handler));

return init.result.get();
}

/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers)
{
return next_layer_.peek(buffers);
}

/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred.
template <typename MutableBufferSequence>
std::size_t peek(const MutableBufferSequence& buffers,
asio::error_code& ec)
{
return next_layer_.peek(buffers, ec);
}

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return next_layer_.in_avail();
}

/// Determine the amount of data that may be read without blocking.
std::size_t in_avail(asio::error_code& ec)
{
return next_layer_.in_avail(ec);
}

private:
/// Copy data into the internal buffer from the specified source buffer.
/// Returns the number of bytes copied.
template <typename ConstBufferSequence>
std::size_t copy(const ConstBufferSequence& buffers);

/// The next layer.
Stream next_layer_;

// The data in the buffer.
detail::buffered_stream_storage storage_;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

#include "asio/impl/buffered_write_stream.hpp"

#endif // ASIO_BUFFERED_WRITE_STREAM_HPP

+ 25
- 0
source/modules/hylia/link/asio/buffered_write_stream_fwd.hpp View File

@@ -0,0 +1,25 @@
//
// buffered_write_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

namespace asio {

template <typename Stream>
class buffered_write_stream;

} // namespace asio

#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP

+ 481
- 0
source/modules/hylia/link/asio/buffers_iterator.hpp View File

@@ -0,0 +1,481 @@
//
// buffers_iterator.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_BUFFERS_ITERATOR_HPP
#define ASIO_BUFFERS_ITERATOR_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include <iterator>
#include "asio/buffer.hpp"
#include "asio/detail/assert.hpp"
#include "asio/detail/type_traits.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {

namespace detail
{
template <bool IsMutable>
struct buffers_iterator_types_helper;

template <>
struct buffers_iterator_types_helper<false>
{
typedef const_buffer buffer_type;
template <typename ByteType>
struct byte_type
{
typedef typename add_const<ByteType>::type type;
};
};

template <>
struct buffers_iterator_types_helper<true>
{
typedef mutable_buffer buffer_type;
template <typename ByteType>
struct byte_type
{
typedef ByteType type;
};
};

template <typename BufferSequence, typename ByteType>
struct buffers_iterator_types
{
enum
{
is_mutable = is_convertible<
typename BufferSequence::value_type,
mutable_buffer>::value
};
typedef buffers_iterator_types_helper<is_mutable> helper;
typedef typename helper::buffer_type buffer_type;
typedef typename helper::template byte_type<ByteType>::type byte_type;
};
}

/// A random access iterator over the bytes in a buffer sequence.
template <typename BufferSequence, typename ByteType = char>
class buffers_iterator
{
private:
typedef typename detail::buffers_iterator_types<
BufferSequence, ByteType>::buffer_type buffer_type;

public:
/// The type used for the distance between two iterators.
typedef std::ptrdiff_t difference_type;

/// The type of the value pointed to by the iterator.
typedef ByteType value_type;

#if defined(GENERATING_DOCUMENTATION)
/// The type of the result of applying operator->() to the iterator.
/**
* If the buffer sequence stores buffer objects that are convertible to
* mutable_buffer, this is a pointer to a non-const ByteType. Otherwise, a
* pointer to a const ByteType.
*/
typedef const_or_non_const_ByteType* pointer;
#else // defined(GENERATING_DOCUMENTATION)
typedef typename detail::buffers_iterator_types<
BufferSequence, ByteType>::byte_type* pointer;
#endif // defined(GENERATING_DOCUMENTATION)

#if defined(GENERATING_DOCUMENTATION)
/// The type of the result of applying operator*() to the iterator.
/**
* If the buffer sequence stores buffer objects that are convertible to
* mutable_buffer, this is a reference to a non-const ByteType. Otherwise, a
* reference to a const ByteType.
*/
typedef const_or_non_const_ByteType& reference;
#else // defined(GENERATING_DOCUMENTATION)
typedef typename detail::buffers_iterator_types<
BufferSequence, ByteType>::byte_type& reference;
#endif // defined(GENERATING_DOCUMENTATION)

/// The iterator category.
typedef std::random_access_iterator_tag iterator_category;

/// Default constructor. Creates an iterator in an undefined state.
buffers_iterator()
: current_buffer_(),
current_buffer_position_(0),
begin_(),
current_(),
end_(),
position_(0)
{
}

/// Construct an iterator representing the beginning of the buffers' data.
static buffers_iterator begin(const BufferSequence& buffers)
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
__attribute__ ((__noinline__))
#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
{
buffers_iterator new_iter;
new_iter.begin_ = buffers.begin();
new_iter.current_ = buffers.begin();
new_iter.end_ = buffers.end();
while (new_iter.current_ != new_iter.end_)
{
new_iter.current_buffer_ = *new_iter.current_;
if (new_iter.current_buffer_.size() > 0)
break;
++new_iter.current_;
}
return new_iter;
}

/// Construct an iterator representing the end of the buffers' data.
static buffers_iterator end(const BufferSequence& buffers)
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
__attribute__ ((__noinline__))
#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
{
buffers_iterator new_iter;
new_iter.begin_ = buffers.begin();
new_iter.current_ = buffers.begin();
new_iter.end_ = buffers.end();
while (new_iter.current_ != new_iter.end_)
{
buffer_type buffer = *new_iter.current_;
new_iter.position_ += buffer.size();
++new_iter.current_;
}
return new_iter;
}

/// Dereference an iterator.
reference operator*() const
{
return dereference();
}

/// Dereference an iterator.
pointer operator->() const
{
return &dereference();
}

/// Access an individual element.
reference operator[](std::ptrdiff_t difference) const
{
buffers_iterator tmp(*this);
tmp.advance(difference);
return *tmp;
}

/// Increment operator (prefix).
buffers_iterator& operator++()
{
increment();
return *this;
}

/// Increment operator (postfix).
buffers_iterator operator++(int)
{
buffers_iterator tmp(*this);
++*this;
return tmp;
}

/// Decrement operator (prefix).
buffers_iterator& operator--()
{
decrement();
return *this;
}

/// Decrement operator (postfix).
buffers_iterator operator--(int)
{
buffers_iterator tmp(*this);
--*this;
return tmp;
}

/// Addition operator.
buffers_iterator& operator+=(std::ptrdiff_t difference)
{
advance(difference);
return *this;
}

/// Subtraction operator.
buffers_iterator& operator-=(std::ptrdiff_t difference)
{
advance(-difference);
return *this;
}

/// Addition operator.
friend buffers_iterator operator+(const buffers_iterator& iter,
std::ptrdiff_t difference)
{
buffers_iterator tmp(iter);
tmp.advance(difference);
return tmp;
}

/// Addition operator.
friend buffers_iterator operator+(std::ptrdiff_t difference,
const buffers_iterator& iter)
{
buffers_iterator tmp(iter);
tmp.advance(difference);
return tmp;
}

/// Subtraction operator.
friend buffers_iterator operator-(const buffers_iterator& iter,
std::ptrdiff_t difference)
{
buffers_iterator tmp(iter);
tmp.advance(-difference);
return tmp;
}

/// Subtraction operator.
friend std::ptrdiff_t operator-(const buffers_iterator& a,
const buffers_iterator& b)
{
return b.distance_to(a);
}

/// Test two iterators for equality.
friend bool operator==(const buffers_iterator& a, const buffers_iterator& b)
{
return a.equal(b);
}

/// Test two iterators for inequality.
friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b)
{
return !a.equal(b);
}

/// Compare two iterators.
friend bool operator<(const buffers_iterator& a, const buffers_iterator& b)
{
return a.distance_to(b) > 0;
}

/// Compare two iterators.
friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b)
{
return !(b < a);
}

/// Compare two iterators.
friend bool operator>(const buffers_iterator& a, const buffers_iterator& b)
{
return b < a;
}

/// Compare two iterators.
friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b)
{
return !(a < b);
}

private:
// Dereference the iterator.
reference dereference() const
{
return static_cast<pointer>(
current_buffer_.data())[current_buffer_position_];
}

// Compare two iterators for equality.
bool equal(const buffers_iterator& other) const
{
return position_ == other.position_;
}

// Increment the iterator.
void increment()
{
ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
++position_;

// Check if the increment can be satisfied by the current buffer.
++current_buffer_position_;
if (current_buffer_position_ != current_buffer_.size())
return;

// Find the next non-empty buffer.
++current_;
current_buffer_position_ = 0;
while (current_ != end_)
{
current_buffer_ = *current_;
if (current_buffer_.size() > 0)
return;
++current_;
}
}

// Decrement the iterator.
void decrement()
{
ASIO_ASSERT(position_ > 0 && "iterator out of bounds");
--position_;

// Check if the decrement can be satisfied by the current buffer.
if (current_buffer_position_ != 0)
{
--current_buffer_position_;
return;
}

// Find the previous non-empty buffer.
typename BufferSequence::const_iterator iter = current_;
while (iter != begin_)
{
--iter;
buffer_type buffer = *iter;
std::size_t buffer_size = buffer.size();
if (buffer_size > 0)
{
current_ = iter;
current_buffer_ = buffer;
current_buffer_position_ = buffer_size - 1;
return;
}
}
}

// Advance the iterator by the specified distance.
void advance(std::ptrdiff_t n)
{
if (n > 0)
{
ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
for (;;)
{
std::ptrdiff_t current_buffer_balance
= current_buffer_.size() - current_buffer_position_;

// Check if the advance can be satisfied by the current buffer.
if (current_buffer_balance > n)
{
position_ += n;
current_buffer_position_ += n;
return;
}

// Update position.
n -= current_buffer_balance;
position_ += current_buffer_balance;

// Move to next buffer. If it is empty then it will be skipped on the
// next iteration of this loop.
if (++current_ == end_)
{
ASIO_ASSERT(n == 0 && "iterator out of bounds");
current_buffer_ = buffer_type();
current_buffer_position_ = 0;
return;
}
current_buffer_ = *current_;
current_buffer_position_ = 0;
}
}
else if (n < 0)
{
std::size_t abs_n = -n;
ASIO_ASSERT(position_ >= abs_n && "iterator out of bounds");
for (;;)
{
// Check if the advance can be satisfied by the current buffer.
if (current_buffer_position_ >= abs_n)
{
position_ -= abs_n;
current_buffer_position_ -= abs_n;
return;
}

// Update position.
abs_n -= current_buffer_position_;
position_ -= current_buffer_position_;

// Check if we've reached the beginning of the buffers.
if (current_ == begin_)
{
ASIO_ASSERT(abs_n == 0 && "iterator out of bounds");
current_buffer_position_ = 0;
return;
}

// Find the previous non-empty buffer.
typename BufferSequence::const_iterator iter = current_;
while (iter != begin_)
{
--iter;
buffer_type buffer = *iter;
std::size_t buffer_size = buffer.size();
if (buffer_size > 0)
{
current_ = iter;
current_buffer_ = buffer;
current_buffer_position_ = buffer_size;
break;
}
}
}
}
}

// Determine the distance between two iterators.
std::ptrdiff_t distance_to(const buffers_iterator& other) const
{
return other.position_ - position_;
}

buffer_type current_buffer_;
std::size_t current_buffer_position_;
typename BufferSequence::const_iterator begin_;
typename BufferSequence::const_iterator current_;
typename BufferSequence::const_iterator end_;
std::size_t position_;
};

/// Construct an iterator representing the beginning of the buffers' data.
template <typename BufferSequence>
inline buffers_iterator<BufferSequence> buffers_begin(
const BufferSequence& buffers)
{
return buffers_iterator<BufferSequence>::begin(buffers);
}

/// Construct an iterator representing the end of the buffers' data.
template <typename BufferSequence>
inline buffers_iterator<BufferSequence> buffers_end(
const BufferSequence& buffers)
{
return buffers_iterator<BufferSequence>::end(buffers);
}

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_BUFFERS_ITERATOR_HPP

+ 218
- 0
source/modules/hylia/link/asio/completion_condition.hpp View File

@@ -0,0 +1,218 @@
//
// completion_condition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef ASIO_COMPLETION_CONDITION_HPP
#define ASIO_COMPLETION_CONDITION_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>

#include "asio/detail/push_options.hpp"

namespace asio {

namespace detail {

// The default maximum number of bytes to transfer in a single operation.
enum default_max_transfer_size_t { default_max_transfer_size = 65536 };

// Adapt result of old-style completion conditions (which had a bool result
// where true indicated that the operation was complete).
inline std::size_t adapt_completion_condition_result(bool result)
{
return result ? 0 : default_max_transfer_size;
}

// Adapt result of current completion conditions (which have a size_t result
// where 0 means the operation is complete, and otherwise the result is the
// maximum number of bytes to transfer on the next underlying operation).
inline std::size_t adapt_completion_condition_result(std::size_t result)
{
return result;
}

class transfer_all_t
{
public:
typedef std::size_t result_type;

template <typename Error>
std::size_t operator()(const Error& err, std::size_t)
{
return !!err ? 0 : default_max_transfer_size;
}
};

class transfer_at_least_t
{
public:
typedef std::size_t result_type;

explicit transfer_at_least_t(std::size_t minimum)
: minimum_(minimum)
{
}

template <typename Error>
std::size_t operator()(const Error& err, std::size_t bytes_transferred)
{
return (!!err || bytes_transferred >= minimum_)
? 0 : default_max_transfer_size;
}

private:
std::size_t minimum_;
};

class transfer_exactly_t
{
public:
typedef std::size_t result_type;

explicit transfer_exactly_t(std::size_t size)
: size_(size)
{
}

template <typename Error>
std::size_t operator()(const Error& err, std::size_t bytes_transferred)
{
return (!!err || bytes_transferred >= size_) ? 0 :
(size_ - bytes_transferred < default_max_transfer_size
? size_ - bytes_transferred : std::size_t(default_max_transfer_size));
}

private:
std::size_t size_;
};

} // namespace detail

/**
* @defgroup completion_condition Completion Condition Function Objects
*
* Function objects used for determining when a read or write operation should
* complete.
*/
/*@{*/

/// Return a completion condition function object that indicates that a read or
/// write operation should continue until all of the data has been transferred,
/// or until an error occurs.
/**
* This function is used to create an object, of unspecified type, that meets
* CompletionCondition requirements.
*
* @par Example
* Reading until a buffer is full:
* @code
* boost::array<char, 128> buf;
* asio::error_code ec;
* std::size_t n = asio::read(
* sock, asio::buffer(buf),
* asio::transfer_all(), ec);
* if (ec)
* {
* // An error occurred.
* }
* else
* {
* // n == 128
* }
* @endcode
*/
#if defined(GENERATING_DOCUMENTATION)
unspecified transfer_all();
#else
inline detail::transfer_all_t transfer_all()
{
return detail::transfer_all_t();
}
#endif

/// Return a completion condition function object that indicates that a read or
/// write operation should continue until a minimum number of bytes has been
/// transferred, or until an error occurs.
/**
* This function is used to create an object, of unspecified type, that meets
* CompletionCondition requirements.
*
* @par Example
* Reading until a buffer is full or contains at least 64 bytes:
* @code
* boost::array<char, 128> buf;
* asio::error_code ec;
* std::size_t n = asio::read(
* sock, asio::buffer(buf),
* asio::transfer_at_least(64), ec);
* if (ec)
* {
* // An error occurred.
* }
* else
* {
* // n >= 64 && n <= 128
* }
* @endcode
*/
#if defined(GENERATING_DOCUMENTATION)
unspecified transfer_at_least(std::size_t minimum);
#else
inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum)
{
return detail::transfer_at_least_t(minimum);
}
#endif

/// Return a completion condition function object that indicates that a read or
/// write operation should continue until an exact number of bytes has been
/// transferred, or until an error occurs.
/**
* This function is used to create an object, of unspecified type, that meets
* CompletionCondition requirements.
*
* @par Example
* Reading until a buffer is full or contains exactly 64 bytes:
* @code
* boost::array<char, 128> buf;
* asio::error_code ec;
* std::size_t n = asio::read(
* sock, asio::buffer(buf),
* asio::transfer_exactly(64), ec);
* if (ec)
* {
* // An error occurred.
* }
* else
* {
* // n == 64
* }
* @endcode
*/
#if defined(GENERATING_DOCUMENTATION)
unspecified transfer_exactly(std::size_t size);
#else
inline detail::transfer_exactly_t transfer_exactly(std::size_t size)
{
return detail::transfer_exactly_t(size);
}
#endif

/*@}*/

} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_COMPLETION_CONDITION_HPP

+ 1055
- 0
source/modules/hylia/link/asio/connect.hpp
File diff suppressed because it is too large
View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save