Comms CCF
This is a simple communication layer based on COBS, CBOR and FNV-1A
Loading...
Searching...
No Matches
cbor.hpp
Go to the documentation of this file.
1
184
185#pragma once
186
187#include "types.hpp"
188
189#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
190#define __STDC_WANT_IEC_60559_TYPES_EXT__
191#endif
192#include <float.h>
193#include <math.h>
194#include <stddef.h>
195#include <stdint.h>
196#include <string.h>
197
198#include <algorithm>
199#include <array>
200#include <bit>
201#include <concepts>
202#include <optional>
203#include <span>
204#include <string_view>
205#include <tuple>
206#include <type_traits>
207#include <utility>
208
209namespace Cbor
210{
211
212#if (defined(FLT16_MIN) || defined(FLT16_MIN)) && !defined(NO_FLOAT16)
213#define CBOR_HALF_SUPPORT
214#endif
215
216#if defined(CBOR_HALF_SUPPORT)
217typedef _Float16 half;
218#endif
219
221 enum class Major : uint8_t
222 {
224 U64 = 0,
226 Neg64 = 1,
230 Bytes = 2,
234 Utf8 = 3,
237 Array = 4,
240 Map = 5,
246 Float = 7,
247 };
248 constexpr Major getMajor(uint8_t initial) { return Major(initial >> 5); }
249 constexpr uint8_t setMajor(uint8_t initial, Major major)
250 {
251 return static_cast<uint8_t>(
252 (initial & ~(~0 << 5)) |
253 static_cast<uint8_t>(major) << 5);
254 }
255
256 enum class Minor : uint8_t
257 {
260 EmbeddedLast = 23,
263 TwoByteFollows = 25,
264 FourByteFollows = 26,
265 EightByteFollows = 27,
270 };
271 constexpr uint8_t EMBEDDED_MIN = static_cast<uint8_t>(Minor::EmbeddedFirst);
272 constexpr uint8_t EMBEDDED_MAX = static_cast<uint8_t>(Minor::EmbeddedLast);
273 constexpr Minor getMinor(uint8_t initial) { return Minor(initial & ((1 << 5) - 1)); }
274 constexpr uint8_t setMinor(uint8_t initial, Minor minor)
275 {
276 return static_cast<uint8_t>(
277 (initial & (~0 << 5)) |
278 static_cast<uint8_t>(minor));
279 }
280 constexpr uint8_t initialByte(Major major, Minor minor)
281 {
282 return setMinor(setMajor(0, major), minor);
283 }
284 consteval Minor minorFromSizeOf(const size_t bytes)
285 {
286 return
287 bytes == 1 ? Minor::OneByteFollows :
288 bytes == 2 ? Minor::TwoByteFollows :
289 bytes == 4 ? Minor::FourByteFollows :
290 Minor::EightByteFollows;
291 }
292
294 enum class SimpleValues : uint8_t
295 {
296 Unassigned0First = 0,
297 Unassigned0Last = 19,
298 False = 20,
299 True = 21,
300 Null = 22,
301 Undefined = 23,
302 ReservedFirst = 24,
303 ReservedLast = 31,
304 Unassigned1First = 32,
305 Unassigned1Last = 255,
306 };
307
308 struct Undefined{};
309
311 template<typename T, typename Placeholder>
312 struct WrapVoid{};;
313
314 template<typename Placeholder>
315 struct WrapVoid<void, Placeholder>
316 {
317 using T = Placeholder;
318 T value;
319 template<typename Fn, typename Tuple>
320 WrapVoid(Fn && fn, Tuple && args)
321 {
322 std::apply(std::forward<Fn>(fn), std::forward<Tuple>(args));
323 }
324 };
325
326 template<typename U, typename Placeholder> requires (!std::same_as<U, void>)
327 struct WrapVoid<U, Placeholder>
328 {
329 using T = U;
330 T value;
331 template<typename Fn, typename Tuple>
332 WrapVoid(Fn && fn, Tuple && args)
333 : value(std::apply(std::forward<Fn>(fn), std::forward<Tuple>(args)))
334 {
335 }
336 };
337
338 template<typename T, typename Placeholder>
339 using WrapVoidT = WrapVoid<T, Placeholder>::T;
340
341
343 bool packEmbedded(Major major, uint8_t value, std::span<uint8_t> & buf);
347 template<std::unsigned_integral Int>
348 bool pack(Major major, Int value, std::span<uint8_t> & buf);
349 template<>
350 bool pack<unsigned char>(Major major, unsigned char value, std::span<uint8_t> & buf);
351 template<>
352 bool pack<unsigned short>(Major major, unsigned short value, std::span<uint8_t> & buf);
353 template<>
354 bool pack<unsigned int>(Major major, unsigned int value, std::span<uint8_t> & buf);
355 template<>
356 bool pack<unsigned long>(Major major, unsigned long value, std::span<uint8_t> & buf);
357 template<>
358 bool pack<unsigned long long>(Major major, unsigned long long value, std::span<uint8_t> & buf);
359
362 struct Item
363 {
364 Major major;
365 Minor minor;
366 uint64_t value;
367 };
368
370 std::optional<Item> unpack(std::span<uint8_t> & buf);
371
374 template<std::unsigned_integral Int>
375 bool encode(Major major, Int value, std::span<uint8_t> & buf);
376 template<>
377 bool encode<unsigned char>(Major major, unsigned char value, std::span<uint8_t> & buf);
378 template<>
379 bool encode<unsigned short>(Major major, unsigned short value, std::span<uint8_t> & buf);
380 template<>
381 bool encode<unsigned int>(Major major, unsigned int value, std::span<uint8_t> & buf);
382 template<>
383 bool encode<unsigned long>(Major major, unsigned long value, std::span<uint8_t> & buf);
384 template<>
385 bool encode<unsigned long long>(Major major, unsigned long long value, std::span<uint8_t> & buf);
386
398 template<typename T>
399 struct Cbor
400 {
401 };
402
403 template<typename I> requires std::integral<I> && (!std::same_as<I, bool>)
404 struct Cbor<I>
405 {
406 static constexpr size_t bytes = sizeof(I);
407 static bool encode(I value, std::span<uint8_t> & buf)
408 {
409 Major major = value >= 0 ? Major::U64 : Major::Neg64;
411 value = value >= 0 ? value : ~value;
412 using UI = std::make_unsigned_t<I>;
413 return ::Cbor::encode<UI>(major, std::bit_cast<UI>(value), buf);
414 }
415 static std::optional<I> decode(std::span<uint8_t> & buf)
416 {
417 auto value = unpack(buf);
418 if (!value)
419 {
420 return {};
421 }
422 if (value->major == Major::Neg64)
423 {
425 value->value = ~value->value;
426 }
427 else if (value->major != Major::U64)
428 {
429 return {};
430 }
431 if (value->minor <= minorFromSizeOf(bytes))
432 {
433 return {value->value};
434 }
435 return {};
436 }
437 static size_t maxSize()
438 {
439 return 1 + bytes;
440 }
441 };
442
443 template<typename F> requires std::floating_point<F>
444#if defined(CBOR_HALF_SUPPORT)
445 || std::same_as<F, _Float16>
446#endif
447 struct Cbor<F>
448 {
449 static constexpr size_t bytes = sizeof(F);
450 using BitT = UintT<bytes * 8>;
451 static bool encode(F value, std::span<uint8_t> & buf)
452 {
453#if defined(CBOR_HALF_SUPPORT)
454 if (std::isnan(value))
455 {
456 const auto nan = std::bit_cast<uint16_t>(static_cast<_Float16>(NAN));
457 return ::Cbor::pack<uint16_t>(Major::Float, nan, buf);
458 }
459 const auto v16 = static_cast<_Float16>(value);
460 if (value == v16)
461 {
462 uint16_t b16 = std::bit_cast<uint16_t>(v16);
463 return ::Cbor::pack<uint16_t>(Major::Float, b16, buf);
464 }
465#endif
466 if (std::isnan(value))
467 {
468 const auto nan = std::bit_cast<uint32_t>(static_cast<float>(NAN));
469 return ::Cbor::pack<uint32_t>(Major::Float, nan, buf);
470 }
471 const auto v32 = static_cast<float>(value);
472 if (v32 == value)
473 {
474 const auto b32 = std::bit_cast<uint32_t>(v32);
475 return ::Cbor::pack<uint32_t>(Major::Float, b32, buf);
476 }
477 const auto v64 = static_cast<double>(value);
478 const auto b64 = std::bit_cast<uint64_t>(v64);
479 return ::Cbor::pack<uint64_t>(Major::Float, b64, buf);
480 }
481 static std::optional<F> decode(std::span<uint8_t> & buf)
482 {
483 auto value = unpack(buf);
484 if (!value || value->major != Major::Float)
485 {
486 return {};
487 }
488 // Allow upcasts to support deterministically encoded CBOR
489#if defined(CBOR_HALF_SUPPORT)
490 if (value->minor == Minor::TwoByteFollows && 2 <= bytes)
491 {
492 return {static_cast<F>(std::bit_cast<_Float16>(static_cast<uint16_t>(value->value)))};
493 }
494#endif
495 if (value->minor == Minor::FourByteFollows && 4 <= bytes)
496 {
497 return {static_cast<F>(std::bit_cast<float>(static_cast<uint32_t>(value->value)))};
498 }
499 if (value->minor == Minor::EightByteFollows && 8 <= bytes)
500 {
501 return {static_cast<F>(std::bit_cast<double>(static_cast<uint64_t>(value->value)))};
502 }
503 return {};
504 }
505 static size_t maxSize()
506 {
507 return 1 + bytes;
508 }
509 };
510
511 template<>
512 struct Cbor<bool>
513 {
514 static bool encode(bool value, std::span<uint8_t> & buf)
515 {
516 return packEmbedded(
518 static_cast<uint8_t>(value ? SimpleValues::True : SimpleValues::False),
519 buf);
520 }
521 static std::optional<bool> decode(std::span<uint8_t> & buf)
522 {
523 auto value = unpack(buf);
524 if (value.has_value() && value->major == Major::Simple)
525 {
526 if (value->minor == static_cast<Minor>(SimpleValues::True))
527 {
528 return {true};
529 }
530 else if (value->minor == static_cast<Minor>(SimpleValues::False))
531 {
532 return {false};
533 }
534 }
535 return {};
536 }
537 };
538
539 template<typename T>
540 struct Cbor<T *>
541 {
542 static bool encode(T * obj, std::span<uint8_t> & buf)
543 {
544 return obj != nullptr
545 ? Cbor<T>::encode(*obj, buf)
546 : packEmbedded(Major::Simple, static_cast<uint8_t>(SimpleValues::Null), buf);
547 }
548 };
549
550 template<>
551 struct Cbor<void>
552 {
553 static bool encode(Undefined, std::span<uint8_t> & buf)
554 {
555 return packEmbedded(
557 static_cast<uint8_t>(SimpleValues::Undefined), buf);
558 }
559
560 static std::optional<Undefined> decode(std::span<uint8_t> & buf)
561 {
562 auto value = unpack(buf);
563 if (value.has_value() &&
564 value->major == Major::Simple &&
565 value->minor == static_cast<Minor>(SimpleValues::Undefined))
566 {
567 return {Undefined{}};
568 }
569 return {};
570 }
571 };
572
573 template<>
574 struct Cbor<std::string_view>
575 {
576 static bool encode(std::string_view str, std::span<uint8_t> & buf)
577 {
578 if (!::Cbor::encode<size_t>(Major::Utf8, str.size(), buf))
579 {
580 return false;
581 }
582 for (char c : str)
583 {
584 if (buf.size() == 0)
585 {
586 return false;
587 }
588 buf[0] = c;
589 buf = buf.subspan(1);
590 }
591 return true;
592 }
593 };
594
595 template<>
596 struct Cbor<const char *>
597 {
598 static bool encode(const char * str, std::span<uint8_t> & buf)
599 {
600 const size_t size = strlen(str);
601 if (!::Cbor::encode<size_t>(Major::Utf8, size, buf))
602 {
603 return false;
604 }
605 for (size_t i = 0; i < size; ++i)
606 {
607 if (buf.size() == 0)
608 {
609 return false;
610 }
611 buf[0] = str[i];
612 buf = buf.subspan(1);
613 }
614 return true;
615 }
616 };
617
618 template<size_t size>
619 struct Cbor<const char (&)[size]>
620 {
621 static bool encode(const char (&str)[size], std::span<uint8_t> & buf)
622 {
623 if (!::Cbor::encode<size_t>(Major::Utf8, size, buf))
624 {
625 return false;
626 }
627 for (size_t i = 0; i < size; ++i)
628 {
629 if (buf.size() == 0)
630 {
631 return false;
632 }
633 buf[0] = str[i];
634 buf = buf.subspan(1);
635 }
636 return true;
637 }
638 };
639
640 template<typename... Item>
641 struct Cbor<std::tuple<Item...>>
642 {
643 using Tuple = std::tuple<Item...>;
644 static bool encode(Tuple tup, std::span<uint8_t> & buf)
645 {
646 if (buf.size() < 1)
647 {
648 return false;
649 }
650 return
651 ::Cbor::encode<size_t>(
653 std::tuple_size_v<Tuple>,
654 buf) &&
655 [&]<size_t... Index>(std::index_sequence<Index...>)
656 {
657 return (
658 Cbor<std::tuple_element_t<Index, Tuple>>::encode(std::get<Index>(tup), buf) &&
659 ...
660 );
661 }(std::index_sequence_for<Item...>{});
662 }
663 template<typename... T>
664 static std::optional<std::tuple<T...>> transpose(std::tuple<std::optional<T>...> ts)
665 {
666 return [&]<size_t... Index>(std::index_sequence<Index...>)
667 {
668 if ((std::get<Index>(ts) && ...))
669 {
670 return std::optional<std::tuple<T...>>{std::in_place, *std::get<Index>(ts)...};
671 }
672 return std::optional<std::tuple<T...>>{};
673 }(std::index_sequence_for<T...>{});
674 }
675 static std::optional<Tuple> decode(std::span<uint8_t> & buf)
676 {
677 auto value = unpack(buf);
678 if (value.has_value() && value->major == Major::Array)
679 {
680 std::tuple<std::optional<Item>...> ts{Cbor<Item>::decode(buf)...};
681 if (value->minor != Minor::Indefinite)
682 {
683 return transpose(ts);
684 }
685 else
686 {
687 auto sentinel = unpack(buf);
688 if (sentinel.has_value() &&
689 sentinel->major == Major::Simple &&
690 sentinel->minor == Minor::Indefinite)
691 {
692 return transpose(ts);
693 }
694 }
695 }
696 return {};
697 }
698 };
699
700 template<size_t Size>
701 struct Cbor<std::span<uint8_t, Size>>
702 {
703 static bool encode(std::span<uint8_t, Size> span, std::span<uint8_t> & buf)
704 {
705 if (buf.size() < 1)
706 {
707 return false;
708 }
709 if (!::Cbor::encode<size_t>(Major::Bytes, span.size(), buf))
710 {
711 return false;
712 }
713 if (buf.size() < span.size())
714 {
715 return false;
716 }
717 std::copy(span.cbegin(), span.cend(), buf.begin());
718 buf = buf.subspan(span.size());
719 return true;
720 }
721 static std::optional<std::span<uint8_t, Size>> decode(std::span<uint8_t> & buf)
722 {
723 auto value = unpack(buf);
724 if (value.has_value() && value->major == Major::Bytes)
725 {
726 if (value->minor == Minor::Indefinite)
727 {
728 return {};
729 }
730 else
731 {
732 return {buf};
733 }
734 }
735 return {};
736 }
737 };
738
739 template<typename T, size_t Size>
740 struct Cbor<std::array<T, Size>>
741 {
742 static bool encode(std::array<T, Size> array, std::span<uint8_t> & buf)
743 {
744 if (buf.size() < 1)
745 {
746 return false;
747 }
748 if (!::Cbor::encode<size_t>(Major::Array, array.size(), buf))
749 {
750 return false;
751 }
752 for (const auto & item : array)
753 {
754 if (!Cbor<T>::encode(item, buf))
755 {
756 return false;
757 }
758 }
759 return true;
760 }
761 static std::optional<std::array<T, Size>> decode(std::span<uint8_t> & buf)
762 {
763 auto value = unpack(buf);
764 if (value.has_value() && value->major == Major::Array)
765 {
766 std::array<T, Size> array =
767 [&]<size_t... Index>(std::index_sequence<Index...>)
768 {
769 return std::array<T, Size>{
770 *((void)Index, Cbor<T>::decode(buf))...
771 };
772 }(std::make_index_sequence<Size>{});
773 if (value->minor != Minor::Indefinite)
774 {
775 return array;
776 }
777 else
778 {
779 auto sentinel = unpack(buf);
780 if (sentinel.has_value() &&
781 sentinel->major == Major::Simple &&
782 sentinel->minor == Minor::Indefinite)
783 {
784 return array;
785 }
786 }
787 }
788 return {};
789 }
790 };
791
792 template<Major major>
793 class Sequence
794 {
795 public:
796 Sequence(std::span<uint8_t> & buf_, size_t extent_ = std::dynamic_extent)
797 : buf(buf_), extent(extent_)
798 {
799 if (extent == std::dynamic_extent)
800 {
801 packEmbedded(major, static_cast<uint8_t>(Minor::Indefinite), buf);
802 }
803 else
804 {
805 ::Cbor::encode<size_t>(major, extent, buf);
806 }
807 }
808 template<Major parentMajor>
809 Sequence(Sequence<parentMajor> & parentSeq, size_t extent_ = std::dynamic_extent)
810 : buf(parentSeq.buf), extent(extent_)
811 {
812 ++parentSeq.packed;
813 if (extent == std::dynamic_extent)
814 {
815 packEmbedded(major, static_cast<uint8_t>(Minor::Indefinite), buf);
816 }
817 else
818 {
819 ::Cbor::encode<size_t>(major, extent, buf);
820 }
821 }
822 bool as_expected()
823 {
824 return extent == std::dynamic_extent || extent == packed;
825 }
826 ~Sequence()
827 {
828 if (extent == std::dynamic_extent)
829 {
830 packEmbedded(Major::Simple, static_cast<uint8_t>(Minor::Indefinite), buf);
831 }
832 }
833
834 template<typename T>
835 bool encode(T value)
836 {
837 ++packed;
838 return Cbor<T>::encode(value, buf);
839 }
840 private:
841 std::span<uint8_t> & buf;
842 const size_t extent;
843 size_t packed = 0;
844 };
845};
bool packEmbedded(Major major, uint8_t value, std::span< uint8_t > &buf)
Pack a value [0, 23] embedded into the first header byte.
Definition cbor.cpp:16
Minor
Definition cbor.hpp:257
@ EmbeddedFirst
Embedded means no next byte, value is contained within.
Definition cbor.hpp:259
@ OneByteFollows
These contain the next value in the following byte(s).
Definition cbor.hpp:262
@ Indefinite
Definition cbor.hpp:269
std::optional< Item > unpack(std::span< uint8_t > &buf)
Unpack one item.
Definition cbor.cpp:121
SimpleValues
Possible values for Minor when Major is Simple/Float.
Definition cbor.hpp:295
bool pack(Major major, Int value, std::span< uint8_t > &buf)
bool encode(Major major, Int value, std::span< uint8_t > &buf)
Major
Upper three bits of the initial/header byte.
Definition cbor.hpp:222
@ Simple
Definition cbor.hpp:245
@ U64
Value is N the positive integer.
Definition cbor.hpp:224
@ Tagged
Value is the tag, followed by a single item.
Definition cbor.hpp:242
@ Array
Definition cbor.hpp:237
@ Map
Definition cbor.hpp:240
@ Bytes
Definition cbor.hpp:230
@ Utf8
Definition cbor.hpp:234
@ Neg64
Value is -N-1 (note this is ~N in 2s complement)
Definition cbor.hpp:226
static bool encode(I value, std::span< uint8_t > &buf)
Definition cbor.hpp:407
static std::optional< I > decode(std::span< uint8_t > &buf)
Definition cbor.hpp:415
Parametrized CBOR interface.
Definition cbor.hpp:400
Definition cbor.hpp:363
Definition cbor.hpp:308
Allows materialising void values using a placeholder.
Definition cbor.hpp:312
Simple type utilities e.g. smallest type for given value.