General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

295 lines
8.0 KiB

#pragma once
#include <gp/bitops.hpp>
#include <gp/optional.hpp>
#include <gp/pair.hpp>
#include <gp/variant.hpp>
#include <gp/vector.hpp>
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)}
{}
};
struct ieee754_hf final {
uint16_t sign : 1;
uint16_t exponent : 5;
uint16_t mantissa : 10;
};
using cbor_floating_point = gp::fixed_variant<
ieee754_hf,
float,
double
>;
struct undefined_t final {};
template<typename T>
using cbor_composite = gp::fixed_variant<
undefined_t,
cbor_number,
gp::buffer<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(cbor_composite<cbor_value>(undefined_t{}))
, alloc(alloc_v)
{}
cbor_value(cbor_number number, allocator& alloc_v)
: contents(number)
, 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 auto encode_float(buffer<std::byte> dest, cbor_floating_point& value) {
switch(value.type()) {
case cbor_floating_point::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.value<ieee754_hf>();
return dest.begin()+3;
}
case cbor_floating_point::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.value<float>();
return dest.begin()+5;
}
case cbor_floating_point::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.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::buffer<std::byte>>(): {
auto& ref = contents.value<gp::buffer<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();
}
}
};
}