diff --git a/LibCBOR/Include/CBOR/Concepts.hpp b/LibCBOR/Include/CBOR/Concepts.hpp deleted file mode 100644 index fa5d8a8..0000000 --- a/LibCBOR/Include/CBOR/Concepts.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef LIBCBOR_CONCEPTS_HPP -#define LIBCBOR_CONCEPTS_HPP - -#include "Core.hpp" - -#include -#include -#include -#include - -namespace CBOR -{ - template - concept SimpleType = std::integral || - std::floating_point || - std::same_as, Special> || - std::same_as, bool>; - - template - concept StringLike = std::convertible_to; - - template - concept BinaryLike = std::convertible_to>; - - template - concept EncodableContainer = !StringLike && requires(T t) { - { t.size() } -> std::same_as; - t.begin(); - t.end(); - typename T::value_type; - }; - - template - concept MapContainer = EncodableContainer && requires { - typename T::key_type; - typename T::mapped_type; - }; - - template - concept SequenceContainer = EncodableContainer && !MapContainer; - - namespace Implementation - { - template - struct First; - - template - struct First - { - using Type = T; - }; - - template - struct First - { - using Type = T; - }; - } - - template - using First = Implementation::First::Type; - - template - struct MemberTraits; - - template - struct MemberTraits - { - using ContainingType = T; - using MemberType = MemberT; - }; -} - -#endif // LIBCBOR_CONCEPTS_HPP diff --git a/LibCBOR/Include/CBOR/Decoder.hpp b/LibCBOR/Include/CBOR/Decoder.hpp index 41663d4..b3c957e 100644 --- a/LibCBOR/Include/CBOR/Decoder.hpp +++ b/LibCBOR/Include/CBOR/Decoder.hpp @@ -1,15 +1,10 @@ #ifndef LIBCBOR_DECODER_HPP #define LIBCBOR_DECODER_HPP -#include "Concepts.hpp" #include "Core.hpp" -#include #include #include -#include -#include -#include namespace CBOR { @@ -64,9 +59,6 @@ namespace CBOR class Map Map(); class TaggedItem TaggedItem(); - template - T Decode(); - private: friend class Decoder; friend class Array; @@ -191,8 +183,6 @@ namespace CBOR Array &operator=(const Array &) = delete; public: - static constexpr std::size_t Indefinite = std::numeric_limits::max(); - Array(Array &&other); Array &operator=(Array &&other); ~Array() = default; @@ -200,8 +190,6 @@ namespace CBOR bool Done(); Item Next(); - std::size_t Size(); - private: friend class Decoder; @@ -222,8 +210,6 @@ namespace CBOR Map &operator=(const Map &) = delete; public: - static constexpr std::size_t Indefinite = std::numeric_limits::max(); - Map(Map &&other); Map &operator=(Map &&other); ~Map() = default; @@ -231,8 +217,6 @@ namespace CBOR bool Done(); KeyValue Next(); - std::size_t Size(); - private: friend class Decoder; @@ -288,10 +272,6 @@ namespace CBOR class TaggedItem TaggedItem(); class Item AsItem(); - - template - T Decode(); - private: friend class Binary; friend class String; @@ -302,136 +282,6 @@ namespace CBOR std::size_t mCurrent; ConstBuffer mBuffer; }; - - template - T Item::Decode() - { - return mDecoder->Decode(); - } - - template - T Decoder::Decode() - { - if constexpr (std::same_as) { - return Bool(); - } - else if constexpr (std::same_as) { - return Special(); - } - else if constexpr (std::signed_integral) { - if constexpr (sizeof(T) == 1) { - return Int8(); - } - else if constexpr (sizeof(T) == 2) { - return Int16(); - } - else if constexpr (sizeof(T) == 4) { - return Int32(); - } - else if constexpr (sizeof(T) == 8) { - return Int64(); - } - } - else if constexpr (std::unsigned_integral) { - if constexpr (sizeof(T) == 1) { - return Uint8(); - } - else if constexpr (sizeof(T) == 2) { - return Uint16(); - } - else if constexpr (sizeof(T) == 4) { - return Uint32(); - } - else if constexpr (sizeof(T) == 8) { - return Uint64(); - } - } - else if constexpr (std::same_as) { - return Float(); - } - else if constexpr (std::same_as) { - return Double(); - } - else if constexpr (std::same_as) { - return String(); - } - else if constexpr (std::same_as) { - return Binary(); - } - else if constexpr (requires (T a) { DecodeHook(*this, a); }) { - T result; - DecodeHook(*this, result); - return result; - } - else { - static_assert(false, "This type is not decodable (needs a custom DecodeHook)"); - } - } - - template - requires std::is_member_object_pointer_v - class StructMember - { - public: - using KeyType = Key; - using MemberPtrType = ValuePtr; - using ContainingType = MemberTraits::ContainingType; - using MemberType = MemberTraits::MemberType; - - StructMember(Key key, ValuePtr ptr) - : mEnabled(true), mKey(key), mValuePtr(ptr) - {} - - bool Match(const Key &other) const - { - return mEnabled && other == mKey; - } - - void Decode(Item &item, ContainingType &result) - { - result.*mValuePtr = item.Decode(); - mEnabled = false; - } - private: - bool mEnabled; - Key mKey; - ValuePtr mValuePtr; - }; - - template - StructMember(const char *, ValuePtr) -> StructMember; - - template - class StructHelper - { - public: - using ContainingType = MemberTraits>::ContainingType; - - StructHelper(StructMember &&...items) - : mStructItems(std::forward>(items)...) - {} - - void Decode(Decoder &decoder, ContainingType &result) - { - return Decode(decoder.AsItem(), result); - } - - void Decode(Item item, ContainingType &result) - { - CBOR::Map map = item.Map(); - while (!map.Done()) { - CBOR::KeyValue kv = map.Next(); - Key key = kv.Key().Decode(); - Item value = kv.Value(); - std::apply([&key, &value, &result](auto &&...args) { - void(((args.Match(key) ? (args.Decode(value, result), true) : false) || ...)); - }, mStructItems); - } - } - - private: - std::tuple...> mStructItems; - }; } #endif // LIBCBOR_DECODER_HPP diff --git a/LibCBOR/Include/CBOR/DecoderHooks.hpp b/LibCBOR/Include/CBOR/DecoderHooks.hpp deleted file mode 100644 index ddd143a..0000000 --- a/LibCBOR/Include/CBOR/DecoderHooks.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef LIBCBOR_DECODERHOOKS_HPP -#define LIBCBOR_DECODERHOOKS_HPP - -#include "Decoder.hpp" - -#include -#include -#include -#include -#include -#include - -namespace CBOR -{ - constexpr - void DecodeHook(Decoder &decoder, std::string &value) - { - String string = decoder.IndefiniteString(); - while (!string.Done()) { - value.append(string.Next()); - } - } - - constexpr - void DecodeHook(Decoder &decoder, std::vector &value) - { - Binary binary = decoder.IndefiniteBinary(); - while(!binary.Done()) { - value.append_range(binary.Next()); - } - } - - template - constexpr - void DecodeHook(Decoder &decoder, std::vector &value) - { - Array array = decoder.Array(); - while (!array.Done()) { - value.push_back(array.Next().Decode()); - } - } - - template - constexpr - void DecodeHook(Decoder &decoder, std::unordered_map &value) - { - Map map = decoder.Map(); - while (!map.Done()) { - KeyValue kv = map.Next(); - value.insert(std::make_pair(kv.Key().Decode(), kv.Value().Decode())); - } - } - - template - constexpr - void DecodeHook(Decoder &decoder, std::map &value) - { - Map map = decoder.Map(); - while (!map.Done()) { - KeyValue kv = map.Next(); - value.insert(std::make_pair(kv.Key().Decode(), kv.Value().Decode())); - } - } - - template - constexpr - void DecodeHook(Decoder &decoder, std::tuple &value) - { - Array array = decoder.Array(); - if (array.Size() != sizeof...(Items)) { - throw DecodeError(std::format("Expected {} elements to fill tuple, got {} instead", - sizeof...(Items), array.Size())); - } - std::apply([&array] (Args &...args) { - ((array.Done(), args = array.Next().Decode()), ...); - }, value); - } -} - -#endif // LIBCBOR_DECODERHOOKS_HPP diff --git a/LibCBOR/Include/CBOR/Encoder.hpp b/LibCBOR/Include/CBOR/Encoder.hpp index 9c42047..9b22bde 100644 --- a/LibCBOR/Include/CBOR/Encoder.hpp +++ b/LibCBOR/Include/CBOR/Encoder.hpp @@ -1,7 +1,6 @@ #ifndef LIBCBOR_ENCODER_HPP #define LIBCBOR_ENCODER_HPP -#include "Concepts.hpp" #include "Core.hpp" #include @@ -40,7 +39,7 @@ namespace CBOR void Write(std::uint16_t value); void Write(std::uint32_t value); void Write(std::uint64_t value); - void Write(ConstBuffer value); + void Write(Buffer value); void Write(std::string_view value); std::size_t Size() const; @@ -52,7 +51,7 @@ namespace CBOR void Write(std::uint16_t value); void Write(std::uint32_t value); void Write(std::uint64_t value); - void Write(ConstBuffer value); + void Write(Buffer value); void Write(std::string_view value); std::size_t Size() const; @@ -68,7 +67,7 @@ namespace CBOR void Write(std::uint16_t value); void Write(std::uint32_t value); void Write(std::uint64_t value); - void Write(ConstBuffer value); + void Write(Buffer value); void Write(std::string_view value); std::size_t Size() const; @@ -109,7 +108,7 @@ namespace CBOR void Encode(float value); void Encode(double value); - void Encode(ConstBuffer value); + void Encode(Buffer value); void Encode(const char *value); void Encode(std::string_view value); @@ -126,91 +125,9 @@ namespace CBOR void EncodeTag(std::uint64_t value); std::size_t Size() const; - private: EncoderBuffer mBuffer; }; - - class Encoder - { - public: - Encoder(EncoderBuffer buffer); - - template - Encoder &Encode(const T &value) - { - if constexpr (SimpleType || StringLike || BinaryLike) { - mEncoder.Encode(value); - } - else if constexpr (SequenceContainer) { - mEncoder.BeginArray(value.size()); - for (const auto &item: value) { - Encode(item); - } - } - else if constexpr (MapContainer) { - mEncoder.BeginMap(value.size()); - for (const auto &[key, value]: value) { - Encode(key); - Encode(value); - } - } - else if constexpr (requires { EncodeHook(*this, value); }) { - EncodeHook(*this, value); - } - else if constexpr (requires { EncodeHook(mEncoder, value); }) { - EncodeHook(mEncoder, value); - } - else { - static_assert(false, "This type is not encodable (needs a custom EncodeHook)"); - } - return *this; - } - - template - Encoder &EncodeArray(Ts &&...values) - { - mEncoder.BeginArray(sizeof...(values)); - (Encode(std::forward(values)), ...); - return *this; - } - - template - requires (sizeof...(Ts) % 2 == 0) - Encoder &EncodeMap(Ts &&...values) - { - mEncoder.BeginMap(sizeof...(values) / 2); - (Encode(std::forward(values)), ...); - return *this; - } - - template - Encoder &EncodeTagged(std::uint64_t tag, const T &value) - { - mEncoder.EncodeTag(tag); - return Encode(value); - } - - template - Encoder &EncodeTaggedArray(std::uint64_t tag, Ts &&...values) - { - mEncoder.EncodeTag(tag); - return EncodeArray(std::forward(values)...); - } - - template - requires (sizeof...(Ts) % 2 == 0) - Encoder &EncodeTaggedMap(std::uint64_t tag, Ts &&...values) - { - mEncoder.EncodeTag(tag); - return EncodeMap(std::forward(values)...); - } - - std::size_t Size() const; - - private: - BasicEncoder mEncoder; - }; } #endif // LIBCBOR_ENCODER_HPP diff --git a/LibCBOR/Include/CBOR/EncoderHooks.hpp b/LibCBOR/Include/CBOR/EncoderHooks.hpp deleted file mode 100644 index 47bc5f1..0000000 --- a/LibCBOR/Include/CBOR/EncoderHooks.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LIBCBOR_ENCODERHOOKS_HPP -#define LIBCBOR_ENCODERHOOKS_HPP - -#include "Encoder.hpp" - -#include - -namespace CBOR -{ - template - constexpr - void EncodeHook(CBOR::Encoder &encoder, const std::tuple &tuple) - { - std::apply([&encoder] (const auto &...args) { - encoder.EncodeArray(args...); - }, tuple); - } -} - -#endif // LIBCBOR_ENCODERHOOKS_HPP diff --git a/LibCBOR/Source/Decoder.cpp b/LibCBOR/Source/Decoder.cpp index 526dda7..dae31e0 100644 --- a/LibCBOR/Source/Decoder.cpp +++ b/LibCBOR/Source/Decoder.cpp @@ -55,6 +55,8 @@ namespace CBOR namespace { + static constexpr std::size_t Indefinite = std::numeric_limits::max(); + std::size_t SpaceLeft(ConstBuffer buffer, std::size_t offset) { if (offset >= buffer.size()) { @@ -849,7 +851,7 @@ namespace CBOR } if (!mCheckedDone) { - throw InvalidUsageError("check whether the array is done first"); + throw InvalidUsageError("check whether the indefinite string is done first"); } mCheckedDone = false; @@ -857,27 +859,6 @@ namespace CBOR return Item(*mDecoder); } - std::size_t Array::Size() - { - if (!mHeaderParsed) { - std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); - mHeaderParsed = true; - if (GetMajorType(header) != MajorType::Array) { - throw TypeMismatchError("array", ToString(GetMajorType(header))); - } - - bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; - mSize = indefinite ? Indefinite - : ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); - - if (!mSize) { - mDone = true; - mCheckedDone = true; - } - } - return mSize; - } - Map::Map(Decoder &decoder) : mHeaderParsed(false) , mDone(false) @@ -954,7 +935,7 @@ namespace CBOR } if (!mCheckedDone) { - throw InvalidUsageError("check whether the map is done first"); + throw InvalidUsageError("check whether the indefinite string is done first"); } mCheckedDone = false; @@ -962,27 +943,6 @@ namespace CBOR return KeyValue(*mDecoder); } - std::size_t Map::Size() - { - if (!mHeaderParsed) { - std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); - mHeaderParsed = true; - if (GetMajorType(header) != MajorType::Map) { - throw TypeMismatchError("map", ToString(GetMajorType(header))); - } - - bool indefinite = GetArgumentPosition(header) == ArgumentPosition::Indefinite; - mSize = indefinite ? Indefinite - : ArgumentValue(header, mDecoder->mBuffer, mDecoder->mCurrent); - - if (!mSize) { - mDone = true; - mCheckedDone = true; - } - } - return mSize; - } - Decoder::Decoder(ConstBuffer buffer) : mCurrent(0), mBuffer(buffer) {} diff --git a/LibCBOR/Source/Encoder.cpp b/LibCBOR/Source/Encoder.cpp index aac2827..f21a177 100644 --- a/LibCBOR/Source/Encoder.cpp +++ b/LibCBOR/Source/Encoder.cpp @@ -120,7 +120,7 @@ namespace CBOR }, mBuffer); } - void EncoderBuffer::Write(ConstBuffer value) + void EncoderBuffer::Write(Buffer value) { std::visit(Overload { [value] (FixedBuffer &buffer) { buffer.Write(value); }, @@ -185,7 +185,7 @@ namespace CBOR mBuffer[mCurrent++] = static_cast((network >> 56) & mask); } - void EncoderBuffer::FixedBuffer::Write(ConstBuffer value) + void EncoderBuffer::FixedBuffer::Write(Buffer value) { EnsureSpace(value.size()); std::memcpy(mBuffer.data() + mCurrent, value.data(), value.size()); @@ -250,7 +250,7 @@ namespace CBOR mBuffer->push_back(static_cast((network >> 56) & mask)); } - void EncoderBuffer::DynamicBuffer::Write(ConstBuffer value) + void EncoderBuffer::DynamicBuffer::Write(Buffer value) { mBuffer->append_range(value); } @@ -447,7 +447,7 @@ namespace CBOR mBuffer.Write(std::bit_cast(value)); } - void BasicEncoder::Encode(ConstBuffer value) + void BasicEncoder::Encode(Buffer value) { WriteHeader(mBuffer, MajorType::Binary, value.size()); mBuffer.Write(value); @@ -508,13 +508,4 @@ namespace CBOR { return mBuffer.Size(); } - - Encoder::Encoder(EncoderBuffer buffer) - : mEncoder(std::move(buffer)) - {} - - std::size_t Encoder::Size() const - { - return mEncoder.Size(); - } } diff --git a/Tests/Main.cpp b/Tests/Main.cpp index a44c00c..67653a7 100644 --- a/Tests/Main.cpp +++ b/Tests/Main.cpp @@ -1,14 +1,11 @@ -#include -#include -#include // IWYU pragma: keep -#include -#include - +#include "CBOR/Decoder.hpp" +#include "CBOR/Encoder.hpp" +#include "CBOR/Printer.hpp" #include -#include // IWYU pragma: keep #include #include #include +#include #include #include @@ -21,47 +18,166 @@ struct SomeStruct std::int64_t slots; std::uint32_t times; std::vector tools; - - constexpr auto operator<=>(const SomeStruct &) const = default; }; -void EncodeHook(CBOR::Encoder &enc, const SomeStruct &value) +std::size_t Encode(const SomeStruct &value, auto &buffer) { - enc.EncodeTaggedMap( - 15'000, - "name", value.name, - "speed", value.speed, - "fov", value.fov, - "thing", value.thing, - "slots", value.slots, - "times", value.times, - "tools", value.tools - ); + CBOR::BasicEncoder enc(buffer); + enc.EncodeTag(15'000); + + enc.BeginMap(7); + + enc.Encode("name"); + enc.Encode(value.name); + + enc.Encode("speed"); + enc.Encode(value.speed); + + enc.Encode("fov"); + enc.Encode(value.fov); + + enc.Encode("thing"); + enc.Encode(value.thing); + + enc.Encode("slots"); + enc.Encode(value.slots); + + enc.Encode("times"); + enc.Encode(value.times); + + enc.Encode("tools"); + enc.BeginIndefiniteArray(); + for (std::string_view tool: value.tools) { + enc.Encode(tool); + } + enc.End(); + + return enc.Size(); } -void DecodeHook(CBOR::Decoder &dec, SomeStruct &value) +SomeStruct Decode1(std::span buffer) { - CBOR::StructHelper helper { - CBOR::StructMember { "name", &SomeStruct::name }, - CBOR::StructMember { "speed", &SomeStruct::speed }, - CBOR::StructMember { "fov", &SomeStruct::fov }, - CBOR::StructMember { "thing", &SomeStruct::thing }, - CBOR::StructMember { "slots", &SomeStruct::slots }, - CBOR::StructMember { "times", &SomeStruct::times }, - CBOR::StructMember { "tools", &SomeStruct::tools }, - }; + SomeStruct result; + + CBOR::Decoder dec(buffer); CBOR::TaggedItem tagged = dec.TaggedItem(); if (tagged.Tag() != 15'000) { - throw std::runtime_error("Expected item with tag value 15000"); + 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(); + CBOR::Item value = kv.Value(); + if (key == "name") { + result.name = value.String(); + } + 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(std::string(tools.Next().String())); + } + } + } + + return result; +} + +SomeStruct Decode2(std::span buffer) +{ + SomeStruct result; + + CBOR::Decoder dec(buffer); + 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(); + CBOR::Item value = kv.Value(); + if (key == "name") { + result.name = value.String(); + } + 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().IndefiniteString(); + while (!tool.Done()) { + result.tools.back().append(tool.Next()); + } + } + } + } + + return result; +} + +void Compare(const SomeStruct &s1, const SomeStruct &s2) +{ + if (s1.name != s2.name) { + throw std::runtime_error("test error: names are not the same"); + } + if (s1.speed != s2.speed) { + throw std::runtime_error("test error: speed is not the same"); + } + if (s1.fov != s2.fov) { + throw std::runtime_error("test error: fovs are not the same"); + } + if (s1.thing != s2.thing) { + throw std::runtime_error("test error: things are not the same"); + } + if (s1.slots != s2.slots) { + throw std::runtime_error("test error: slots are not the same"); + } + if (s1.times != s2.times) { + throw std::runtime_error("test error: times are not the same"); + } + for (const auto &[t1, t2]: std::ranges::views::zip(s1.tools, s2.tools)) { + if (t1 != t2) { + throw std::runtime_error("test error: some tools are not the same"); + } } - helper.Decode(tagged.Item(), value); } int main() { using namespace std::string_view_literals; - std::array buffer {}; + //std::array buffer {}; + std::vector buffer; SomeStruct expected { .name = "Player1", @@ -80,20 +196,23 @@ int main() }; try { - CBOR::Encoder enc(buffer); - enc.Encode(expected); + std::size_t encodedSize = Encode(expected, buffer); + std::println("Encoded size: {}", encodedSize); - CBOR::ConstBuffer encoded(buffer.data(), enc.Size()); - - CBOR::Decoder dec(encoded); - SomeStruct res = dec.Decode(); - - if (res != expected) { - throw std::runtime_error("test error: the encode/decode round trip should be lossless"); + std::print("Encoded hex: "); + for (std::size_t i = 0; i < encodedSize; ++i) { + std::print("{:02X} ", buffer[i]); } + std::println(); - CBOR::Print(std::cout, encoded); + SomeStruct result1 = Decode1(std::span(buffer.data(), encodedSize)); + SomeStruct result2 = Decode2(std::span(buffer.data(), encodedSize)); + std::println("JSON-esque serialization:"); + CBOR::Print(std::cout, std::span(buffer.data(), encodedSize)); + + Compare(expected, result1); + Compare(expected, result2); std::println("The test has been completed successfully."); } catch (const std::exception &e) {