diff --git a/LibCBOR/Include/CBOR/Decoder.hpp b/LibCBOR/Include/CBOR/Decoder.hpp index dd62dca..a135bda 100644 --- a/LibCBOR/Include/CBOR/Decoder.hpp +++ b/LibCBOR/Include/CBOR/Decoder.hpp @@ -4,6 +4,7 @@ #include "Core.hpp" #include +#include namespace CBOR { @@ -125,8 +126,6 @@ namespace CBOR Item Next(); private: - static constexpr std::size_t Indefinite = std::numeric_limits::max(); - friend class Decoder; bool mHeaderParsed; @@ -147,8 +146,6 @@ namespace CBOR KeyValue Next(); private: - static constexpr std::size_t Indefinite = std::numeric_limits::max(); - friend class Decoder; bool mHeaderParsed; @@ -192,26 +189,6 @@ namespace CBOR 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 ExtractBinary(std::size_t size); - std::string_view ExtractString(std::size_t size); - - State mState; std::size_t mCurrent; std::span mBuffer; }; diff --git a/LibCBOR/Source/Decoder.cpp b/LibCBOR/Source/Decoder.cpp index 0cdaf0f..442ee1f 100644 --- a/LibCBOR/Source/Decoder.cpp +++ b/LibCBOR/Source/Decoder.cpp @@ -15,14 +15,6 @@ namespace CBOR { - class ImplementationError: public DecodeError - { - public: - ImplementationError(std::string_view message) - : DecodeError(std::string("internal implementation error: ").append(message)) - {} - }; - class InvalidUsageError: public DecodeError { public: @@ -63,6 +55,8 @@ namespace CBOR namespace { + static constexpr std::size_t Indefinite = std::numeric_limits::max(); + std::size_t SpaceLeft(std::span buffer, std::size_t offset) { if (offset >= buffer.size()) { @@ -87,41 +81,6 @@ namespace CBOR return buffer[current]; } - std::uint16_t Read2B(std::span 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 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 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 buffer, std::size_t ¤t) { EnsureEnoughSpace(buffer, current, 1); @@ -178,6 +137,27 @@ namespace CBOR return ArgumentPosition(header & std::to_underlying(ArgumentPosition::PositionMask)); } + std::uint64_t ArgumentValue(std::uint8_t header, std::span buffer, std::size_t ¤t) + { + 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 T ExtractUnsigned(std::span buffer, std::size_t ¤t) { @@ -354,6 +334,22 @@ namespace CBOR return SignedNegative(header, buffer, current); } } + + std::span ExtractBinary(std::size_t size, std::span buffer, std::size_t ¤t) + { + EnsureEnoughSpace(buffer, current, size); + std::span result(buffer.data() + current, size); + current += size; + return result; + } + + std::string_view ExtractString(std::size_t size, std::span buffer, std::size_t ¤t) + { + EnsureEnoughSpace(buffer, current, size); + std::string_view result(reinterpret_cast(buffer.data() + current), size); + current += size; + return result; + } } Item::Item(Decoder &decoder) @@ -483,18 +479,20 @@ namespace CBOR "use the funcions for those instead"); } - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + MajorType major = GetMajorType(header); mHeaderParsed = true; - if (header.Type != MajorType::Binary) { - throw TypeMismatchError("binary", ToString(header.Type)); + if (major != MajorType::Binary) { + throw TypeMismatchError("binary", ToString(major)); } - if (header.ArgPosition == ArgumentPosition::Indefinite) { + if (GetArgumentPosition(header) == ArgumentPosition::Indefinite) { throw IndefiniteLengthError(); } mDone = true; - return mDecoder->ExtractBinary(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent); } void Binary::AllowIndefinite() @@ -512,10 +510,10 @@ namespace CBOR return true; } - Decoder::Header header = mDecoder->PeekHeader(); - if (header.Type == MajorType::Other && header.Minor == MinorType::Break) { + std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); + if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { mDone = true; - mDecoder->ExtractHeader(); + Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); } mCheckedDone = true; return mDone; @@ -524,16 +522,18 @@ namespace CBOR std::span Binary::Next() { if (!mHeaderParsed) { - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mHeaderParsed = true; - if (header.Type != MajorType::Binary) { - throw TypeMismatchError("binary", ToString(header.Type)); + MajorType major = GetMajorType(header); + if (major != MajorType::Binary) { + throw TypeMismatchError("binary", ToString(major)); } - if (header.ArgPosition != ArgumentPosition::Indefinite) { + if (GetArgumentPosition(header) != ArgumentPosition::Indefinite) { mDone = true; mCheckedDone = true; - return mDecoder->ExtractBinary(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent); } return std::span(); @@ -548,13 +548,16 @@ namespace CBOR } mCheckedDone = false; - Decoder::Header header = mDecoder->ExtractHeader(); - if (header.Type != MajorType::Binary || header.ArgPosition == ArgumentPosition::Indefinite){ - throw MalformedDataError("an indefinite length binary may only contain " - "definite length binaries"); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + MajorType major = GetMajorType(header); + ArgumentPosition pos = GetArgumentPosition(header); + if (major != MajorType::Binary || pos == ArgumentPosition::Indefinite){ + throw MalformedDataError("an indefinite length string may only contain " + "definite length strings"); } - return mDecoder->ExtractBinary(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractBinary(arg, mDecoder->mBuffer, mDecoder->mCurrent); } String::String(Decoder &decoder) @@ -576,18 +579,20 @@ namespace CBOR "use the funcions for those instead"); } - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mHeaderParsed = true; - if (header.Type != MajorType::String) { - throw TypeMismatchError("string", ToString(header.Type)); + MajorType major = GetMajorType(header); + if (major != MajorType::String) { + throw TypeMismatchError("string", ToString(major)); } - if (header.ArgPosition == ArgumentPosition::Indefinite) { + if (GetArgumentPosition(header) == ArgumentPosition::Indefinite) { throw IndefiniteLengthError(); } mDone = true; - return mDecoder->ExtractString(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent); } void String::AllowIndefinite() @@ -605,10 +610,10 @@ namespace CBOR return true; } - std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { mDone = true; - mDecoder->ExtractHeader(); + Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); } mCheckedDone = true; return mDone; @@ -617,16 +622,18 @@ namespace CBOR std::string_view String::Next() { if (!mHeaderParsed) { - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + MajorType major = GetMajorType(header); mHeaderParsed = true; - if (header.Type != MajorType::String) { - throw TypeMismatchError("string", ToString(header.Type)); + if (major != MajorType::String) { + throw TypeMismatchError("string", ToString(major)); } - if (header.ArgPosition != ArgumentPosition::Indefinite) { + if (GetArgumentPosition(header) != ArgumentPosition::Indefinite) { mDone = true; mCheckedDone = true; - return mDecoder->ExtractString(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent); } return std::string_view(); @@ -641,13 +648,16 @@ namespace CBOR } mCheckedDone = false; - Decoder::Header header = mDecoder->ExtractHeader(); - if (header.Type != MajorType::Binary || header.ArgPosition == ArgumentPosition::Indefinite){ + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + MajorType major = GetMajorType(header); + ArgumentPosition pos = GetArgumentPosition(header); + if (major != MajorType::String || pos == ArgumentPosition::Indefinite){ throw MalformedDataError("an indefinite length string may only contain " "definite length strings"); } - return mDecoder->ExtractString(header.Argument); + std::uint64_t arg = ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + return ExtractString(arg, mDecoder->mBuffer, mDecoder->mCurrent); } Array::Array(Decoder &decoder) @@ -662,14 +672,15 @@ namespace CBOR bool Array::Done() { if (!mHeaderParsed) { - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mHeaderParsed = true; - if (header.Type != MajorType::Array) { - throw TypeMismatchError("array", ToString(header.Type)); + if (GetMajorType(header) != MajorType::Array) { + throw TypeMismatchError("array", ToString(GetMajorType(header))); } - bool indefinite = header.ArgPosition == ArgumentPosition::Indefinite; - mSize = indefinite ? Indefinite : header.Argument; + bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; + mSize = indefinite ? Indefinite + : ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); if (!mSize) { mDone = true; @@ -689,10 +700,10 @@ namespace CBOR return mDone; } - Decoder::Header header = mDecoder->PeekHeader(); - if (header.Type == MajorType::Other && header.Minor == MinorType::Break) { + std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); + if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { mDone = true; - mDecoder->ExtractHeader(); + Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); } mCheckedDone = true; return mDone; @@ -725,14 +736,15 @@ namespace CBOR bool Map::Done() { if (!mHeaderParsed) { - Decoder::Header header = mDecoder->ExtractHeader(); + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); mHeaderParsed = true; - if (header.Type != MajorType::Map) { - throw TypeMismatchError("map", ToString(header.Type)); + if (GetMajorType(header) != MajorType::Map) { + throw TypeMismatchError("map", ToString(GetMajorType(header))); } - bool indefinite = header.ArgPosition == ArgumentPosition::Indefinite; - mSize = indefinite ? Indefinite : header.Argument; + bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; + mSize = indefinite ? Indefinite + : ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); if (!mSize) { mDone = true; @@ -752,10 +764,10 @@ namespace CBOR return mDone; } - Decoder::Header header = mDecoder->PeekHeader(); - if (header.Type == MajorType::Other && header.Minor == MinorType::Break) { + std::uint8_t header = Read1B(mDecoder->mBuffer, mDecoder->mCurrent); + if (GetMajorType(header) == MajorType::Other && GetMinorType(header) == MinorType::Break) { mDone = true; - mDecoder->ExtractHeader(); + Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); } mCheckedDone = true; return mDone; @@ -777,7 +789,7 @@ namespace CBOR } Decoder::Decoder(std::span buffer) - : mState(State::Initial), mCurrent(0), mBuffer(buffer) + : mCurrent(0), mBuffer(buffer) {} bool Decoder::Bool() @@ -909,95 +921,4 @@ namespace CBOR { 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 Decoder::ExtractBinary(std::size_t size) - { - EnsureEnoughSpace(mBuffer, mCurrent, size); - std::span 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(mBuffer.data() + mCurrent), size); - mCurrent += size; - return result; - } } diff --git a/Tests/Main.cpp b/Tests/Main.cpp index 4094ac6..4dcd20e 100644 --- a/Tests/Main.cpp +++ b/Tests/Main.cpp @@ -21,7 +21,7 @@ struct SomeStruct std::size_t Encode(const SomeStruct &value, std::span buffer) { CBOR::BasicEncoder enc(buffer); - enc.BeginIndefiniteMap(); + enc.BeginMap(7); enc.Encode("name"); enc.Encode(value.name); @@ -48,12 +48,10 @@ std::size_t Encode(const SomeStruct &value, std::span buffer) } enc.End(); - enc.End(); - return enc.EncodedSize(); } -SomeStruct Decode(std::span buffer) +SomeStruct Decode1(std::span buffer) { SomeStruct result; @@ -92,6 +90,50 @@ SomeStruct Decode(std::span buffer) return result; } +SomeStruct Decode2(std::span 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) { if (s1.name != s2.name) { @@ -145,9 +187,11 @@ int main() std::size_t encodedSize = Encode(expected, buffer); std::println("Encoded size: {}", encodedSize); - SomeStruct result = Decode(std::span(buffer.data(), encodedSize)); + SomeStruct result1 = Decode1(std::span(buffer.data(), encodedSize)); + SomeStruct result2 = Decode2(std::span(buffer.data(), encodedSize)); - Compare(expected, result); + Compare(expected, result1); + Compare(expected, result2); std::println("The test has been completed successfully."); } catch (const std::exception &e) {