/* 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 #if LINK_PLATFORM_MACOSX #include #elif LINK_PLATFORM_LINUX #include #endif #include #include #include #include #include #if LINK_PLATFORM_WINDOWS #include #include #include #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 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 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 static std::pair fromNetworkByteStream(It begin, It end) { return T::fromNetworkByteStream(std::move(begin), std::move(end)); } }; // Default size implementation. Works for primitive types. template std::uint32_t sizeInByteStream(T) { return sizeof(T); } namespace detail { // utilities for implementing concept for primitive types template It copyToByteStream(T t, It out) { using namespace std; return copy_n( reinterpret_cast::pointer>(&t), sizeof(t), out); } template std::pair copyFromByteStream(It begin, const It end) { using namespace std; using ItDiff = typename iterator_traits::difference_type; if (distance(begin, end) < static_cast(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(&t)); return make_pair(t, begin + n); } } } // namespace detail // Model the concept for unsigned integral types // uint8_t template It toNetworkByteStream(const uint8_t byte, It out) { return detail::copyToByteStream(byte, std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { return detail::copyFromByteStream(std::move(begin), std::move(end)); } }; // uint16_t template It toNetworkByteStream(uint16_t s, It out) { return detail::copyToByteStream(htons(s), std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { auto result = detail::copyFromByteStream(std::move(begin), std::move(end)); result.first = ntohs(result.first); return result; } }; // uint32_t template It toNetworkByteStream(uint32_t l, It out) { return detail::copyToByteStream(htonl(l), std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { auto result = detail::copyFromByteStream(std::move(begin), std::move(end)); result.first = ntohl(result.first); return result; } }; // int32_t in terms of uint32_t template It toNetworkByteStream(int32_t l, It out) { return toNetworkByteStream(reinterpret_cast(l), std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { auto result = Deserialize::fromNetworkByteStream(std::move(begin), std::move(end)); return std::make_pair(reinterpret_cast(result.first), result.second); } }; // uint64_t template It toNetworkByteStream(uint64_t ll, It out) { return detail::copyToByteStream(htonll(ll), std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { auto result = detail::copyFromByteStream(std::move(begin), std::move(end)); result.first = ntohll(result.first); return result; } }; // int64_t in terms of uint64_t template It toNetworkByteStream(int64_t ll, It out) { return toNetworkByteStream(reinterpret_cast(ll), std::move(out)); } template <> struct Deserialize { template static std::pair fromNetworkByteStream(It begin, It end) { auto result = Deserialize::fromNetworkByteStream(std::move(begin), std::move(end)); return std::make_pair(reinterpret_cast(result.first), result.second); } }; // overloads for std::chrono durations template std::uint32_t sizeInByteStream(const std::chrono::duration dur) { return sizeInByteStream(dur.count()); } template It toNetworkByteStream(const std::chrono::duration dur, It out) { return toNetworkByteStream(dur.count(), std::move(out)); } template struct Deserialize> { template static std::pair, It> fromNetworkByteStream( It begin, It end) { using namespace std; auto result = Deserialize::fromNetworkByteStream(move(begin), move(end)); return make_pair(std::chrono::duration{result.first}, result.second); } }; namespace detail { // Generic serialize/deserialize utilities for containers template std::uint32_t containerSizeInByteStream(const Container& container) { std::uint32_t totalSize = 0; for (const auto& val : container) { totalSize += sizeInByteStream(val); } return totalSize; } template It containerToNetworkByteStream(const Container& container, It out) { for (const auto& val : container) { out = toNetworkByteStream(val, out); } return out; } template 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::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 std::uint32_t sizeInByteStream(const std::array& arr) { return detail::containerSizeInByteStream(arr); } template It toNetworkByteStream(const std::array& arr, It out) { return detail::containerToNetworkByteStream(arr, std::move(out)); } template struct Deserialize> { template static std::pair, It> fromNetworkByteStream(It begin, It end) { using namespace std; array result{}; auto resultIt = detail::deserializeContainer(move(begin), move(end), move(result.begin()), Size); return make_pair(move(result), move(resultIt)); } }; // vector template std::uint32_t sizeInByteStream(const std::vector& vec) { return detail::containerSizeInByteStream(vec); } template It toNetworkByteStream(const std::vector& vec, It out) { return detail::containerToNetworkByteStream(vec, std::move(out)); } template struct Deserialize> { template static std::pair, It> fromNetworkByteStream( It bytesBegin, It bytesEnd) { using namespace std; vector 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(move(bytesBegin), move(bytesEnd), back_inserter(result), static_cast(distance(bytesBegin, bytesEnd))); return make_pair(move(result), move(resultIt)); } }; // 3-tuple template std::uint32_t sizeInByteStream(const std::tuple& tup) { return sizeInByteStream(std::get<0>(tup)) + sizeInByteStream(std::get<1>(tup)) + sizeInByteStream(std::get<2>(tup)); } template It toNetworkByteStream(const std::tuple& tup, It out) { return toNetworkByteStream( std::get<2>(tup), toNetworkByteStream(std::get<1>(tup), toNetworkByteStream(std::get<0>(tup), std::move(out)))); } template struct Deserialize> { template static std::pair, It> fromNetworkByteStream(It begin, It end) { using namespace std; auto xres = Deserialize::fromNetworkByteStream(begin, end); auto yres = Deserialize::fromNetworkByteStream(xres.second, end); auto zres = Deserialize::fromNetworkByteStream(yres.second, end); return make_pair( std::tuple{move(xres.first), move(yres.first), move(zres.first)}, move(zres.second)); } }; } // namespace discovery } // namespace ableton