|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- /* 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
|