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:
TennesseeTrash 2025-09-18 23:23:33 +02:00
parent 4159fc4643
commit 11636af323
9 changed files with 1431 additions and 112 deletions

View file

@ -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("");
}