OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
encoding.hpp
1#pragma once
2#include <algorithm>
3#include <cstddef>
4#include <cstdint>
5#include <cstring>
6#include <vector>
7
8namespace ossia::net
9{
10enum class encoding : uint8_t
11{
12 none,
13 base64,
14 ascii85,
15 hex,
16 intel_hex,
17 srec
18};
19
20// Upper-bound output sizes for pre-allocation
21inline std::size_t max_encoded_size(encoding enc, std::size_t sz)
22{
23 switch(enc)
24 {
25 case encoding::base64:
26 return 4 * ((sz + 2) / 3);
27 case encoding::ascii85:
28 return sz * 5 / 4 + 5;
29 case encoding::hex:
30 return sz * 2;
31 case encoding::intel_hex:
32 // per 16-byte line: ':' + LL(2) + AAAA(4) + TT(2) + data(32) + CC(2) + \r\n = 45
33 return ((sz + 15) / 16) * 45 + 13;
34 case encoding::srec:
35 // per 16-byte line: S1(2) + LL(2) + AAAA(4) + data(32) + CC(2) + \r\n = 44
36 return ((sz + 15) / 16) * 44 + 12;
37 default:
38 return sz;
39 }
40}
41
42inline std::size_t max_decoded_size(encoding enc, std::size_t sz)
43{
44 switch(enc)
45 {
46 case encoding::base64:
47 return 3 * sz / 4 + 3;
48 case encoding::ascii85:
49 // Not bounded by 4/5 of the input: the 'z' shorthand decodes one
50 // character into four zero bytes, so an all-'z' input - what a run of
51 // zeroes encodes to - expands fourfold. 4 * sz is the tight bound.
52 return sz * 4;
53 case encoding::hex:
54 case encoding::intel_hex:
55 case encoding::srec:
56 return sz / 2 + 1;
57 default:
58 return sz;
59 }
60}
61
62namespace detail
63{
64
65inline uint8_t hex_val(char c)
66{
67 if(c >= '0' && c <= '9')
68 return c - '0';
69 if(c >= 'A' && c <= 'F')
70 return c - 'A' + 10;
71 if(c >= 'a' && c <= 'f')
72 return c - 'a' + 10;
73 return 0;
74}
75
76inline uint8_t hex_pair(const char* p) { return (hex_val(p[0]) << 4) | hex_val(p[1]); }
77
78inline char* hex_push(char* p, uint8_t b)
79{
80 static constexpr char H[] = "0123456789ABCDEF";
81 *p++ = H[b >> 4];
82 *p++ = H[b & 0x0F];
83 return p;
84}
85
86// --- Base64 (RFC 4648) ---
87
88inline std::size_t base64_encode(const char* data, std::size_t sz, char* out)
89{
90 static constexpr char T[]
91 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
92 char* p = out;
93 for(std::size_t i = 0; i < sz; i += 3)
94 {
95 uint32_t n = uint8_t(data[i]) << 16;
96 if(i + 1 < sz)
97 n |= uint8_t(data[i + 1]) << 8;
98 if(i + 2 < sz)
99 n |= uint8_t(data[i + 2]);
100 *p++ = T[(n >> 18) & 0x3F];
101 *p++ = T[(n >> 12) & 0x3F];
102 *p++ = i + 1 < sz ? T[(n >> 6) & 0x3F] : '=';
103 *p++ = i + 2 < sz ? T[n & 0x3F] : '=';
104 }
105 return std::size_t(p - out);
106}
107
108inline std::size_t base64_decode(const char* data, std::size_t sz, char* out)
109{
110 auto val = [](uint8_t c) -> uint8_t {
111 if(c >= 'A' && c <= 'Z')
112 return c - 'A';
113 if(c >= 'a' && c <= 'z')
114 return c - 'a' + 26;
115 if(c >= '0' && c <= '9')
116 return c - '0' + 52;
117 if(c == '+')
118 return 62;
119 if(c == '/')
120 return 63;
121 return 0xFF;
122 };
123 char* p = out;
124 uint32_t buf = 0;
125 int bits = 0;
126 for(std::size_t i = 0; i < sz; i++)
127 {
128 uint8_t v = val(uint8_t(data[i]));
129 if(v == 0xFF)
130 continue;
131 buf = (buf << 6) | v;
132 bits += 6;
133 if(bits >= 8)
134 {
135 bits -= 8;
136 *p++ = char((buf >> bits) & 0xFF);
137 }
138 }
139 return std::size_t(p - out);
140}
141
142// --- Ascii85 ---
143
144inline std::size_t ascii85_encode(const char* data, std::size_t sz, char* out)
145{
146 char* p = out;
147 std::size_t i = 0;
148 for(; i + 3 < sz; i += 4)
149 {
150 uint32_t n = (uint8_t(data[i]) << 24) | (uint8_t(data[i + 1]) << 16)
151 | (uint8_t(data[i + 2]) << 8) | uint8_t(data[i + 3]);
152 if(n == 0)
153 {
154 *p++ = 'z';
155 }
156 else
157 {
158 char c[5];
159 for(int j = 4; j >= 0; j--)
160 {
161 c[j] = char('!' + n % 85);
162 n /= 85;
163 }
164 std::memcpy(p, c, 5);
165 p += 5;
166 }
167 }
168 if(auto rem = sz - i; rem > 0)
169 {
170 uint32_t n = 0;
171 for(std::size_t j = 0; j < rem; j++)
172 n |= uint8_t(data[i + j]) << (24 - 8 * j);
173 char c[5];
174 for(int j = 4; j >= 0; j--)
175 {
176 c[j] = char('!' + n % 85);
177 n /= 85;
178 }
179 std::memcpy(p, c, rem + 1);
180 p += rem + 1;
181 }
182 return std::size_t(p - out);
183}
184
185inline std::size_t ascii85_decode(const char* data, std::size_t sz, char* out)
186{
187 char* p = out;
188 std::size_t i = 0;
189 while(i < sz)
190 {
191 if(data[i] == 'z')
192 {
193 *p++ = 0;
194 *p++ = 0;
195 *p++ = 0;
196 *p++ = 0;
197 i++;
198 continue;
199 }
200 uint32_t n = 0;
201 int count = 0;
202 while(i < sz && count < 5 && data[i] != 'z')
203 {
204 if(data[i] >= '!' && data[i] <= 'u')
205 {
206 n = n * 85 + uint32_t(data[i] - '!');
207 count++;
208 }
209 i++;
210 }
211 if(count == 5)
212 {
213 *p++ = char((n >> 24) & 0xFF);
214 *p++ = char((n >> 16) & 0xFF);
215 *p++ = char((n >> 8) & 0xFF);
216 *p++ = char(n & 0xFF);
217 }
218 else if(count > 1)
219 {
220 for(int j = count; j < 5; j++)
221 n = n * 85 + 84;
222 for(int j = 0; j < count - 1; j++)
223 *p++ = char((n >> (24 - 8 * j)) & 0xFF);
224 }
225 }
226 return std::size_t(p - out);
227}
228
229// --- Hex string ---
230
231inline std::size_t hex_encode(const char* data, std::size_t sz, char* out)
232{
233 char* p = out;
234 for(std::size_t i = 0; i < sz; i++)
235 p = hex_push(p, uint8_t(data[i]));
236 return std::size_t(p - out);
237}
238
239inline std::size_t hex_decode(const char* data, std::size_t sz, char* out)
240{
241 char* p = out;
242 std::size_t i = 0;
243 while(i + 1 < sz)
244 {
245 while(i < sz
246 && (data[i] == ' ' || data[i] == ':' || data[i] == '-' || data[i] == '\r'
247 || data[i] == '\n'))
248 i++;
249 if(i + 1 >= sz)
250 break;
251 *p++ = char(hex_pair(data + i));
252 i += 2;
253 }
254 return std::size_t(p - out);
255}
256
257// --- Intel HEX ---
258
259inline std::size_t ihex_encode(const char* data, std::size_t sz, char* out)
260{
261 char* p = out;
262 uint16_t addr = 0;
263 std::size_t i = 0;
264 while(i < sz)
265 {
266 auto chunk = std::min<std::size_t>(sz - i, 16);
267 uint8_t sum = 0;
268 *p++ = ':';
269 auto bc = uint8_t(chunk);
270 p = hex_push(p, bc);
271 sum += bc;
272 p = hex_push(p, uint8_t(addr >> 8));
273 sum += uint8_t(addr >> 8);
274 p = hex_push(p, uint8_t(addr));
275 sum += uint8_t(addr);
276 p = hex_push(p, 0x00);
277 for(std::size_t j = 0; j < chunk; j++)
278 {
279 auto b = uint8_t(data[i + j]);
280 p = hex_push(p, b);
281 sum += b;
282 }
283 p = hex_push(p, uint8_t(-sum));
284 *p++ = '\r';
285 *p++ = '\n';
286 i += chunk;
287 addr += uint16_t(chunk);
288 }
289 static constexpr char eof[] = ":00000001FF\r\n";
290 std::memcpy(p, eof, 13);
291 p += 13;
292 return std::size_t(p - out);
293}
294
295inline std::size_t ihex_decode(const char* data, std::size_t sz, char* out)
296{
297 char* op = out;
298 const char* p = data;
299 const char* end = data + sz;
300 while(p < end)
301 {
302 while(p < end && *p != ':')
303 p++;
304 if(p >= end)
305 break;
306 p++;
307 if(p + 8 > end)
308 break;
309 auto bc = hex_pair(p);
310 p += 2;
311 p += 4; // address
312 auto type = hex_pair(p);
313 p += 2;
314 if(type == 0x01)
315 break;
316 if(type == 0x00 && p + bc * 2 <= end)
317 {
318 for(int j = 0; j < bc; j++)
319 {
320 *op++ = char(hex_pair(p));
321 p += 2;
322 }
323 }
324 else
325 {
326 p += bc * 2;
327 }
328 if(p + 2 <= end)
329 p += 2; // checksum
330 while(p < end && (*p == '\r' || *p == '\n'))
331 p++;
332 }
333 return std::size_t(op - out);
334}
335
336// --- Motorola S-Record ---
337
338inline std::size_t srec_encode(const char* data, std::size_t sz, char* out)
339{
340 char* p = out;
341 uint16_t addr = 0;
342 std::size_t i = 0;
343 while(i < sz)
344 {
345 auto chunk = std::min<std::size_t>(sz - i, 16);
346 uint8_t sum = 0;
347 *p++ = 'S';
348 *p++ = '1';
349 auto bc = uint8_t(2 + chunk + 1); // address(2) + data + checksum(1)
350 p = hex_push(p, bc);
351 sum += bc;
352 p = hex_push(p, uint8_t(addr >> 8));
353 sum += uint8_t(addr >> 8);
354 p = hex_push(p, uint8_t(addr));
355 sum += uint8_t(addr);
356 for(std::size_t j = 0; j < chunk; j++)
357 {
358 auto b = uint8_t(data[i + j]);
359 p = hex_push(p, b);
360 sum += b;
361 }
362 p = hex_push(p, uint8_t(~sum));
363 *p++ = '\r';
364 *p++ = '\n';
365 i += chunk;
366 addr += uint16_t(chunk);
367 }
368 static constexpr char eof[] = "S9030000FC\r\n";
369 std::memcpy(p, eof, 12);
370 p += 12;
371 return std::size_t(p - out);
372}
373
374inline std::size_t srec_decode(const char* data, std::size_t sz, char* out)
375{
376 char* op = out;
377 const char* p = data;
378 const char* end = data + sz;
379 while(p < end)
380 {
381 while(p < end && *p != 'S')
382 p++;
383 if(p + 1 >= end)
384 break;
385 p++;
386 char type = *p;
387 p++;
388 if(type == '9' || type == '8' || type == '7')
389 break;
390 if(p + 2 > end)
391 break;
392 auto bc = hex_pair(p);
393 p += 2;
394 int ab = (type == '0' || type == '1') ? 2
395 : (type == '2') ? 3
396 : (type == '3') ? 4
397 : 2;
398 if(p + ab * 2 > end)
399 break;
400 p += ab * 2;
401 int db = bc - ab - 1;
402 if(type >= '1' && type <= '3' && db > 0 && p + db * 2 <= end)
403 {
404 for(int j = 0; j < db; j++)
405 {
406 *op++ = char(hex_pair(p));
407 p += 2;
408 }
409 }
410 else if(db > 0)
411 {
412 p += db * 2;
413 }
414 if(p + 2 <= end)
415 p += 2; // checksum
416 while(p < end && (*p == '\r' || *p == '\n'))
417 p++;
418 }
419 return std::size_t(op - out);
420}
421
422} // namespace detail
423
424// Write encoded data directly into caller-provided buffer.
425// Buffer must have at least max_encoded_size() bytes.
426// Returns actual bytes written.
427inline std::size_t
428encode_to(encoding enc, const char* data, std::size_t sz, char* out)
429{
430 switch(enc)
431 {
432 case encoding::base64:
433 return detail::base64_encode(data, sz, out);
434 case encoding::ascii85:
435 return detail::ascii85_encode(data, sz, out);
436 case encoding::hex:
437 return detail::hex_encode(data, sz, out);
438 case encoding::intel_hex:
439 return detail::ihex_encode(data, sz, out);
440 case encoding::srec:
441 return detail::srec_encode(data, sz, out);
442 default:
443 std::memcpy(out, data, sz);
444 return sz;
445 }
446}
447
448// Write decoded data directly into caller-provided buffer.
449// Buffer must have at least max_decoded_size() bytes.
450// Returns actual bytes written.
451inline std::size_t
452decode_to(encoding enc, const char* data, std::size_t sz, char* out)
453{
454 switch(enc)
455 {
456 case encoding::base64:
457 return detail::base64_decode(data, sz, out);
458 case encoding::ascii85:
459 return detail::ascii85_decode(data, sz, out);
460 case encoding::hex:
461 return detail::hex_decode(data, sz, out);
462 case encoding::intel_hex:
463 return detail::ihex_decode(data, sz, out);
464 case encoding::srec:
465 return detail::srec_decode(data, sz, out);
466 default:
467 std::memcpy(out, data, sz);
468 return sz;
469 }
470}
471
472// Convenience: allocating versions
473inline std::vector<char> encode_bytes(encoding enc, const char* data, std::size_t sz)
474{
475 if(enc == encoding::none)
476 return {data, data + sz};
477 std::vector<char> out(max_encoded_size(enc, sz));
478 auto actual = encode_to(enc, data, sz, out.data());
479 out.resize(actual);
480 return out;
481}
482
483inline std::vector<char> decode_bytes(encoding enc, const char* data, std::size_t sz)
484{
485 if(enc == encoding::none)
486 return {data, data + sz};
487 std::vector<char> out(max_decoded_size(enc, sz));
488 auto actual = decode_to(enc, data, sz, out.data());
489 out.resize(actual);
490 return out;
491}
492
493} // namespace ossia::net