#pragma once
|
|
|
|
#include <gp/algorithms/repeat.hpp>
|
|
#include <gp/math/boolean/bitops.hpp>
|
|
#include <gp/functional/optional.hpp>
|
|
#include <gp/utils/pair.hpp>
|
|
#include <gp/functional/variant.hpp>
|
|
#include <gp/containers/vector.hpp>
|
|
|
|
// TODO: Rewrite in a more ORM-like way
|
|
// TODO: Implement some bignum for support
|
|
|
|
namespace gp {
|
|
enum class cbor_type : uint8_t {
|
|
uint = 0,
|
|
nint = 1,
|
|
bstr = 2,
|
|
tstr = 3,
|
|
list = 4,
|
|
hmap = 5,
|
|
tags = 6,
|
|
oths = 7
|
|
};
|
|
|
|
enum class cbor_oths : uint8_t {
|
|
value_false = 20,
|
|
value_true = 21,
|
|
value_null = 22,
|
|
value_undefined = 23,
|
|
byte = 24,
|
|
word = 25,
|
|
dword = 26,
|
|
qword = 27,
|
|
terminator = 31
|
|
};
|
|
|
|
enum class cbor_tags {
|
|
datetime = 0,
|
|
unix_time = 1,
|
|
ubignum = 2,
|
|
nbignum = 3,
|
|
decimal = 4,
|
|
bigfloat = 5,
|
|
cose_encrypt0 = 16,
|
|
cose_mac0 = 17,
|
|
cose_sign1 = 18,
|
|
expected_base64url = 21,
|
|
expected_base64 = 22,
|
|
expected_base16 = 23,
|
|
encoded_cbor = 24,
|
|
url = 32,
|
|
base64url = 33,
|
|
base64 = 34,
|
|
regexp = 35,
|
|
mime = 36,
|
|
cose_encrypt = 96,
|
|
cose_mac = 97,
|
|
cose_sign = 98,
|
|
signature = 55799
|
|
};
|
|
|
|
struct cbor_number final {
|
|
bool sign;
|
|
uint64_t value;
|
|
|
|
bool is_negative() {
|
|
return sign;
|
|
}
|
|
|
|
cbor_number(int64_t v)
|
|
: sign{v < 0}
|
|
, value{uint64_t((sign ? -1 : 1) * v)}
|
|
{}
|
|
|
|
cbor_number(bool s, uint64_t v)
|
|
: sign{s}
|
|
, value{v}
|
|
{}
|
|
};
|
|
|
|
struct ieee754_hf final {
|
|
uint16_t sign : 1;
|
|
uint16_t exponent : 5;
|
|
uint16_t mantissa : 10;
|
|
|
|
// TODO: support for denormalized values and NaNs
|
|
operator float() {
|
|
auto a = (uint32_t)((sign<<16) | ((exponent+0x1C000)<<13) | (mantissa<<13));
|
|
return *(float*)&a;
|
|
}
|
|
|
|
operator double() {
|
|
return (float)*this;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
vector<char>& push_as_cbor(vector<char>&, T);
|
|
|
|
template<typename T>
|
|
vector<char>& push_as_cbor(vector<char>&, T&);
|
|
|
|
inline vector<char>& push_integer_with_header_as_cbor(vector<char>& src, uint8_t header, uint64_t value) {
|
|
auto norm_v = (value<0) ? -value : value;
|
|
if(norm_v <= 23) {
|
|
src.push_back(header+norm_v);
|
|
} else if(norm_v < (1ll<<8ll)) {
|
|
src.push_back(header+24);
|
|
src.push_back(norm_v);
|
|
} else if(norm_v < (1ll<<16ll)) {
|
|
endian_wrapper<uint16_t, endian::big> wrapper = norm_v;
|
|
src.push_back(header+25);
|
|
for(auto byte : wrapper.bytes) {
|
|
src.push_back(byte);
|
|
}
|
|
} else if(norm_v < (1ll<<32ll)) {
|
|
endian_wrapper<uint32_t, endian::big> wrapper = norm_v;
|
|
src.push_back(header+26);
|
|
for(auto byte : wrapper.bytes) {
|
|
src.push_back(byte);
|
|
}
|
|
} else {
|
|
endian_wrapper<uint64_t, endian::big> wrapper = norm_v;
|
|
src.push_back(header+27);
|
|
for(auto byte : wrapper.bytes) {
|
|
src.push_back(byte);
|
|
}
|
|
}
|
|
return src;
|
|
}
|
|
|
|
/**
|
|
* @brief Pushes an integer as CBOR on the vector
|
|
*
|
|
* @param src the vector on which the push happens
|
|
* @param value the value to push, can be signed
|
|
* @return vector<char>& the same reference that was received for the source
|
|
*/
|
|
template<>
|
|
vector<char>& push_as_cbor<int64_t>(vector<char>& src, int64_t& value) {
|
|
uint8_t sign = (value<0) ? 0b00100000 : 0;
|
|
auto norm_v = (value<0) ? -value : value;
|
|
return push_integer_with_header_as_cbor(src, sign, norm_v);
|
|
}
|
|
|
|
/**
|
|
* @brief Pushes an unsigned integer as CBOR on the vector
|
|
*
|
|
* @param src the vector on which the push happens
|
|
* @param value the value to push, cannot be signed
|
|
* @return vector<char>& the same reference that was received for the source
|
|
*/
|
|
template<>
|
|
vector<char>& push_as_cbor<uint64_t>(vector<char>& src, uint64_t& value) {
|
|
return push_integer_with_header_as_cbor(src, 0, value);
|
|
}
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<std::nullptr_t>(vector<char>& src, std::nullptr_t) {
|
|
src.push_back(0b11110110);
|
|
return src;
|
|
}
|
|
|
|
struct cbor_undefined{};
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<cbor_undefined>(vector<char>& src, cbor_undefined) {
|
|
src.push_back(0b11110111);
|
|
return src;
|
|
}
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<bool>(vector<char>& src, bool value) {
|
|
src.push_back(0b11110100+(value ? 1 : 0));
|
|
return src;
|
|
}
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<gp::buffer<char>>(vector<char>& src, gp::buffer<char> value) {
|
|
push_integer_with_header_as_cbor(src, (uint8_t)0b01000000, value.size());
|
|
for(auto byte : value) {
|
|
src.push_back(byte);
|
|
}
|
|
return src;
|
|
}
|
|
|
|
struct cbor_array_initiator {
|
|
size_t size;
|
|
};
|
|
|
|
struct cbor_associative_array_initiator {
|
|
size_t size;
|
|
};
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<cbor_array_initiator>(vector<char>& src, cbor_array_initiator value) {
|
|
return push_integer_with_header_as_cbor(src, (uint8_t)0b10000000, value.size);
|
|
}
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<cbor_associative_array_initiator>(vector<char>& src, cbor_associative_array_initiator value) {
|
|
return push_integer_with_header_as_cbor(src, (uint8_t)0b10100000, value.size);
|
|
}
|
|
|
|
template<typename First, typename Second>
|
|
vector<char>& push_as_cbor(vector<char>& src, gp::pair<First, Second>& value) {
|
|
push_as_cbor<First>(src,value.first);
|
|
return push_as_cbor<Second>(src,value.second);
|
|
}
|
|
|
|
struct cbor_tag_initiator {
|
|
union {
|
|
size_t as_integer;
|
|
cbor_tags tag;
|
|
};
|
|
};
|
|
|
|
template<>
|
|
vector<char>& push_as_cbor<cbor_tag_initiator>(vector<char>& src, cbor_tag_initiator value) {
|
|
return push_integer_with_header_as_cbor(src, (uint8_t)0b11000000, value.as_integer);
|
|
}
|
|
|
|
using parsing_state = gp::buffer<char>;
|
|
|
|
template<typename T>
|
|
gp::pair<gp::optional<T>, parsing_state> read_cbor(parsing_state state);
|
|
|
|
inline gp::pair<gp::optional<uint64_t>, parsing_state> pull_arbitrary_integer_from_cbor(parsing_state state) {
|
|
auto local = (uint8_t)0b00011111 & (uint8_t)*state.begin();
|
|
if(local <= 23) {
|
|
return {*state.begin(), {state.begin()+1, state.end()}};
|
|
} else {
|
|
switch((cbor_oths)local) {
|
|
case cbor_oths::byte: {
|
|
if(state.size() < 2) return {nullopt, state};
|
|
return {*(state.begin()+1), {state.begin()+2, state.end()}};
|
|
}
|
|
case cbor_oths::word: {
|
|
if(state.size() < 3) return {nullopt, state};
|
|
return {
|
|
uint16_t(*(state.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, endian::big>>().begin().data)),
|
|
{state.begin()+3, state.end()}
|
|
};
|
|
}
|
|
case cbor_oths::dword: {
|
|
if(state.size() < 5) return {nullopt, state};
|
|
return {
|
|
uint32_t(*(state.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, endian::big>>().begin().data)),
|
|
{state.begin()+5, state.end()}
|
|
};
|
|
}
|
|
case cbor_oths::qword: {
|
|
if(state.size() < 9) return {nullopt, state};
|
|
return {
|
|
uint64_t(*(state.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, endian::big>>().begin().data)),
|
|
{state.begin()+9, state.end()}
|
|
};
|
|
}
|
|
default: {
|
|
return {nullopt, state};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<>
|
|
gp::pair<gp::optional<int64_t>, parsing_state> read_cbor<int64_t>(parsing_state state) {
|
|
if(state.size()) return {nullopt, state};
|
|
auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
|
|
|
|
int64_t sign = type == cbor_type::nint ? -1 : 1;
|
|
|
|
switch(type) {
|
|
case cbor_type::uint:
|
|
case cbor_type::nint:
|
|
{
|
|
auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
|
|
if(value.has_value()) return {value.value() * sign, new_state};
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return {nullopt, state};
|
|
}
|
|
|
|
template<>
|
|
gp::pair<gp::optional<uint64_t>, parsing_state> read_cbor<uint64_t>(parsing_state state) {
|
|
if(state.size()) return {nullopt, state};
|
|
auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
|
|
|
|
switch(type) {
|
|
case cbor_type::uint:
|
|
{
|
|
auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
|
|
if(value.has_value()) return {value.value(), new_state};
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return {nullopt, state};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct cbor_floating_point final {
|
|
using backing = gp::fixed_variant<
|
|
ieee754_hf,
|
|
float,
|
|
double
|
|
>;
|
|
backing contents;
|
|
|
|
cbor_floating_point(backing& p)
|
|
: contents{p}{}
|
|
|
|
cbor_floating_point(ieee754_hf p)
|
|
: contents{p}{}
|
|
|
|
cbor_floating_point(float p)
|
|
: contents{p}{}
|
|
|
|
cbor_floating_point(double p)
|
|
: contents{p}{}
|
|
|
|
operator ieee754_hf() {
|
|
return contents.value<ieee754_hf>();
|
|
}
|
|
|
|
operator float() {
|
|
switch(contents.type()) {
|
|
case backing::alt<ieee754_hf>():
|
|
return contents.value<ieee754_hf>();
|
|
case backing::alt<float>():
|
|
return contents.value<float>();
|
|
default:
|
|
gp_config::assertion(false,"this code should never be reached");
|
|
return std::numeric_limits<float>::quiet_NaN();
|
|
}
|
|
}
|
|
|
|
operator double() {
|
|
switch(contents.type()) {
|
|
case backing::alt<ieee754_hf>():
|
|
return contents.value<ieee754_hf>();
|
|
case backing::alt<float>():
|
|
return contents.value<float>();
|
|
case backing::alt<double>():
|
|
return contents.value<double>();
|
|
default:
|
|
gp_config::assertion(false,"this code should never be reached");
|
|
return std::numeric_limits<double>::quiet_NaN();
|
|
}
|
|
}
|
|
};
|
|
|
|
struct undefined_t final {};
|
|
|
|
template<typename T>
|
|
using cbor_composite = gp::fixed_variant<
|
|
gp::undefined_t,
|
|
cbor_number,
|
|
gp::vector<std::byte>,
|
|
bool,
|
|
gp::vector<T>,
|
|
gp::vector<gp::pair<T, T>>,
|
|
gp::nullopt_t,
|
|
cbor_floating_point
|
|
>;
|
|
|
|
class cbor_value {
|
|
|
|
cbor_composite<cbor_value> contents;
|
|
gp::reference_wrapper<allocator> alloc;
|
|
public:
|
|
|
|
cbor_value(allocator& alloc_v)
|
|
: contents()
|
|
, alloc(alloc_v)
|
|
{}
|
|
|
|
cbor_value(cbor_number number, allocator& alloc_v)
|
|
: contents(number)
|
|
, alloc(alloc_v)
|
|
{}
|
|
|
|
cbor_value(cbor_composite<cbor_value> val, allocator& alloc_v)
|
|
: contents(val)
|
|
, alloc(alloc_v)
|
|
{}
|
|
|
|
cbor_value(const cbor_value& oth)
|
|
: contents(oth.contents)
|
|
, alloc(oth.alloc)
|
|
{}
|
|
|
|
cbor_value(cbor_value&& oth)
|
|
: contents(gp::move(oth.contents))
|
|
, alloc(gp::move(oth.alloc))
|
|
{}
|
|
|
|
cbor_value& operator=(cbor_value& value) {
|
|
contents = value.contents;
|
|
alloc = value.alloc;
|
|
return *this;
|
|
}
|
|
|
|
cbor_value& operator=(cbor_value&& value) {
|
|
gp::swap(contents, value.contents);
|
|
gp::swap(alloc, value.alloc);
|
|
return *this;
|
|
}
|
|
|
|
cbor_value& operator=(cbor_composite<cbor_value>& value) {
|
|
contents = value;
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
cbor_value& operator=(T& value) {
|
|
contents = value;
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
cbor_value& operator=(T&& value) {
|
|
contents = gp::move(value);
|
|
return *this;
|
|
}
|
|
|
|
auto new_array() {
|
|
return gp::vector<cbor_value>{alloc};
|
|
}
|
|
|
|
auto new_object() {
|
|
return gp::vector<gp::pair<cbor_value, cbor_value>>{alloc};
|
|
}
|
|
|
|
static gp::pair<uint64_t, gp::buffer<std::byte>::associated_iterator> decode_integer(gp::buffer<std::byte> src) {
|
|
#define ERROR return {0, src.begin()}
|
|
auto local = (uint8_t)0b00011111 & (uint8_t)src[0];
|
|
if(local <= 23) {
|
|
return {local, src.begin()+1};
|
|
} else {
|
|
switch((cbor_oths)local) {
|
|
case cbor_oths::byte: {
|
|
if(src.size()<2) ERROR;
|
|
return {
|
|
(uint8_t)*(src.begin()+1),
|
|
src.begin()+2
|
|
};
|
|
}
|
|
case cbor_oths::word: {
|
|
if(src.size()<3) ERROR;
|
|
return {
|
|
uint16_t(*(src.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, endian::big>>().begin().data)),
|
|
src.begin()+3
|
|
};
|
|
}
|
|
case cbor_oths::dword: {
|
|
if(src.size()<5) ERROR;
|
|
return {
|
|
uint32_t(*(src.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, endian::big>>().begin().data)),
|
|
src.begin()+5
|
|
};
|
|
}
|
|
case cbor_oths::qword: {
|
|
if(src.size()<9) ERROR;
|
|
return {
|
|
uint64_t(*(src.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, endian::big>>().begin().data)),
|
|
src.begin()+9
|
|
};
|
|
}
|
|
default: {
|
|
ERROR;
|
|
}
|
|
}
|
|
}
|
|
#undef ERROR
|
|
}
|
|
|
|
template<typename U>
|
|
bool is_a() {
|
|
if constexpr (
|
|
std::is_same_v<U, uint8_t>
|
|
|| std::is_same_v<U, uint16_t>
|
|
|| std::is_same_v<U, uint32_t>
|
|
|| std::is_same_v<U, uint64_t>
|
|
|| std::is_same_v<U, int8_t>
|
|
|| std::is_same_v<U, int16_t>
|
|
|| std::is_same_v<U, int32_t>
|
|
|| std::is_same_v<U, int64_t>
|
|
) {
|
|
if(contents.is_a<cbor_number>())
|
|
{
|
|
auto& v = contents.value<cbor_number>();
|
|
if(!std::is_signed_v<U> && v.is_negative()) return false;
|
|
if(
|
|
std::numeric_limits<int64_t>::max()<v.value
|
|
&& (
|
|
!std::is_same_v<U, uint64_t>
|
|
|| v.is_negative()
|
|
)
|
|
) return false;
|
|
int64_t signed_v = (v.is_negative() ? -1 : 1 ) * v.value;
|
|
if(
|
|
std::numeric_limits<U>::min() <= signed_v
|
|
&& std::numeric_limits<U>::max() >= signed_v
|
|
) return true;
|
|
return false;
|
|
} else return false;
|
|
} else if constexpr (
|
|
std::is_same_v<U, ieee754_hf>
|
|
|| std::is_same_v<U, float>
|
|
|| std::is_same_v<U, double>
|
|
) {
|
|
auto& v = contents.value<cbor_floating_point>();
|
|
switch(v.contents.type()) {
|
|
case cbor_floating_point::backing::alt<ieee754_hf>():
|
|
return std::is_same_v<U, ieee754_hf>;
|
|
case cbor_floating_point::backing::alt<float>():
|
|
return std::is_same_v<U, float> || std::is_same_v<U, ieee754_hf>;
|
|
case cbor_floating_point::backing::alt<double>():
|
|
return std::is_same_v<U, double> || std::is_same_v<U, float> || std::is_same_v<U, ieee754_hf>;
|
|
default:
|
|
return false;
|
|
}
|
|
} else {
|
|
return contents.is_a<U>();
|
|
}
|
|
}
|
|
|
|
template<typename U>
|
|
U get_value() {
|
|
gp_config::assertion(is_a<U>(), "can't convert value legally");
|
|
if constexpr (
|
|
std::is_same_v<U, uint8_t>
|
|
|| std::is_same_v<U, uint16_t>
|
|
|| std::is_same_v<U, uint32_t>
|
|
|| std::is_same_v<U, uint64_t>
|
|
|| std::is_same_v<U, int8_t>
|
|
|| std::is_same_v<U, int16_t>
|
|
|| std::is_same_v<U, int32_t>
|
|
|| std::is_same_v<U, int64_t>
|
|
) {
|
|
auto& v = contents.value<cbor_number>();
|
|
return (v.is_negative() ? -1 : 1 ) * v.value;
|
|
} else if constexpr (
|
|
std::is_same_v<U, ieee754_hf>
|
|
|| std::is_same_v<U, float>
|
|
|| std::is_same_v<U, double>
|
|
) {
|
|
auto& v = contents.value<cbor_floating_point>();
|
|
return v;
|
|
} else {
|
|
return contents.value<U>();
|
|
}
|
|
}
|
|
|
|
static gp::pair<cbor_value, gp::buffer<std::byte>::associated_iterator> decode(allocator& alloc, gp::buffer<std::byte> src) {
|
|
#define ERROR return {cbor_value{alloc}, src.begin()}
|
|
if(src.size()==0) ERROR;
|
|
auto discriminant = (cbor_type)(((uint8_t)*src.begin()) >> 5);
|
|
auto local = uint8_t(((uint8_t)*src.begin()) & 0b00011111);
|
|
switch(discriminant) {
|
|
case cbor_type::uint:
|
|
case cbor_type::nint: {
|
|
auto nb = decode_integer(src);
|
|
return {cbor_value{cbor_number{(bool)discriminant, nb.first}, alloc}, nb.second};
|
|
}
|
|
case cbor_type::bstr: {
|
|
gp::vector<std::byte> str{alloc};
|
|
if(local == 31) {
|
|
auto sub = src.slice_end(src.size() - 1);
|
|
if(!sub.size()) ERROR;
|
|
while((uint8_t)sub[0] && 0b00011111 != 31) {
|
|
auto len = decode_integer(sub);
|
|
if(len.second == sub.begin()) ERROR;
|
|
str.reserve(str.size() + len.first);
|
|
auto end = (len.second + len.first);
|
|
if(len.first + (len.second - src.begin()) > src.size()) ERROR;
|
|
for(auto it = len.second; it != end; it++) {
|
|
str.push_back(*it);
|
|
}
|
|
sub = sub.slice_end(sub.size() - (len.first + (len.second - sub.begin())));
|
|
}
|
|
return {cbor_value{str, alloc}, sub.begin()+1};
|
|
} else if(auto len = decode_integer(src); len.second != src.begin()) {
|
|
str.reserve(len.first);
|
|
auto end = (len.second + len.first);
|
|
if(len.first + (len.second - src.begin()) > src.size()) ERROR;
|
|
for(auto it = len.second; it != end; it++) {
|
|
str.push_back(*it);
|
|
}
|
|
return {cbor_value{str, alloc}, end};
|
|
}
|
|
ERROR;
|
|
}
|
|
case cbor_type::tstr: {
|
|
ERROR;
|
|
}
|
|
case cbor_type::list: {
|
|
gp::vector<cbor_value> list{alloc};
|
|
if(local == 31) {
|
|
// TODO: Add the indefinite length version
|
|
} else if(auto len = decode_integer(src); len.second != src.begin()) {
|
|
auto sub = src.slice_end(src.size()-(len.second - src.begin()));
|
|
while(len.first--) {
|
|
auto tmp = decode(alloc, sub);
|
|
if(tmp.second == sub.begin()) ERROR;
|
|
list.push_back(tmp.first);
|
|
sub = sub.slice_end(sub.size() - (tmp.second - sub.begin()));
|
|
}
|
|
return {cbor_value{list, alloc}, sub.begin()};
|
|
}
|
|
ERROR;
|
|
}
|
|
case cbor_type::hmap: {
|
|
gp::vector<gp::pair<cbor_value, cbor_value>> list{alloc};
|
|
if(local == 31) {
|
|
// TODO: Add the indefinite length version
|
|
} else if(auto len = decode_integer(src); len.second != src.begin()) {
|
|
auto sub = src.slice_end(src.size()-(len.second - src.begin()));
|
|
while(len.first--) {
|
|
auto tmpl = decode(alloc, sub);
|
|
if(tmpl.second == sub.begin()) ERROR;
|
|
sub = sub.slice_end(sub.size() - (tmpl.second - sub.begin()));
|
|
auto tmpr = decode(alloc, sub);
|
|
if(tmpr.second == sub.begin()) ERROR;
|
|
list.push_back(gp::make_pair(
|
|
tmpl.first,
|
|
tmpr.first
|
|
));
|
|
sub = sub.slice_end(sub.size() - (tmpr.second - sub.begin()));
|
|
}
|
|
return {cbor_value{list, alloc}, sub.begin()};
|
|
}
|
|
ERROR;
|
|
}
|
|
case cbor_type::tags: {
|
|
// TODO: add tag decoding methods
|
|
}
|
|
case cbor_type::oths: {
|
|
switch((cbor_oths)local) {
|
|
case cbor_oths::value_false: {
|
|
return {
|
|
cbor_value{
|
|
cbor_composite<cbor_value>(false),
|
|
alloc
|
|
},
|
|
src.begin()+1
|
|
};
|
|
}
|
|
case cbor_oths::value_true: {
|
|
return {
|
|
cbor_value{
|
|
cbor_composite<cbor_value>(true),
|
|
alloc
|
|
},
|
|
src.begin()+1
|
|
};
|
|
}
|
|
case cbor_oths::value_null: {
|
|
return {
|
|
cbor_value{
|
|
cbor_composite<cbor_value>(nullopt),
|
|
alloc
|
|
},
|
|
src.begin()+1
|
|
};
|
|
}
|
|
case cbor_oths::value_undefined: {
|
|
return {
|
|
cbor_value{
|
|
cbor_composite<cbor_value>(undefined_t{}),
|
|
alloc
|
|
},
|
|
src.begin()+1
|
|
};
|
|
}
|
|
case cbor_oths::word: {
|
|
if(src.size()<3) ERROR;
|
|
return {
|
|
cbor_value{
|
|
cbor_floating_point{ieee754_hf(*(src.slice_start(3).slice_end(2).cast<gp::endian_wrapper<ieee754_hf, endian::big>>().begin().data))},
|
|
alloc
|
|
},
|
|
src.begin()+3
|
|
};
|
|
}
|
|
case cbor_oths::dword: {
|
|
if(src.size()<5) ERROR;
|
|
return {
|
|
cbor_value{
|
|
cbor_floating_point{float(*(src.slice_start(5).slice_end(4).cast<gp::endian_wrapper<float, endian::big>>().begin().data))},
|
|
alloc
|
|
},
|
|
src.begin()+5
|
|
};
|
|
}
|
|
case cbor_oths::qword: {
|
|
if(src.size()<9) ERROR;
|
|
return {
|
|
cbor_value{
|
|
cbor_floating_point{double(*(src.slice_start(9).slice_end(8).cast<gp::endian_wrapper<double, endian::big>>().begin().data))},
|
|
alloc
|
|
},
|
|
src.begin()+9
|
|
};
|
|
}
|
|
default: {
|
|
ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ERROR;
|
|
#undef ERROR
|
|
}
|
|
|
|
static auto encode_float(buffer<std::byte> dest, cbor_floating_point& value) {
|
|
switch(value.contents.type()) {
|
|
case cbor_floating_point::backing::alt<ieee754_hf>():{
|
|
if(dest.size() < 3) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::word);
|
|
(dest.slice_start(3).slice_end(2).cast<gp::endian_wrapper<ieee754_hf, gp::endian::big>>())[0] = value.contents.value<ieee754_hf>();
|
|
return dest.begin()+3;
|
|
}
|
|
case cbor_floating_point::backing::alt<float>():{
|
|
if(dest.size() < 5) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::dword);
|
|
(dest.slice_start(5).slice_end(4).cast<gp::endian_wrapper<float, gp::endian::big>>())[0] = value.contents.value<float>();
|
|
return dest.begin()+5;
|
|
}
|
|
case cbor_floating_point::backing::alt<double>():{
|
|
if(dest.size() < 9) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::qword);
|
|
(dest.slice_start(9).slice_end(8).cast<gp::endian_wrapper<double, gp::endian::big>>())[0] = value.contents.value<double>();
|
|
return dest.begin()+9;
|
|
}
|
|
default: return dest.begin();
|
|
}
|
|
}
|
|
|
|
static auto encode_length(buffer<std::byte> dest, cbor_type major, uint64_t value) {
|
|
auto num = value;
|
|
if(value <= 23) {
|
|
if(dest.size() < 1) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)major << 5u) + value);
|
|
return dest.begin()+1;
|
|
} else if(value <= 255) {
|
|
if(dest.size() < 2) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::byte);
|
|
dest[1] = std::byte(value);
|
|
return dest.begin() + 2;
|
|
} else if(value <= 65535) {
|
|
if(dest.size() < 3) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::word);
|
|
(dest.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, gp::endian::big>>())[0] = num;
|
|
return dest.begin()+3;
|
|
} else if(value <= 4294967295) {
|
|
if(dest.size() < 5) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::dword);
|
|
(dest.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, gp::endian::big>>())[0] = num;
|
|
return dest.begin()+5;
|
|
} else {
|
|
if(dest.size() < 9) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::qword);
|
|
(dest.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, gp::endian::big>>())[0] = num;
|
|
return dest.begin()+9;
|
|
}
|
|
}
|
|
|
|
auto encode(buffer<std::byte> dest) {
|
|
switch(contents.type()) {
|
|
case cbor_composite<cbor_value>::alt<undefined_t>(): {
|
|
if(dest.size() < 1) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_undefined);
|
|
return dest.begin()+1;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<cbor_number>(): {
|
|
auto& ref = contents.value<cbor_number>();
|
|
return encode_length(
|
|
dest,
|
|
ref.is_negative() ? cbor_type::nint : cbor_type::uint,
|
|
ref.value
|
|
);
|
|
}
|
|
case cbor_composite<cbor_value>::alt<gp::vector<std::byte>>(): {
|
|
auto& ref = contents.value<gp::vector<std::byte>>();
|
|
auto it = encode_length(
|
|
dest,
|
|
cbor_type::bstr,
|
|
ref.size()
|
|
);
|
|
if(it == dest.begin()) return it;
|
|
|
|
for(auto a : ref) {
|
|
*(it++) = a;
|
|
}
|
|
return it;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<bool>(): {
|
|
if(dest.size() < 1) return dest.begin();
|
|
if(contents.value<bool>())
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_true);
|
|
else
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_false);
|
|
return dest.begin()+1;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<gp::vector<cbor_value>>(): {
|
|
auto& ary = contents.value<gp::vector<cbor_value>>();
|
|
auto it_begin = encode_length(dest, cbor_type::list, ary.size());
|
|
if(it_begin == dest.begin()) return dest.begin();
|
|
for(auto& elem : ary) {
|
|
auto slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
|
|
auto it = elem.encode(slice);
|
|
if(it == it_begin)
|
|
return dest.begin();
|
|
it_begin = it;
|
|
}
|
|
return it_begin;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<gp::vector<gp::pair<cbor_value, cbor_value>>>(): {
|
|
auto& ary = contents.value<gp::vector<gp::pair<cbor_value,cbor_value>>>();
|
|
auto it_begin = encode_length(dest, cbor_type::hmap, ary.size());
|
|
if(it_begin == dest.begin()) return dest.begin();
|
|
|
|
for(auto& elem : ary) {
|
|
auto slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
|
|
auto it = elem.first.encode(slice);
|
|
if(it == it_begin) return dest.begin();
|
|
it_begin = it;
|
|
|
|
slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
|
|
it = elem.second.encode(slice);
|
|
if(it == it_begin) return dest.begin();
|
|
it_begin = it;
|
|
}
|
|
return it_begin;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<gp::nullopt_t>(): {
|
|
if(dest.size() < 1) return dest.begin();
|
|
dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_null);
|
|
return dest.begin()+1;
|
|
}
|
|
case cbor_composite<cbor_value>::alt<cbor_floating_point>(): {
|
|
return encode_float(dest, contents.value<cbor_floating_point>());
|
|
}
|
|
default: return dest.begin();
|
|
}
|
|
}
|
|
};
|
|
}
|