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