HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
BON8.hpp
1// Copyright Take Vos 2020-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#include "../byte_string.hpp"
6#include "../required.hpp"
7#include "../datum.hpp"
8#include "../exception.hpp"
9#include "../cast.hpp"
10#include <cstddef>
11
12#pragma once
13
14namespace tt {
15namespace detail {
16constexpr auto BON8_code_float_min_one = uint8_t{0xba};
17constexpr auto BON8_code_float_zero = uint8_t{0xbb};
18constexpr auto BON8_code_float_one = uint8_t{0xbc};
19constexpr auto BON8_code_array_empty = uint8_t{0xbd};
20constexpr auto BON8_code_object_empty = uint8_t{0xbe};
21constexpr auto BON8_code_null = uint8_t{0xbf};
22constexpr auto BON8_code_bool_false = uint8_t{0xc0};
23constexpr auto BON8_code_bool_true = uint8_t{0xc1};
24constexpr auto BON8_code_int32 = uint8_t{0xf8};
25constexpr auto BON8_code_int64 = uint8_t{0xf9};
26constexpr auto BON8_code_binary32 = uint8_t{0xfa};
27constexpr auto BON8_code_binary64 = uint8_t{0xfb};
28constexpr auto BON8_code_array = uint8_t{0xfc};
29constexpr auto BON8_code_object = uint8_t{0xfd};
30constexpr auto BON8_code_eoc = uint8_t{0xfe};
31constexpr auto BON8_code_eot = uint8_t{0xff};
32
39[[nodiscard]] datum decode_BON8(cbyteptr &ptr, cbyteptr last);
40
41[[nodiscard]] bstring encode_BON8(datum const &value);
42
43
47 bool open_string;
48 bstring output;
49
50public:
51 BON8_encoder() noexcept :
52 open_string(false), output() {}
53
56 bstring const &get() const noexcept {
57 return output;
58 }
59
63 void add(signed long long value) noexcept {
64 open_string = false;
65
67 output += static_cast<std::byte>(BON8_code_int64);
68 for (int i = 0; i != 8; ++i) {
69 output += static_cast<std::byte>(value >> (56 - i*8));
70 }
71
72 } else if (value < -33554432) {
73 output += static_cast<std::byte>(BON8_code_int32);
74 for (int i = 0; i != 4; ++i) {
75 output += static_cast<std::byte>(value >> (24 - i*8));
76 }
77
78 } else if (value < -262144) {
79 value = -value - 1;
80 output += static_cast<std::byte>(0xf0 + (value >> 22 & 0x07));
81 output += static_cast<std::byte>(0xc0 + (value >> 16 & 0x3f));
82 output += static_cast<std::byte>(value >> 8);
83 output += static_cast<std::byte>(value);
84
85 } else if (value < -1920) {
86 value = -value - 1;
87 output += static_cast<std::byte>(0xe0 + (value >> 14 & 0x0f));
88 output += static_cast<std::byte>(0xc0 + (value >> 8 & 0x3f));
89 output += static_cast<std::byte>(value);
90
91 } else if (value < -10) {
92 value = -value - 1;
93 output += static_cast<std::byte>(0xc2 + (value >> 6 & 0x1f));
94 output += static_cast<std::byte>(0xc0 + (value & 0x3f));
95
96 } else if (value < 0) {
97 value = -value - 1;
98 output += static_cast<std::byte>(0xb0 + value);
99
100 } else if (value <= 47) {
101 output += static_cast<std::byte>(0x80 + value);
102
103 } else if (value <= 3839) {
104 output += static_cast<std::byte>(0xc2 + (value >> 7 & 0x1f));
105 output += static_cast<std::byte>(value & 0x7f);
106
107 } else if (value <= 524287) {
108 output += static_cast<std::byte>(0xe0 + (value >> 15 & 0x0f));
109 output += static_cast<std::byte>(value >> 8 & 0x7f);
110 output += static_cast<std::byte>(value);
111
112 } else if (value <= 67108863) {
113 output += static_cast<std::byte>(0xf0 + (value >> 23 & 0x17));
114 output += static_cast<std::byte>(value >> 16 & 0x7f);
115 output += static_cast<std::byte>(value >> 8);
116 output += static_cast<std::byte>(value);
117
118 } else if (value <= std::numeric_limits<int32_t>::max()) {
119 output += static_cast<std::byte>(BON8_code_int32);
120 for (int i = 0; i != 4; ++i) {
121 output += static_cast<std::byte>(value >> (24 - i*8));
122 }
123
124 } else {
125 output += static_cast<std::byte>(BON8_code_int64);
126 for (int i = 0; i != 8; ++i) {
127 output += static_cast<std::byte>(value >> (56 - i*8));
128 }
129 }
130 }
131
135 void add(unsigned long long value) noexcept {
136 return add(narrow_cast<signed long long>(value));
137 }
138
142 void add(signed long value) noexcept {
143 return add(narrow_cast<signed long long>(value));
144 }
145
149 void add(unsigned long value) noexcept {
150 return add(narrow_cast<signed long long>(value));
151 }
152
156 void add(signed int value) noexcept {
157 return add(narrow_cast<signed long long>(value));
158 }
159
163 void add(unsigned int value) noexcept {
164 return add(narrow_cast<signed long long>(value));
165 }
166
170 void add(signed short value) noexcept {
171 return add(narrow_cast<signed long long>(value));
172 }
173
177 void add(unsigned short value) noexcept {
178 return add(narrow_cast<signed long long>(value));
179 }
180
184 void add(signed char value) noexcept {
185 return add(narrow_cast<signed long long>(value));
186 }
187
191 void add(unsigned char value) noexcept {
192 return add(narrow_cast<signed long long>(value));
193 }
194
198 void add(double value) noexcept {
199 open_string = false;
200
201 ttlet f32 = static_cast<float>(value);
202 ttlet f32_64 = static_cast<double>(f32);
203
204 if (value == -1.0) {
205 output += static_cast<std::byte>(BON8_code_float_min_one);
206
207 } else if (value == 0.0 || value == -0.0) {
208 output += static_cast<std::byte>(BON8_code_float_zero);
209
210 } else if (value == 1.0) {
211 output += static_cast<std::byte>(BON8_code_float_one);
212
213 } else if (f32_64 == value) {
214 uint32_t u32;
215 std::memcpy(&u32, &f32, sizeof(u32));
216
217 output += static_cast<std::byte>(BON8_code_binary32);
218 for (int i = 0; i != 4; ++i) {
219 output += static_cast<std::byte>(u32 >> (24 - i*8));
220 }
221
222 } else {
223 uint64_t u64;
224 std::memcpy(&u64, &value, sizeof(u64));
225
226 output += static_cast<std::byte>(BON8_code_binary64);
227 for (int i = 0; i != 8; ++i) {
228 output += static_cast<std::byte>(u64 >> (56 - i*8));
229 }
230 }
231 }
232
236 void add(float value) noexcept {
237 return add(narrow_cast<double>(value));
238 }
239
243 void add(bool value) noexcept {
244 open_string = false;
245 output += static_cast<std::byte>(value ? BON8_code_bool_true : BON8_code_bool_false);
246 }
247
251 void add(nullptr_t value) noexcept {
252 open_string = false;
253 output += static_cast<std::byte>(BON8_code_null);
254 }
255
261 void add(std::u8string_view value) noexcept {
262 if (open_string) {
263 output += static_cast<std::byte>(BON8_code_eot);
264 }
265
266 if (std::ssize(value) == 0) {
267 output += static_cast<std::byte>(BON8_code_eot);
268 open_string = false;
269
270 } else {
271 int multi_byte = 0;
272
273 for (ttlet _c : value) {
274 ttlet c = static_cast<uint8_t>(_c);
275
276 if constexpr (BuildType::current == BuildType::Debug) {
277 if (multi_byte == 0) {
278 if (c >= 0xc2 && c <= 0xdf) {
279 multi_byte = 1;
280 } else if (c >= 0xe0 && c <= 0xef) {
281 multi_byte = 2;
282 } else if (c >= 0xf0 && c <= 0xf7) {
283 multi_byte = 3;
284 } else {
285 tt_assert(c <= 0x7f);
286 }
287
288 } else {
289 tt_assert(c >= 0x80 && c <= 0xbf);
290 --multi_byte;
291 }
292 }
293
294 output += static_cast<std::byte>(c);
295 }
296
297 tt_assert(multi_byte == 0);
298
299 open_string = true;
300 }
301 }
302
308 void add(std::u8string const &value) noexcept {
309 return add(std::u8string_view{value});
310 }
311
317 void add(char8_t const *value) noexcept {
318 return add(std::u8string_view{value});
319 }
320
324 void add(datum const &value);
325
330 template<typename T>
331 void add(std::vector<T> const &items) {
332 open_string = false;
333 if (std::ssize(items) == 0) {
334 output += static_cast<std::byte>(BON8_code_array_empty);
335 } else {
336 output += static_cast<std::byte>(BON8_code_array);
337
338 for (ttlet &item: items) {
339 add(item);
340 }
341
342 output += static_cast<std::byte>(BON8_code_eoc);
343 }
344 open_string = false;
345 }
346
352 template<typename Key, typename Value>
353 void add(std::map<Key,Value> const &items) {
354 using item_type = typename std::map<Key,Value>::value_type;
355
356 open_string = false;
357 if (std::ssize(items) == 0) {
358 output += static_cast<std::byte>(BON8_code_object_empty);
359 } else {
360 // Keys must be ordered lexically.
361 auto sorted_items = std::vector<std::reference_wrapper<item_type>>{items.begin(), items.end()};
362 std::sort(sorted_items.begin(), sorted_items.end(), [](item_type const &a, item_type const &b) {
363 return
364 static_cast<std::u8string_view>(a.first) <
365 static_cast<std::u8string_view>(b.first);
366 });
367
368 output += static_cast<std::byte>(BON8_code_object);
369 for (item_type const &item: sorted_items) {
370 add(static_cast<std::u8string_view>(item.first));
371 add(item.second);
372 }
373 output += static_cast<std::byte>(BON8_code_eoc);
374 }
375 open_string = false;
376 }
377};
378
379void BON8_encoder::add(datum const &value) {
380 if (value.is_string() || value.is_url()) {
381 add(static_cast<std::string>(value));
382 } else if (value.is_bool()) {
383 add(static_cast<bool>(value));
384 } else if (value.is_null()) {
385 add(nullptr);
386 } else if (value.is_integer()) {
387 add(static_cast<signed long long>(value));
388 } else if (value.is_float()) {
389 add(static_cast<double>(value));
390 } else if (value.is_vector()) {
391 add(static_cast<datum::vector>(value));
392 } else if (value.is_map()) {
393 add(static_cast<datum::map>(value));
394 } else {
395 throw operation_error("Datum value can not be encoded to BON8");
396 }
397}
398
407[[nodiscard]] int BON8_multibyte_count(cbyteptr ptr, cbyteptr last) {
408 ttlet c0 = static_cast<uint8_t>(*ptr);
409 int count =
410 c0 <= 0xdf ? 2 :
411 c0 <= 0xef ? 3 :
412 4;
413
414 tt_parse_check(ptr + count <= last, "Incomplete Multi-byte character at end of buffer");
415
416 ttlet c1 = static_cast<uint8_t>(*(ptr + 1));
417 return (c1 < 0x80 || c1 > 0xbf) ? -count : count;
418}
419
428[[nodiscard]] datum decode_BON8_int(cbyteptr &ptr, cbyteptr last, int count)
429{
430 tt_axiom(count == 4 || count == 8);
431
432 auto u64 = uint64_t{0};
433 for (int i = 0; i != count; ++i) {
434 tt_parse_check(ptr != last, "Incomplete signed integer at end of buffer");
435 u64 <<= 8;
436 u64 |= static_cast<uint64_t>(*(ptr++));
437 }
438
439 if (count == 4) {
440 ttlet u32 = static_cast<uint32_t>(u64);
441 ttlet i32 = static_cast<int32_t>(u32);
442 return datum{i32};
443 } else {
444 ttlet i64 = static_cast<int64_t>(u64);
445 return datum{i64};
446 }
447}
448
449[[nodiscard]] datum decode_BON8_float(cbyteptr &ptr, cbyteptr last, int count)
450{
451 tt_axiom(count == 4 || count == 8);
452
453 auto u64 = uint64_t{0};
454 for (int i = 0; i != count; ++i) {
455 tt_parse_check(ptr != last, "Incomplete signed integer at end of buffer");
456 u64 <<= 8;
457 u64 |= static_cast<uint64_t>(*(ptr++));
458 }
459
460 if (count == 4) {
461 ttlet u32 = static_cast<uint32_t>(u64);
462 float f32;
463 std::memcpy(&f32, &u32, sizeof(f32));
464 return datum{f32};
465
466 } else {
467 double f64;
468 std::memcpy(&f64, &u64, sizeof(f64));
469 return datum{f64};
470 }
471}
472
473[[nodiscard]] datum decode_BON8_array(cbyteptr &ptr, cbyteptr last)
474{
475 auto r = datum::vector{};
476
477 while (ptr != last) {
478 if (*ptr == static_cast<std::byte>(BON8_code_eoc)) {
479 ++ptr;
480 return datum{std::move(r)};
481
482 } else {
483 r.push_back(decode_BON8(ptr, last));
484 }
485 }
486 throw parse_error("Incomplete array at end of buffer");
487}
488
489[[nodiscard]] datum decode_BON8_object(cbyteptr &ptr, cbyteptr last)
490{
491 auto r = datum::map{};
492
493 while (ptr != last) {
494 if (*ptr == static_cast<std::byte>(BON8_code_eoc)) {
495 ++ptr;
496 return datum{std::move(r)};
497
498 } else {
499 auto key = decode_BON8(ptr, last);
500 tt_parse_check(key.is_string(), "Key in object is not a string");
501
502 auto value = decode_BON8(ptr, last);
503 r.emplace(std::move(key), std::move(value));
504 }
505 }
506 throw parse_error("Incomplete array at end of buffer");
507}
508
509[[nodiscard]] datum decode_BON8_UTF8_like_int(cbyteptr &ptr, cbyteptr last, int count) noexcept
510{
511 tt_axiom(count >= 2 && count <= 4);
512 tt_axiom(ptr != last);
513 ttlet c0 = static_cast<uint8_t>(*(ptr++));
514
515 ttlet mask = int{0b0111'1111} >> count;
516 auto value = static_cast<int>(c0) & mask;
517 if (count == 2) {
518 value -= 2;
519 }
520
521 tt_axiom(ptr != last);
522 ttlet c1 = static_cast<uint8_t>(*(ptr++));
523 ttlet is_positive = c1 <= 0x7f;
524 if (is_positive) {
525 value <<= 7;
526 value |= static_cast<int>(c1);
527 } else {
528 value <<= 6;
529 value |= static_cast<int>(c1 & 0b0011'11111);
530 }
531
532 switch (count) {
533 case 4:
534 tt_axiom(ptr != last);
535 value <<= 8;
536 value |= static_cast<int>(*(ptr++));
537 [[fallthrough]];
538 case 3:
539 tt_axiom(ptr != last);
540 value <<= 8;
541 value |= static_cast<int>(*(ptr++));
542 [[fallthrough]];
543 default:;
544 }
545
546 return datum{is_positive ? value : -value};
547}
548
549[[nodiscard]] datum decode_BON8(cbyteptr &ptr, cbyteptr last) {
550 std::string str;
551
552 while (ptr != last) {
553 ttlet c = static_cast<uint8_t>(*ptr);
554
555 if (c == BON8_code_eot) {
556 // End of string found, return the current string.
557 ++ptr;
558 return datum{str};
559
560 } else if (c <= 0x7f) {
561 // ASCII character.
562 str += static_cast<char>(*(ptr++));
563 continue;
564
565 } else if (c >= 0xc2 && c <= 0xf7) {
566 ttlet count = BON8_multibyte_count(ptr, last);
567 if (count > 0) {
568 // Multibyte UTF-8 character
569 for (int i = 0; i != count; ++i) {
570 str += static_cast<char>(*(ptr++));
571 }
572 continue;
573
574 } else if (std::ssize(str) != 0) {
575 // Multibyte integer found, but first return the current string.
576 return datum{str};
577
578 } else {
579 // Multibyte integer.
580 return decode_BON8_UTF8_like_int(ptr, last, -count);
581 }
582
583 } else if (std::ssize(str) != 0) {
584 // This must be a non-string type, but first return the current string.
585 return datum{str};
586
587 // Everything below this, are non-string types.
588 } else if (c <= 0xaf) {
589 // 1 byte positive integer
590 return datum{c - 0x80};
591
592 } else if (c <= 0xb9) {
593 // 1 byte negative integer
594 return datum{-static_cast<int>(c - 0xb0)};
595
596 } else {
597 // This is one of the non-string types.
598 switch (c) {
599 case BON8_code_null:
600 ++ptr;
601 return datum{datum::null{}};
602
603 case BON8_code_bool_false:
604 ++ptr;
605 return datum{false};
606
607 case BON8_code_bool_true:
608 ++ptr;
609 return datum{true};
610
611 case BON8_code_float_min_one:
612 ++ptr;
613 return datum{-1.0f};
614
615 case BON8_code_float_zero:
616 ++ptr;
617 return datum{-0.0f};
618
619 case BON8_code_float_one:
620 ++ptr;
621 return datum{1.0f};
622
623 case BON8_code_int32:
624 ++ptr;
625 return decode_BON8_int(ptr, last, 4);
626
627 case BON8_code_int64:
628 ++ptr;
629 return decode_BON8_int(ptr, last, 8);
630
631 case BON8_code_binary32:
632 ++ptr;
633 return decode_BON8_float(ptr, last, 4);
634
635 case BON8_code_binary64:
636 ++ptr;
637 return decode_BON8_float(ptr, last, 8);
638
639 case BON8_code_eoc:
640 throw parse_error("Unexpected end-of-container");
641
642 case BON8_code_array:
643 ++ptr;
644 return decode_BON8_array(ptr, last);
645
646 case BON8_code_object:
647 ++ptr;
648 return decode_BON8_object(ptr, last);
649
650 default:
651 tt_no_default();
652 }
653 }
654 }
655 throw parse_error("Unexpected end-of-buffer");
656}
657} // namespace detail
658
663[[nodiscard]] datum decode_BON8(std::span<const std::byte> buffer)
664{
665 auto *ptr = buffer.data();
666 auto *last = ptr + buffer.size();
667 return detail::decode_BON8(ptr, last);
668}
669
674[[nodiscard]] datum decode_BON8(bstring const &buffer)
675{
676 auto *ptr = buffer.data();
677 auto *last = ptr + buffer.size();
678 return detail::decode_BON8(ptr, last);
679}
680
685[[nodiscard]] datum decode_BON8(bstring_view buffer)
686{
687 auto *ptr = buffer.data();
688 auto *last = ptr + buffer.size();
689 return detail::decode_BON8(ptr, last);
690}
691
696[[nodiscard]] bstring encode_BON8(datum const &value)
697{
698 auto encoder = detail::BON8_encoder{};
699 encoder.add(value);
700 return encoder.get();
701}
702
703}
BON8 encoder.
Definition BON8.hpp:46
void add(unsigned short value) noexcept
And a unsigned integer.
Definition BON8.hpp:177
void add(unsigned long long value) noexcept
And a unsigned integer.
Definition BON8.hpp:135
void add(unsigned char value) noexcept
And a unsigned integer.
Definition BON8.hpp:191
void add(double value) noexcept
Add a floating point number.
Definition BON8.hpp:198
void add(std::vector< T > const &items)
Add a vector of values of the same type.
Definition BON8.hpp:331
void add(signed int value) noexcept
And a signed integer.
Definition BON8.hpp:156
void add(unsigned long value) noexcept
And a unsigned integer.
Definition BON8.hpp:149
void add(unsigned int value) noexcept
And a unsigned integer.
Definition BON8.hpp:163
void add(signed short value) noexcept
And a signed integer.
Definition BON8.hpp:170
bstring const & get() const noexcept
Return a byte_string of the encoded object.
Definition BON8.hpp:56
void add(char8_t const *value) noexcept
Add a UTF-8 string.
Definition BON8.hpp:317
void add(signed long value) noexcept
And a signed integer.
Definition BON8.hpp:142
void add(std::u8string_view value) noexcept
Add a UTF-8 string.
Definition BON8.hpp:261
void add(float value) noexcept
Add a floating point number.
Definition BON8.hpp:236
void add(std::u8string const &value) noexcept
Add a UTF-8 string.
Definition BON8.hpp:308
void add(signed char value) noexcept
And a signed integer.
Definition BON8.hpp:184
void add(bool value) noexcept
Add a boolean.
Definition BON8.hpp:243
void add(nullptr_t value) noexcept
Add a null.
Definition BON8.hpp:251
void add(signed long long value) noexcept
And a signed integer.
Definition BON8.hpp:63
void add(std::map< Key, Value > const &items)
Add a map of key/values pairs.
Definition BON8.hpp:353
Exception thrown during execution of a dynamic operation.
Definition exception.hpp:37
T begin(T... args)
T count(T... args)
T end(T... args)
T memcpy(T... args)
T move(T... args)
T sort(T... args)