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