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
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