520 lines
17 KiB
C++
520 lines
17 KiB
C++
#include "Encoder.hpp"
|
|
|
|
#include "Core.hpp"
|
|
#include "Utils.hpp"
|
|
|
|
#include <cstring>
|
|
#include <format>
|
|
#include <span>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
namespace CBOR
|
|
{
|
|
class NotEnoughSpace: public EncodeError
|
|
{
|
|
public:
|
|
NotEnoughSpace(std::size_t remaining, std::size_t needed)
|
|
: EncodeError(std::format("the encoder needs at least more {} bytes, "
|
|
"but only {} are left",
|
|
needed, remaining))
|
|
{}
|
|
};
|
|
|
|
class UnknownValue: public EncodeError
|
|
{
|
|
public:
|
|
UnknownValue()
|
|
: EncodeError("tried to encode an unknown CBOR value")
|
|
{}
|
|
};
|
|
|
|
namespace
|
|
{
|
|
template <typename... Funcs>
|
|
struct Overload: public Funcs...
|
|
{
|
|
using Funcs::operator()...;
|
|
};
|
|
|
|
std::uint8_t EncodeHeader(MajorType type, ArgumentPosition pos)
|
|
{
|
|
return std::to_underlying(type) | std::to_underlying(pos);
|
|
}
|
|
|
|
std::uint8_t EncodeHeader(MajorType type, MinorType minor)
|
|
{
|
|
return std::to_underlying(type) | std::to_underlying(minor);
|
|
}
|
|
|
|
std::uint8_t EncodeHeaderDirect(MajorType type, std::uint8_t value)
|
|
{
|
|
return std::to_underlying(type) | value;
|
|
}
|
|
|
|
void WriteHeader(EncoderBuffer &buffer, MajorType type, std::uint64_t argument)
|
|
{
|
|
if (argument <= 23) {
|
|
buffer.Write(EncodeHeaderDirect(type, std::uint8_t(argument)));
|
|
}
|
|
else if (argument <= 255) {
|
|
buffer.Write(EncodeHeader(type, ArgumentPosition::Next1B));
|
|
buffer.Write(std::uint8_t(argument));
|
|
}
|
|
else if (argument <= 65535) {
|
|
buffer.Write(EncodeHeader(type, ArgumentPosition::Next2B));
|
|
buffer.Write(std::uint16_t(argument));
|
|
}
|
|
else if (argument <= 4294967295) {
|
|
buffer.Write(EncodeHeader(type, ArgumentPosition::Next4B));
|
|
buffer.Write(std::uint32_t(argument));
|
|
}
|
|
else {
|
|
buffer.Write(EncodeHeader(type, ArgumentPosition::Next8B));
|
|
buffer.Write(std::uint64_t(argument));
|
|
}
|
|
}
|
|
}
|
|
|
|
EncoderBuffer::EncoderBuffer(Buffer buffer)
|
|
: mBuffer(FixedBuffer { .mCurrent = 0, .mBuffer = buffer, })
|
|
{}
|
|
|
|
EncoderBuffer::EncoderBuffer(std::vector<std::uint8_t> &buffer)
|
|
: mBuffer(DynamicBuffer { .mBuffer = &buffer })
|
|
{}
|
|
|
|
EncoderBuffer::EncoderBuffer(EncoderBuffer &&other)
|
|
: mBuffer(std::exchange(other.mBuffer, FixedBuffer { .mCurrent = 0, .mBuffer = {}, }))
|
|
{}
|
|
|
|
void EncoderBuffer::Write(std::uint8_t value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::Write(std::uint16_t value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::Write(std::uint32_t value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::Write(std::uint64_t value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::Write(ConstBuffer value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::Write(std::string_view value)
|
|
{
|
|
std::visit(Overload {
|
|
[value] (FixedBuffer &buffer) { buffer.Write(value); },
|
|
[value] (DynamicBuffer &buffer) { buffer.Write(value); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
std::size_t EncoderBuffer::Size() const
|
|
{
|
|
return std::visit(Overload {
|
|
[] (const FixedBuffer &buffer) { return buffer.Size(); },
|
|
[] (const DynamicBuffer &buffer) { return buffer.Size(); },
|
|
}, mBuffer);
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(std::uint8_t value)
|
|
{
|
|
EnsureSpace(1);
|
|
mBuffer[mCurrent++] = value;
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(std::uint16_t value)
|
|
{
|
|
EnsureSpace(2);
|
|
static constexpr std::uint16_t mask = 0x00'FF;
|
|
std::uint16_t network = HostToNetwork(value);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network ) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(std::uint32_t value)
|
|
{
|
|
EnsureSpace(4);
|
|
static constexpr std::uint32_t mask = 0x00'00'00'FF;
|
|
std::uint32_t network = HostToNetwork(value);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network ) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 16) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 24) & mask);
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(std::uint64_t value)
|
|
{
|
|
EnsureSpace(8);
|
|
static constexpr std::uint64_t mask = 0x00'00'00'00'00'00'00'FF;
|
|
std::uint64_t network = HostToNetwork(value);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network ) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 16) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 24) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 32) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 40) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 48) & mask);
|
|
mBuffer[mCurrent++] = static_cast<std::uint8_t>((network >> 56) & mask);
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(ConstBuffer value)
|
|
{
|
|
EnsureSpace(value.size());
|
|
std::memcpy(mBuffer.data() + mCurrent, value.data(), value.size());
|
|
mCurrent += value.size();
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::Write(std::string_view value)
|
|
{
|
|
EnsureSpace(value.size());
|
|
std::memcpy(mBuffer.data() + mCurrent, value.data(), value.size());
|
|
mCurrent += value.size();
|
|
}
|
|
|
|
void EncoderBuffer::FixedBuffer::EnsureSpace(std::size_t size) const
|
|
{
|
|
std::size_t spaceLeft = mCurrent > mBuffer.size() ? 0 : mBuffer.size() - mCurrent;
|
|
|
|
if (size > spaceLeft) {
|
|
throw NotEnoughSpace(spaceLeft, size);
|
|
}
|
|
}
|
|
|
|
std::size_t EncoderBuffer::FixedBuffer::Size() const
|
|
{
|
|
return mCurrent;
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(std::uint8_t value)
|
|
{
|
|
mBuffer->push_back(value);
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(std::uint16_t value)
|
|
{
|
|
static constexpr std::uint16_t mask = 0x00'FF;
|
|
std::uint16_t network = HostToNetwork(value);
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network ) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 8) & mask));
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(std::uint32_t value)
|
|
{
|
|
static constexpr std::uint32_t mask = 0x00'00'00'FF;
|
|
std::uint32_t network = HostToNetwork(value);
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network ) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 8) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 16) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 24) & mask));
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(std::uint64_t value)
|
|
{
|
|
static constexpr std::uint64_t mask = 0x00'00'00'00'00'00'00'FF;
|
|
std::uint64_t network = HostToNetwork(value);
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network ) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 8) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 16) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 24) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 32) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 40) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 48) & mask));
|
|
mBuffer->push_back(static_cast<std::uint8_t>((network >> 56) & mask));
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(ConstBuffer value)
|
|
{
|
|
mBuffer->append_range(value);
|
|
}
|
|
|
|
void EncoderBuffer::DynamicBuffer::Write(std::string_view value)
|
|
{
|
|
mBuffer->append_range(value);
|
|
}
|
|
|
|
std::size_t EncoderBuffer::DynamicBuffer::Size() const
|
|
{
|
|
return mBuffer->size();
|
|
}
|
|
|
|
BasicEncoder::BasicEncoder(EncoderBuffer buffer)
|
|
: mBuffer(std::move(buffer))
|
|
{}
|
|
|
|
void BasicEncoder::Encode(bool value)
|
|
{
|
|
if (value) {
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::True));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::False));
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(Special value)
|
|
{
|
|
switch (value) {
|
|
case Special::Null:
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::Null));
|
|
break;
|
|
case Special::Undefined:
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::Undefined));
|
|
break;
|
|
default:
|
|
throw UnknownValue();
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::int8_t value)
|
|
{
|
|
if (value >= 0) {
|
|
if (value <= 23) {
|
|
mBuffer.Write(EncodeHeaderDirect(MajorType::Unsigned, std::uint8_t(value)));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next1B));
|
|
mBuffer.Write(static_cast<std::uint8_t>(value));
|
|
}
|
|
}
|
|
else {
|
|
std::int8_t actual = -(value + 1);
|
|
if (actual <= 23) {
|
|
mBuffer.Write(EncodeHeaderDirect(MajorType::Negative, std::uint8_t(actual)));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next1B));
|
|
mBuffer.Write(static_cast<std::uint8_t>(actual));
|
|
}
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::int16_t value)
|
|
{
|
|
if (value >= -128 && value <= 127) {
|
|
Encode(static_cast<std::int8_t>(value));
|
|
}
|
|
else if (value >= 0 && value <= 255) {
|
|
Encode(static_cast<std::uint8_t>(value));
|
|
}
|
|
else if (value >= -256 && value <= -1) {
|
|
std::uint8_t actual = static_cast<std::uint8_t>(-(value + 1));
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next1B));
|
|
mBuffer.Write(actual);
|
|
}
|
|
else if (value >= 0) {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next2B));
|
|
mBuffer.Write(static_cast<std::uint16_t>(value));
|
|
}
|
|
else {
|
|
std::int16_t actual = -(value + 1);
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next2B));
|
|
mBuffer.Write(static_cast<std::uint16_t>(actual));
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::int32_t value)
|
|
{
|
|
if (value >= -32768 && value <= 32767){
|
|
Encode(static_cast<std::int16_t>(value));
|
|
}
|
|
else if (value >= 0 && value <= 65535) {
|
|
Encode(static_cast<std::uint16_t>(value));
|
|
}
|
|
else if (value >= -65536 && value <= -1) {
|
|
std::uint16_t actual = static_cast<std::uint16_t>(std::abs(value + 1));
|
|
mBuffer.Write(actual);
|
|
}
|
|
else if (value >= 0) {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next4B));
|
|
mBuffer.Write(static_cast<std::uint32_t>(value));
|
|
}
|
|
else {
|
|
std::int32_t actual = std::abs(value + 1);
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next4B));
|
|
mBuffer.Write(static_cast<std::uint32_t>(actual));
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::int64_t value)
|
|
{
|
|
if (value >= -2147483648 && value <= 2147483647){
|
|
Encode(static_cast<std::int32_t>(value));
|
|
}
|
|
else if (value >= 0 && value <= 4294967295) {
|
|
Encode(static_cast<std::uint32_t>(value));
|
|
}
|
|
else if (value >= -2147483648 && value <= -1) {
|
|
std::uint32_t actual = static_cast<std::uint32_t>(std::abs(value + 1));
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next4B));
|
|
mBuffer.Write(actual);
|
|
}
|
|
else if (value >= 0) {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next8B));
|
|
mBuffer.Write(static_cast<std::uint64_t>(value));
|
|
}
|
|
else {
|
|
std::int64_t actual = std::abs(value + 1);
|
|
mBuffer.Write(EncodeHeader(MajorType::Negative, ArgumentPosition::Next8B));
|
|
mBuffer.Write(static_cast<std::uint64_t>(actual));
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::uint8_t value)
|
|
{
|
|
if (value <= 23) {
|
|
mBuffer.Write(EncodeHeaderDirect(MajorType::Unsigned, value));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next1B));
|
|
mBuffer.Write(value);
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::uint16_t value)
|
|
{
|
|
if (value <= 255) {
|
|
Encode(static_cast<std::uint8_t>(value));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next2B));
|
|
mBuffer.Write(value);
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::uint32_t value)
|
|
{
|
|
if (value <= 65535) {
|
|
Encode(static_cast<std::uint16_t>(value));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next4B));
|
|
mBuffer.Write(value);
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::uint64_t value)
|
|
{
|
|
if (value <= 4294967295) {
|
|
Encode(static_cast<std::uint32_t>(value));
|
|
}
|
|
else {
|
|
mBuffer.Write(EncodeHeader(MajorType::Unsigned, ArgumentPosition::Next8B));
|
|
mBuffer.Write(value);
|
|
}
|
|
}
|
|
|
|
void BasicEncoder::Encode(float value)
|
|
{
|
|
// Note(3011): This is suboptimal, but dealing with IEEE 754 numbers correctly
|
|
// is difficult, and has been left for later.
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::Float));
|
|
mBuffer.Write(std::bit_cast<std::uint32_t>(value));
|
|
}
|
|
|
|
void BasicEncoder::Encode(double value)
|
|
{
|
|
// Note(3011): This is suboptimal, but dealing with IEEE 754 numbers correctly
|
|
// is difficult, and has been left for later.
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::Double));
|
|
mBuffer.Write(std::bit_cast<std::uint64_t>(value));
|
|
}
|
|
|
|
void BasicEncoder::Encode(ConstBuffer value)
|
|
{
|
|
WriteHeader(mBuffer, MajorType::Binary, value.size());
|
|
mBuffer.Write(value);
|
|
}
|
|
|
|
void BasicEncoder::Encode(const char *value)
|
|
{
|
|
Encode(std::string_view(value));
|
|
}
|
|
|
|
void BasicEncoder::Encode(std::string_view value)
|
|
{
|
|
WriteHeader(mBuffer, MajorType::String, value.size());
|
|
mBuffer.Write(value);
|
|
}
|
|
|
|
void BasicEncoder::BeginArray(std::size_t size)
|
|
{
|
|
WriteHeader(mBuffer, MajorType::Array, size);
|
|
}
|
|
|
|
void BasicEncoder::BeginMap(std::size_t size)
|
|
{
|
|
WriteHeader(mBuffer, MajorType::Map, size);
|
|
}
|
|
|
|
void BasicEncoder::BeginIndefiniteBinary()
|
|
{
|
|
mBuffer.Write(EncodeHeader(MajorType::Binary, ArgumentPosition::Indefinite));
|
|
}
|
|
|
|
void BasicEncoder::BeginIndefiniteString()
|
|
{
|
|
mBuffer.Write(EncodeHeader(MajorType::String, ArgumentPosition::Indefinite));
|
|
}
|
|
|
|
void BasicEncoder::BeginIndefiniteArray()
|
|
{
|
|
mBuffer.Write(EncodeHeader(MajorType::Array, ArgumentPosition::Indefinite));
|
|
}
|
|
|
|
void BasicEncoder::BeginIndefiniteMap()
|
|
{
|
|
mBuffer.Write(EncodeHeader(MajorType::Map, ArgumentPosition::Indefinite));
|
|
}
|
|
|
|
void BasicEncoder::End()
|
|
{
|
|
mBuffer.Write(EncodeHeader(MajorType::Other, MinorType::Break));
|
|
}
|
|
|
|
void BasicEncoder::EncodeTag(std::uint64_t value)
|
|
{
|
|
WriteHeader(mBuffer, MajorType::Tag, value);
|
|
}
|
|
|
|
std::size_t BasicEncoder::Size() const
|
|
{
|
|
return mBuffer.Size();
|
|
}
|
|
|
|
Encoder::Encoder(EncoderBuffer buffer)
|
|
: mEncoder(std::move(buffer))
|
|
{}
|
|
|
|
std::size_t Encoder::Size() const
|
|
{
|
|
return mEncoder.Size();
|
|
}
|
|
}
|