1 /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
2  *
3  *  This program is free software: you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation, either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  *  If you would like to incorporate Link into a proprietary software application,
17  *  please contact <link-devs@ableton.com>.
18  */
19 
20 #pragma once
21 
22 #include <ableton/discovery/NetworkByteStreamSerializable.hpp>
23 #include <functional>
24 #include <unordered_map>
25 
26 namespace ableton
27 {
28 namespace discovery
29 {
30 
31 struct PayloadEntryHeader
32 {
33   using Key = std::uint32_t;
34   using Size = std::uint32_t;
35 
36   Key key;
37   Size size;
38 
sizeInByteStream(const PayloadEntryHeader & header)39   friend Size sizeInByteStream(const PayloadEntryHeader& header)
40   {
41     return sizeInByteStream(header.key) + sizeInByteStream(header.size);
42   }
43 
44   template <typename It>
toNetworkByteStream(const PayloadEntryHeader & header,It out)45   friend It toNetworkByteStream(const PayloadEntryHeader& header, It out)
46   {
47     return toNetworkByteStream(
48       header.size, toNetworkByteStream(header.key, std::move(out)));
49   }
50 
51   template <typename It>
fromNetworkByteStreamableton::discovery::PayloadEntryHeader52   static std::pair<PayloadEntryHeader, It> fromNetworkByteStream(It begin, const It end)
53   {
54     using namespace std;
55     Key key;
56     Size size;
57     tie(key, begin) = Deserialize<Key>::fromNetworkByteStream(begin, end);
58     tie(size, begin) = Deserialize<Size>::fromNetworkByteStream(begin, end);
59     return make_pair(PayloadEntryHeader{move(key), move(size)}, move(begin));
60   }
61 };
62 
63 template <typename EntryType>
64 struct PayloadEntry
65 {
PayloadEntryableton::discovery::PayloadEntry66   PayloadEntry(EntryType entryVal)
67     : value(std::move(entryVal))
68   {
69     header = {EntryType::key, sizeInByteStream(value)};
70   }
71 
72   PayloadEntryHeader header;
73   EntryType value;
74 
sizeInByteStream(const PayloadEntry & entry)75   friend std::uint32_t sizeInByteStream(const PayloadEntry& entry)
76   {
77     return sizeInByteStream(entry.header) + sizeInByteStream(entry.value);
78   }
79 
80   template <typename It>
toNetworkByteStream(const PayloadEntry & entry,It out)81   friend It toNetworkByteStream(const PayloadEntry& entry, It out)
82   {
83     return toNetworkByteStream(
84       entry.value, toNetworkByteStream(entry.header, std::move(out)));
85   }
86 };
87 
88 namespace detail
89 {
90 
91 template <typename It>
92 using HandlerMap =
93   std::unordered_map<typename PayloadEntryHeader::Key, std::function<void(It, It)>>;
94 
95 // Given an index of handlers and a byte range, parse the bytes as a
96 // sequence of payload entries and invoke the appropriate handler for
97 // each entry type. Entries that are encountered that do not have a
98 // corresponding handler in the map are ignored. Throws
99 // std::runtime_error if parsing fails for any entry. Note that if an
100 // exception is thrown, some of the handlers may have already been called.
101 template <typename It>
parseByteStream(HandlerMap<It> & map,It bsBegin,const It bsEnd)102 void parseByteStream(HandlerMap<It>& map, It bsBegin, const It bsEnd)
103 {
104   using namespace std;
105 
106   while (bsBegin < bsEnd)
107   {
108     // Try to parse an entry header at this location in the byte stream
109     PayloadEntryHeader header;
110     It valueBegin;
111     tie(header, valueBegin) =
112       Deserialize<PayloadEntryHeader>::fromNetworkByteStream(bsBegin, bsEnd);
113 
114     // Ensure that the reported size of the entry does not exceed the
115     // length of the byte stream
116     It valueEnd = valueBegin + header.size;
117     if (bsEnd < valueEnd)
118     {
119       throw range_error("Payload with incorrect size.");
120     }
121 
122     // The next entry will start at the end of this one
123     bsBegin = valueEnd;
124 
125     // Use the appropriate handler for this entry, if available
126     auto handlerIt = map.find(header.key);
127     if (handlerIt != end(map))
128     {
129       handlerIt->second(move(valueBegin), move(valueEnd));
130     }
131   }
132 }
133 
134 } // namespace detail
135 
136 
137 // Payload encoding
138 template <typename... Entries>
139 struct Payload;
140 
141 template <typename First, typename Rest>
142 struct Payload<First, Rest>
143 {
Payloadableton::discovery::Payload144   Payload(First first, Rest rest)
145     : mFirst(std::move(first))
146     , mRest(std::move(rest))
147   {
148   }
149 
Payloadableton::discovery::Payload150   Payload(PayloadEntry<First> first, Rest rest)
151     : mFirst(std::move(first))
152     , mRest(std::move(rest))
153   {
154   }
155 
156   template <typename RhsFirst, typename RhsRest>
157   using PayloadSum =
158     Payload<First, typename Rest::template PayloadSum<RhsFirst, RhsRest>>;
159 
160   // Concatenate payloads together into a single payload
161   template <typename RhsFirst, typename RhsRest>
operator +(Payload lhs,Payload<RhsFirst,RhsRest> rhs)162   friend PayloadSum<RhsFirst, RhsRest> operator+(
163     Payload lhs, Payload<RhsFirst, RhsRest> rhs)
164   {
165     return {std::move(lhs.mFirst), std::move(lhs.mRest) + std::move(rhs)};
166   }
167 
sizeInByteStream(const Payload & payload)168   friend std::size_t sizeInByteStream(const Payload& payload)
169   {
170     return sizeInByteStream(payload.mFirst) + sizeInByteStream(payload.mRest);
171   }
172 
173   template <typename It>
toNetworkByteStream(const Payload & payload,It streamIt)174   friend It toNetworkByteStream(const Payload& payload, It streamIt)
175   {
176     return toNetworkByteStream(
177       payload.mRest, toNetworkByteStream(payload.mFirst, std::move(streamIt)));
178   }
179 
180   PayloadEntry<First> mFirst;
181   Rest mRest;
182 };
183 
184 template <>
185 struct Payload<>
186 {
187   template <typename RhsFirst, typename RhsRest>
188   using PayloadSum = Payload<RhsFirst, RhsRest>;
189 
190   template <typename RhsFirst, typename RhsRest>
operator +(Payload,Payload<RhsFirst,RhsRest> rhs)191   friend PayloadSum<RhsFirst, RhsRest> operator+(Payload, Payload<RhsFirst, RhsRest> rhs)
192   {
193     return rhs;
194   }
195 
sizeInByteStream(const Payload &)196   friend std::size_t sizeInByteStream(const Payload&)
197   {
198     return 0;
199   }
200 
201   template <typename It>
toNetworkByteStream(const Payload &,It streamIt)202   friend It toNetworkByteStream(const Payload&, It streamIt)
203   {
204     return streamIt;
205   }
206 };
207 
208 template <typename... Entries>
209 struct PayloadBuilder;
210 
211 // Payload factory function
212 template <typename... Entries>
makePayload(Entries...entries)213 auto makePayload(Entries... entries)
214   -> decltype(PayloadBuilder<Entries...>{}(std::move(entries)...))
215 {
216   return PayloadBuilder<Entries...>{}(std::move(entries)...);
217 }
218 
219 template <typename First, typename... Rest>
220 struct PayloadBuilder<First, Rest...>
221 {
operator ()ableton::discovery::PayloadBuilder222   auto operator()(First first, Rest... rest)
223     -> Payload<First, decltype(makePayload(std::move(rest)...))>
224   {
225     return {std::move(first), makePayload(std::move(rest)...)};
226   }
227 };
228 
229 template <>
230 struct PayloadBuilder<>
231 {
operator ()ableton::discovery::PayloadBuilder232   Payload<> operator()()
233   {
234     return {};
235   }
236 };
237 
238 // Parse payloads to values
239 template <typename... Entries>
240 struct ParsePayload;
241 
242 template <typename First, typename... Rest>
243 struct ParsePayload<First, Rest...>
244 {
245   template <typename It, typename... Handlers>
parseableton::discovery::ParsePayload246   static void parse(It begin, It end, Handlers... handlers)
247   {
248     detail::HandlerMap<It> map;
249     collectHandlers(map, std::move(handlers)...);
250     detail::parseByteStream(map, std::move(begin), std::move(end));
251   }
252 
253   template <typename It, typename FirstHandler, typename... RestHandlers>
collectHandlersableton::discovery::ParsePayload254   static void collectHandlers(
255     detail::HandlerMap<It>& map, FirstHandler handler, RestHandlers... rest)
256   {
257     using namespace std;
258     map[First::key] = [handler](const It begin, const It end) {
259       const auto res = First::fromNetworkByteStream(begin, end);
260       if (res.second != end)
261       {
262         std::ostringstream stringStream;
263         stringStream << "Parsing payload entry " << First::key
264                      << " did not consume the expected number of bytes. "
265                      << " Expected: " << distance(begin, end)
266                      << ", Actual: " << distance(begin, res.second);
267         throw range_error(stringStream.str());
268       }
269       handler(res.first);
270     };
271 
272     ParsePayload<Rest...>::collectHandlers(map, std::move(rest)...);
273   }
274 };
275 
276 template <>
277 struct ParsePayload<>
278 {
279   template <typename It>
collectHandlersableton::discovery::ParsePayload280   static void collectHandlers(detail::HandlerMap<It>&)
281   {
282   }
283 };
284 
285 template <typename... Entries, typename It, typename... Handlers>
parsePayload(It begin,It end,Handlers...handlers)286 void parsePayload(It begin, It end, Handlers... handlers)
287 {
288   using namespace std;
289   ParsePayload<Entries...>::parse(move(begin), move(end), move(handlers)...);
290 }
291 
292 } // namespace discovery
293 } // namespace ableton
294