Initial decoder implementation, including a basic two-way test
The implementation is still far from perfect, it's at beast a proof of concept. There are many edge cases that are definitely still not covered, and many rough edges in the quality of the code. I am also not convinced that exceptions are the best error handling method for this, particularly for publicly exposes interfaces that may be much more susceptible to DoS attacks due to malformed input (and the resulting overhead in handling exceptions).
This commit is contained in:
parent
4159fc4643
commit
11636af323
9 changed files with 1431 additions and 112 deletions
|
@ -5,6 +5,13 @@ target_compile_features(LibCBOR
|
|||
cxx_std_23
|
||||
)
|
||||
|
||||
target_compile_options(LibCBOR PUBLIC
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4>
|
||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Wold-style-cast -Wcast-align -Wunused
|
||||
-Woverloaded-virtual -Wconversion -Wsign-conversion -Wformat=2
|
||||
-Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough>
|
||||
)
|
||||
|
||||
target_include_directories(LibCBOR
|
||||
PUBLIC
|
||||
"Include"
|
||||
|
@ -15,6 +22,7 @@ target_include_directories(LibCBOR
|
|||
|
||||
target_sources(LibCBOR
|
||||
PRIVATE
|
||||
"Source/Core.cpp"
|
||||
"Source/Decoder.cpp"
|
||||
"Source/Encoder.cpp"
|
||||
)
|
||||
|
|
|
@ -49,49 +49,52 @@ namespace CBOR
|
|||
String = 0b0110'0000,
|
||||
Array = 0b1000'0000,
|
||||
Map = 0b1010'0000,
|
||||
Tagged = 0b1100'0000,
|
||||
Tag = 0b1100'0000,
|
||||
Other = 0b1110'0000,
|
||||
|
||||
TypeMask = 0b1110'0000,
|
||||
};
|
||||
std::string_view ToString(MajorType type);
|
||||
|
||||
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,
|
||||
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,
|
||||
Direct20 = 0b0001'0100,
|
||||
Direct21 = 0b0001'0101,
|
||||
Direct22 = 0b0001'0110,
|
||||
Direct23 = 0b0001'0111,
|
||||
|
||||
Next1B = 0b0001'1000,
|
||||
Next2B = 0b0001'1001,
|
||||
Next4B = 0b0001'1010,
|
||||
Next8B = 0b0001'1011,
|
||||
Next1B = 0b0001'1000,
|
||||
Next2B = 0b0001'1001,
|
||||
Next4B = 0b0001'1010,
|
||||
Next8B = 0b0001'1011,
|
||||
|
||||
Reserved28 = 0b0001'1100,
|
||||
Reserved29 = 0b0001'1101,
|
||||
Reserved30 = 0b0001'1110,
|
||||
Reserved28 = 0b0001'1100,
|
||||
Reserved29 = 0b0001'1101,
|
||||
Reserved30 = 0b0001'1110,
|
||||
|
||||
Indefinite = 0b0001'1111,
|
||||
Indefinite = 0b0001'1111,
|
||||
|
||||
PositionMask = 0b0001'1111,
|
||||
};
|
||||
|
||||
enum class MinorType: std::uint8_t
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "Core.hpp"
|
||||
|
||||
#include <span>
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
class DecodeError: public Error
|
||||
|
@ -11,16 +13,207 @@ namespace CBOR
|
|||
using Error::Error;
|
||||
};
|
||||
|
||||
// Forward decl
|
||||
class Decoder;
|
||||
|
||||
class Item
|
||||
{
|
||||
private:
|
||||
Item(Decoder &decoder);
|
||||
|
||||
public:
|
||||
bool Bool ();
|
||||
Special Special();
|
||||
|
||||
std::int8_t Int8 ();
|
||||
std::int16_t Int16 ();
|
||||
std::int32_t Int32 ();
|
||||
std::int64_t Int64 ();
|
||||
|
||||
std::uint8_t Uint8 ();
|
||||
std::uint16_t Uint16 ();
|
||||
std::uint32_t Uint32 ();
|
||||
std::uint64_t Uint64 ();
|
||||
|
||||
// Note(3011): float16_t is currently not supported
|
||||
float Float ();
|
||||
double Double ();
|
||||
|
||||
class Binary Binary ();
|
||||
class String String ();
|
||||
class Array Array ();
|
||||
class Map Map ();
|
||||
|
||||
private:
|
||||
friend class Decoder;
|
||||
friend class Array;
|
||||
friend class KeyValue;
|
||||
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class KeyValue
|
||||
{
|
||||
private:
|
||||
KeyValue(Decoder &decoder);
|
||||
|
||||
public:
|
||||
Item Key();
|
||||
Item Value();
|
||||
private:
|
||||
friend class Decoder;
|
||||
friend class Map;
|
||||
|
||||
enum class State
|
||||
{
|
||||
Initial, KeyPulled, Done,
|
||||
};
|
||||
|
||||
State mState;
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class Binary
|
||||
{
|
||||
private:
|
||||
Binary(Decoder &decoder);
|
||||
|
||||
public:
|
||||
std::span<std::uint8_t> Get();
|
||||
|
||||
void AllowIndefinite();
|
||||
bool Done();
|
||||
std::span<std::uint8_t> Next();
|
||||
private:
|
||||
friend class Decoder;
|
||||
|
||||
bool mHeaderParsed;
|
||||
bool mIndefiniteAllowed;
|
||||
bool mDone;
|
||||
bool mCheckedDone;
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class String
|
||||
{
|
||||
private:
|
||||
String(Decoder &decoder);
|
||||
|
||||
public:
|
||||
std::string_view Get();
|
||||
|
||||
void AllowIndefinite();
|
||||
bool Done();
|
||||
std::string_view Next();
|
||||
private:
|
||||
friend class Decoder;
|
||||
|
||||
bool mHeaderParsed;
|
||||
bool mIndefiniteAllowed;
|
||||
bool mDone;
|
||||
bool mCheckedDone;
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class Array
|
||||
{
|
||||
private:
|
||||
Array(Decoder &decoder);
|
||||
|
||||
public:
|
||||
bool Done();
|
||||
Item Next();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t Indefinite = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
friend class Decoder;
|
||||
|
||||
bool mHeaderParsed;
|
||||
bool mDone;
|
||||
bool mCheckedDone;
|
||||
std::size_t mCurrent;
|
||||
std::size_t mSize;
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class Map
|
||||
{
|
||||
private:
|
||||
Map(Decoder &decoder);
|
||||
|
||||
public:
|
||||
bool Done();
|
||||
KeyValue Next();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t Indefinite = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
friend class Decoder;
|
||||
|
||||
bool mHeaderParsed;
|
||||
bool mDone;
|
||||
bool mCheckedDone;
|
||||
std::size_t mCurrent;
|
||||
std::size_t mSize;
|
||||
Decoder *mDecoder;
|
||||
};
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
private:
|
||||
};
|
||||
Decoder(std::span<std::uint8_t> buffer);
|
||||
|
||||
bool Bool ();
|
||||
Special Special();
|
||||
|
||||
std::int8_t Int8 ();
|
||||
std::int16_t Int16 ();
|
||||
std::int32_t Int32 ();
|
||||
std::int64_t Int64 ();
|
||||
|
||||
std::uint8_t Uint8 ();
|
||||
std::uint16_t Uint16 ();
|
||||
std::uint32_t Uint32 ();
|
||||
std::uint64_t Uint64 ();
|
||||
|
||||
// Note(3011): float16_t is currently not supported
|
||||
float Float ();
|
||||
double Double ();
|
||||
|
||||
Binary Binary ();
|
||||
String String ();
|
||||
Array Array ();
|
||||
Map Map ();
|
||||
|
||||
class Validator
|
||||
{
|
||||
public:
|
||||
private:
|
||||
friend class Binary;
|
||||
friend class String;
|
||||
friend class Array;
|
||||
friend class Map;
|
||||
|
||||
enum class State: std::uint8_t
|
||||
{
|
||||
Initial,
|
||||
HeaderExtracted,
|
||||
};
|
||||
|
||||
struct Header
|
||||
{
|
||||
MajorType Type;
|
||||
MinorType Minor;
|
||||
ArgumentPosition ArgPosition;
|
||||
std::uint64_t Argument;
|
||||
};
|
||||
Header PeekHeader();
|
||||
Header ExtractHeader();
|
||||
|
||||
std::span<std::uint8_t> ExtractBinary(std::size_t size);
|
||||
std::string_view ExtractString(std::size_t size);
|
||||
|
||||
State mState;
|
||||
std::size_t mCurrent;
|
||||
std::span<std::uint8_t> mBuffer;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@ namespace CBOR
|
|||
void BeginIndefiniteMap();
|
||||
|
||||
void End();
|
||||
|
||||
std::size_t EncodedSize() const;
|
||||
private:
|
||||
std::size_t mCurrent;
|
||||
std::span<std::uint8_t> mBuffer;
|
||||
|
|
30
LibCBOR/Source/Core.cpp
Normal file
30
LibCBOR/Source/Core.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "Core.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace CBOR
|
||||
{
|
||||
std::string_view ToString(MajorType type)
|
||||
{
|
||||
switch (type) {
|
||||
case MajorType::Unsigned:
|
||||
return "unsigned";
|
||||
case MajorType::Negative:
|
||||
return "negative";
|
||||
case MajorType::Binary:
|
||||
return "binary";
|
||||
case MajorType::String:
|
||||
return "string";
|
||||
case MajorType::Array:
|
||||
return "array";
|
||||
case MajorType::Map:
|
||||
return "map";
|
||||
case MajorType::Tag:
|
||||
return "tag";
|
||||
case MajorType::Other:
|
||||
return "other";
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
|||
#include "Encoder.hpp"
|
||||
|
||||
#include "Core.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
|
@ -133,26 +134,26 @@ namespace CBOR
|
|||
if (value >= 0) {
|
||||
if (value <= 23) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Unsigned) | value;
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(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;
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::int8_t actual = std::abs(value + 1);
|
||||
std::int8_t actual = -(value + 1);
|
||||
if (actual <= 23) {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative) | actual;
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(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;
|
||||
mBuffer[mCurrent++] = static_cast<std::uint8_t>(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ namespace CBOR
|
|||
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));
|
||||
std::uint8_t actual = static_cast<std::uint8_t>(-(value + 1));
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 2);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next1B);
|
||||
|
@ -180,10 +181,10 @@ namespace CBOR
|
|||
}
|
||||
else {
|
||||
EnsureEnoughSpace(mBuffer, mCurrent, 3);
|
||||
std::int16_t actual = std::abs(value + 1);
|
||||
std::int16_t actual = -(value + 1);
|
||||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Negative)
|
||||
| std::to_underlying(ArgumentPosition::Next2B);
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value));
|
||||
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(actual));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,4 +476,9 @@ namespace CBOR
|
|||
mBuffer[mCurrent++] = std::to_underlying(MajorType::Other)
|
||||
| std::to_underlying(MinorType::Break);
|
||||
}
|
||||
|
||||
std::size_t BasicEncoder::EncodedSize() const
|
||||
{
|
||||
return mBuffer.size() - (mBuffer.size() - mCurrent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,52 +3,9 @@
|
|||
|
||||
#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)
|
||||
|
|
163
Tests/Main.cpp
163
Tests/Main.cpp
|
@ -1,39 +1,156 @@
|
|||
#include "CBOR/Core.hpp"
|
||||
#include "CBOR/Decoder.hpp"
|
||||
#include "CBOR/Encoder.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <print>
|
||||
#include <ranges>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
struct SomeStruct
|
||||
{
|
||||
std::string name;
|
||||
double speed;
|
||||
float fov;
|
||||
std::int8_t thing;
|
||||
std::int64_t slots;
|
||||
std::uint32_t times;
|
||||
std::vector<std::string> tools;
|
||||
};
|
||||
|
||||
std::size_t Encode(const SomeStruct &value, std::span<std::uint8_t> buffer)
|
||||
{
|
||||
CBOR::BasicEncoder enc(buffer);
|
||||
enc.BeginIndefiniteMap();
|
||||
|
||||
enc.Encode("name");
|
||||
enc.Encode(value.name);
|
||||
|
||||
enc.Encode("speed");
|
||||
enc.Encode(value.speed);
|
||||
|
||||
enc.Encode("fov");
|
||||
enc.Encode(value.fov);
|
||||
|
||||
enc.Encode("thing");
|
||||
enc.Encode(value.thing);
|
||||
|
||||
enc.Encode("slots");
|
||||
enc.Encode(value.slots);
|
||||
|
||||
enc.Encode("times");
|
||||
enc.Encode(value.times);
|
||||
|
||||
enc.Encode("tools");
|
||||
enc.BeginIndefiniteArray();
|
||||
for (std::string_view tool: value.tools) {
|
||||
enc.Encode(tool);
|
||||
}
|
||||
enc.End();
|
||||
|
||||
enc.End();
|
||||
|
||||
return enc.EncodedSize();
|
||||
}
|
||||
|
||||
SomeStruct Decode(std::span<std::uint8_t> buffer)
|
||||
{
|
||||
SomeStruct result;
|
||||
|
||||
CBOR::Decoder dec(buffer);
|
||||
CBOR::Map object = dec.Map();
|
||||
while (!object.Done()) {
|
||||
CBOR::KeyValue kv = object.Next();
|
||||
std::string_view key = kv.Key().String().Get();
|
||||
CBOR::Item value = kv.Value();
|
||||
if (key == "name") {
|
||||
result.name = value.String().Get();
|
||||
}
|
||||
else if (key == "speed") {
|
||||
result.speed = value.Double();
|
||||
}
|
||||
else if (key == "fov") {
|
||||
result.fov = value.Float();
|
||||
}
|
||||
else if (key == "thing") {
|
||||
result.thing = value.Int8();
|
||||
}
|
||||
else if (key == "slots") {
|
||||
result.slots = value.Int64();
|
||||
}
|
||||
else if (key == "times") {
|
||||
result.times = value.Uint32();
|
||||
}
|
||||
else if (key == "tools") {
|
||||
CBOR::Array tools = value.Array();
|
||||
while(!tools.Done()) {
|
||||
result.tools.push_back(std::string(tools.Next().String().Get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Compare(const SomeStruct &s1, const SomeStruct &s2)
|
||||
{
|
||||
if (s1.name != s2.name) {
|
||||
throw std::runtime_error("test error: names are not the same");
|
||||
}
|
||||
if (s1.speed != s2.speed) {
|
||||
throw std::runtime_error("test error: speed is not the same");
|
||||
}
|
||||
if (s1.fov != s2.fov) {
|
||||
throw std::runtime_error("test error: fovs are not the same");
|
||||
}
|
||||
if (s1.thing != s2.thing) {
|
||||
throw std::runtime_error("test error: things are not the same");
|
||||
}
|
||||
if (s1.slots != s2.slots) {
|
||||
throw std::runtime_error("test error: slots are not the same");
|
||||
}
|
||||
if (s1.times != s2.times) {
|
||||
throw std::runtime_error("test error: times are not the same");
|
||||
}
|
||||
for (const auto &[t1, t2]: std::ranges::views::zip(s1.tools, s2.tools)) {
|
||||
if (t1 != t2) {
|
||||
throw std::runtime_error("test error: some tools are not the same");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
std::array<std::uint8_t, 256> buffer = {0};
|
||||
std::array<std::uint8_t, 1024> buffer = {0};
|
||||
|
||||
std::vector<std::uint8_t> binData(5, 'g');
|
||||
SomeStruct expected {
|
||||
.name = "Player1",
|
||||
.speed = 5.0,
|
||||
.fov = 110.0f,
|
||||
.thing = -15,
|
||||
.slots = 40'000'000,
|
||||
.times = 1234567,
|
||||
.tools = {
|
||||
"pickaxe",
|
||||
"sword",
|
||||
"axe",
|
||||
"magical arrow",
|
||||
"iron ore",
|
||||
},
|
||||
};
|
||||
|
||||
CBOR::BasicEncoder enc(buffer);
|
||||
try {
|
||||
std::size_t encodedSize = Encode(expected, buffer);
|
||||
std::println("Encoded size: {}", encodedSize);
|
||||
|
||||
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();
|
||||
SomeStruct result = Decode(std::span<std::uint8_t>(buffer.data(), encodedSize));
|
||||
|
||||
for (const auto &byte: buffer) {
|
||||
std::print("{:02x} ", byte);
|
||||
Compare(expected, result);
|
||||
std::println("The test has been completed successfully.");
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::println("Error: {}", e.what());
|
||||
}
|
||||
std::println("");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue