|
Comms CCF
This is a simple communication layer based on COBS, CBOR and FNV-1A
|
Encoding for structured data (numbers, strings, bytes, arrays and maps). Also supports custom tags. More...
#include "types.hpp"#include <float.h>#include <math.h>#include <stddef.h>#include <stdint.h>#include <string.h>#include <algorithm>#include <array>#include <bit>#include <concepts>#include <optional>#include <span>#include <string_view>#include <tuple>#include <type_traits>#include <utility>Go to the source code of this file.
Classes | |
| struct | Cbor::Undefined |
| struct | Cbor::WrapVoid< T, Placeholder > |
| Allows materialising void values using a placeholder. More... | |
| struct | Cbor::WrapVoid< void, Placeholder > |
| struct | Cbor::WrapVoid< U, Placeholder > |
| struct | Cbor::Item |
| struct | Cbor::Cbor< T > |
| Parametrized CBOR interface. More... | |
| struct | Cbor::Cbor< I > |
| struct | Cbor::Cbor< F > |
| struct | Cbor::Cbor< bool > |
| struct | Cbor::Cbor< T * > |
| struct | Cbor::Cbor< void > |
| struct | Cbor::Cbor< std::string_view > |
| struct | Cbor::Cbor< const char * > |
| struct | Cbor::Cbor< const char(&)[size]> |
| struct | Cbor::Cbor< std::tuple< Item... > > |
| struct | Cbor::Cbor< std::span< uint8_t, Size > > |
| struct | Cbor::Cbor< std::array< T, Size > > |
| class | Cbor::Sequence< major > |
Typedefs | |
| template<typename T, typename Placeholder> | |
| using | Cbor::WrapVoidT = WrapVoid<T, Placeholder>::T |
Enumerations | |
| enum class | Cbor::Major : uint8_t { U64 = 0 , Neg64 = 1 , Bytes = 2 , Utf8 = 3 , Array = 4 , Map = 5 , Tagged = 6 , Simple = 7 , Float = 7 } |
| Upper three bits of the initial/header byte. More... | |
| enum class | Cbor::Minor : uint8_t { EmbeddedFirst = 0 , EmbeddedLast = 23 , OneByteFollows = 24 , TwoByteFollows = 25 , FourByteFollows = 26 , EightByteFollows = 27 , Indefinite = 31 } |
| enum class | Cbor::SimpleValues : uint8_t { Unassigned0First = 0 , Unassigned0Last = 19 , False = 20 , True = 21 , Null = 22 , Undefined = 23 , ReservedFirst = 24 , ReservedLast = 31 , Unassigned1First = 32 , Unassigned1Last = 255 } |
| Possible values for Minor when Major is Simple/Float. | |
Functions | |
| constexpr Major | Cbor::getMajor (uint8_t initial) |
| constexpr uint8_t | Cbor::setMajor (uint8_t initial, Major major) |
| constexpr Minor | Cbor::getMinor (uint8_t initial) |
| constexpr uint8_t | Cbor::setMinor (uint8_t initial, Minor minor) |
| constexpr uint8_t | Cbor::initialByte (Major major, Minor minor) |
| consteval Minor | Cbor::minorFromSizeOf (const size_t bytes) |
| bool | Cbor::packEmbedded (Major major, uint8_t value, std::span< uint8_t > &buf) |
| Pack a value [0, 23] embedded into the first header byte. | |
| template<std::unsigned_integral Int> | |
| bool | Cbor::pack (Major major, Int value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::pack< unsigned char > (Major major, unsigned char value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::pack< unsigned short > (Major major, unsigned short value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::pack< unsigned int > (Major major, unsigned int value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::pack< unsigned long > (Major major, unsigned long value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::pack< unsigned long long > (Major major, unsigned long long value, std::span< uint8_t > &buf) |
| std::optional< Item > | Cbor::unpack (std::span< uint8_t > &buf) |
| Unpack one item. | |
| template<std::unsigned_integral Int> | |
| bool | Cbor::encode (Major major, Int value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::encode< unsigned char > (Major major, unsigned char value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::encode< unsigned short > (Major major, unsigned short value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::encode< unsigned int > (Major major, unsigned int value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::encode< unsigned long > (Major major, unsigned long value, std::span< uint8_t > &buf) |
| template<> | |
| bool | Cbor::encode< unsigned long long > (Major major, unsigned long long value, std::span< uint8_t > &buf) |
Variables | |
| constexpr uint8_t | Cbor::EMBEDDED_MIN = static_cast<uint8_t>(Minor::EmbeddedFirst) |
| constexpr uint8_t | Cbor::EMBEDDED_MAX = static_cast<uint8_t>(Minor::EmbeddedLast) |
Encoding for structured data (numbers, strings, bytes, arrays and maps). Also supports custom tags.
We need a simple, standardised (makes implementing in other languages easier) way to serialise and deserialise data. There's a couple of approaches:
The CBOR format has a consistent encoding across the different data-types, which makes implementation simple. The initial byte of a CBOR value is composed of:
[](u3 major, u5 minor) { return ((major << 5) | minor); }
The major types are (from RFC8949 table 1):
| Major Type | Meaning | Content |
|---|---|---|
| 0 | unsigned integer N | - |
| 1 | negative integer -1-N | - |
| 2 | byte string | N bytes |
| 3 | text string | N bytes (UTF-8 text) |
| 4 | array | N data items (elements) |
| 5 | map | 2N data items (key/value pairs) |
| 6 | tag of number N | 1 data item |
| 7 | simple/float | - |
And the value has the following forms (adapted from RFC8949 table 3):
| 5-Bit Minor | Semantics |
|---|---|
| 0..23/0x17 | Simple value (value 0..23) |
| 24/0x18 | Simple value (value 32..255 in following byte) |
| 25/0x19 | A 16 bit value |
| 26/0x1A | A 32 bit value |
| 27/0x1B | A 64 bit value |
| 28-30/C/D/E | Reserved, not well-formed in the present document |
| 31/0x1F | Start/stop indefinite length values |
Floating-point values are either 16/32/64 bit values, the major 7 minor values 0-255 are simple values. Indefinite length values are strings/arrays/maps without a length known ahead of time. They are started by e.g. an array with minor value 31, and are ended with a simple/float of minor value 31. In the case of strings, it is a concatenation of definite length strings
The table of simple values from RFC8949 table 4
| Value | Semantics |
|---|---|
| 0..19 | (unassigned) |
| 20 | false |
| 21 | true |
| 22 | null |
| 23 | undefined |
| 24..31 | (reserved) |
| 32..255 | (unassigned) |
I will be using the byte-string syntax b"\1\x0A" to denote a sequence of byte values in hexadecimal, so this is bytes 1, 10. The syntax N(X) denotes an item X that has an integer tag N.
Some examples to aid, from RFC8949 Appendix A (apologies about the wide table, the test/cbor.cpp code parses it so for simplicity I didn't split the rows up, but if this is rendered as HTML that doesn't matter):
Marker for test/cbor.cpp*
| Decoded | Encoded (initial byte, hexdump) | Type |
|---|---|---|
| 0 | 0:0 \ilinebr </td> <td class="markdownTableBodyNone"> uint64_t \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> 1 \ilinebr </td> <td class="markdownTableBodyNone"> `0:1` | uint64_t |
| 10 | 0:10 \ilinebr </td> <td class="markdownTableBodyNone"> uint64_t \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> 23 \ilinebr </td> <td class="markdownTableBodyNone"> `0:23` | uint64_t |
| 24 | 0:24 18 | uint64_t |
| 25 | 0:24 19 | uint64_t |
| 100 | 0:24 64 | uint64_t |
| 1000 | 0:25 03E8 | uint64_t |
| 1000000 | 0:26 000F4240 | uint64_t |
| 1000000000000 | 0:27 000000E8D4A51000 | uint64_t |
| 18446744073709551615 | 0:27 FFFFFFFFFFFFFFFF | uint64_t |
| 18446744073709551616 | 6:2 49010000000000000000 | Skip (value too large for 64_t) |
| -18446744073709551616 | 1:27 FFFFFFFFFFFFFFFF | Skip (value too large for 64_t) |
| -18446744073709551617 | 6:3 49010000000000000000 | Skip (value too large for 64_t) |
| -1 | 1:0 \ilinebr </td> <td class="markdownTableBodyNone"> int64_t \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> -10 \ilinebr </td> <td class="markdownTableBodyNone"> `1:9` | int64_t |
| -100 | 1:24 63 | int64_t |
| -1000 | 1:25 03E7 | int64_t |
| 0.0 | 7:25 0000 | _Float16 |
| -0.0 | 7:25 8000 | _Float16 |
| 1.0 | 7:25 3C00 | _Float16 |
| 1.1 | 7:27 3FF199999999999A | double |
| 1.5 | 7:25 3E00 | _Float16 |
| 65504.0 | 7:25 7BFF | _Float16 |
| 100000.0 | 7:26 47C35000 | float |
| 3.4028234663852886e+38 | 7:26 7F7FFFFF | float |
| 1.0e+300 | 7:27 7E37E43C8800759C | double |
| 5.960464477539063e-8 | 7:25 0001 | _Float16 |
| 0.00006103515625 | 7:25 0400 | _Float16 |
| -4.0 | 7:25 C400 | _Float16 |
| -4.1 | 7:27 C010666666666666 | double |
| Infinity | 7:25 7C00 | _Float16 |
| NaN | 7:25 7E00 | _Float16 |
| -Infinity | 7:25 FC00 | _Float16 |
| Infinity | 7:25 7C00 | float |
| NaN | 7:25 7E00 | float |
| -Infinity | 7:25 FC00 | float |
| Infinity | 7:25 7C00 | double |
| NaN | 7:25 7E00 | double |
| -Infinity | 7:25 FC00 | double |
| false | 7:20 \ilinebr </td> <td class="markdownTableBodyNone"> bool \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> true \ilinebr </td> <td class="markdownTableBodyNone"> `7:21` | bool |
| null | 7:22 \ilinebr </td> <td class="markdownTableBodyNone"> void * \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> undefined \ilinebr </td> <td class="markdownTableBodyNone"> `7:23` | N/A |
| simple(16) | 7:16 \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> simple(255) \ilinebr </td> <td class="markdownTableBodyNone"> `7:24` `FF` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> 0("2013-03-21T20:04:00Z") \ilinebr </td> <td class="markdownTableBodyNone"> `6:0` `74323031332D30332D32315432303A 30343A30305A` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> 1(1363896240) \ilinebr </td> <td class="markdownTableBodyNone"> `6:1` `1A514B67B0` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> 1(1363896240.5) \ilinebr </td> <td class="markdownTableBodyNone"> `6:1` `FB41D452D9EC200000` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> 23(h'01020304') \ilinebr </td> <td class="markdownTableBodyNone"> `6:23` `4401020304` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> 24(h'6449455446') \ilinebr </td> <td class="markdownTableBodyNone"> `6:24` `18456449455446` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> 32("http://www.example.com") \ilinebr </td> <td class="markdownTableBodyNone"> `6:24` `2076687474703A2F2F7777772E6578 616D706C652E636F6D` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> h'' \ilinebr </td> <td class="markdownTableBodyNone"> `2:0` | std::span<uint8_t> |
| h'01020304' | 2:4 01020304 | std::span<uint8_t> |
| h'31323334' | 2:4 31323334 | std::span<uint8_t> |
| "" | 3:0 \ilinebr </td> <td class="markdownTableBodyNone"> std::string_view \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> "a" \ilinebr </td> <td class="markdownTableBodyNone"> `3:1` `61` \ilinebr </td> <td class="markdownTableBodyNone"> std::string_view \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> "IETF" \ilinebr </td> <td class="markdownTableBodyNone"> `3:4` `49455446` \ilinebr </td> <td class="markdownTableBodyNone"> std::string_view \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> "\"" \ilinebr </td> <td class="markdownTableBodyNone"> `3:2` `225C` \ilinebr </td> <td class="markdownTableBodyNone"> Skip (not parsing escapes) \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> "\u00fc" \ilinebr </td> <td class="markdownTableBodyNone"> `3:2` `C3BC` \ilinebr </td> <td class="markdownTableBodyNone"> Skip (not parsing escapes) \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> "\u6c34" \ilinebr </td> <td class="markdownTableBodyNone"> `3:3` `E6B0B4` \ilinebr </td> <td class="markdownTableBodyNone"> Skip (not parsing escapes) \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> "\ud800\udd51" \ilinebr </td> <td class="markdownTableBodyNone"> `3:4` `F0908591` \ilinebr </td> <td class="markdownTableBodyNone"> Skip (not parsing escapes) \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> [] \ilinebr </td> <td class="markdownTableBodyNone"> `4:0` | std::span<int> |
| [1, 2, 3] | 4:3 010203 | std::span<int> |
| [1, [2, 3], [4, 5]] | 4:3 01820203820405 | tuple<int, span<int>, span<int>> |
| [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] | 4:24 190102030405060708090A0B0C0D0E 0F101112131415161718181819 | std::span<int> |
| {} | 5:0 ` \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> {1: 2, 3: 4} \ilinebr </td> <td class="markdownTableBodyNone"> 5:2 01020304 \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> {"a": 1, "b": [2, 3]} \ilinebr </td> <td class="markdownTableBodyNone"> 5:2 6161016162820203 \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone"> ["a", {"b": "c"}] \ilinebr </td> <td class="markdownTableBodyNone"> 4:2 6161A161626163 \ilinebr </td> <td class="markdownTableBodyNone"> N/A \ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone"> {"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"} \ilinebr </td> <td class="markdownTableBodyNone"> 5:5 616161416162614261636143616461 4461656145‘ | N/A |
| (_ h'0102’, h'030405') | 2:31 42010243030405FF | N/A |
| (_ "strea", "ming") | 3:31 657374726561646D696E67FF | N/A |
| [_ ] | 4:31 FF | N/A |
| [_ 1, [2, 3], [_ 4, 5]] | 4:31 018202039F0405FFFF | N/A |
| [_ 1, [2, 3], [4, 5]] | 4:31 01820203820405FF | N/A |
| [1, [2, 3], [_ 4, 5]] | 4:3 018202039F0405FF | N/A |
| [1, [_ 2, 3], [4, 5]] | 4:3 019F0203FF820405 | N/A |
| [_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] | 4:31 0102030405060708090A0B0C0D0E0F 101112131415161718181819FF | N/A |
| {_ "a": 1, "b": [_ 2, 3]} | 5:31 61610161629F0203FFFF | N/A |
| ["a", {_ "b": "c"}] | 4:2 6161BF61626163FF | N/A |
| {_ "Fun": true, "Amt": -2} | 5:31 6346756EF563416D7421FF | N/A |
|
strong |
Upper three bits of the initial/header byte.
|
strong |
| bool Cbor::encode | ( | Major | major, |
| Int | value, | ||
| std::span< uint8_t > & | buf ) |
Encodes a value that can be up to N bits. Unlike
| bool Cbor::pack | ( | Major | major, |
| Int | value, | ||
| std::span< uint8_t > & | buf ) |
Pack an N bit value (usually if the value is [0, 23] you want packEmbedded). Ends up as an initial header byte and ceil(N/8) value bytes