Basic project structure, basic encoder implementation
This commit is contained in:
commit
4159fc4643
12 changed files with 897 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.cache/
|
||||
build/
|
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
project(LibCBOR
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_subdirectory(LibCBOR)
|
||||
if(${PROJECT_IS_TOP_LEVEL})
|
||||
add_subdirectory(Tests)
|
||||
endif()
|
20
LibCBOR/CMakeLists.txt
Normal file
20
LibCBOR/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
add_library(LibCBOR STATIC)
|
||||
|
||||
target_compile_features(LibCBOR
|
||||
PUBLIC
|
||||
cxx_std_23
|
||||
)
|
||||
|
||||
target_include_directories(LibCBOR
|
||||
PUBLIC
|
||||
"Include"
|
||||
|
||||
PRIVATE
|
||||
"Include/CBOR"
|
||||
)
|
||||
|
||||
target_sources(LibCBOR
|
||||
PRIVATE
|
||||
"Source/Decoder.cpp"
|
||||
"Source/Encoder.cpp"
|
||||
)
|
8
LibCBOR/Include/CBOR/CBOR.hpp
Normal file
8
LibCBOR/Include/CBOR/CBOR.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef LIBCBOR_CBOR_HPP
|
||||
#define LIBCBOR_CBOR_HPP
|
||||
|
||||
#include "Core.hpp" // IWYU pragma: export
|
||||
#include "Decoder.hpp" // IWYU pragma: export
|
||||
#include "Encoder.hpp" // IWYU pragma: export
|
||||
|
||||
#endif // LIBCBOR_CBOR_HPP
|
150
LibCBOR/Include/CBOR/Core.hpp
Normal file
150
LibCBOR/Include/CBOR/Core.hpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
#ifndef LIBCBOR_CORE_HPP
|
||||
#define LIBCBOR_CORE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
// Note(3011): how2basic explantion of the CBOR format:
|
||||
// - Major types are the basic divisions into data types
|
||||
// - Major type 0 and 1 are integers, the argument is the value itself
|
||||
// - However, nothing in life is simple, and the values 24, 25, 26, and 27 are special, as
|
||||
// they specify that the actual value is stored in the next 1, 2, 4, or 8 bytes. Values
|
||||
// 28, 29, and 30 are reserved, and currently should be treated as malformed. Specifically
|
||||
// for these data types, the value 31 should also be treated as malformed.
|
||||
// - Major types 2~5 use the argument value as a length specifier, but first, the meanings.
|
||||
// - 2 is a binary string, i.e. raw binary data
|
||||
// - 3 is a utf-8 string (always unescaped)
|
||||
// - Indefinite length strings are composed of definite length chunks of the same major
|
||||
// type.
|
||||
// - 4 is an array of data items
|
||||
// - 5 is a key:value map of data items
|
||||
// - For these major types, the argument value specified the length of the data item.
|
||||
// Similarly to before, values 0-23 have their direct meaning, and values 24-27 use 1-8B
|
||||
// to store the length. Values 28-30 are reserved, and currently malformed. Value 31
|
||||
// signifies an item with indefinite length (these must be terminated with the special
|
||||
// "break" stop code).
|
||||
// - Major type 6 is a tag type that assigns a special meaning to the data items immediately
|
||||
// after it. It's otherwise equivalent to major type 0.
|
||||
// - Major type 7 specifies simple, or floating point values. Argument values 0-19 are
|
||||
// unassigned, 20 means false, 21 means true, 22 means null, 23 means undefined. Value 24
|
||||
// signifies a simple value stored in the next byte (only values 32~255 are allowed,
|
||||
// but none of these are currently in use). Values 25, 26, and 27 signify the the next 2, 4,
|
||||
// or 8 bytes store an IEEE 754 floating point type (half, single, or double precision
|
||||
// respectively). Values 28~30 are reserved, and currently malformed. Value 31 is used as the
|
||||
// "break" stop code for terminating indefinite length items.
|
||||
// - TREACHERY: The argument value is always in BIG ENDIAN BYTE ORDER.
|
||||
|
||||
// Floating point types must be IEEE 754 (equivalent to IEC 559).
|
||||
static_assert(std::numeric_limits<float>::is_iec559);
|
||||
static_assert(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
enum class MajorType: std::uint8_t
|
||||
{
|
||||
Unsigned = 0b0000'0000,
|
||||
Negative = 0b0010'0000,
|
||||
Binary = 0b0100'0000,
|
||||
String = 0b0110'0000,
|
||||
Array = 0b1000'0000,
|
||||
Map = 0b1010'0000,
|
||||
Tagged = 0b1100'0000,
|
||||
Other = 0b1110'0000,
|
||||
|
||||
TypeMask = 0b1110'0000,
|
||||
};
|
||||
|
||||
enum class ArgumentPosition: std::uint8_t
|
||||
{
|
||||
Direct00 = 0b0000'0000,
|
||||
Direct01 = 0b0000'0001,
|
||||
Direct02 = 0b0000'0010,
|
||||
Direct03 = 0b0000'0011,
|
||||
Direct04 = 0b0000'0100,
|
||||
Direct05 = 0b0000'0101,
|
||||
Direct06 = 0b0000'0110,
|
||||
Direct07 = 0b0000'0111,
|
||||
Direct08 = 0b0000'1000,
|
||||
Direct09 = 0b0000'1001,
|
||||
Direct10 = 0b0000'1010,
|
||||
Direct11 = 0b0000'1011,
|
||||
Direct12 = 0b0000'1100,
|
||||
Direct13 = 0b0000'1101,
|
||||
Direct14 = 0b0000'1110,
|
||||
Direct15 = 0b0000'1111,
|
||||
Direct16 = 0b0001'0000,
|
||||
Direct17 = 0b0001'0001,
|
||||
Direct18 = 0b0001'0010,
|
||||
Direct19 = 0b0001'0011,
|
||||
Direct21 = 0b0001'0101,
|
||||
Direct20 = 0b0001'0100,
|
||||
Direct23 = 0b0001'0111,
|
||||
Direct22 = 0b0001'0110,
|
||||
|
||||
Next1B = 0b0001'1000,
|
||||
Next2B = 0b0001'1001,
|
||||
Next4B = 0b0001'1010,
|
||||
Next8B = 0b0001'1011,
|
||||
|
||||
Reserved28 = 0b0001'1100,
|
||||
Reserved29 = 0b0001'1101,
|
||||
Reserved30 = 0b0001'1110,
|
||||
|
||||
Indefinite = 0b0001'1111,
|
||||
};
|
||||
|
||||
enum class MinorType: std::uint8_t
|
||||
{ // in the context of RFC 8949, read "unused" as "unassigned"
|
||||
Unused00 = 0b0000'0000,
|
||||
Unused01 = 0b0000'0001,
|
||||
Unused02 = 0b0000'0010,
|
||||
Unused03 = 0b0000'0011,
|
||||
Unused04 = 0b0000'0100,
|
||||
Unused05 = 0b0000'0101,
|
||||
Unused06 = 0b0000'0110,
|
||||
Unused07 = 0b0000'0111,
|
||||
Unused08 = 0b0000'1000,
|
||||
Unused09 = 0b0000'1001,
|
||||
Unused10 = 0b0000'1010,
|
||||
Unused11 = 0b0000'1011,
|
||||
Unused12 = 0b0000'1100,
|
||||
Unused13 = 0b0000'1101,
|
||||
Unused14 = 0b0000'1110,
|
||||
Unused15 = 0b0000'1111,
|
||||
Unused16 = 0b0001'0000,
|
||||
Unused17 = 0b0001'0001,
|
||||
Unused18 = 0b0001'0010,
|
||||
Unused19 = 0b0001'0011,
|
||||
|
||||
False = 0b0001'0100,
|
||||
True = 0b0001'0101,
|
||||
Null = 0b0001'0110,
|
||||
Undefined = 0b0001'0111,
|
||||
|
||||
SimpleNext = 0b0001'1000, // followed by 1B with either 0-31 (malformed) or 32-255 (unused)
|
||||
Half = 0b0001'1001, // followed by 2B with an IEEE 754 half precision float
|
||||
Float = 0b0001'1010, // followed by 4B with an IEEE 754 single precision float
|
||||
Double = 0b0001'1011, // followed by 8B with an IEEE 754 double precision float
|
||||
Reserved28 = 0b0001'1100,
|
||||
Reserved29 = 0b0001'1101,
|
||||
Reserved30 = 0b0001'1110,
|
||||
Break = 0b0001'1111,
|
||||
|
||||
TypeMask = 0b0001'1111,
|
||||
};
|
||||
|
||||
enum class Special: std::uint8_t
|
||||
{
|
||||
Null,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
class Error: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBCBOR_CORE_HPP
|
27
LibCBOR/Include/CBOR/Decoder.hpp
Normal file
27
LibCBOR/Include/CBOR/Decoder.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef LIBCBOR_DECODER_HPP
|
||||
#define LIBCBOR_DECODER_HPP
|
||||
|
||||
#include "Core.hpp"
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
class DecodeError: public Error
|
||||
{
|
||||
public:
|
||||
using Error::Error;
|
||||
};
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
private:
|
||||
};
|
||||
|
||||
class Validator
|
||||
{
|
||||
public:
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBCBOR_DECODER_HPP
|
65
LibCBOR/Include/CBOR/Encoder.hpp
Normal file
65
LibCBOR/Include/CBOR/Encoder.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef LIBCBOR_ENCODER_HPP
|
||||
#define LIBCBOR_ENCODER_HPP
|
||||
|
||||
#include "Core.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
class EncodeError: public Error
|
||||
{
|
||||
public:
|
||||
using Error::Error;
|
||||
};
|
||||
|
||||
// Note(3011): This is the basic CBOR encoder implementation. It will encode just about any
|
||||
// data into a valid CBOR representation (with the exception of half precision floating point
|
||||
// numbers). It will also select the optimal storage for data item arguments, with the exception
|
||||
// of floating point numbers, that are stored directly (i.e. a float will always take up 5B with
|
||||
// the header, and a double will always take up 9B including the header). The main shortcoming
|
||||
// of this interface is that the user must always keep track of all structures himself.
|
||||
class BasicEncoder
|
||||
{
|
||||
public:
|
||||
BasicEncoder(std::span<std::uint8_t> buffer);
|
||||
|
||||
void Encode(bool value);
|
||||
void Encode(Special value);
|
||||
|
||||
void Encode(std::int8_t value);
|
||||
void Encode(std::int16_t value);
|
||||
void Encode(std::int32_t value);
|
||||
void Encode(std::int64_t value);
|
||||
|
||||
void Encode(std::uint8_t value);
|
||||
void Encode(std::uint16_t value);
|
||||
void Encode(std::uint32_t value);
|
||||
void Encode(std::uint64_t value);
|
||||
|
||||
// Note(3011): float16_t is currently not supported
|
||||
void Encode(float value);
|
||||
void Encode(double value);
|
||||
|
||||
void Encode(std::span<std::uint8_t> value);
|
||||
void Encode(const char *value);
|
||||
void Encode(std::string_view value);
|
||||
|
||||
void BeginArray(std::size_t size);
|
||||
void BeginMap(std::size_t size);
|
||||
|
||||
void BeginIndefiniteBinary();
|
||||
void BeginIndefiniteString();
|
||||
void BeginIndefiniteArray();
|
||||
void BeginIndefiniteMap();
|
||||
|
||||
void End();
|
||||
private:
|
||||
std::size_t mCurrent;
|
||||
std::span<std::uint8_t> mBuffer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBCBOR_ENCODER_HPP
|
0
LibCBOR/Source/Decoder.cpp
Normal file
0
LibCBOR/Source/Decoder.cpp
Normal file
478
LibCBOR/Source/Encoder.cpp
Normal file
478
LibCBOR/Source/Encoder.cpp
Normal file
|
@ -0,0 +1,478 @@
|
|||
#include "Encoder.hpp"
|
||||
#include "Core.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#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
|
||||
{
|
||||
std::size_t ArgumentSize(std::size_t value)
|
||||
{
|
||||
if (value <= 23) {
|
||||
return 0;
|
||||
}
|
||||
if (value <= 255) {
|
||||
return 1;
|
||||
}
|
||||
else if (value <= 65535) {
|
||||
return 2;
|
||||
}
|
||||
else if (value <= 4294967295) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t SpaceLeft(std::span<std::uint8_t> buffer, std::size_t offset)
|
||||
{
|
||||
return buffer.size() - offset;
|
||||
}
|
||||
|
||||
void EnsureEnoughSpace(std::span<std::uint8_t> buffer, std::size_t offset,
|
||||
std::size_t spaceRequired)
|
||||
{
|
||||
if (SpaceLeft(buffer, offset) < spaceRequired) {
|
||||
throw NotEnoughSpace(SpaceLeft(buffer, offset), spaceRequired);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(std::span<std::uint8_t> &buffer, std::size_t ¤t, std::uint16_t value)
|
||||
{
|
||||
static constexpr std::uint16_t mask = 0x00'FF;
|
||||
std::uint16_t network = HostToNetwork(value);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network ) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
||||
}
|
||||
|
||||
void Write(std::span<std::uint8_t> &buffer, std::size_t ¤t, std::uint32_t value)
|
||||
{
|
||||
static constexpr std::uint32_t mask = 0x00'00'00'FF;
|
||||
std::uint32_t network = HostToNetwork(value);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network ) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 16) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 24) & mask);
|
||||
}
|
||||
|
||||
void Write(std::span<std::uint8_t> &buffer, std::size_t ¤t, std::uint64_t value)
|
||||
{
|
||||
static constexpr std::uint64_t mask = 0x00'00'00'00'00'00'00'FF;
|
||||
std::uint64_t network = HostToNetwork(value);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network ) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 8) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 16) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 24) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 32) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 40) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 48) & mask);
|
||||
buffer[current++] = static_cast<std::uint8_t>((network >> 56) & mask);
|
||||
}
|
||||
}
|
||||
|
||||
BasicEncoder::BasicEncoder(std::span<std::uint8_t> buffer)
|
||||
: mCurrent(0), mBuffer(buffer)
|
||||
{}
|
||||
|
||||
void BasicEncoder::Encode(bool value)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
if (value) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::True);
|
||||
}
|
||||
else {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::False);
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(Special value)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
switch (value) {
|
||||
case Special::Null:
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Null);
|
||||
break;
|
||||
case Special::Undefined:
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Undefined);
|
||||
break;
|
||||
default:
|
||||
throw UnknownValue();
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::int8_t value)
|
||||
{
|
||||
if (value >= 0) {
|
||||
if (value <= 23) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned) | value;
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 2);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::int8_t actual = std::abs(value + 1);
|
||||
if (actual <= 23) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative) | actual;
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 2);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = 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>(std::abs(value + 1));
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 2);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = actual;
|
||||
}
|
||||
else if (value >= 0) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 3);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 3);
|
||||
std::int16_t actual = std::abs(value + 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 3);
|
||||
Write(mBuffer, mCurrent, actual);
|
||||
}
|
||||
else if (value >= 0) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 5);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 5);
|
||||
std::int32_t actual = std::abs(value + 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, 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));
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 5);
|
||||
Write(mBuffer, mCurrent, actual);
|
||||
}
|
||||
else if (value >= 0) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 9);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 9);
|
||||
std::int64_t actual = std::abs(value + 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(actual));
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::uint8_t value)
|
||||
{
|
||||
if (value <= 23) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned) | value;
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 2);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::uint16_t value)
|
||||
{
|
||||
if (value <= 255) {
|
||||
Encode(static_cast<std::uint8_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 3);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, value);
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::uint32_t value)
|
||||
{
|
||||
if (value <= 65535) {
|
||||
Encode(static_cast<std::uint16_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 5);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, value);
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::uint64_t value)
|
||||
{
|
||||
if (value <= 4294967295) {
|
||||
Encode(static_cast<std::uint32_t>(value));
|
||||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 9);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, 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.
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 5);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Float);
|
||||
Write(mBuffer, mCurrent, 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.
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 9);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Double);
|
||||
Write(mBuffer, mCurrent, std::bit_cast<std::uint64_t>(value));
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::span<std::uint8_t> value)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, value.size() + 1 + ArgumentSize(value.size()));
|
||||
if (value.size() <= 23) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| static_cast<std::uint8_t>(value.size());
|
||||
}
|
||||
else if (value.size() <= 255) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(value.size());
|
||||
}
|
||||
else if (value.size() <= 65535) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value.size()));
|
||||
}
|
||||
else if (value.size() <= 4294967295) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(value.size()));
|
||||
}
|
||||
else {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(value.size()));
|
||||
}
|
||||
std::memcpy(mBuffer.data() + mCurrent, value.data(), value.size());
|
||||
mCurrent += value.size();
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(const char *value)
|
||||
{
|
||||
Encode(std::string_view(value));
|
||||
}
|
||||
|
||||
void BasicEncoder::Encode(std::string_view value)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, value.size() + ArgumentSize(value.size()));
|
||||
if (value.size() <= 23) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| static_cast<std::uint8_t>(value.size());
|
||||
}
|
||||
else if (value.size() <= 255) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(value.size());
|
||||
}
|
||||
else if (value.size() <= 65535) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value.size()));
|
||||
}
|
||||
else if (value.size() <= 4294967295) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(value.size()));
|
||||
}
|
||||
else {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(value.size()));
|
||||
}
|
||||
std::memcpy(mBuffer.data() + mCurrent, value.data(), value.size());
|
||||
mCurrent += value.size();
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginArray(std::size_t size)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size));
|
||||
if (size <= 23) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| static_cast<std::uint8_t>(size);
|
||||
}
|
||||
else if (size <= 255) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(size);
|
||||
}
|
||||
else if (size <= 65535) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(size));
|
||||
}
|
||||
else if (size <= 4294967295) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(size));
|
||||
}
|
||||
else {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginMap(std::size_t size)
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size));
|
||||
if (size <= 23) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| static_cast<std::uint8_t>(size);
|
||||
}
|
||||
else if (size <= 255) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(size);
|
||||
}
|
||||
else if (size <= 65535) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(size));
|
||||
}
|
||||
else if (size <= 4294967295) {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| std::to_underlying(ArgumentPosition::Next4B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(size));
|
||||
}
|
||||
else {
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| std::to_underlying(ArgumentPosition::Next8B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginIndefiniteBinary()
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
|
||||
| std::to_underlying(ArgumentPosition::Indefinite);
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginIndefiniteString()
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
|
||||
| std::to_underlying(ArgumentPosition::Indefinite);
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginIndefiniteArray()
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
|
||||
| std::to_underlying(ArgumentPosition::Indefinite);
|
||||
}
|
||||
|
||||
void BasicEncoder::BeginIndefiniteMap()
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
|
||||
| std::to_underlying(ArgumentPosition::Indefinite);
|
||||
}
|
||||
|
||||
void BasicEncoder::End()
|
||||
{
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Break);
|
||||
}
|
||||
}
|
77
LibCBOR/Source/Utils.hpp
Normal file
77
LibCBOR/Source/Utils.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef LIBCBOR_INTERNAL_UTILS_HPP
|
||||
#define LIBCBOR_INTERNAL_UTILS_HPP
|
||||
|
||||
#include <bit>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
[[nodiscard, deprecated("Prefer HostToNetwork or NetworkToHost")]] constexpr
|
||||
std::uint8_t FlipBytes(std::uint8_t value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard, deprecated("Prefer HostToNetwork or NetworkToHost")]] constexpr
|
||||
std::uint16_t FlipBytes(std::uint16_t value)
|
||||
{
|
||||
static constexpr std::uint16_t upperMask = 0b1111'1111'0000'0000;
|
||||
static constexpr std::uint16_t lowerMask = 0b0000'0000'1111'1111;
|
||||
return (value & upperMask) >> 8 | (value & lowerMask) << 8;
|
||||
}
|
||||
|
||||
[[nodiscard, deprecated("Prefer HostToNetwork or NetworkToHost")]] constexpr
|
||||
std::uint32_t FlipBytes(std::uint32_t value)
|
||||
{
|
||||
static constexpr std::uint32_t firstMask = 0xFF'00'00'00;
|
||||
static constexpr std::uint32_t secondMask = 0x00'FF'00'00;
|
||||
static constexpr std::uint32_t thirdMask = 0x00'00'FF'00;
|
||||
static constexpr std::uint32_t fourthMask = 0x00'00'00'FF;
|
||||
return (value & firstMask) >> 24 | (value & secondMask) >> 8 |
|
||||
(value & thirdMask) << 8 | (value & fourthMask) << 24;
|
||||
}
|
||||
|
||||
[[nodiscard, deprecated("Prefer HostToNetwork or NetworkToHost")]] constexpr
|
||||
std::uint64_t FlipBytes(std::uint64_t value)
|
||||
{
|
||||
static constexpr std::uint64_t firstMask = 0xFF'00'00'00'00'00'00'00;
|
||||
static constexpr std::uint64_t secondMask = 0x00'FF'00'00'00'00'00'00;
|
||||
static constexpr std::uint64_t thirdMask = 0x00'00'FF'00'00'00'00'00;
|
||||
static constexpr std::uint64_t fourthMask = 0x00'00'00'FF'00'00'00'00;
|
||||
static constexpr std::uint64_t fifthMask = 0x00'00'00'00'FF'00'00'00;
|
||||
static constexpr std::uint64_t sixthMask = 0x00'00'00'00'00'FF'00'00;
|
||||
static constexpr std::uint64_t seventhMask = 0x00'00'00'00'00'00'FF'00;
|
||||
static constexpr std::uint64_t eighthMask = 0x00'00'00'00'00'00'00'FF;
|
||||
return (value & firstMask ) >> 56 | (value & secondMask) >> 40 |
|
||||
(value & thirdMask ) >> 24 | (value & fourthMask) >> 8 |
|
||||
(value & fifthMask ) << 8 | (value & sixthMask ) >> 24 |
|
||||
(value & seventhMask) << 40 | (value & eighthMask) << 56;
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
[[nodiscard]] constexpr
|
||||
T HostToNetwork(T value)
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
return std::byteswap(value);
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
[[nodiscard]] constexpr
|
||||
T NetworkToHost(T value)
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
return std::byteswap(value);
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LIBCBOR_INTERNAL_UTILS_HPP
|
21
Tests/CMakeLists.txt
Normal file
21
Tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
add_executable(Tests)
|
||||
|
||||
target_compile_features(Tests
|
||||
PRIVATE
|
||||
cxx_std_23
|
||||
)
|
||||
|
||||
target_include_directories(Tests
|
||||
PRIVATE
|
||||
"Include"
|
||||
)
|
||||
|
||||
target_link_libraries(Tests
|
||||
PRIVATE
|
||||
LibCBOR
|
||||
)
|
||||
|
||||
target_sources(Tests
|
||||
PRIVATE
|
||||
"Main.cpp"
|
||||
)
|
39
Tests/Main.cpp
Normal file
39
Tests/Main.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "CBOR/Core.hpp"
|
||||
#include "CBOR/Encoder.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <print>
|
||||
#include <vector>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
std::array<std::uint8_t, 256> buffer = {0};
|
||||
|
||||
std::vector<std::uint8_t> binData(5, 'g');
|
||||
|
||||
CBOR::BasicEncoder enc(buffer);
|
||||
|
||||
enc.BeginMap(7);
|
||||
enc.Encode("Hello ");
|
||||
enc.Encode("World! ");
|
||||
enc.Encode("Behold by new power! ");
|
||||
enc.Encode("It truly is a sight to see ..."sv);
|
||||
enc.Encode(std::int64_t(1212121212121212));
|
||||
enc.Encode(binData);
|
||||
enc.Encode("random double");
|
||||
enc.Encode(420.69);
|
||||
enc.Encode("random float");
|
||||
enc.Encode(420.69f);
|
||||
enc.Encode("undefined?");
|
||||
enc.Encode(CBOR::Special::Undefined);
|
||||
enc.Encode("null?");
|
||||
enc.Encode(CBOR::Special::Null);
|
||||
enc.End();
|
||||
|
||||
for (const auto &byte: buffer) {
|
||||
std::print("{:02x} ", byte);
|
||||
}
|
||||
std::println("");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue