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