HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
datum.hpp
1// Copyright Take Vos 2019-2020.
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#pragma once
6
7#include "required.hpp"
8#include "URL.hpp"
9#include "decimal.hpp"
10#include "memory.hpp"
11#include "type_traits.hpp"
12#include "exception.hpp"
13#include "math.hpp"
14#include "algorithm.hpp"
15#include "byte_string.hpp"
16#include "codec/base_n.hpp"
17#include <chrono>
18#include <vector>
19#include <unordered_map>
20#include <memory>
21#include <cstring>
22#include <cstdint>
23#include <variant>
24#include <limits>
25#include <type_traits>
26#include <typeinfo>
27#include <ostream>
28#include <numeric>
29#include <string_view>
30#include <cmath>
31#include <stdexcept>
32
33namespace tt {
34template<bool HasLargeObjects>
35class datum_impl;
36
37}
38
39namespace std {
40
41template<bool HasLargeObjects>
42class hash<tt::datum_impl<HasLargeObjects>> {
43public:
44 size_t operator()(tt::datum_impl<HasLargeObjects> const &value) const;
45};
46
47} // namespace std
48
49namespace tt {
50
51enum class datum_type_t {
52 Null,
53 Undefined,
54 Break,
55 Continue,
56 Boolean,
57 Integer,
58 Decimal,
59 Float,
60 String,
61 URL,
62 Map,
63 Vector,
64 YearMonthDay,
65 Bytes,
66};
67
68constexpr char const *to_const_string(datum_type_t rhs) noexcept
69{
70 switch (rhs) {
71 case datum_type_t::Null: return "Null";
72 case datum_type_t::Break: return "Break";
73 case datum_type_t::Continue: return "Continue";
74 case datum_type_t::Undefined: return "Undefined";
75 case datum_type_t::Boolean: return "Boolean";
76 case datum_type_t::Integer: return "Integer";
77 case datum_type_t::Decimal: return "Decimal";
78 case datum_type_t::Float: return "Float";
79 case datum_type_t::String: return "String";
80 case datum_type_t::URL: return "URL";
81 case datum_type_t::Map: return "Map";
82 case datum_type_t::Vector: return "Vector";
83 case datum_type_t::YearMonthDay: return "YearMonthDay";
84 case datum_type_t::Bytes: return "Bytes";
85 default: tt_no_default();
86 }
87}
88
89inline std::ostream &operator<<(std::ostream &lhs, datum_type_t rhs)
90{
91 return lhs << to_const_string(rhs);
92}
93
94template<typename CharT>
95struct std::formatter<tt::datum_type_t, CharT> : std::formatter<char const *, CharT> {
96 auto format(tt::datum_type_t const &t, auto &fc)
97 {
98 return std::formatter<char const *, CharT>::format(tt::to_const_string(t), fc);
99 }
100};
101
123template<bool HasLargeObjects>
125private:
131 static constexpr uint64_t make_string(std::string_view str)
132 {
133 ttlet len = str.size();
134
135 if (len > 5) {
136 return 0;
137 }
138
139 uint64_t x = 0;
140 for (uint64_t i = 0; i < len; i++) {
141 x <<= 8;
142 x |= str[i];
143 }
144 return (string_mask + (len << 40)) | x;
145 }
146
153 static uint64_t make_pointer(uint64_t mask, void *ptr)
154 {
155 return mask | (reinterpret_cast<uint64_t>(ptr) & pointer_mask);
156 }
157
163 static constexpr uint64_t id_to_mask(uint64_t id)
164 {
165 return id << 48;
166 }
167
176 static constexpr uint16_t make_id(uint16_t id)
177 {
178 return ((id & 0x10) << 11) | (id & 0xf) | 0x7ff0;
179 }
180
182 static constexpr int64_t minimum_int = 0xffff'8000'0000'0000LL;
184 static constexpr int64_t maximum_int = 0x0000'7fff'ffff'ffffLL;
185
186 static constexpr int64_t minimum_mantissa = 0xffff'ff80'0000'0000LL;
187 static constexpr int64_t maximum_mantissa = 0x0000'007f'ffff'ffffLL;
188
189 static constexpr uint16_t exponent_mask = 0b0111'1111'1111'0000;
190 static constexpr uint64_t pointer_mask = 0x0000'ffff'ffff'ffff;
191
192 static constexpr uint64_t small_undefined = 0;
193 static constexpr uint64_t small_null = 1;
194 static constexpr uint64_t small_true = 2;
195 static constexpr uint64_t small_false = 3;
196 static constexpr uint64_t small_break = 4;
197 static constexpr uint64_t small_continue = 5;
198
199 static constexpr uint16_t phy_small_id = make_id(0b00001);
200 static constexpr uint16_t phy_decimal_id = make_id(0b00010);
201 static constexpr uint16_t phy_ymd_id = make_id(0b00011);
202 static constexpr uint16_t phy_integer_id = make_id(0b00100);
203 static constexpr uint16_t phy_string_id = make_id(0b00101);
204 static constexpr uint16_t phy_reserved_id0 = make_id(0b00110);
205 static constexpr uint16_t phy_reserved_id1 = make_id(0b00111);
206 static constexpr uint16_t phy_reserved_id2 = make_id(0b01000);
207 static constexpr uint16_t phy_reserved_id3 = make_id(0b01001);
208 static constexpr uint16_t phy_reserved_id4 = make_id(0b01010);
209 static constexpr uint16_t phy_reserved_id5 = make_id(0b01011);
210 static constexpr uint16_t phy_reserved_id6 = make_id(0b01100);
211 static constexpr uint16_t phy_reserved_id7 = make_id(0b01101);
212 static constexpr uint16_t phy_reserved_id8 = make_id(0b01110);
213 static constexpr uint16_t phy_reserved_id9 = make_id(0b01111);
214
215 static constexpr uint16_t phy_string_ptr_id = make_id(0b10001);
216 static constexpr uint16_t phy_url_ptr_id = make_id(0b10010);
217 static constexpr uint16_t phy_integer_ptr_id = make_id(0b10011);
218 static constexpr uint16_t phy_vector_ptr_id = make_id(0b10100);
219 static constexpr uint16_t phy_map_ptr_id = make_id(0b10101);
220 static constexpr uint16_t phy_decimal_ptr_id = make_id(0b10110);
221 static constexpr uint16_t phy_bytes_ptr_id = make_id(0b10111);
222 static constexpr uint16_t phy_reserved_ptr_id0 = make_id(0b11000);
223 static constexpr uint16_t phy_reserved_ptr_id1 = make_id(0b11001);
224 static constexpr uint16_t phy_reserved_ptr_id2 = make_id(0b11010);
225 static constexpr uint16_t phy_reserved_ptr_id3 = make_id(0b11011);
226 static constexpr uint16_t phy_reserved_ptr_id4 = make_id(0b11100);
227 static constexpr uint16_t phy_reserved_ptr_id5 = make_id(0b11101);
228 static constexpr uint16_t phy_reserved_ptr_id6 = make_id(0b11110);
229 static constexpr uint16_t phy_reserved_ptr_id7 = make_id(0b11111);
230
231 static constexpr uint64_t small_mask = id_to_mask(phy_small_id);
232 static constexpr uint64_t undefined_mask = small_mask | small_undefined;
233 static constexpr uint64_t null_mask = small_mask | small_null;
234 static constexpr uint64_t true_mask = small_mask | small_true;
235 static constexpr uint64_t false_mask = small_mask | small_false;
236 static constexpr uint64_t break_mask = small_mask | small_break;
237 static constexpr uint64_t continue_mask = small_mask | small_continue;
238 static constexpr uint64_t string_mask = id_to_mask(phy_string_id);
239 static constexpr uint64_t integer_mask = id_to_mask(phy_integer_id);
240 static constexpr uint64_t decimal_mask = id_to_mask(phy_decimal_id);
241 static constexpr uint64_t ymd_mask = id_to_mask(phy_ymd_id);
242 static constexpr uint64_t string_ptr_mask = id_to_mask(phy_string_ptr_id);
243 static constexpr uint64_t url_ptr_mask = id_to_mask(phy_url_ptr_id);
244 static constexpr uint64_t integer_ptr_mask = id_to_mask(phy_integer_ptr_id);
245 static constexpr uint64_t vector_ptr_mask = id_to_mask(phy_vector_ptr_id);
246 static constexpr uint64_t map_ptr_mask = id_to_mask(phy_map_ptr_id);
247 static constexpr uint64_t decimal_ptr_mask = id_to_mask(phy_decimal_ptr_id);
248 static constexpr uint64_t bytes_ptr_mask = id_to_mask(phy_bytes_ptr_id);
249
250 union {
251 double f64;
252 uint64_t u64;
253 };
254
263 uint16_t type_id() const noexcept
264 {
265 uint64_t data;
266 std::memcpy(&data, this, sizeof(data));
267 return static_cast<uint16_t>(data >> 48);
268 }
269
270 bool is_phy_float() const noexcept
271 {
272 ttlet id = type_id();
273 return (id & 0x7ff0) != 0x7ff0 || (id & 0x000f) == 0;
274 }
275
276 bool is_phy_integer() const noexcept
277 {
278 return type_id() == phy_integer_id;
279 }
280
281 bool is_phy_string() const noexcept
282 {
283 return type_id() == phy_string_id;
284 }
285
286 bool is_phy_decimal() const noexcept
287 {
288 return type_id() == phy_decimal_id;
289 }
290
291 bool is_phy_ymd() const noexcept
292 {
293 return type_id() == phy_ymd_id;
294 }
295
296 bool is_phy_small() const noexcept
297 {
298 return type_id() == phy_small_id;
299 }
300
301 bool is_phy_pointer() const noexcept
302 {
303 return HasLargeObjects && (type_id() & 0xfff0) == 0xfff0;
304 }
305
306 bool is_phy_string_ptr() const noexcept
307 {
308 return HasLargeObjects && type_id() == phy_string_ptr_id;
309 }
310
311 bool is_phy_url_ptr() const noexcept
312 {
313 return HasLargeObjects && type_id() == phy_url_ptr_id;
314 }
315
316 bool is_phy_integer_ptr() const noexcept
317 {
318 return HasLargeObjects && type_id() == phy_integer_ptr_id;
319 }
320
321 bool is_phy_vector_ptr() const noexcept
322 {
323 return HasLargeObjects && type_id() == phy_vector_ptr_id;
324 }
325
326 bool is_phy_map_ptr() const noexcept
327 {
328 return HasLargeObjects && type_id() == phy_map_ptr_id;
329 }
330
331 bool is_phy_decimal_ptr() const noexcept
332 {
333 return HasLargeObjects && type_id() == phy_decimal_ptr_id;
334 }
335
336 bool is_phy_bytes_ptr() const noexcept
337 {
338 return HasLargeObjects && type_id() == phy_bytes_ptr_id;
339 }
340
343 uint64_t get_unsigned_integer() const noexcept
344 {
345 return (u64 << 16) >> 16;
346 }
347
350 int64_t get_signed_integer() const noexcept
351 {
352 return static_cast<int64_t>(u64 << 16) >> 16;
353 }
354
359 template<typename O>
360 O *get_pointer() const
361 {
362 return std::launder(reinterpret_cast<O *>(get_signed_integer()));
363 }
364
368 void delete_pointer() noexcept
369 {
370 if constexpr (HasLargeObjects) {
371 switch (type_id()) {
372 case phy_integer_ptr_id: delete get_pointer<int64_t>(); break;
373 case phy_string_ptr_id: delete get_pointer<std::string>(); break;
374 case phy_url_ptr_id: delete get_pointer<URL>(); break;
375 case phy_vector_ptr_id: delete get_pointer<datum_impl::vector>(); break;
376 case phy_map_ptr_id: delete get_pointer<datum_impl::map>(); break;
377 case phy_decimal_ptr_id: delete get_pointer<decimal>(); break;
378 case phy_bytes_ptr_id: delete get_pointer<bstring>(); break;
379 default: tt_no_default();
380 }
381 }
382 }
383
389 void copy_pointer(datum_impl const &other) noexcept
390 {
391 if constexpr (HasLargeObjects) {
392 switch (other.type_id()) {
393 case phy_integer_ptr_id: {
394 auto *const p = new int64_t(*other.get_pointer<int64_t>());
395 u64 = make_pointer(integer_ptr_mask, p);
396 } break;
397
398 case phy_string_ptr_id: {
399 auto *const p = new std::string(*other.get_pointer<std::string>());
400 u64 = make_pointer(string_ptr_mask, p);
401 } break;
402
403 case phy_url_ptr_id: {
404 auto *const p = new URL(*other.get_pointer<URL>());
405 u64 = make_pointer(url_ptr_mask, p);
406 } break;
407
408 case phy_vector_ptr_id: {
409 auto *const p = new datum_impl::vector(*other.get_pointer<datum_impl::vector>());
410 u64 = make_pointer(vector_ptr_mask, p);
411 } break;
412
413 case phy_map_ptr_id: {
414 auto *const p = new datum_impl::map(*other.get_pointer<datum_impl::map>());
415 u64 = make_pointer(map_ptr_mask, p);
416 } break;
417
418 case phy_decimal_ptr_id: {
419 auto *const p = new decimal(*other.get_pointer<decimal>());
420 u64 = make_pointer(decimal_ptr_mask, p);
421 } break;
422
423 case phy_bytes_ptr_id: {
424 auto *const p = new bstring(*other.get_pointer<bstring>());
425 u64 = make_pointer(bytes_ptr_mask, p);
426 } break;
427
428 default: tt_no_default();
429 }
430 }
431 }
432
433public:
436 struct undefined {
437 };
438 struct null {
439 };
440 struct _continue {
441 };
442 struct _break {
443 };
444
445 datum_impl() noexcept : u64(undefined_mask) {}
446
447 ~datum_impl() noexcept
448 {
449 if (is_phy_pointer()) {
450 [[unlikely]] delete_pointer();
451 }
452 }
453
454 datum_impl(datum_impl const &other) noexcept
455 {
456 tt_axiom(&other != this);
457 if (other.is_phy_pointer()) {
458 [[unlikely]] copy_pointer(other);
459
460 } else {
461 // We do a memcpy, because we don't know the type in the union.
462 std::memcpy(this, &other, sizeof(*this));
463 }
464 }
465
466 datum_impl &operator=(datum_impl const &other) noexcept
467 {
468 tt_return_on_self_assignment(other);
469 if (is_phy_pointer()) {
470 [[unlikely]] delete_pointer();
471 }
472
473 if (other.is_phy_pointer()) {
474 [[unlikely]] copy_pointer(other);
475
476 } else {
477 // We do a memcpy, because we don't know the type in the union.
478 std::memcpy(this, &other, sizeof(*this));
479 }
480 return *this;
481 }
482
483 datum_impl(datum_impl &&other) noexcept : u64(undefined_mask)
484 {
485 tt_axiom(&other != this);
486 // We do a memcpy, because we don't know the type in the union.
487 std::memcpy(this, &other, sizeof(*this));
488 other.u64 = undefined_mask;
489 }
490
491 datum_impl &operator=(datum_impl &&other) noexcept
492 {
493 tt_return_on_self_assignment(other);
494 // We do a memcpy, because we don't know the type in the union.
495 std::memcpy(this, &other, sizeof(*this));
496 other.u64 = undefined_mask;
497 return *this;
498 }
499
500 datum_impl(datum_impl::undefined) noexcept : u64(undefined_mask) {}
501 datum_impl(datum_impl::null) noexcept : u64(null_mask) {}
502 datum_impl(datum_impl::_break) noexcept : u64(break_mask) {}
503 datum_impl(datum_impl::_continue) noexcept : u64(continue_mask) {}
504
505 datum_impl(double value) noexcept : f64(value)
506 {
507 if (value != value) {
508 u64 = undefined_mask;
509 }
510 }
511 datum_impl(float value) noexcept : datum_impl(static_cast<double>(value)) {}
512
513 datum_impl(decimal value) noexcept
514 {
515 long long m = value.mantissa();
516
517 if (m < minimum_mantissa || m > maximum_mantissa) {
518 [[unlikely]] if constexpr (HasLargeObjects)
519 {
520 auto *const p = new decimal(value);
521 u64 = make_pointer(decimal_ptr_mask, p);
522 }
523 else
524 {
525 throw std::overflow_error(std::format("Constructing decimal {} to datum", value));
526 }
527 } else {
528 int e = value.exponent();
529
530 u64 = decimal_mask | static_cast<uint8_t>(e) | ((static_cast<uint64_t>(m) << 24) >> 16);
531 }
532 }
533
534 datum_impl(std::chrono::year_month_day const &ymd) noexcept :
535 u64(ymd_mask |
536 (((static_cast<uint64_t>(static_cast<int>(ymd.year())) << 9) |
537 (static_cast<uint64_t>(static_cast<unsigned>(ymd.month())) << 5) |
538 static_cast<uint64_t>(static_cast<unsigned>(ymd.day()))) &
539 0x0000ffff'ffffffff))
540 {
541 }
542
543 datum_impl(unsigned long long value) noexcept : u64(integer_mask | value)
544 {
545 if (value > maximum_int) {
546 [[unlikely]] if constexpr (HasLargeObjects)
547 {
548 auto *const p = new uint64_t(value);
549 u64 = make_pointer(integer_ptr_mask, p);
550 }
551 else
552 {
553 throw std::overflow_error(std::format("Constructing datum from integer {}, larger than {}", value, maximum_int));
554 }
555 }
556 }
557 datum_impl(unsigned long value) noexcept : datum_impl(static_cast<unsigned long long>(value)) {}
558 datum_impl(unsigned int value) noexcept : datum_impl(static_cast<unsigned long long>(value)) {}
559 datum_impl(unsigned short value) noexcept : datum_impl(static_cast<unsigned long long>(value)) {}
560 datum_impl(unsigned char value) noexcept : datum_impl(static_cast<unsigned long long>(value)) {}
561
562 datum_impl(signed long long value) noexcept : u64(integer_mask | (static_cast<uint64_t>(value) & 0x0000ffff'ffffffff))
563 {
564 if (value < minimum_int || value > maximum_int)
565 [[unlikely]]
566 {
567 if constexpr (HasLargeObjects) {
568 auto *const p = new int64_t(value);
569 u64 = make_pointer(integer_ptr_mask, p);
570 } else {
571 throw std::overflow_error(std::format(
572 "Constructing integer {} to datum, outside {} and {}", value, minimum_int, maximum_int));
573 }
574 }
575 }
576 datum_impl(signed long value) noexcept : datum_impl(static_cast<signed long long>(value)) {}
577 datum_impl(signed int value) noexcept : datum_impl(static_cast<signed long long>(value)) {}
578 datum_impl(signed short value) noexcept : datum_impl(static_cast<signed long long>(value)) {}
579 datum_impl(signed char value) noexcept : datum_impl(static_cast<signed long long>(value)) {}
580
581 datum_impl(bool value) noexcept : u64(value ? true_mask : false_mask) {}
582 datum_impl(char value) noexcept : u64(string_mask | (uint64_t{1} << 40) | value) {}
583
584 datum_impl(std::string_view value) noexcept : u64(make_string(value))
585 {
586 if (u64 == 0) {
587 if constexpr (HasLargeObjects) {
588 auto *const p = new std::string(value);
589 u64 = make_pointer(string_ptr_mask, p);
590 } else {
591 throw std::overflow_error(std::format("Constructing string {} to datum, larger than 6 characters", value));
592 }
593 }
594 }
595
596 datum_impl(std::string const &value) noexcept : datum_impl(std::string_view(value)) {}
597 datum_impl(char const *value) noexcept : datum_impl(std::string_view(value)) {}
598
599 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
600 datum_impl(URL const &value) noexcept
601 {
602 auto *const p = new URL(value);
603 u64 = make_pointer(url_ptr_mask, p);
604 }
605
606 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
607 datum_impl(URL &&value) noexcept
608 {
609 auto *const p = new URL(std::move(value));
610 u64 = make_pointer(url_ptr_mask, p);
611 }
612
613 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
614 datum_impl(datum_impl::vector const &value) noexcept
615 {
616 auto *const p = new datum_impl::vector(value);
617 u64 = make_pointer(vector_ptr_mask, p);
618 }
619
620 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
621 datum_impl(datum_impl::vector &&value) noexcept
622 {
623 auto *const p = new datum_impl::vector(std::move(value));
624 u64 = make_pointer(vector_ptr_mask, p);
625 }
626
627 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
628 datum_impl(datum_impl::map const &value) noexcept
629 {
630 auto *const p = new datum_impl::map(value);
631 u64 = make_pointer(map_ptr_mask, p);
632 }
633
634 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
635 datum_impl(datum_impl::map &&value) noexcept
636 {
637 auto *const p = new datum_impl::map(std::move(value));
638 u64 = make_pointer(map_ptr_mask, p);
639 }
640
641 datum_impl &operator=(datum_impl::undefined rhs) noexcept
642 {
643 if (is_phy_pointer()) {
644 [[unlikely]] delete_pointer();
645 }
646 u64 = undefined_mask;
647 return *this;
648 }
649
650 datum_impl &operator=(datum_impl::null rhs) noexcept
651 {
652 if (is_phy_pointer()) {
653 [[unlikely]] delete_pointer();
654 }
655 u64 = null_mask;
656 return *this;
657 }
658
659 datum_impl &operator=(datum_impl::_break rhs) noexcept
660 {
661 if (is_phy_pointer()) {
662 [[unlikely]] delete_pointer();
663 }
664 u64 = break_mask;
665 return *this;
666 }
667
668 datum_impl &operator=(datum_impl::_continue rhs) noexcept
669 {
670 if (is_phy_pointer()) {
671 [[unlikely]] delete_pointer();
672 }
673 u64 = continue_mask;
674 return *this;
675 }
676
677 datum_impl &operator=(double rhs) noexcept
678 {
679 if (is_phy_pointer()) {
680 [[unlikely]] delete_pointer();
681 }
682
683 if (rhs == rhs) {
684 f64 = rhs;
685 } else {
686 u64 = undefined_mask;
687 }
688 return *this;
689 }
690 datum_impl &operator=(float rhs) noexcept
691 {
692 return *this = static_cast<double>(rhs);
693 }
694
695 datum_impl &operator=(decimal rhs) noexcept
696 {
697 if (is_phy_pointer()) {
698 [[unlikely]] delete_pointer();
699 }
700
701 long long m = rhs.mantissa();
702 if (m < minimum_mantissa || m > maximum_mantissa) {
703 [[unlikely]] if constexpr (HasLargeObjects)
704 {
705 auto *const p = new decimal(rhs);
706 u64 = make_pointer(decimal_ptr_mask, p);
707 }
708 else
709 {
710 throw std::overflow_error(std::format("Constructing decimal {} to datum", rhs));
711 }
712 } else {
713 int e = rhs.exponent();
714
715 u64 = decimal_mask | static_cast<uint8_t>(e) | ((static_cast<uint64_t>(m) << 24) >> 16);
716 }
717 return *this;
718 }
719
720 datum_impl &operator=(std::chrono::year_month_day const &ymd) noexcept
721 {
722 if (is_phy_pointer()) {
723 [[unlikely]] delete_pointer();
724 }
725
726 u64 = ymd_mask |
727 (((static_cast<uint64_t>(static_cast<int>(ymd.year())) << 9) |
728 (static_cast<uint64_t>(static_cast<unsigned>(ymd.month())) << 5) |
729 static_cast<uint64_t>(static_cast<unsigned>(ymd.day()))) &
730 0x0000ffff'ffffffff);
731 return *this;
732 }
733
734 datum_impl &operator=(unsigned long long rhs)
735 {
736 if (is_phy_pointer()) {
737 [[unlikely]] delete_pointer();
738 }
739
740 u64 = integer_mask | static_cast<uint64_t>(rhs);
741 if (rhs > maximum_int) {
742 [[unlikely]] if constexpr (HasLargeObjects)
743 {
744 auto *const p = new uint64_t(rhs);
745 u64 = make_pointer(integer_ptr_mask, p);
746 }
747 else
748 {
749 throw std::overflow_error(std::format("Assigning integer {} to datum, larger than {}", rhs, maximum_int));
750 }
751 }
752 return *this;
753 }
754 datum_impl &operator=(unsigned long rhs) noexcept
755 {
756 return *this = static_cast<unsigned long long>(rhs);
757 }
758 datum_impl &operator=(unsigned int rhs) noexcept
759 {
760 return *this = static_cast<unsigned long long>(rhs);
761 }
762 datum_impl &operator=(unsigned short rhs) noexcept
763 {
764 return *this = static_cast<unsigned long long>(rhs);
765 }
766 datum_impl &operator=(unsigned char rhs) noexcept
767 {
768 return *this = static_cast<unsigned long long>(rhs);
769 }
770
771 datum_impl &operator=(signed long long rhs) noexcept
772 {
773 if (is_phy_pointer()) {
774 [[unlikely]] delete_pointer();
775 }
776
777 u64 = integer_mask | (static_cast<uint64_t>(rhs) & 0x0000ffff'ffffffff);
778 if (rhs < minimum_int || rhs > maximum_int) {
779 [[unlikely]] if constexpr (HasLargeObjects)
780 {
781 auto *const p = new int64_t(rhs);
782 u64 = make_pointer(integer_ptr_mask, p);
783 }
784 else
785 {
786 throw std::overflow_error(std::format("Assigning integer {} to datum, outside {} and {}", rhs, minimum_int, maximum_int));
787 }
788 }
789
790 return *this;
791 }
792 datum_impl &operator=(signed long rhs) noexcept
793 {
794 return *this = static_cast<signed long long>(rhs);
795 }
796 datum_impl &operator=(signed int rhs) noexcept
797 {
798 return *this = static_cast<signed long long>(rhs);
799 }
800 datum_impl &operator=(signed short rhs) noexcept
801 {
802 return *this = static_cast<signed long long>(rhs);
803 }
804 datum_impl &operator=(signed char rhs) noexcept
805 {
806 return *this = static_cast<signed long long>(rhs);
807 }
808
809 datum_impl &operator=(bool rhs) noexcept
810 {
811 if (is_phy_pointer()) {
812 [[unlikely]] delete_pointer();
813 }
814 u64 = rhs ? true_mask : false_mask;
815 return *this;
816 }
817
818 datum_impl &operator=(char rhs) noexcept
819 {
820 if (is_phy_pointer()) {
821 [[unlikely]] delete_pointer();
822 }
823 u64 = string_mask | (uint64_t{1} << 40) | static_cast<uint64_t>(rhs);
824 return *this;
825 }
826
827 datum_impl &operator=(std::string_view rhs)
828 {
829 if (is_phy_pointer()) {
830 [[unlikely]] delete_pointer();
831 }
832
833 u64 = make_string(rhs);
834 if (u64 == 0) {
835 if constexpr (HasLargeObjects) {
836 auto *const p = new std::string(rhs);
837 u64 = make_pointer(string_ptr_mask, p);
838 } else {
839 throw std::overflow_error(std::format("Assigning string {} to datum, larger than 6 characters", rhs));
840 }
841 }
842 return *this;
843 }
844
845 datum_impl &operator=(std::string const &rhs)
846 {
847 *this = std::string_view{rhs};
848 return *this;
849 }
850
851 datum_impl &operator=(char const *rhs)
852 {
853 *this = std::string_view{rhs};
854 return *this;
855 }
856
857 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
858 datum_impl &operator=(URL const &rhs) noexcept
859 {
860 if (is_phy_pointer()) {
861 [[unlikely]] delete_pointer();
862 }
863
864 auto *const p = new URL(rhs);
865 u64 = make_pointer(url_ptr_mask, p);
866 return *this;
867 }
868
869 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
870 datum_impl &operator=(URL &&rhs) noexcept
871 {
872 if (is_phy_pointer()) {
873 [[unlikely]] delete_pointer();
874 }
875
876 auto *const p = new URL(std::move(rhs));
877 u64 = make_pointer(url_ptr_mask, p);
878 return *this;
879 }
880
881 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
882 datum_impl &operator=(datum_impl::vector const &rhs)
883 {
884 if (is_phy_pointer()) {
885 [[unlikely]] delete_pointer();
886 }
887
888 auto *const p = new datum_impl::vector(rhs);
889 u64 = make_pointer(vector_ptr_mask, p);
890
891 return *this;
892 }
893
894 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
895 datum_impl &operator=(datum_impl::vector &&rhs)
896 {
897 if (is_phy_pointer()) {
898 [[unlikely]] delete_pointer();
899 }
900
901 auto *const p = new datum_impl::vector(std::move(rhs));
902 u64 = make_pointer(vector_ptr_mask, p);
903
904 return *this;
905 }
906
907 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
908 datum_impl &operator=(datum_impl::map const &rhs)
909 {
910 if (is_phy_pointer()) {
911 [[unlikely]] delete_pointer();
912 }
913
914 auto *const p = new datum_impl::map(rhs);
915 u64 = make_pointer(map_ptr_mask, p);
916
917 return *this;
918 }
919
920 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
921 datum_impl &operator=(datum_impl::map &&rhs)
922 {
923 if (is_phy_pointer()) {
924 [[unlikely]] delete_pointer();
925 }
926
927 auto *const p = new datum_impl::map(std::move(rhs));
928 u64 = make_pointer(map_ptr_mask, p);
929
930 return *this;
931 }
932
933 explicit operator double() const
934 {
935 if (is_phy_float()) {
936 return f64;
937 } else if (is_decimal()) {
938 return static_cast<double>(static_cast<decimal>(*this));
939 } else if (is_phy_integer()) {
940 return static_cast<double>(get_signed_integer());
941 } else if (is_phy_integer_ptr()) {
942 return static_cast<double>(*get_pointer<int64_t>());
943 } else {
944 throw operation_error(
945 "Value {} of type {} can not be converted to a double", this->repr(), this->type_name());
946 }
947 }
948
949 explicit operator float() const
950 {
951 return static_cast<float>(static_cast<double>(*this));
952 }
953
954 explicit operator decimal() const
955 {
956 if (is_phy_decimal()) {
957 uint64_t v = get_unsigned_integer();
958 int e = static_cast<int8_t>(v);
959 long long m = static_cast<int64_t>(v << 16) >> 24;
960 return decimal{e, m};
961 } else if (is_phy_decimal_ptr()) {
962 return *get_pointer<decimal>();
963 } else if (is_phy_integer() || is_phy_integer_ptr()) {
964 return decimal{static_cast<signed long long>(*this)};
965 } else if (is_phy_float()) {
966 return decimal{static_cast<double>(*this)};
967 } else {
968 throw operation_error(
969 "Value {} of type {} can not be converted to a decimal", this->repr(), this->type_name());
970 }
971 }
972
973 explicit operator std::chrono::year_month_day() const
974 {
975 if (is_phy_ymd()) {
976 ttlet u = get_unsigned_integer();
977 ttlet i = get_signed_integer();
978 ttlet day = static_cast<unsigned>(u & 0x1f);
979 ttlet month = static_cast<unsigned>((u >> 5) & 0xf);
980 ttlet year = static_cast<signed>(i >> 9);
981
982 if (day == 0) {
983 throw operation_error(
984 "Value {} of type {} can not be converted to a year-month-day", this->repr(), this->type_name());
985 }
986 if (month == 0) {
987 throw operation_error(
988 "Value {} of type {} can not be converted to a year-month-day", this->repr(), this->type_name());
989 }
990 return std::chrono::year_month_day{std::chrono::year{year}, std::chrono::month{month}, std::chrono::day{day}};
991 } else {
992 throw operation_error(
993 "Value {} of type {} can not be converted to a year-month-day", this->repr(), this->type_name());
994 }
995 }
996
997 explicit operator signed long long() const
998 {
999 if (is_phy_integer()) {
1000 return get_signed_integer();
1001 } else if (is_phy_integer_ptr()) {
1002 return *get_pointer<signed long long>();
1003 } else if (is_phy_float()) {
1004 return static_cast<signed long long>(f64);
1005 } else if (is_phy_small()) {
1006 switch (get_unsigned_integer()) {
1007 case small_true: return 1;
1008 case small_false: return 0;
1009 }
1010 }
1011 throw operation_error(
1012 "Value {} of type {} can not be converted to a signed long long", this->repr(), this->type_name());
1013 }
1014
1015 explicit operator signed long() const
1016 {
1017 ttlet v = static_cast<signed long long>(*this);
1019 throw operation_error(
1020 "Value {} of type {} can not be converted to a signed long", this->repr(), this->type_name());
1021 }
1022 return static_cast<signed long>(v);
1023 }
1024
1025 explicit operator signed int() const
1026 {
1027 ttlet v = static_cast<signed long long>(*this);
1029 throw operation_error(
1030 "Value {} of type {} can not be converted to a signed int", this->repr(), this->type_name());
1031 }
1032 return static_cast<signed int>(v);
1033 }
1034
1035 explicit operator signed short() const
1036 {
1037 ttlet v = static_cast<signed long long>(*this);
1039 throw operation_error(
1040 "Value {} of type {} can not be converted to a signed short", this->repr(), this->type_name());
1041 }
1042 return static_cast<signed short>(v);
1043 }
1044
1045 explicit operator signed char() const
1046 {
1047 ttlet v = static_cast<int64_t>(*this);
1049 throw operation_error(
1050 "Value {} of type {} can not be converted to a signed char", this->repr(), this->type_name());
1051 }
1052 return static_cast<signed char>(v);
1053 }
1054
1055 explicit operator unsigned long long() const
1056 {
1057 ttlet v = static_cast<signed long long>(*this);
1058 if (v < 0) {
1059 throw operation_error(
1060 "Value {} of type {} can not be converted to a unsigned long long", this->repr(), this->type_name());
1061 }
1062 return static_cast<unsigned long long>(v);
1063 }
1064
1065 explicit operator unsigned long() const
1066 {
1067 ttlet v = static_cast<unsigned long long>(*this);
1069 throw operation_error(
1070 "Value {} of type {} can not be converted to a unsigned long", this->repr(), this->type_name());
1071 }
1072 return static_cast<unsigned long>(v);
1073 }
1074
1075 explicit operator unsigned int() const
1076 {
1077 ttlet v = static_cast<unsigned long long>(*this);
1079 throw operation_error(
1080 "Value {} of type {} can not be converted to a unsigned int", this->repr(), this->type_name());
1081 }
1082 return static_cast<unsigned int>(v);
1083 }
1084
1085 explicit operator unsigned short() const
1086 {
1087 ttlet v = static_cast<unsigned long long>(*this);
1089 throw operation_error(
1090 "Value {} of type {} can not be converted to a unsigned short", this->repr(), this->type_name());
1091 }
1092 return static_cast<unsigned short>(v);
1093 }
1094
1095 explicit operator unsigned char() const
1096 {
1097 ttlet v = static_cast<unsigned long long>(*this);
1099 throw operation_error(
1100 "Value {} of type {} can not be converted to a unsigned char", this->repr(), this->type_name());
1101 }
1102 return static_cast<unsigned char>(v);
1103 }
1104
1105 explicit operator bool() const noexcept
1106 {
1107 switch (type_id()) {
1108 case phy_small_id: return get_unsigned_integer() == small_true;
1109 case phy_integer_id: return static_cast<int64_t>(*this) != 0;
1110 case phy_decimal_id: return static_cast<decimal>(*this) != 0;
1111 case phy_ymd_id: return true;
1112 case phy_integer_ptr_id: return *get_pointer<int64_t>() != 0;
1113 case phy_string_id:
1114 case phy_string_ptr_id: return this->size() > 0;
1115 case phy_url_ptr_id: return true;
1116 case phy_vector_ptr_id: return this->size() > 0;
1117 case phy_map_ptr_id: return this->size() > 0;
1118 case phy_decimal_ptr_id: return static_cast<decimal>(*this) != 0;
1119 case phy_bytes_ptr_id: return this->size() > 0;
1120 default:
1121 if (is_phy_float()) {
1122 return static_cast<double>(*this) != 0.0;
1123 } else {
1124 tt_no_default();
1125 };
1126 }
1127 }
1128
1129 explicit operator char() const
1130 {
1131 if (is_phy_string() && size() == 1) {
1132 return u64 & 0xff;
1133 } else if (is_phy_string_ptr() && size() == 1) {
1134 return get_pointer<std::string>()->at(0);
1135 } else {
1136 throw operation_error(
1137 "Value {} of type {} can not be converted to a char", this->repr(), this->type_name());
1138 }
1139 }
1140
1141 explicit operator bstring() const
1142 {
1143 if (is_bytes()) {
1144 if constexpr (HasLargeObjects) {
1145 return *get_pointer<bstring>();
1146 } else {
1147 tt_no_default();
1148 }
1149 } else {
1150 throw operation_error(
1151 "Value {} of type {} can not be converted to bytes", this->repr(), this->type_name());
1152 }
1153 }
1154
1155 explicit operator std::string() const noexcept
1156 {
1157 switch (type_id()) {
1158 case phy_small_id:
1159 switch (get_unsigned_integer()) {
1160 case small_undefined: return "undefined";
1161 case small_null: return "null";
1162 case small_true: return "true";
1163 case small_false: return "false";
1164 case small_break: return "break";
1165 case small_continue: return "continue";
1166 default: tt_no_default();
1167 }
1168
1169 case phy_integer_id: return std::format("{}", static_cast<int64_t>(*this));
1170 case phy_decimal_id: return std::format("{}", static_cast<decimal>(*this));
1171 case phy_ymd_id: return std::format("{}", static_cast<std::chrono::year_month_day>(*this));
1172 case phy_integer_ptr_id:
1173 if constexpr (HasLargeObjects) {
1174 return std::format("{}", static_cast<int64_t>(*this));
1175 } else {
1176 tt_no_default();
1177 }
1178
1179 case phy_string_id: {
1180 ttlet length = size();
1181 char buffer[5];
1182 for (int i = 0; i < length; i++) {
1183 buffer[i] = (u64 >> ((length - i - 1) * 8)) & 0xff;
1184 }
1185 return std::string(buffer, length);
1186 }
1187
1188 case phy_string_ptr_id:
1189 if constexpr (HasLargeObjects) {
1190 return *get_pointer<std::string>();
1191 } else {
1192 tt_no_default();
1193 }
1194
1195 case phy_url_ptr_id:
1196 if constexpr (HasLargeObjects) {
1197 return to_string(*get_pointer<URL>());
1198 } else {
1199 tt_no_default();
1200 }
1201
1202 case phy_decimal_ptr_id:
1203 if constexpr (HasLargeObjects) {
1204 return std::format("{}", static_cast<decimal>(*this));
1205 } else {
1206 tt_no_default();
1207 }
1208
1209 case phy_vector_ptr_id:
1210 if constexpr (HasLargeObjects) {
1211 // The string representation of a vector is like a json array.
1212 // Enclosed with brackets '[' and ']'. Each value separated with
1213 // comma ','.
1214 std::string r = "[";
1215 auto count = 0;
1216 for (auto i = vector_begin(); i != vector_end(); i++) {
1217 if (count++ > 0) {
1218 r += ", ";
1219 }
1220 r += i->repr();
1221 }
1222 r += "]";
1223 return r;
1224 } else {
1225 tt_no_default();
1226 }
1227
1228 case phy_map_ptr_id:
1229 if constexpr (HasLargeObjects) {
1230 // We sort the map based on keys before creating a string
1231 // so that the representation of a map is stable between implementations.
1233 items.reserve(size());
1234 std::copy(map_begin(), map_end(), std::back_inserter(items));
1235 std::sort(items.begin(), items.end(), [](auto &a, auto &b) {
1236 return a.first < b.first;
1237 });
1238
1239 // The string representation for a map is like a json object.
1240 // Enclosed with braces '{' and '}' Each pair is separated with
1241 // comma ',' and key and value separated with ':'.
1242 std::string r = "{";
1243 auto count = 0;
1244 for (auto &item : items) {
1245 if (count++ > 0) {
1246 r += ", ";
1247 }
1248 r += item.first.repr();
1249 r += ": ";
1250 r += item.second.repr();
1251 }
1252 r += "}";
1253 return r;
1254 } else {
1255 tt_no_default();
1256 }
1257
1258 case phy_bytes_ptr_id:
1259 if constexpr (HasLargeObjects) {
1260 return base16::encode(*get_pointer<bstring>());
1261 } else {
1262 tt_no_default();
1263 }
1264
1265 default:
1266 if (is_phy_float()) {
1267 auto str = std::format("{:g}", static_cast<double>(*this));
1268 if (str.find('.') == str.npos) {
1269 str += ".0";
1270 }
1271 return str;
1272 } else {
1273 tt_no_default();
1274 }
1275 }
1276 }
1277
1278 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1279 explicit operator URL() const
1280 {
1281 if (is_string()) {
1282 return URL{static_cast<std::string>(*this)};
1283 } else if (is_url()) {
1284 return *get_pointer<URL>();
1285 } else {
1286 throw operation_error(
1287 "Value {} of type {} can not be converted to a URL", this->repr(), this->type_name());
1288 }
1289 }
1290
1291 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1292 explicit operator datum_impl::vector() const
1293 {
1294 if (is_vector()) {
1295 return *get_pointer<datum_impl::vector>();
1296 } else {
1297 throw operation_error(
1298 "Value {} of type {} can not be converted to a Vector", this->repr(), this->type_name());
1299 }
1300 }
1301
1302 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1303 explicit operator datum_impl::map() const
1304 {
1305 if (is_map()) {
1306 return *get_pointer<datum_impl::map>();
1307 } else {
1308 throw operation_error(
1309 "Value {} of type {} can not be converted to a Map", this->repr(), this->type_name());
1310 }
1311 }
1312
1320 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1322 {
1323 if (is_undefined()) {
1324 // When accessing a name on an undefined it means we need replace it with an empty map.
1325 auto *p = new datum_impl::map();
1326 u64 = map_ptr_mask | (reinterpret_cast<uint64_t>(p) & pointer_mask);
1327 }
1328
1329 if (is_map()) {
1330 auto &m = *get_pointer<datum_impl::map>();
1331 auto [i, did_insert] = m.try_emplace(rhs);
1332 return i->second;
1333
1334 } else if (is_vector() && rhs.is_integer()) {
1335 auto index = static_cast<int64_t>(rhs);
1336 auto &v = *get_pointer<datum_impl::vector>();
1337
1338 if (index < 0) {
1339 index = std::ssize(v) + index;
1340 }
1341
1342 if (index < 0 || index >= std::ssize(v)) {
1343 throw operation_error(
1344 "Index {} out of range to access value in vector of size {}", index, std::ssize(v));
1345 } else {
1346 return v[index];
1347 }
1348 } else {
1349 throw operation_error(
1350 "Cannot index value of type {} with {} of type {}", type_name(), rhs.repr(), rhs.type_name());
1351 }
1352 }
1353
1361 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1363 {
1364 if (is_map()) {
1365 ttlet &m = *get_pointer<datum_impl::map>();
1366 ttlet i = m.find(rhs);
1367 if (i == m.cend()) {
1368 throw operation_error("Could not find key {} in map of size {}", rhs.repr(), std::ssize(m));
1369 }
1370 return i->second;
1371
1372 } else if (is_vector() && rhs.is_integer()) {
1373 auto index = static_cast<int64_t>(rhs);
1374 ttlet &v = *get_pointer<datum_impl::vector>();
1375
1376 if (index < 0) {
1377 index = std::ssize(v) + index;
1378 }
1379
1380 if (index < 0 || index >= std::ssize(v)) {
1381 throw operation_error(
1382 "Index {} out of range to access value in vector of size {}", index, std::ssize(v));
1383 } else {
1384 return v[index];
1385 }
1386 } else {
1387 throw operation_error(
1388 "Cannot index value of type {} with {} of type {}", type_name(), rhs.repr(), rhs.type_name());
1389 }
1390 }
1391
1400 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1401 bool contains(datum_impl const &rhs) const noexcept
1402 {
1403 if (is_map()) {
1404 ttlet &m = *get_pointer<datum_impl::map>();
1405 ttlet i = m.find(rhs);
1406 return i != m.cend();
1407
1408 } else if (is_vector() && rhs.is_integer()) {
1409 auto index = static_cast<int64_t>(rhs);
1410 ttlet &v = *get_pointer<datum_impl::vector>();
1411
1412 if (index < 0) {
1413 index = std::ssize(v) + index;
1414 }
1415
1416 return index >= 0 && index < std::ssize(v);
1417
1418 } else {
1419 return false;
1420 }
1421 }
1422
1428 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1430 {
1431 if (is_undefined()) {
1432 // When appending on undefined it means we need replace it with an empty vector.
1433 auto *p = new datum_impl::vector();
1434 u64 = vector_ptr_mask | (reinterpret_cast<uint64_t>(p) & pointer_mask);
1435 }
1436
1437 if (is_vector()) {
1438 auto *v = get_pointer<datum_impl::vector>();
1439 v->emplace_back();
1440 return v->back();
1441
1442 } else {
1443 throw operation_error("Cannot append new item onto type {}", type_name());
1444 }
1445 }
1446
1447 template<typename... Args>
1448 void emplace_back(Args &&... args)
1449 {
1450 if (is_undefined()) {
1451 // When appending on undefined it means we need replace it with an empty vector.
1452 auto *p = new datum_impl::vector();
1453 u64 = vector_ptr_mask | (reinterpret_cast<uint64_t>(p) & pointer_mask);
1454 }
1455
1456 if (is_vector()) {
1457 auto *v = get_pointer<datum_impl::vector>();
1458 v->emplace_back(std::forward<Args>(args)...);
1459
1460 } else {
1461 throw operation_error("Cannot append new item onto type {}", type_name());
1462 }
1463 }
1464
1465 template<typename Arg>
1466 void push_back(Arg &&arg)
1467 {
1468 if (is_undefined()) {
1469 // When appending on undefined it means we need replace it with an empty vector.
1470 auto *p = new datum_impl::vector();
1471 u64 = vector_ptr_mask | (reinterpret_cast<uint64_t>(p) & pointer_mask);
1472 }
1473
1474 if (is_vector()) {
1475 auto *v = get_pointer<datum_impl::vector>();
1476 v->push_back(std::forward<Arg>(arg));
1477
1478 } else {
1479 throw operation_error("Cannot append new item onto type {}", type_name());
1480 }
1481 }
1482
1483 void pop_back()
1484 {
1485 if (is_vector()) {
1486 auto *v = get_pointer<datum_impl::vector>();
1487 v->pop_back();
1488
1489 } else {
1490 throw operation_error("Cannot pop_back() onto type {}", type_name());
1491 }
1492 }
1493
1494 datum_impl year() const
1495 {
1496 if (is_ymd()) {
1497 return {static_cast<signed>(static_cast<std::chrono::year_month_day>(*this).year())};
1498 } else {
1499 throw operation_error("Cannot get year() from type {}", type_name());
1500 }
1501 }
1502
1503 datum_impl quarter() const
1504 {
1505 if (is_ymd()) {
1506 auto month = static_cast<unsigned>(static_cast<std::chrono::year_month_day>(*this).month());
1507 return {((month - 1) / 3) + 1};
1508 } else {
1509 throw operation_error("Cannot get month() from type {}", type_name());
1510 }
1511 }
1512
1513 datum_impl month() const
1514 {
1515 if (is_ymd()) {
1516 return {static_cast<unsigned>(static_cast<std::chrono::year_month_day>(*this).month())};
1517 } else {
1518 throw operation_error("Cannot get month() from type {}", type_name());
1519 }
1520 }
1521
1522 datum_impl day() const
1523 {
1524 if (is_ymd()) {
1525 return {static_cast<unsigned>(static_cast<std::chrono::year_month_day>(*this).day())};
1526 } else {
1527 throw operation_error("Cannot get day() from type {}", type_name());
1528 }
1529 }
1530
1531 datum_impl const &front() const
1532 {
1533 if (is_vector()) {
1534 ttlet *v = get_pointer<datum_impl::vector>();
1535 return v->front();
1536
1537 } else {
1538 throw operation_error("Cannot front() onto type {}", type_name());
1539 }
1540 }
1541
1542 datum_impl &front()
1543 {
1544 if (is_vector()) {
1545 auto *v = get_pointer<datum_impl::vector>();
1546 return v->front();
1547
1548 } else {
1549 throw operation_error("Cannot front() onto type {}", type_name());
1550 }
1551 }
1552
1553 datum_impl const &back() const
1554 {
1555 if (is_vector()) {
1556 ttlet *v = get_pointer<datum_impl::vector>();
1557 return v->back();
1558
1559 } else {
1560 throw operation_error("Cannot back() onto type {}", type_name());
1561 }
1562 }
1563
1564 datum_impl &back()
1565 {
1566 if (is_vector()) {
1567 auto *v = get_pointer<datum_impl::vector>();
1568 return v->back();
1569
1570 } else {
1571 throw operation_error("Cannot back() onto type {}", type_name());
1572 }
1573 }
1574
1575 std::string repr() const noexcept
1576 {
1577 switch (type_id()) {
1578 case phy_small_id:
1579 switch (get_unsigned_integer()) {
1580 case small_undefined: return "undefined";
1581 case small_null: return "null";
1582 case small_true: return "true";
1583 case small_false: return "false";
1584 case small_break: return "break";
1585 case small_continue: return "continue";
1586 default: tt_no_default();
1587 }
1588 case phy_integer_id:
1589 case phy_integer_ptr_id: return static_cast<std::string>(*this);
1590 case phy_decimal_id:
1591 case phy_decimal_ptr_id: return static_cast<std::string>(*this);
1592 case phy_ymd_id: return static_cast<std::string>(*this);
1593 case phy_string_id:
1594 case phy_string_ptr_id: return std::format("\"{}\"", static_cast<std::string>(*this));
1595 case phy_url_ptr_id: return std::format("<URL {}>", static_cast<std::string>(*this));
1596 case phy_vector_ptr_id: return static_cast<std::string>(*this);
1597 case phy_map_ptr_id: return static_cast<std::string>(*this);
1598 case phy_bytes_ptr_id: return static_cast<std::string>(*this);
1599 default:
1600 if (is_phy_float()) {
1601 return static_cast<std::string>(*this);
1602 } else {
1603 tt_no_default();
1604 }
1605 }
1606 }
1607
1611 int type_order() const noexcept
1612 {
1613 if (is_numeric()) {
1614 // Fold all numeric values into the same group (literal integers).
1615 return phy_integer_id;
1616 } else {
1617 return type_id();
1618 }
1619 }
1620
1621 datum_impl &get_by_path(std::vector<std::string> const &key)
1622 {
1623 if (key.size() > 0 && is_map()) {
1624 ttlet index = key.at(0);
1625 auto &next = (*this)[index];
1626 ttlet next_key = std::vector<std::string>{key.begin() + 1, key.end()};
1627 return next.get_by_path(next_key);
1628
1629 } else if (key.size() > 0 && is_vector()) {
1630 size_t const index = std::stoll(key.at(0));
1631 auto &next = (*this)[index];
1632 ttlet next_key = std::vector<std::string>{key.begin() + 1, key.end()};
1633 return next.get_by_path(next_key);
1634
1635 } else if (key.size() > 0) {
1636 throw operation_error("type {} does not support get() with '{}'", type_name(), key.at(0));
1637 } else {
1638 return *this;
1639 }
1640 }
1641
1642 datum_impl get_by_path(std::vector<std::string> const &key) const
1643 {
1644 if (key.size() > 0 && is_map()) {
1645 ttlet index = key.at(0);
1646 ttlet next = (*this)[index];
1647 return next.get_by_path({key.begin() + 1, key.end()});
1648
1649 } else if (key.size() > 0 && is_vector()) {
1650 size_t const index = std::stoll(key.at(0));
1651 ttlet next = (*this)[index];
1652 return next.get_by_path({key.begin() + 1, key.end()});
1653
1654 } else if (key.size() > 0) {
1655 throw operation_error("type {} does not support get() with '{}'", type_name(), key.at(0));
1656 } else {
1657 return *this;
1658 }
1659 }
1660
1661 bool is_integer() const noexcept
1662 {
1663 return is_phy_integer() || is_phy_integer_ptr();
1664 }
1665 bool is_decimal() const noexcept
1666 {
1667 return is_phy_decimal() || is_phy_decimal_ptr();
1668 }
1669 bool is_ymd() const noexcept
1670 {
1671 return is_phy_ymd();
1672 }
1673 bool is_float() const noexcept
1674 {
1675 return is_phy_float();
1676 }
1677 bool is_string() const noexcept
1678 {
1679 return is_phy_string() || is_phy_string_ptr();
1680 }
1681 bool is_bytes() const noexcept
1682 {
1683 return is_phy_bytes_ptr();
1684 }
1685
1686 bool is_bool() const noexcept
1687 {
1688 if (is_phy_small()) {
1689 ttlet tmp = get_unsigned_integer();
1690 return tmp == small_true || tmp == small_false;
1691 } else {
1692 return false;
1693 }
1694 }
1695
1696 bool is_null() const noexcept
1697 {
1698 return is_phy_small() && get_unsigned_integer() == small_null;
1699 }
1700
1701 bool is_undefined() const noexcept
1702 {
1703 return is_phy_small() && get_unsigned_integer() == small_undefined;
1704 }
1705
1706 bool is_break() const noexcept
1707 {
1708 return is_phy_small() && get_unsigned_integer() == small_break;
1709 }
1710
1711 bool is_continue() const noexcept
1712 {
1713 return is_phy_small() && get_unsigned_integer() == small_continue;
1714 }
1715 bool is_url() const noexcept
1716 {
1717 return is_phy_url_ptr();
1718 }
1719
1720 bool is_vector() const noexcept
1721 {
1722 return is_phy_vector_ptr();
1723 }
1724 bool is_map() const noexcept
1725 {
1726 return is_phy_map_ptr();
1727 }
1728 bool is_numeric() const noexcept
1729 {
1730 return is_integer() || is_decimal() || is_float();
1731 }
1732
1733 datum_type_t type() const noexcept
1734 {
1735 switch (type_id()) {
1736 case phy_small_id:
1737 switch (get_unsigned_integer()) {
1738 case small_undefined: return datum_type_t::Undefined;
1739 case small_null: return datum_type_t::Null;
1740 case small_false: return datum_type_t::Boolean;
1741 case small_true: return datum_type_t::Boolean;
1742 case small_break: return datum_type_t::Break;
1743 case small_continue: return datum_type_t::Continue;
1744 default: tt_no_default();
1745 }
1746
1747 case phy_integer_id:
1748 case phy_integer_ptr_id: return datum_type_t::Integer;
1749 case phy_decimal_id:
1750 case phy_decimal_ptr_id: return datum_type_t::Decimal;
1751 case phy_ymd_id: return datum_type_t::YearMonthDay;
1752 case phy_string_id:
1753 case phy_string_ptr_id: return datum_type_t::String;
1754 case phy_url_ptr_id: return datum_type_t::URL;
1755 case phy_vector_ptr_id: return datum_type_t::Vector;
1756 case phy_map_ptr_id: return datum_type_t::Map;
1757 case phy_bytes_ptr_id: return datum_type_t::Bytes;
1758 default:
1759 if (is_phy_float()) {
1760 return datum_type_t::Float;
1761 } else {
1762 tt_no_default();
1763 }
1764 }
1765 }
1766
1767 char const *type_name() const noexcept
1768 {
1769 switch (type_id()) {
1770 case phy_small_id:
1771 switch (get_unsigned_integer()) {
1772 case small_undefined: return "Undefined";
1773 case small_null: return "Null";
1774 case small_false: return "Boolean";
1775 case small_true: return "Boolean";
1776 case small_break: return "Break";
1777 case small_continue: return "Continue";
1778 default: tt_no_default();
1779 }
1780
1781 case phy_integer_id:
1782 case phy_integer_ptr_id: return "Integer";
1783 case phy_decimal_id:
1784 case phy_decimal_ptr_id: return "Decimal";
1785 case phy_ymd_id: return "YearMonthDay";
1786 case phy_string_id:
1787 case phy_string_ptr_id: return "String";
1788 case phy_url_ptr_id: return "URL";
1789 case phy_vector_ptr_id: return "Vector";
1790 case phy_map_ptr_id: return "Map";
1791 case phy_bytes_ptr_id: return "Bytes";
1792 default:
1793 if (is_phy_float()) {
1794 return "Float";
1795 } else {
1796 tt_no_default();
1797 }
1798 }
1799 }
1800
1801 size_t size() const
1802 {
1803 switch (type_id()) {
1804 case phy_string_id: return (u64 >> 40) & 0xff;
1805 case phy_string_ptr_id: return get_pointer<std::string>()->size();
1806 case phy_vector_ptr_id: return get_pointer<datum_impl::vector>()->size();
1807 case phy_map_ptr_id: return get_pointer<datum_impl::map>()->size();
1808 case phy_bytes_ptr_id: return get_pointer<bstring>()->size();
1809 default: throw operation_error("Can't get size of value {} of type {}.", this->repr(), this->type_name());
1810 }
1811 }
1812
1813 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1814 typename map::const_iterator map_begin() const
1815 {
1816 if (is_phy_map_ptr()) {
1817 return get_pointer<datum_impl::map>()->begin();
1818 } else {
1819 throw operation_error("map_begin() expect datum to be a map, but it is a {}.", this->type_name());
1820 }
1821 }
1822
1823 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1824 typename map::const_iterator map_end() const
1825 {
1826 if (is_phy_map_ptr()) {
1827 return get_pointer<datum_impl::map>()->end();
1828 } else {
1829 throw operation_error("map_end() expect datum to be a map, but it is a {}.", this->type_name());
1830 }
1831 }
1832
1833 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1834 typename vector::const_iterator vector_begin() const
1835 {
1836 if (is_phy_vector_ptr()) {
1837 return get_pointer<datum_impl::vector>()->begin();
1838 } else {
1839 throw operation_error(
1840 "vector_begin() expect datum to be a vector, but it is a {}.", this->type_name());
1841 }
1842 }
1843
1844 template<bool P = HasLargeObjects, std::enable_if_t<P, int> = 0>
1845 typename vector::const_iterator vector_end() const
1846 {
1847 if (is_phy_vector_ptr()) {
1848 return get_pointer<datum_impl::vector>()->end();
1849 } else {
1850 throw operation_error("vector_end() expect datum to be a vector, but it is a {}.", this->type_name());
1851 }
1852 }
1853
1854 size_t hash() const noexcept
1855 {
1856 if (is_phy_float()) {
1857 return std::hash<double>{}(f64);
1858 } else if (is_phy_pointer()) {
1859 [[unlikely]] switch (type_id())
1860 {
1861 case phy_string_ptr_id: return std::hash<std::string>{}(*get_pointer<std::string>());
1862 case phy_url_ptr_id: return std::hash<URL>{}(*get_pointer<URL>());
1863 case phy_vector_ptr_id:
1864 return std::accumulate(vector_begin(), vector_end(), size_t{0}, [](size_t a, auto x) {
1865 return a ^ x.hash();
1866 });
1867 case phy_map_ptr_id:
1868 return std::accumulate(map_begin(), map_end(), size_t{0}, [](size_t a, auto x) {
1869 return a ^ (x.first.hash() ^ x.second.hash());
1870 });
1871 case phy_decimal_ptr_id: return std::hash<decimal>{}(*get_pointer<decimal>());
1872 case phy_bytes_ptr_id: return std::hash<bstring>{}(*get_pointer<bstring>());
1873 default: tt_no_default();
1874 }
1875 } else {
1876 return std::hash<uint64_t>{}(u64);
1877 }
1878 }
1879
1880 datum_impl &operator++()
1881 {
1882 if (!this->is_numeric()) {
1883 throw operation_error("Can't increment '++' value {} of type {}", this->repr(), this->type_name());
1884 }
1885
1886 return *this += 1;
1887 }
1888
1889 datum_impl &operator--()
1890 {
1891 if (!this->is_numeric()) {
1892 throw operation_error("Can't increment '--' value {} of type {}", this->repr(), this->type_name());
1893 }
1894
1895 return *this -= 1;
1896 }
1897
1898 datum_impl operator++(int)
1899 {
1900 auto tmp = *this;
1901 ++*this;
1902 return tmp;
1903 }
1904
1905 datum_impl operator--(int)
1906 {
1907 auto tmp = *this;
1908 --*this;
1909 return tmp;
1910 }
1911
1912 datum_impl &operator+=(datum_impl const &rhs)
1913 {
1914 if (this->is_vector()) {
1915 this->push_back(rhs);
1916 } else {
1917 *this = *this + rhs;
1918 }
1919 return *this;
1920 }
1921
1922 datum_impl &operator-=(datum_impl const &rhs)
1923 {
1924 return *this = *this - rhs;
1925 }
1926
1927 datum_impl &operator*=(datum_impl const &rhs)
1928 {
1929 return *this = *this * rhs;
1930 }
1931
1932 datum_impl &operator/=(datum_impl const &rhs)
1933 {
1934 return *this = *this / rhs;
1935 }
1936
1937 datum_impl &operator%=(datum_impl const &rhs)
1938 {
1939 return *this = *this % rhs;
1940 }
1941
1942 datum_impl &operator<<=(datum_impl const &rhs)
1943 {
1944 return *this = *this << rhs;
1945 }
1946
1947 datum_impl &operator>>=(datum_impl const &rhs)
1948 {
1949 return *this = *this >> rhs;
1950 }
1951
1952 datum_impl &operator&=(datum_impl const &rhs)
1953 {
1954 return *this = *this & rhs;
1955 }
1956
1957 datum_impl &operator|=(datum_impl const &rhs)
1958 {
1959 return *this = *this | rhs;
1960 }
1961
1962 datum_impl &operator^=(datum_impl const &rhs)
1963 {
1964 return *this = *this ^ rhs;
1965 }
1966
1967 friend datum_impl operator~(datum_impl const &rhs)
1968 {
1969 if (rhs.is_integer()) {
1970 return datum_impl{~static_cast<int64_t>(rhs)};
1971 } else {
1972 throw operation_error("Can't bit-wise negate '~' value {} of type {}", rhs.repr(), rhs.type_name());
1973 }
1974 }
1975
1976 friend datum_impl operator-(datum_impl const &rhs)
1977 {
1978 if (rhs.is_integer()) {
1979 return datum_impl{-static_cast<int64_t>(rhs)};
1980 } else if (rhs.is_decimal()) {
1981 return datum_impl{-static_cast<decimal>(rhs)};
1982 } else if (rhs.is_float()) {
1983 return datum_impl{-static_cast<double>(rhs)};
1984 } else {
1985 throw operation_error("Can't arithmetic negate '-' value {} of type {}", rhs.repr(), rhs.type_name());
1986 }
1987 }
1988
1989 friend datum_impl operator+(datum_impl const &rhs)
1990 {
1991 if (rhs.is_numeric()) {
1992 return rhs;
1993 } else {
1994 throw operation_error("Can't arithmetic posgate '+' value {} of type {}", rhs.repr(), rhs.type_name());
1995 }
1996 }
1997
1998 friend bool operator==(datum_impl const &lhs, datum_impl const &rhs) noexcept
1999 {
2000 switch (lhs.type_id()) {
2001 case datum_impl::phy_small_id: return rhs.is_phy_small() && lhs.get_unsigned_integer() == rhs.get_unsigned_integer();
2002 case datum_impl::phy_integer_id:
2003 case datum_impl::phy_integer_ptr_id:
2004 return (
2005 (rhs.is_float() && static_cast<double>(lhs) == static_cast<double>(rhs)) || // lgtm[cpp/equality-on-floats]
2006 (rhs.is_decimal() && static_cast<decimal>(lhs) == static_cast<decimal>(rhs)) ||
2007 (rhs.is_integer() && static_cast<int64_t>(lhs) == static_cast<int64_t>(rhs)));
2008 case datum_impl::phy_decimal_id:
2009 case datum_impl::phy_decimal_ptr_id:
2010 return (
2011 (rhs.is_float() && static_cast<double>(lhs) == static_cast<double>(rhs)) || // lgtm[cpp/equality-on-floats]
2012 (rhs.is_decimal() && static_cast<decimal>(lhs) == static_cast<decimal>(rhs)) ||
2013 (rhs.is_integer() && static_cast<decimal>(lhs) == static_cast<decimal>(rhs)));
2014 case datum_impl::phy_ymd_id: return rhs.is_ymd() && lhs.get_unsigned_integer() == rhs.get_unsigned_integer();
2015 case datum_impl::phy_string_id:
2016 case datum_impl::phy_string_ptr_id:
2017 return (
2018 (rhs.is_string() && static_cast<std::string>(lhs) == static_cast<std::string>(rhs)) ||
2019 (rhs.is_url() && static_cast<URL>(lhs) == static_cast<URL>(rhs)));
2020 case datum_impl::phy_url_ptr_id:
2021 return (rhs.is_url() || rhs.is_string()) && static_cast<URL>(lhs) == static_cast<URL>(rhs);
2022 case datum_impl::phy_vector_ptr_id:
2023 return rhs.is_vector() && *lhs.get_pointer<datum_impl::vector>() == *rhs.get_pointer<datum_impl::vector>();
2024 case datum_impl::phy_map_ptr_id:
2025 return rhs.is_map() && *lhs.get_pointer<datum_impl::map>() == *rhs.get_pointer<datum_impl::map>();
2026 case datum_impl::phy_bytes_ptr_id: return (rhs.is_bytes() && static_cast<bstring>(lhs) == static_cast<bstring>(rhs));
2027 default:
2028 if (lhs.is_phy_float()) {
2029 return rhs.is_numeric() && static_cast<double>(lhs) == static_cast<double>(rhs); // lgtm[cpp/equality-on-floats]
2030 } else {
2031 tt_no_default();
2032 }
2033 }
2034 }
2035
2036 friend bool operator<(datum_impl const &lhs, datum_impl const &rhs) noexcept
2037 {
2038 switch (lhs.type_id()) {
2039 case datum_impl::phy_small_id:
2040 if (lhs.is_bool() && rhs.is_bool()) {
2041 return static_cast<bool>(lhs) < static_cast<bool>(rhs);
2042 } else {
2043 return lhs.get_unsigned_integer() < rhs.get_unsigned_integer();
2044 }
2045 case datum_impl::phy_integer_id:
2046 case datum_impl::phy_integer_ptr_id:
2047 if (rhs.is_float()) {
2048 return static_cast<double>(lhs) < static_cast<double>(rhs);
2049 } else if (rhs.is_decimal()) {
2050 return static_cast<decimal>(lhs) < static_cast<decimal>(rhs);
2051 } else if (rhs.is_integer()) {
2052 return static_cast<int64_t>(lhs) < static_cast<int64_t>(rhs);
2053 } else {
2054 return lhs.type_order() < rhs.type_order();
2055 }
2056 case datum_impl::phy_decimal_id:
2057 case datum_impl::phy_decimal_ptr_id:
2058 if (rhs.is_float()) {
2059 return static_cast<double>(lhs) < static_cast<double>(rhs);
2060 } else if (rhs.is_decimal()) {
2061 return static_cast<decimal>(lhs) < static_cast<decimal>(rhs);
2062 } else if (rhs.is_integer()) {
2063 return static_cast<decimal>(lhs) < static_cast<decimal>(rhs);
2064 } else {
2065 return lhs.type_order() < rhs.type_order();
2066 }
2067 case datum_impl::phy_ymd_id:
2068 if (rhs.is_ymd()) {
2069 return static_cast<std::chrono::year_month_day>(lhs) < static_cast<std::chrono::year_month_day>(rhs);
2070 } else {
2071 return lhs.type_order() < rhs.type_order();
2072 }
2073 case datum_impl::phy_string_id:
2074 case datum_impl::phy_string_ptr_id:
2075 if (rhs.is_string()) {
2076 return static_cast<std::string>(lhs) < static_cast<std::string>(rhs);
2077 } else if (rhs.is_url()) {
2078 return static_cast<URL>(lhs) < static_cast<URL>(rhs);
2079 } else {
2080 return lhs.type_order() < rhs.type_order();
2081 }
2082 case datum_impl::phy_url_ptr_id:
2083 if (rhs.is_url() || rhs.is_string()) {
2084 return static_cast<URL>(lhs) < static_cast<URL>(rhs);
2085 } else {
2086 return lhs.type_order() < rhs.type_order();
2087 }
2088 case datum_impl::phy_vector_ptr_id:
2089 if (rhs.is_vector()) {
2090 return *lhs.get_pointer<datum_impl::vector>() < *rhs.get_pointer<datum_impl::vector>();
2091 } else {
2092 return lhs.type_order() < rhs.type_order();
2093 }
2094 case datum_impl::phy_map_ptr_id:
2095 if (rhs.is_map()) {
2096 return *lhs.get_pointer<datum_impl::map>() < *rhs.get_pointer<datum_impl::map>();
2097 } else {
2098 return lhs.type_order() < rhs.type_order();
2099 }
2100 case datum_impl::phy_bytes_ptr_id:
2101 if (rhs.is_bytes()) {
2102 return static_cast<bstring>(lhs) < static_cast<bstring>(rhs);
2103 } else {
2104 return lhs.type_order() < rhs.type_order();
2105 }
2106 default:
2107 if (lhs.is_phy_float()) {
2108 if (rhs.is_numeric()) {
2109 return static_cast<double>(lhs) < static_cast<double>(rhs);
2110 } else {
2111 return lhs.type_order() < rhs.type_order();
2112 }
2113 } else {
2114 tt_no_default();
2115 }
2116 }
2117 }
2118
2119 friend bool operator!=(datum_impl const &lhs, datum_impl const &rhs) noexcept
2120 {
2121 return !(lhs == rhs);
2122 }
2123
2124 friend bool operator>(datum_impl const &lhs, datum_impl const &rhs) noexcept
2125 {
2126 return rhs < lhs;
2127 }
2128
2129 friend bool operator<=(datum_impl const &lhs, datum_impl const &rhs) noexcept
2130 {
2131 return !(rhs < lhs);
2132 }
2133
2134 friend bool operator>=(datum_impl const &lhs, datum_impl const &rhs) noexcept
2135 {
2136 return !(lhs < rhs);
2137 }
2138
2139 friend datum_impl operator+(datum_impl const &lhs, datum_impl const &rhs)
2140 {
2141 if (lhs.is_float() || rhs.is_float()) {
2142 ttlet lhs_ = static_cast<double>(lhs);
2143 ttlet rhs_ = static_cast<double>(rhs);
2144 return datum_impl{lhs_ + rhs_};
2145
2146 } else if (lhs.is_decimal() || rhs.is_decimal()) {
2147 ttlet lhs_ = static_cast<decimal>(lhs);
2148 ttlet rhs_ = static_cast<decimal>(rhs);
2149 return datum_impl{lhs_ + rhs_};
2150
2151 } else if (lhs.is_integer() || rhs.is_integer()) {
2152 ttlet lhs_ = static_cast<long long int>(lhs);
2153 ttlet rhs_ = static_cast<long long int>(rhs);
2154 return datum_impl{lhs_ + rhs_};
2155
2156 } else if (lhs.is_string() && rhs.is_string()) {
2157 ttlet lhs_ = static_cast<std::string>(lhs);
2158 ttlet rhs_ = static_cast<std::string>(rhs);
2159 return datum_impl{std::move(lhs_ + rhs_)};
2160
2161 } else if (lhs.is_vector() && rhs.is_vector()) {
2162 auto lhs_ = static_cast<datum_impl::vector>(lhs);
2163 ttlet &rhs_ = *(rhs.get_pointer<datum_impl::vector>());
2164 std::copy(rhs_.begin(), rhs_.end(), std::back_inserter(lhs_));
2165 return datum_impl{std::move(lhs_)};
2166
2167 } else if (lhs.is_map() && rhs.is_map()) {
2168 ttlet &lhs_ = *(lhs.get_pointer<datum_impl::map>());
2169 auto rhs_ = static_cast<datum_impl::map>(rhs);
2170 for (ttlet &item : lhs_) {
2171 rhs_.try_emplace(item.first, item.second);
2172 }
2173 return datum_impl{std::move(rhs_)};
2174
2175 } else {
2176 throw operation_error(
2177 "Can't add '+' value {} of type {} to value {} of type {}",
2178 lhs.repr(),
2179 lhs.type_name(),
2180 rhs.repr(),
2181 rhs.type_name());
2182 }
2183 }
2184
2185 friend datum_impl operator-(datum_impl const &lhs, datum_impl const &rhs)
2186 {
2187 if (lhs.is_float() || rhs.is_float()) {
2188 ttlet lhs_ = static_cast<double>(lhs);
2189 ttlet rhs_ = static_cast<double>(rhs);
2190 return datum_impl{lhs_ - rhs_};
2191
2192 } else if (lhs.is_decimal() || rhs.is_decimal()) {
2193 ttlet lhs_ = static_cast<decimal>(lhs);
2194 ttlet rhs_ = static_cast<decimal>(rhs);
2195 return datum_impl{lhs_ - rhs_};
2196
2197 } else if (lhs.is_integer() || rhs.is_integer()) {
2198 ttlet lhs_ = static_cast<long long int>(lhs);
2199 ttlet rhs_ = static_cast<long long int>(rhs);
2200 return datum_impl{lhs_ - rhs_};
2201
2202 } else {
2203 throw operation_error(
2204 "Can't subtract '-' value {} of type {} from value {} of type {}",
2205 rhs.repr(),
2206 rhs.type_name(),
2207 lhs.repr(),
2208 lhs.type_name());
2209 }
2210 }
2211
2212 friend datum_impl operator*(datum_impl const &lhs, datum_impl const &rhs)
2213 {
2214 if (lhs.is_float() || rhs.is_float()) {
2215 ttlet lhs_ = static_cast<double>(lhs);
2216 ttlet rhs_ = static_cast<double>(rhs);
2217 return datum_impl{lhs_ * rhs_};
2218
2219 } else if (lhs.is_decimal() || rhs.is_decimal()) {
2220 ttlet lhs_ = static_cast<decimal>(lhs);
2221 ttlet rhs_ = static_cast<decimal>(rhs);
2222 return datum_impl{lhs_ * rhs_};
2223
2224 } else if (lhs.is_integer() || rhs.is_integer()) {
2225 ttlet lhs_ = static_cast<long long int>(lhs);
2226 ttlet rhs_ = static_cast<long long int>(rhs);
2227 return datum_impl{lhs_ * rhs_};
2228
2229 } else {
2230 throw operation_error(
2231 "Can't multiply '*' value {} of type {} with value {} of type {}",
2232 lhs.repr(),
2233 lhs.type_name(),
2234 rhs.repr(),
2235 rhs.type_name());
2236 }
2237 }
2238
2239 friend datum_impl operator/(datum_impl const &lhs, datum_impl const &rhs)
2240 {
2241 if (lhs.is_float() || rhs.is_float()) {
2242 ttlet lhs_ = static_cast<double>(lhs);
2243 ttlet rhs_ = static_cast<double>(rhs);
2244 return datum_impl{lhs_ / rhs_};
2245
2246 } else if (lhs.is_decimal() || rhs.is_decimal()) {
2247 ttlet lhs_ = static_cast<decimal>(lhs);
2248 ttlet rhs_ = static_cast<decimal>(rhs);
2249 return datum_impl{lhs_ / rhs_};
2250
2251 } else if (lhs.is_integer() || rhs.is_integer()) {
2252 ttlet lhs_ = static_cast<long long int>(lhs);
2253 ttlet rhs_ = static_cast<long long int>(rhs);
2254 return datum_impl{lhs_ / rhs_};
2255
2256 } else if (lhs.is_url() && (rhs.is_url() || rhs.is_string())) {
2257 ttlet lhs_ = static_cast<URL>(lhs);
2258 ttlet rhs_ = static_cast<URL>(rhs);
2259 return datum_impl{lhs_ / rhs_};
2260
2261 } else {
2262 throw operation_error(
2263 "Can't divide '/' value {} of type {} by value {} of type {}",
2264 lhs.repr(),
2265 lhs.type_name(),
2266 rhs.repr(),
2267 rhs.type_name());
2268 }
2269 }
2270
2271 friend datum_impl operator%(datum_impl const &lhs, datum_impl const &rhs)
2272 {
2273 if (lhs.is_float() || rhs.is_float()) {
2274 ttlet lhs_ = static_cast<double>(lhs);
2275 ttlet rhs_ = static_cast<double>(rhs);
2276 return datum_impl{fmod(lhs_, rhs_)};
2277
2278 } else if (lhs.is_decimal() || rhs.is_decimal()) {
2279 ttlet lhs_ = static_cast<decimal>(lhs);
2280 ttlet rhs_ = static_cast<decimal>(rhs);
2281 return datum_impl{lhs_ % rhs_};
2282
2283 } else if (lhs.is_integer() || rhs.is_integer()) {
2284 ttlet lhs_ = static_cast<long long int>(lhs);
2285 ttlet rhs_ = static_cast<long long int>(rhs);
2286 return datum_impl{lhs_ % rhs_};
2287
2288 } else {
2289 throw operation_error(
2290 "Can't take modulo '%' value {} of type {} by value {} of type {}",
2291 lhs.repr(),
2292 lhs.type_name(),
2293 rhs.repr(),
2294 rhs.type_name());
2295 }
2296 }
2297
2298 friend datum_impl operator<<(datum_impl const &lhs, datum_impl const &rhs)
2299 {
2300 if (lhs.is_integer() && rhs.is_integer()) {
2301 ttlet lhs_ = static_cast<long long>(lhs);
2302 ttlet rhs_ = static_cast<long long>(rhs);
2303
2304 if (rhs_ < 0) {
2305 return lhs >> -rhs;
2306 }
2307
2308 if (lhs_ < 0) {
2309 throw operation_error("lhs value {} of left shift must not be negative", lhs);
2310 }
2311
2312 if (rhs_ > 63) {
2313 return datum_impl{0};
2314 } else {
2315 return datum_impl{lhs_ << rhs_};
2316 }
2317
2318 } else {
2319 throw operation_error(
2320 "Can't arithmetic shift-left '<<' value {} of type {} with value {} of type {}",
2321 lhs.repr(),
2322 lhs.type_name(),
2323 rhs.repr(),
2324 rhs.type_name());
2325 }
2326 }
2327
2328 friend datum_impl operator>>(datum_impl const &lhs, datum_impl const &rhs)
2329 {
2330 if (lhs.is_integer() && rhs.is_integer()) {
2331 ttlet lhs_ = static_cast<long long>(lhs);
2332 ttlet rhs_ = static_cast<long long>(rhs);
2333
2334 if (rhs < 0) {
2335 return lhs << -rhs;
2336 }
2337
2338 if (rhs_ > 63) {
2339 return lhs_ < 0 ? datum_impl{-1} : datum_impl{0};
2340 } else {
2341 return datum_impl{lhs_ >> rhs_};
2342 }
2343
2344 } else {
2345 throw operation_error(
2346 "Can't arithmetic shift-right '>>' value {} of type {} with value {} of type {}",
2347 lhs.repr(),
2348 lhs.type_name(),
2349 rhs.repr(),
2350 rhs.type_name());
2351 }
2352 }
2353
2354 friend datum_impl operator&(datum_impl const &lhs, datum_impl const &rhs)
2355 {
2356 if (lhs.is_integer() && rhs.is_integer()) {
2357 ttlet lhs_ = static_cast<uint64_t>(lhs);
2358 ttlet rhs_ = static_cast<uint64_t>(rhs);
2359 return datum_impl{lhs_ & rhs_};
2360
2361 } else {
2362 throw operation_error(
2363 "Can't AND '&' value {} of type {} with value {} of type {}",
2364 lhs.repr(),
2365 lhs.type_name(),
2366 rhs.repr(),
2367 rhs.type_name());
2368 }
2369 }
2370
2371 friend datum_impl operator|(datum_impl const &lhs, datum_impl const &rhs)
2372 {
2373 if (lhs.is_integer() && rhs.is_integer()) {
2374 ttlet lhs_ = static_cast<uint64_t>(lhs);
2375 ttlet rhs_ = static_cast<uint64_t>(rhs);
2376 return datum_impl{lhs_ | rhs_};
2377
2378 } else {
2379 throw operation_error(
2380 "Can't OR '|' value {} of type {} with value {} of type {}",
2381 lhs.repr(),
2382 lhs.type_name(),
2383 rhs.repr(),
2384 rhs.type_name());
2385 }
2386 }
2387
2388 friend datum_impl operator^(datum_impl const &lhs, datum_impl const &rhs)
2389 {
2390 if (lhs.is_integer() && rhs.is_integer()) {
2391 ttlet lhs_ = static_cast<uint64_t>(lhs);
2392 ttlet rhs_ = static_cast<uint64_t>(rhs);
2393 return datum_impl{lhs_ ^ rhs_};
2394
2395 } else {
2396 throw operation_error(
2397 "Can't XOR '^' value {} of type {} with value {} of type {}",
2398 lhs.repr(),
2399 lhs.type_name(),
2400 rhs.repr(),
2401 rhs.type_name());
2402 }
2403 }
2404
2405 friend std::string to_string(datum_impl const &d)
2406 {
2407 return static_cast<std::string>(d);
2408 }
2409
2410 friend std::ostream &operator<<(std::ostream &os, datum_impl const &d)
2411 {
2412 return os << static_cast<std::string>(d);
2413 }
2414
2415 friend void swap(datum_impl &lhs, datum_impl &rhs) noexcept
2416 {
2417 memswap(lhs, rhs);
2418 }
2419
2420 friend datum_impl pow(datum_impl const &lhs, datum_impl const &rhs)
2421 {
2422 if (lhs.is_numeric() || rhs.is_numeric()) {
2423 ttlet lhs_ = static_cast<double>(lhs);
2424 ttlet rhs_ = static_cast<double>(rhs);
2425 return datum_impl{std::pow(lhs_, rhs_)};
2426
2427 } else {
2428 throw operation_error(
2429 "Can't raise to a power '**' value {} of type {} with value {} of type {}",
2430 lhs.repr(),
2431 lhs.type_name(),
2432 rhs.repr(),
2433 rhs.type_name());
2434 }
2435 }
2436
2444 friend datum_impl deep_merge(datum_impl const &lhs, datum_impl const &rhs) noexcept
2445 {
2446 datum_impl result;
2447
2448 if (lhs.is_map() && rhs.is_map()) {
2449 result = lhs;
2450
2451 auto result_map = result.get_pointer<datum_impl::map>();
2452 for (auto rhs_i = rhs.map_begin(); rhs_i != rhs.map_end(); rhs_i++) {
2453 auto result_i = result_map->find(rhs_i->first);
2454 if (result_i == result_map->end()) {
2455 result_map->insert(*rhs_i);
2456 } else {
2457 result_i->second = deep_merge(result_i->second, rhs_i->second);
2458 }
2459 }
2460
2461 } else if (lhs.is_vector() && rhs.is_vector()) {
2462 result = lhs;
2463
2464 auto result_vector = result.get_pointer<datum_impl::vector>();
2465 for (auto rhs_i = rhs.vector_begin(); rhs_i != rhs.vector_end(); rhs_i++) {
2466 result_vector->push_back(*rhs_i);
2467 }
2468
2469 } else {
2470 result = rhs;
2471 }
2472
2473 return result;
2474 }
2475
2476 template<typename Alternative>
2477 friend bool holds_alternative(datum_impl const &rhs) noexcept
2478 {
2479 if constexpr (std::is_same_v<Alternative, std::string>) {
2480 return rhs.is_string();
2481 } else {
2482 return false;
2483 }
2484 }
2485};
2486
2487template<typename T, bool HasLargeObjects>
2488inline bool will_cast_to(datum_impl<HasLargeObjects> const &rhs)
2489{
2490 if constexpr (std::is_same_v<T, bool>) {
2491 return true;
2492 } else if constexpr (std::is_same_v<T, char>) {
2493 return rhs.is_string();
2494 } else if constexpr (std::is_arithmetic_v<T>) {
2495 return rhs.is_numeric();
2496 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::undefined>) {
2497 return rhs.is_undefined();
2498 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::null>) {
2499 return rhs.is_null();
2500 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::_break>) {
2501 return rhs.is_break();
2502 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::_continue>) {
2503 return rhs.is_continue();
2504 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::vector>) {
2505 return rhs.is_vector();
2506 } else if constexpr (std::is_same_v<T, typename datum_impl<HasLargeObjects>::map>) {
2507 return rhs.is_map();
2508 } else if constexpr (std::is_same_v<T, URL>) {
2509 return rhs.is_url() || rhs.is_string();
2510 } else if constexpr (std::is_same_v<T, std::string>) {
2511 return true;
2512 } else {
2513 return false;
2514 }
2515}
2516
2517template<bool HasLargeObjects>
2518bool operator<(
2519 typename datum_impl<HasLargeObjects>::map const &lhs,
2520 typename datum_impl<HasLargeObjects>::map const &rhs) noexcept
2521{
2522 auto lhs_keys = transform<datum_impl<HasLargeObjects>::vector>(lhs, [](auto x) {
2523 return x.first;
2524 });
2525 auto rhs_keys = transform<datum_impl<HasLargeObjects>::vector>(lhs, [](auto x) {
2526 return x.first;
2527 });
2528
2529 if (lhs_keys == rhs_keys) {
2530 for (ttlet &k : lhs_keys) {
2531 if (lhs.at(k) == rhs.at(k)) {
2532 continue;
2533 } else if (lhs.at(k) < rhs.at(k)) {
2534 return true;
2535 } else {
2536 return false;
2537 }
2538 }
2539 } else {
2540 return lhs_keys < rhs_keys;
2541 }
2542 return false;
2543}
2544
2545using datum = datum_impl<true>;
2546using sdatum = datum_impl<false>;
2547
2548} // namespace tt
2549
2550namespace std {
2551
2552template<bool HasLargeObjects>
2553inline size_t hash<tt::datum_impl<HasLargeObjects>>::operator()(tt::datum_impl<HasLargeObjects> const &value) const
2554{
2555 return value.hash();
2556}
2557
2558template<typename CharT>
2559struct formatter<tt::datum_impl<false>, CharT> : formatter<string_view, CharT> {
2560 auto format(tt::datum_impl<false> t, auto &fc)
2561 {
2562 return formatter<string_view, CharT>::format(to_string(t), fc);
2563 }
2564};
2565
2566template<typename CharT>
2567struct formatter<tt::datum_impl<true>, CharT> : formatter<string_view, CharT> {
2568 auto format(tt::datum_impl<true> t, auto &fc)
2569 {
2570 return formatter<string_view, CharT>::format(to_string(t), fc);
2571 }
2572};
2573
2574} // namespace std
STL namespace.
static constexpr void encode(ItIn ptr, ItIn last, ItOut output)
Encode bytes into a string.
Definition base_n.hpp:148
A fixed size (64 bits) class for a generic value type.
Definition datum.hpp:124
datum_impl & operator[](datum_impl const &rhs)
Index into a datum::map or datum::vector.
Definition datum.hpp:1321
friend datum_impl deep_merge(datum_impl const &lhs, datum_impl const &rhs) noexcept
Merge two datums together, such that the second will override values on the first.
Definition datum.hpp:2444
datum_impl & append()
Append and return a reference to a datum holding undefined to this datum.
Definition datum.hpp:1429
datum_impl operator[](datum_impl const &rhs) const
Index into a datum::map or datum::vector.
Definition datum.hpp:1362
int type_order() const noexcept
Definition datum.hpp:1611
bool contains(datum_impl const &rhs) const noexcept
Check if an index is contained in a datum.
Definition datum.hpp:1401
Definition datum.hpp:436
Definition datum.hpp:438
Definition datum.hpp:440
Definition datum.hpp:442
Definition decimal.hpp:18
Exception thrown during execution of a dynamic operation.
Definition exception.hpp:42
type-trait to convert a character to a string type.
Definition type_traits.hpp:49
Definition URL.hpp:47
T accumulate(T... args)
T at(T... args)
T back_inserter(T... args)
T begin(T... args)
T copy(T... args)
T count(T... args)
T end(T... args)
T find(T... args)
T fmod(T... args)
T memcpy(T... args)
T move(T... args)
T next(T... args)
T operator()(T... args)
T pow(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
T stoll(T... args)
T to_string(T... args)