From b65c700c29ce5a766f12774d9942f107c27b9338 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Fri, 19 Sep 2025 18:51:23 +0200 Subject: [PATCH] Add support for CBOR tags --- LibCBOR/Include/CBOR/Decoder.hpp | 40 +++++++++++++++++++++++------- LibCBOR/Include/CBOR/Encoder.hpp | 2 ++ LibCBOR/Source/Decoder.cpp | 42 +++++++++++++++++++++++++++++++- LibCBOR/Source/Encoder.cpp | 37 +++++++++++++++++++++++++--- Tests/Main.cpp | 16 +++++++++--- 5 files changed, 120 insertions(+), 17 deletions(-) diff --git a/LibCBOR/Include/CBOR/Decoder.hpp b/LibCBOR/Include/CBOR/Decoder.hpp index b552054..40b2096 100644 --- a/LibCBOR/Include/CBOR/Decoder.hpp +++ b/LibCBOR/Include/CBOR/Decoder.hpp @@ -14,13 +14,10 @@ namespace CBOR using Error::Error; }; - // Forward decl - class Decoder; - class Item { private: - Item(Decoder &decoder); + Item(class Decoder &decoder); public: bool Bool(); @@ -46,19 +43,42 @@ namespace CBOR class String IndefiniteString(); class Array Array(); class Map Map(); + class TaggedItem TaggedItem(); private: friend class Decoder; friend class Array; + friend class TaggedItem; friend class KeyValue; Decoder *mDecoder; }; + class TaggedItem + { + private: + TaggedItem(class Decoder &decoder); + + public: + std::uint64_t Tag(); + Item Item(); + + private: + friend class Decoder; + + enum class State + { + Initial, TagPulled, Done, + }; + + State mState; + Decoder *mDecoder; + }; + class KeyValue { private: - KeyValue(Decoder &decoder); + KeyValue(class Decoder &decoder); public: Item Key(); @@ -79,7 +99,7 @@ namespace CBOR class Binary { private: - Binary(Decoder &decoder); + Binary(class Decoder &decoder); public: bool Done(); @@ -97,7 +117,7 @@ namespace CBOR class String { private: - String(Decoder &decoder); + String(class Decoder &decoder); public: bool Done(); @@ -115,7 +135,7 @@ namespace CBOR class Array { private: - Array(Decoder &decoder); + Array(class Decoder &decoder); public: bool Done(); @@ -135,7 +155,7 @@ namespace CBOR class Map { private: - Map(Decoder &decoder); + Map(class Decoder &decoder); public: bool Done(); @@ -180,12 +200,14 @@ namespace CBOR class String IndefiniteString(); class Array Array(); class Map Map(); + class TaggedItem TaggedItem(); private: friend class Binary; friend class String; friend class Array; friend class Map; + friend class TaggedItem; std::size_t mCurrent; std::span mBuffer; diff --git a/LibCBOR/Include/CBOR/Encoder.hpp b/LibCBOR/Include/CBOR/Encoder.hpp index bcde5ec..efb115d 100644 --- a/LibCBOR/Include/CBOR/Encoder.hpp +++ b/LibCBOR/Include/CBOR/Encoder.hpp @@ -57,6 +57,8 @@ namespace CBOR void End(); + void EncodeTag(std::uint64_t value); + std::size_t EncodedSize() const; private: std::size_t mCurrent; diff --git a/LibCBOR/Source/Decoder.cpp b/LibCBOR/Source/Decoder.cpp index 8cb2ca3..c0dce05 100644 --- a/LibCBOR/Source/Decoder.cpp +++ b/LibCBOR/Source/Decoder.cpp @@ -446,6 +446,41 @@ namespace CBOR return mDecoder->Map(); } + TaggedItem Item::TaggedItem() + { + return mDecoder->TaggedItem(); + } + + TaggedItem::TaggedItem(Decoder &decoder) + : mState(State::Initial), mDecoder(&decoder) + {} + + std::uint64_t TaggedItem::Tag() + { + if (mState != State::Initial) { + throw InvalidUsageError("the tag has already been pulled, or this pair is done"); + } + + mState = State::TagPulled; + std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); + MajorType major = GetMajorType(header); + if (major != MajorType::Tag) { + throw TypeMismatchError("tagged item", ToString(major)); + } + + return ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); + } + + Item TaggedItem::Item() + { + if (mState != State::TagPulled) { + throw InvalidUsageError("the tag must be pulled first, or this item is done"); + } + + mState = State::Done; + return { *mDecoder }; + } + KeyValue::KeyValue(Decoder &decoder) : mState(State::Initial), mDecoder(&decoder) {} @@ -457,7 +492,7 @@ namespace CBOR } mState = State::KeyPulled; - return Item(*mDecoder); + return { *mDecoder }; } Item KeyValue::Value() @@ -897,4 +932,9 @@ namespace CBOR { return { *this }; } + + TaggedItem Decoder::TaggedItem() + { + return { *this }; + } } diff --git a/LibCBOR/Source/Encoder.cpp b/LibCBOR/Source/Encoder.cpp index d4bbae1..7c7ae08 100644 --- a/LibCBOR/Source/Encoder.cpp +++ b/LibCBOR/Source/Encoder.cpp @@ -318,7 +318,7 @@ namespace CBOR void BasicEncoder::Encode(std::span value) { - EnsureEnoughSpace(mBuffer, mCurrent, value.size() + 1 + ArgumentSize(value.size())); + EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(value.size()) + value.size()); if (value.size() <= 23) { mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary) | static_cast(value.size()); @@ -354,7 +354,7 @@ namespace CBOR void BasicEncoder::Encode(std::string_view value) { - EnsureEnoughSpace(mBuffer, mCurrent, value.size() + ArgumentSize(value.size())); + EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(value.size()) + value.size()); if (value.size() <= 23) { mBuffer[mCurrent++] = std::to_underlying(MajorType::String) | static_cast(value.size()); @@ -385,7 +385,7 @@ namespace CBOR void BasicEncoder::BeginArray(std::size_t size) { - EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size)); + EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(size)); if (size <= 23) { mBuffer[mCurrent++] = std::to_underlying(MajorType::Array) | static_cast(size); @@ -414,7 +414,7 @@ namespace CBOR void BasicEncoder::BeginMap(std::size_t size) { - EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size)); + EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(size)); if (size <= 23) { mBuffer[mCurrent++] = std::to_underlying(MajorType::Map) | static_cast(size); @@ -476,6 +476,35 @@ namespace CBOR | std::to_underlying(MinorType::Break); } + void BasicEncoder::EncodeTag(std::uint64_t value) + { + EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(value)); + if (value <= 23) { + mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag) + | static_cast(value); + } + else if (value <= 255) { + mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag) + | std::to_underlying(ArgumentPosition::Next1B); + mBuffer[mCurrent++] = static_cast(value); + } + else if (value <= 65535) { + mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag) + | std::to_underlying(ArgumentPosition::Next2B); + Write(mBuffer, mCurrent, static_cast(value)); + } + else if (value <= 4294967295) { + mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag) + | std::to_underlying(ArgumentPosition::Next4B); + Write(mBuffer, mCurrent, static_cast(value)); + } + else { + mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag) + | std::to_underlying(ArgumentPosition::Next8B); + Write(mBuffer, mCurrent, static_cast(value)); + } + } + std::size_t BasicEncoder::EncodedSize() const { return mBuffer.size() - (mBuffer.size() - mCurrent); diff --git a/Tests/Main.cpp b/Tests/Main.cpp index cae2a7d..0819924 100644 --- a/Tests/Main.cpp +++ b/Tests/Main.cpp @@ -21,6 +21,8 @@ struct SomeStruct std::size_t Encode(const SomeStruct &value, std::span buffer) { CBOR::BasicEncoder enc(buffer); + enc.EncodeTag(15'000); + enc.BeginMap(7); enc.Encode("name"); @@ -56,7 +58,11 @@ SomeStruct Decode1(std::span buffer) SomeStruct result; CBOR::Decoder dec(buffer); - CBOR::Map object = dec.Map(); + CBOR::TaggedItem tagged = dec.TaggedItem(); + if (tagged.Tag() != 15'000) { + throw std::runtime_error("test error: could not extract object tag"); + } + CBOR::Map object = tagged.Item().Map(); while (!object.Done()) { CBOR::KeyValue kv = object.Next(); std::string_view key = kv.Key().String(); @@ -95,7 +101,11 @@ SomeStruct Decode2(std::span buffer) SomeStruct result; CBOR::Decoder dec(buffer); - CBOR::Map object = dec.Map(); + CBOR::TaggedItem tagged = dec.TaggedItem(); + if (tagged.Tag() != 15'000) { + throw std::runtime_error("test error: could not extract object tag"); + } + CBOR::Map object = tagged.Item().Map(); while (!object.Done()) { CBOR::KeyValue kv = object.Next(); std::string_view key = kv.Key().String(); @@ -164,7 +174,7 @@ int main() { using namespace std::string_view_literals; - std::array buffer = {0}; + std::array buffer {}; SomeStruct expected { .name = "Player1",