/* 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 .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
#include
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;
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
friend It toNetworkByteStream(const MessageHeader& header, It out)
{
return discovery::toNetworkByteStream(header.messageType, std::move(out));
}
template
static std::pair fromNetworkByteStream(It begin, const It end)
{
using namespace discovery;
MessageHeader header;
std::tie(header.messageType, begin) =
Deserialize::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;
const ProtocolHeader kProtocolHeader = {{'_', 'l', 'i', 'n', 'k', '_', 'v', 1}};
// Must have at least kMaxMessageSize bytes available in the output stream
template
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), std::move(out))));
}
else
{
throw range_error("Exceeded maximum message size");
}
}
} // namespace detail
template
It pingMessage(const Payload& payload, It out)
{
return detail::encodeMessage(kPing, payload, std::move(out));
}
template
It pongMessage(const Payload& payload, It out)
{
return detail::encodeMessage(kPong, payload, std::move(out));
}
template
std::pair parseMessageHeader(It bytesBegin, const It bytesEnd)
{
using ItDiff = typename std::iterator_traits::difference_type;
MessageHeader header = {};
const auto protocolHeaderSize = discovery::sizeInByteStream(detail::kProtocolHeader);
const auto minMessageSize =
static_cast(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