Add support for CBOR tags

This commit is contained in:
TennesseeTrash 2025-09-19 18:51:23 +02:00
parent acf4d93fde
commit b65c700c29
5 changed files with 120 additions and 17 deletions

View file

@ -14,13 +14,10 @@ namespace CBOR
using Error::Error; using Error::Error;
}; };
// Forward decl
class Decoder;
class Item class Item
{ {
private: private:
Item(Decoder &decoder); Item(class Decoder &decoder);
public: public:
bool Bool(); bool Bool();
@ -46,19 +43,42 @@ namespace CBOR
class String IndefiniteString(); class String IndefiniteString();
class Array Array(); class Array Array();
class Map Map(); class Map Map();
class TaggedItem TaggedItem();
private: private:
friend class Decoder; friend class Decoder;
friend class Array; friend class Array;
friend class TaggedItem;
friend class KeyValue; friend class KeyValue;
Decoder *mDecoder; 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 class KeyValue
{ {
private: private:
KeyValue(Decoder &decoder); KeyValue(class Decoder &decoder);
public: public:
Item Key(); Item Key();
@ -79,7 +99,7 @@ namespace CBOR
class Binary class Binary
{ {
private: private:
Binary(Decoder &decoder); Binary(class Decoder &decoder);
public: public:
bool Done(); bool Done();
@ -97,7 +117,7 @@ namespace CBOR
class String class String
{ {
private: private:
String(Decoder &decoder); String(class Decoder &decoder);
public: public:
bool Done(); bool Done();
@ -115,7 +135,7 @@ namespace CBOR
class Array class Array
{ {
private: private:
Array(Decoder &decoder); Array(class Decoder &decoder);
public: public:
bool Done(); bool Done();
@ -135,7 +155,7 @@ namespace CBOR
class Map class Map
{ {
private: private:
Map(Decoder &decoder); Map(class Decoder &decoder);
public: public:
bool Done(); bool Done();
@ -180,12 +200,14 @@ namespace CBOR
class String IndefiniteString(); class String IndefiniteString();
class Array Array(); class Array Array();
class Map Map(); class Map Map();
class TaggedItem TaggedItem();
private: private:
friend class Binary; friend class Binary;
friend class String; friend class String;
friend class Array; friend class Array;
friend class Map; friend class Map;
friend class TaggedItem;
std::size_t mCurrent; std::size_t mCurrent;
std::span<std::uint8_t> mBuffer; std::span<std::uint8_t> mBuffer;

View file

@ -57,6 +57,8 @@ namespace CBOR
void End(); void End();
void EncodeTag(std::uint64_t value);
std::size_t EncodedSize() const; std::size_t EncodedSize() const;
private: private:
std::size_t mCurrent; std::size_t mCurrent;

View file

@ -446,6 +446,41 @@ namespace CBOR
return mDecoder->Map(); 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) KeyValue::KeyValue(Decoder &decoder)
: mState(State::Initial), mDecoder(&decoder) : mState(State::Initial), mDecoder(&decoder)
{} {}
@ -457,7 +492,7 @@ namespace CBOR
} }
mState = State::KeyPulled; mState = State::KeyPulled;
return Item(*mDecoder); return { *mDecoder };
} }
Item KeyValue::Value() Item KeyValue::Value()
@ -897,4 +932,9 @@ namespace CBOR
{ {
return { *this }; return { *this };
} }
TaggedItem Decoder::TaggedItem()
{
return { *this };
}
} }

View file

@ -318,7 +318,7 @@ namespace CBOR
void BasicEncoder::Encode(std::span<std::uint8_t> value) void BasicEncoder::Encode(std::span<std::uint8_t> value)
{ {
EnsureEnoughSpace(mBuffer, mCurrent, value.size() + 1 + ArgumentSize(value.size())); EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(value.size()) + value.size());
if (value.size() <= 23) { if (value.size() <= 23) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary) mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
| static_cast<std::uint8_t>(value.size()); | static_cast<std::uint8_t>(value.size());
@ -354,7 +354,7 @@ namespace CBOR
void BasicEncoder::Encode(std::string_view value) 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) { if (value.size() <= 23) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::String) mBuffer[mCurrent++] = std::to_underlying(MajorType::String)
| static_cast<std::uint8_t>(value.size()); | static_cast<std::uint8_t>(value.size());
@ -385,7 +385,7 @@ namespace CBOR
void BasicEncoder::BeginArray(std::size_t size) void BasicEncoder::BeginArray(std::size_t size)
{ {
EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size)); EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(size));
if (size <= 23) { if (size <= 23) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Array) mBuffer[mCurrent++] = std::to_underlying(MajorType::Array)
| static_cast<std::uint8_t>(size); | static_cast<std::uint8_t>(size);
@ -414,7 +414,7 @@ namespace CBOR
void BasicEncoder::BeginMap(std::size_t size) void BasicEncoder::BeginMap(std::size_t size)
{ {
EnsureEnoughSpace(mBuffer, mCurrent, ArgumentSize(size)); EnsureEnoughSpace(mBuffer, mCurrent, 1 + ArgumentSize(size));
if (size <= 23) { if (size <= 23) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Map) mBuffer[mCurrent++] = std::to_underlying(MajorType::Map)
| static_cast<std::uint8_t>(size); | static_cast<std::uint8_t>(size);
@ -476,6 +476,35 @@ namespace CBOR
| std::to_underlying(MinorType::Break); | 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<std::uint8_t>(value);
}
else if (value <= 255) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag)
| std::to_underlying(ArgumentPosition::Next1B);
mBuffer[mCurrent++] = static_cast<std::uint8_t>(value);
}
else if (value <= 65535) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag)
| std::to_underlying(ArgumentPosition::Next2B);
Write(mBuffer, mCurrent, static_cast<std::uint16_t>(value));
}
else if (value <= 4294967295) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag)
| std::to_underlying(ArgumentPosition::Next4B);
Write(mBuffer, mCurrent, static_cast<std::uint32_t>(value));
}
else {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Tag)
| std::to_underlying(ArgumentPosition::Next8B);
Write(mBuffer, mCurrent, static_cast<std::uint64_t>(value));
}
}
std::size_t BasicEncoder::EncodedSize() const std::size_t BasicEncoder::EncodedSize() const
{ {
return mBuffer.size() - (mBuffer.size() - mCurrent); return mBuffer.size() - (mBuffer.size() - mCurrent);

View file

@ -21,6 +21,8 @@ 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.EncodeTag(15'000);
enc.BeginMap(7); enc.BeginMap(7);
enc.Encode("name"); enc.Encode("name");
@ -56,7 +58,11 @@ SomeStruct Decode1(std::span<std::uint8_t> buffer)
SomeStruct result; SomeStruct result;
CBOR::Decoder dec(buffer); 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()) { while (!object.Done()) {
CBOR::KeyValue kv = object.Next(); CBOR::KeyValue kv = object.Next();
std::string_view key = kv.Key().String(); std::string_view key = kv.Key().String();
@ -95,7 +101,11 @@ SomeStruct Decode2(std::span<std::uint8_t> buffer)
SomeStruct result; SomeStruct result;
CBOR::Decoder dec(buffer); 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()) { while (!object.Done()) {
CBOR::KeyValue kv = object.Next(); CBOR::KeyValue kv = object.Next();
std::string_view key = kv.Key().String(); std::string_view key = kv.Key().String();
@ -164,7 +174,7 @@ int main()
{ {
using namespace std::string_view_literals; using namespace std::string_view_literals;
std::array<std::uint8_t, 1024> buffer = {0}; std::array<std::uint8_t, 1024> buffer {};
SomeStruct expected { SomeStruct expected {
.name = "Player1", .name = "Player1",