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;
};
// 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<std::uint8_t> mBuffer;

View file

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

View file

@ -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 };
}
}

View file

@ -318,7 +318,7 @@ namespace CBOR
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) {
mBuffer[mCurrent++] = std::to_underlying(MajorType::Binary)
| static_cast<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<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
{
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)
{
CBOR::BasicEncoder enc(buffer);
enc.EncodeTag(15'000);
enc.BeginMap(7);
enc.Encode("name");
@ -56,7 +58,11 @@ SomeStruct Decode1(std::span<std::uint8_t> 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<std::uint8_t> 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<std::uint8_t, 1024> buffer = {0};
std::array<std::uint8_t, 1024> buffer {};
SomeStruct expected {
.name = "Player1",