Compare commits

..

No commits in common. "257be2e57a383851fa2335dfed4a6f959d607de3" and "11636af323899a8651266f6407a9aa7a00e665e2" have entirely different histories.

4 changed files with 215 additions and 156 deletions

View file

@ -4,7 +4,6 @@
#include "Core.hpp" #include "Core.hpp"
#include <span> #include <span>
#include <string_view>
namespace CBOR namespace CBOR
{ {
@ -126,6 +125,8 @@ namespace CBOR
Item Next(); Item Next();
private: private:
static constexpr std::size_t Indefinite = std::numeric_limits<std::size_t>::max();
friend class Decoder; friend class Decoder;
bool mHeaderParsed; bool mHeaderParsed;
@ -146,6 +147,8 @@ namespace CBOR
KeyValue Next(); KeyValue Next();
private: private:
static constexpr std::size_t Indefinite = std::numeric_limits<std::size_t>::max();
friend class Decoder; friend class Decoder;
bool mHeaderParsed; bool mHeaderParsed;
@ -189,6 +192,26 @@ namespace CBOR
friend class Array; friend class Array;
friend class Map; 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::size_t mCurrent;
std::span<std::uint8_t> mBuffer; std::span<std::uint8_t> mBuffer;
}; };

View file

@ -15,6 +15,14 @@
namespace CBOR namespace CBOR
{ {
class ImplementationError: public DecodeError
{
public:
ImplementationError(std::string_view message)
: DecodeError(std::string("internal implementation error: ").append(message))
{}
};
class InvalidUsageError: public DecodeError class InvalidUsageError: public DecodeError
{ {
public: public:
@ -55,8 +63,6 @@ namespace CBOR
namespace namespace
{ {
static constexpr std::size_t Indefinite = std::numeric_limits<std::size_t>::max();
std::size_t SpaceLeft(std::span<std::uint8_t> buffer, std::size_t offset) std::size_t SpaceLeft(std::span<std::uint8_t> buffer, std::size_t offset)
{ {
if (offset >= buffer.size()) { if (offset >= buffer.size()) {
@ -81,6 +87,41 @@ namespace CBOR
return buffer[current]; return buffer[current];
} }
std::uint16_t Read2B(std::span<std::uint8_t> buffer, std::size_t current)
{
EnsureEnoughSpace(buffer, current, 2);
std::uint16_t result = 0;
result |= std::uint16_t(buffer[current ]) ;
result |= std::uint16_t(buffer[current + 1]) << 8;
return NetworkToHost(result);
}
std::uint32_t Read4B(std::span<std::uint8_t> buffer, std::size_t current)
{
EnsureEnoughSpace(buffer, current, 4);
std::uint32_t result = 0;
result |= std::uint32_t(buffer[current ]) ;
result |= std::uint32_t(buffer[current + 1]) << 8;
result |= std::uint32_t(buffer[current + 2]) << 16;
result |= std::uint32_t(buffer[current + 3]) << 24;
return NetworkToHost(result);
}
std::uint64_t Read8B(std::span<std::uint8_t> buffer, std::size_t current)
{
EnsureEnoughSpace(buffer, current, 8);
std::uint64_t result = 0;
result |= std::uint64_t(buffer[current ]) ;
result |= std::uint64_t(buffer[current + 1]) << 8;
result |= std::uint64_t(buffer[current + 2]) << 16;
result |= std::uint64_t(buffer[current + 3]) << 24;
result |= std::uint64_t(buffer[current + 4]) << 32;
result |= std::uint64_t(buffer[current + 5]) << 40;
result |= std::uint64_t(buffer[current + 6]) << 48;
result |= std::uint64_t(buffer[current + 7]) << 56;
return NetworkToHost(result);
}
std::uint8_t Consume1B(std::span<std::uint8_t> buffer, std::size_t &current) std::uint8_t Consume1B(std::span<std::uint8_t> buffer, std::size_t &current)
{ {
EnsureEnoughSpace(buffer, current, 1); EnsureEnoughSpace(buffer, current, 1);
@ -137,27 +178,6 @@ namespace CBOR
return ArgumentPosition(header & std::to_underlying(ArgumentPosition::PositionMask)); return ArgumentPosition(header & std::to_underlying(ArgumentPosition::PositionMask));
} }
std::uint64_t ArgumentValue(std::uint8_t header, std::span<std::uint8_t> buffer, std::size_t &current)
{
ArgumentPosition pos = GetArgumentPosition(header);
if (std::to_underlying(pos) <= 23) {
return std::to_underlying(pos);
}
switch (pos) {
case ArgumentPosition::Next1B:
return Consume1B(buffer, current);
case ArgumentPosition::Next2B:
return Consume2B(buffer, current);
case ArgumentPosition::Next4B:
return Consume4B(buffer, current);
case ArgumentPosition::Next8B:
return Consume8B(buffer, current);
default:
throw MalformedDataError("argument position is reserved for future use, incorrect, "
"or the parser is out of date");
}
}
template <std::unsigned_integral T> template <std::unsigned_integral T>
T ExtractUnsigned(std::span<std::uint8_t> buffer, std::size_t &current) T ExtractUnsigned(std::span<std::uint8_t> buffer, std::size_t &current)
{ {
@ -334,22 +354,6 @@ namespace CBOR
return SignedNegative<T>(header, buffer, current); return SignedNegative<T>(header, buffer, current);
} }
} }
std::span<std::uint8_t> ExtractBinary(std::size_t size, std::span<std::uint8_t> buffer, std::size_t &current)
{
EnsureEnoughSpace(buffer, current, size);
std::span<std::uint8_t> result(buffer.data() + current, size);
current += size;
return result;
}
std::string_view ExtractString(std::size_t size, std::span<std::uint8_t> buffer, std::size_t &current)
{
EnsureEnoughSpace(buffer, current, size);
std::string_view result(reinterpret_cast<const char *>(buffer.data() + current), size);
current += size;
return result;
}
} }
Item::Item(Decoder &decoder) Item::Item(Decoder &decoder)
@ -479,20 +483,18 @@ namespace CBOR
"use the funcions for those instead"); "use the funcions for those instead");
} }
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
MajorType major = GetMajorType(header);
mHeaderParsed = true; mHeaderParsed = true;
if (major != MajorType::Binary) { if (header.Type != MajorType::Binary) {
throw TypeMismatchError("binary", ToString(major)); throw TypeMismatchError("binary", ToString(header.Type));
} }
if (GetArgumentPosition(header) == ArgumentPosition::Indefinite) { if (header.ArgPosition == ArgumentPosition::Indefinite) {
throw IndefiniteLengthError(); throw IndefiniteLengthError();
} }
mDone = true; mDone = true;
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractBinary(header.Argument);
return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
void Binary::AllowIndefinite() void Binary::AllowIndefinite()
@ -510,10 +512,10 @@ namespace CBOR
return true; return true;
} }
std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->PeekHeader();
if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { if (header.Type == MajorType::Other && header.Minor == MinorType::Break) {
mDone = true; mDone = true;
Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mDecoder->ExtractHeader();
} }
mCheckedDone = true; mCheckedDone = true;
return mDone; return mDone;
@ -522,18 +524,16 @@ namespace CBOR
std::span<std::uint8_t> Binary::Next() std::span<std::uint8_t> Binary::Next()
{ {
if (!mHeaderParsed) { if (!mHeaderParsed) {
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
mHeaderParsed = true; mHeaderParsed = true;
MajorType major = GetMajorType(header); if (header.Type != MajorType::Binary) {
if (major != MajorType::Binary) { throw TypeMismatchError("binary", ToString(header.Type));
throw TypeMismatchError("binary", ToString(major));
} }
if (GetArgumentPosition(header) != ArgumentPosition::Indefinite) { if (header.ArgPosition != ArgumentPosition::Indefinite) {
mDone = true; mDone = true;
mCheckedDone = true; mCheckedDone = true;
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractBinary(header.Argument);
return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
return std::span<std::uint8_t>(); return std::span<std::uint8_t>();
@ -548,16 +548,13 @@ namespace CBOR
} }
mCheckedDone = false; mCheckedDone = false;
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
MajorType major = GetMajorType(header); if (header.Type != MajorType::Binary || header.ArgPosition == ArgumentPosition::Indefinite){
ArgumentPosition pos = GetArgumentPosition(header); throw MalformedDataError("an indefinite length binary may only contain "
if (major != MajorType::Binary || pos == ArgumentPosition::Indefinite){ "definite length binaries");
throw MalformedDataError("an indefinite length string may only contain "
"definite length strings");
} }
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractBinary(header.Argument);
return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
String::String(Decoder &decoder) String::String(Decoder &decoder)
@ -579,20 +576,18 @@ namespace CBOR
"use the funcions for those instead"); "use the funcions for those instead");
} }
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
mHeaderParsed = true; mHeaderParsed = true;
MajorType major = GetMajorType(header); if (header.Type != MajorType::String) {
if (major != MajorType::String) { throw TypeMismatchError("string", ToString(header.Type));
throw TypeMismatchError("string", ToString(major));
} }
if (GetArgumentPosition(header) == ArgumentPosition::Indefinite) { if (header.ArgPosition == ArgumentPosition::Indefinite) {
throw IndefiniteLengthError(); throw IndefiniteLengthError();
} }
mDone = true; mDone = true;
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractString(header.Argument);
return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
void String::AllowIndefinite() void String::AllowIndefinite()
@ -610,10 +605,10 @@ namespace CBOR
return true; return true;
} }
std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent);
if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) {
mDone = true; mDone = true;
Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mDecoder->ExtractHeader();
} }
mCheckedDone = true; mCheckedDone = true;
return mDone; return mDone;
@ -622,18 +617,16 @@ namespace CBOR
std::string_view String::Next() std::string_view String::Next()
{ {
if (!mHeaderParsed) { if (!mHeaderParsed) {
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
MajorType major = GetMajorType(header);
mHeaderParsed = true; mHeaderParsed = true;
if (major != MajorType::String) { if (header.Type != MajorType::String) {
throw TypeMismatchError("string", ToString(major)); throw TypeMismatchError("string", ToString(header.Type));
} }
if (GetArgumentPosition(header) != ArgumentPosition::Indefinite) { if (header.ArgPosition != ArgumentPosition::Indefinite) {
mDone = true; mDone = true;
mCheckedDone = true; mCheckedDone = true;
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractString(header.Argument);
return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
return std::string_view(); return std::string_view();
@ -648,16 +641,13 @@ namespace CBOR
} }
mCheckedDone = false; mCheckedDone = false;
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
MajorType major = GetMajorType(header); if (header.Type != MajorType::Binary || header.ArgPosition == ArgumentPosition::Indefinite){
ArgumentPosition pos = GetArgumentPosition(header);
if (major != MajorType::String || pos == ArgumentPosition::Indefinite){
throw MalformedDataError("an indefinite length string may only contain " throw MalformedDataError("an indefinite length string may only contain "
"definite length strings"); "definite length strings");
} }
std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); return mDecoder->ExtractString(header.Argument);
return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent);
} }
Array::Array(Decoder &decoder) Array::Array(Decoder &decoder)
@ -672,15 +662,14 @@ namespace CBOR
bool Array::Done() bool Array::Done()
{ {
if (!mHeaderParsed) { if (!mHeaderParsed) {
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
mHeaderParsed = true; mHeaderParsed = true;
if (GetMajorType(header) != MajorType::Array) { if (header.Type != MajorType::Array) {
throw TypeMismatchError("array", ToString(GetMajorType(header))); throw TypeMismatchError("array", ToString(header.Type));
} }
bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; bool indefinite = header.ArgPosition == ArgumentPosition::Indefinite;
mSize = indefinite ? Indefinite mSize = indefinite ? Indefinite : header.Argument;
: ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent);
if (!mSize) { if (!mSize) {
mDone = true; mDone = true;
@ -700,10 +689,10 @@ namespace CBOR
return mDone; return mDone;
} }
std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->PeekHeader();
if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { if (header.Type == MajorType::Other && header.Minor == MinorType::Break) {
mDone = true; mDone = true;
Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mDecoder->ExtractHeader();
} }
mCheckedDone = true; mCheckedDone = true;
return mDone; return mDone;
@ -736,15 +725,14 @@ namespace CBOR
bool Map::Done() bool Map::Done()
{ {
if (!mHeaderParsed) { if (!mHeaderParsed) {
std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->ExtractHeader();
mHeaderParsed = true; mHeaderParsed = true;
if (GetMajorType(header) != MajorType::Map) { if (header.Type != MajorType::Map) {
throw TypeMismatchError("map", ToString(GetMajorType(header))); throw TypeMismatchError("map", ToString(header.Type));
} }
bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; bool indefinite = header.ArgPosition == ArgumentPosition::Indefinite;
mSize = indefinite ? Indefinite mSize = indefinite ? Indefinite : header.Argument;
: ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent);
if (!mSize) { if (!mSize) {
mDone = true; mDone = true;
@ -764,10 +752,10 @@ namespace CBOR
return mDone; return mDone;
} }
std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); Decoder::Header header = mDecoder->PeekHeader();
if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { if (header.Type == MajorType::Other && header.Minor == MinorType::Break) {
mDone = true; mDone = true;
Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mDecoder->ExtractHeader();
} }
mCheckedDone = true; mCheckedDone = true;
return mDone; return mDone;
@ -789,7 +777,7 @@ namespace CBOR
} }
Decoder::Decoder(std::span<std::uint8_t> buffer) Decoder::Decoder(std::span<std::uint8_t> buffer)
: mCurrent(0), mBuffer(buffer) : mState(State::Initial), mCurrent(0), mBuffer(buffer)
{} {}
bool Decoder::Bool() bool Decoder::Bool()
@ -921,4 +909,95 @@ namespace CBOR
{ {
return { *this }; return { *this };
} }
Decoder::Header Decoder::PeekHeader()
{
EnsureEnoughSpace(mBuffer, mCurrent, 1);
std::uint8_t rawHeader = mBuffer[mCurrent];
Header header {
.Type = MajorType(rawHeader & std::to_underlying(MajorType::TypeMask)),
.Minor = MinorType(rawHeader & std::to_underlying(MinorType::TypeMask)),
.ArgPosition = ArgumentPosition(rawHeader & std::to_underlying(ArgumentPosition::PositionMask)),
.Argument = 0,
};
if (std::to_underlying(header.ArgPosition) <= 23) {
header.Argument = std::to_underlying(header.ArgPosition);
}
else if (header.ArgPosition == ArgumentPosition::Next1B) {
header.Argument = Read1B(mBuffer, mCurrent + 1);
}
else if (header.ArgPosition == ArgumentPosition::Next2B) {
header.Argument = Read2B(mBuffer, mCurrent + 1);
}
else if (header.ArgPosition == ArgumentPosition::Next4B) {
header.Argument = Read4B(mBuffer, mCurrent + 1);
}
else if (header.ArgPosition == ArgumentPosition::Next8B) {
header.Argument = Read8B(mBuffer, mCurrent + 1);
}
else if (header.ArgPosition == ArgumentPosition::Indefinite) {
// Nothing more needs to happen
}
else {
// bruh, this happends even with the special ending values ...
//throw MalformedDataError("value reserved for future use in the input buffer "
// "(this version may be too old to parse this data)");
}
return header;
}
Decoder::Header Decoder::ExtractHeader()
{
EnsureEnoughSpace(mBuffer, mCurrent, 1);
std::uint8_t rawHeader = mBuffer[mCurrent++];
Header header {
.Type = MajorType(rawHeader & std::to_underlying(MajorType::TypeMask)),
.Minor = MinorType(rawHeader & std::to_underlying(MinorType::TypeMask)),
.ArgPosition = ArgumentPosition(rawHeader & std::to_underlying(ArgumentPosition::PositionMask)),
.Argument = 0,
};
if (std::to_underlying(header.ArgPosition) <= 23) {
header.Argument = std::to_underlying(header.ArgPosition);
}
else if (header.ArgPosition == ArgumentPosition::Next1B) {
header.Argument = Consume1B(mBuffer, mCurrent);
}
else if (header.ArgPosition == ArgumentPosition::Next2B) {
header.Argument = Consume2B(mBuffer, mCurrent);
}
else if (header.ArgPosition == ArgumentPosition::Next4B) {
header.Argument = Consume4B(mBuffer, mCurrent);
}
else if (header.ArgPosition == ArgumentPosition::Next8B) {
header.Argument = Consume8B(mBuffer, mCurrent);
}
else if (header.ArgPosition == ArgumentPosition::Indefinite) {
// Nothing more needs to happen
}
else {
throw MalformedDataError("value reserved for future use in the input buffer "
"(this version may be too old to parse this data)");
}
return header;
}
std::span<std::uint8_t> Decoder::ExtractBinary(std::size_t size)
{
EnsureEnoughSpace(mBuffer, mCurrent, size);
std::span<std::uint8_t> result(mBuffer.data() + mCurrent, size);
mCurrent += size;
return result;
}
std::string_view Decoder::ExtractString(std::size_t size)
{
EnsureEnoughSpace(mBuffer, mCurrent, size);
std::string_view result(reinterpret_cast<const char *>(mBuffer.data() + mCurrent), size);
mCurrent += size;
return result;
}
} }

View file

@ -3,6 +3,7 @@
#include "Core.hpp" #include "Core.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include <cmath>
#include <cstring> #include <cstring>
#include <format> #include <format>
#include <utility> #include <utility>

View file

@ -21,7 +21,7 @@ struct SomeStruct
std::size_t Encode(const SomeStruct &value, std::span<std::uint8_t> buffer) std::size_t Encode(const SomeStruct &value, std::span<std::uint8_t> buffer)
{ {
CBOR::BasicEncoder enc(buffer); CBOR::BasicEncoder enc(buffer);
enc.BeginMap(7); enc.BeginIndefiniteMap();
enc.Encode("name"); enc.Encode("name");
enc.Encode(value.name); enc.Encode(value.name);
@ -48,10 +48,12 @@ std::size_t Encode(const SomeStruct &value, std::span<std::uint8_t> buffer)
} }
enc.End(); enc.End();
enc.End();
return enc.EncodedSize(); return enc.EncodedSize();
} }
SomeStruct Decode1(std::span<std::uint8_t> buffer) SomeStruct Decode(std::span<std::uint8_t> buffer)
{ {
SomeStruct result; SomeStruct result;
@ -90,50 +92,6 @@ SomeStruct Decode1(std::span<std::uint8_t> buffer)
return result; return result;
} }
SomeStruct Decode2(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("");
CBOR::String tool = tools.Next().String();
tool.AllowIndefinite();
while (!tool.Done()) {
result.tools.back().append(tool.Next());
}
}
}
}
return result;
}
void Compare(const SomeStruct &s1, const SomeStruct &s2) void Compare(const SomeStruct &s1, const SomeStruct &s2)
{ {
if (s1.name != s2.name) { if (s1.name != s2.name) {
@ -187,11 +145,9 @@ int main()
std::size_t encodedSize = Encode(expected, buffer); std::size_t encodedSize = Encode(expected, buffer);
std::println("Encoded size: {}", encodedSize); std::println("Encoded size: {}", encodedSize);
SomeStruct result1 = Decode1(std::span<std::uint8_t>(buffer.data(), encodedSize)); SomeStruct result = Decode(std::span<std::uint8_t>(buffer.data(), encodedSize));
SomeStruct result2 = Decode2(std::span<std::uint8_t>(buffer.data(), encodedSize));
Compare(expected, result1); Compare(expected, result);
Compare(expected, result2);
std::println("The test has been completed successfully."); std::println("The test has been completed successfully.");
} }
catch (const std::exception &e) { catch (const std::exception &e) {