diff --git a/LibCBOR/CMakeLists.txt b/LibCBOR/CMakeLists.txt index 76fda8d..03af6a0 100644 --- a/LibCBOR/CMakeLists.txt +++ b/LibCBOR/CMakeLists.txt @@ -25,4 +25,5 @@ target_sources(LibCBOR "Source/Core.cpp" "Source/Decoder.cpp" "Source/Encoder.cpp" + "Source/Printer.cpp" ) diff --git a/LibCBOR/Include/CBOR/Decoder.hpp b/LibCBOR/Include/CBOR/Decoder.hpp index 40b2096..9d36d69 100644 --- a/LibCBOR/Include/CBOR/Decoder.hpp +++ b/LibCBOR/Include/CBOR/Decoder.hpp @@ -3,6 +3,7 @@ #include "Core.hpp" +#include #include #include @@ -20,6 +21,13 @@ namespace CBOR Item(class Decoder &decoder); public: + static constexpr + std::uint64_t ArgumentIndefinite = std::numeric_limits::max(); + + MajorType GetMajor() const; + MinorType GetMinor() const; + std::uint64_t GetArgument() const; + bool Bool(); Special Special(); @@ -37,13 +45,13 @@ namespace CBOR float Float(); double Double(); - std::span Binary(); - std::string_view String(); - class Binary IndefiniteBinary(); - class String IndefiniteString(); - class Array Array(); - class Map Map(); - class TaggedItem TaggedItem(); + std::span Binary(); + std::string_view String(); + class Binary IndefiniteBinary(); + class String IndefiniteString(); + class Array Array(); + class Map Map(); + class TaggedItem TaggedItem(); private: friend class Decoder; @@ -102,8 +110,8 @@ namespace CBOR Binary(class Decoder &decoder); public: - bool Done(); - std::span Next(); + bool Done(); + std::span Next(); private: friend class Decoder; @@ -175,7 +183,14 @@ namespace CBOR class Decoder { public: - Decoder(std::span buffer); + Decoder(std::span buffer); + + static constexpr + std::uint64_t ArgumentIndefinite = std::numeric_limits::max(); + + MajorType GetMajor() const; + MinorType GetMinor() const; + std::uint64_t GetArgument() const; bool Bool(); Special Special(); @@ -194,14 +209,15 @@ namespace CBOR float Float(); double Double(); - std::span Binary(); - std::string_view String(); - class Binary IndefiniteBinary(); - class String IndefiniteString(); - class Array Array(); - class Map Map(); - class TaggedItem TaggedItem(); + std::span Binary(); + std::string_view String(); + class Binary IndefiniteBinary(); + class String IndefiniteString(); + class Array Array(); + class Map Map(); + class TaggedItem TaggedItem(); + class Item AsItem(); private: friend class Binary; friend class String; @@ -209,8 +225,8 @@ namespace CBOR friend class Map; friend class TaggedItem; - std::size_t mCurrent; - std::span mBuffer; + std::size_t mCurrent; + std::span mBuffer; }; } diff --git a/LibCBOR/Include/CBOR/Printer.hpp b/LibCBOR/Include/CBOR/Printer.hpp new file mode 100644 index 0000000..8efe7df --- /dev/null +++ b/LibCBOR/Include/CBOR/Printer.hpp @@ -0,0 +1,12 @@ +#ifndef LIBCBOR_PRINTER_HPP +#define LIBCBOR_PRINTER_HPP + +#include +#include + +namespace CBOR +{ + void Print(std::ostream &out, std::span buffer); +} + +#endif // LIBCBOR_PRINTER_HPP diff --git a/LibCBOR/Source/Decoder.cpp b/LibCBOR/Source/Decoder.cpp index c0dce05..c15bbb3 100644 --- a/LibCBOR/Source/Decoder.cpp +++ b/LibCBOR/Source/Decoder.cpp @@ -57,7 +57,7 @@ namespace CBOR { static constexpr std::size_t Indefinite = std::numeric_limits::max(); - std::size_t SpaceLeft(std::span buffer, std::size_t offset) + std::size_t SpaceLeft(std::span buffer, std::size_t offset) { if (offset >= buffer.size()) { return 0; @@ -65,7 +65,7 @@ namespace CBOR return buffer.size() - offset; } - void EnsureEnoughSpace(std::span buffer, std::size_t offset, + void EnsureEnoughSpace(std::span buffer, std::size_t offset, std::size_t spaceRequired) { if (SpaceLeft(buffer, offset) < spaceRequired) { @@ -75,19 +75,13 @@ namespace CBOR } } - std::uint8_t Read1B(std::span buffer, std::size_t current) + std::uint8_t Read1B(std::span buffer, std::size_t current) { EnsureEnoughSpace(buffer, current, 1); return buffer[current]; } - std::uint8_t Consume1B(std::span buffer, std::size_t ¤t) - { - EnsureEnoughSpace(buffer, current, 1); - return buffer[current++]; - } - - std::uint16_t Consume2B(std::span buffer, std::size_t ¤t) + std::uint16_t Read2B(std::span buffer, std::size_t current) { EnsureEnoughSpace(buffer, current, 2); std::uint16_t result = 0; @@ -96,7 +90,7 @@ namespace CBOR return NetworkToHost(result); } - std::uint32_t Consume4B(std::span buffer, std::size_t ¤t) + std::uint32_t Read4B(std::span buffer, std::size_t current) { EnsureEnoughSpace(buffer, current, 4); std::uint32_t result = 0; @@ -107,7 +101,48 @@ namespace CBOR return NetworkToHost(result); } - std::uint64_t Consume8B(std::span buffer, std::size_t ¤t) + 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++]) << 8; + result |= std::uint64_t(buffer[current++]) << 16; + result |= std::uint64_t(buffer[current++]) << 24; + result |= std::uint64_t(buffer[current++]) << 32; + result |= std::uint64_t(buffer[current++]) << 40; + result |= std::uint64_t(buffer[current++]) << 48; + result |= std::uint64_t(buffer[current++]) << 56; + return NetworkToHost(result); + } + + std::uint8_t Consume1B(std::span buffer, std::size_t ¤t) + { + EnsureEnoughSpace(buffer, current, 1); + return buffer[current++]; + } + + std::uint16_t Consume2B(std::span buffer, std::size_t ¤t) + { + EnsureEnoughSpace(buffer, current, 2); + std::uint16_t result = 0; + result |= std::uint16_t(buffer[current++]) ; + result |= std::uint16_t(buffer[current++]) << 8; + return NetworkToHost(result); + } + + std::uint32_t Consume4B(std::span buffer, std::size_t ¤t) + { + EnsureEnoughSpace(buffer, current, 4); + std::uint32_t result = 0; + result |= std::uint32_t(buffer[current++]) ; + result |= std::uint32_t(buffer[current++]) << 8; + result |= std::uint32_t(buffer[current++]) << 16; + result |= std::uint32_t(buffer[current++]) << 24; + return NetworkToHost(result); + } + + std::uint64_t Consume8B(std::span buffer, std::size_t ¤t) { EnsureEnoughSpace(buffer, current, 8); std::uint64_t result = 0; @@ -137,7 +172,8 @@ 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) + std::uint64_t ArgumentValue(std::uint8_t header, std::span buffer, + std::size_t ¤t) { ArgumentPosition position = GetArgumentPosition(header); if (std::to_underlying(position) <= 23) { @@ -158,8 +194,30 @@ namespace CBOR } } + std::uint64_t ReadArgumentValue(std::uint8_t header, std::span buffer, + std::size_t current) + { + ArgumentPosition position = GetArgumentPosition(header); + if (std::to_underlying(position) <= 23) { + return std::to_underlying(position); + } + switch (position) { + case ArgumentPosition::Next1B: + return Read1B(buffer, current); + case ArgumentPosition::Next2B: + return Read2B(buffer, current); + case ArgumentPosition::Next4B: + return Read4B(buffer, current); + case ArgumentPosition::Next8B: + return Read8B(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) + T ExtractUnsigned(std::span buffer, std::size_t ¤t) { static constexpr std::uint64_t maxValue = std::numeric_limits::max(); @@ -214,7 +272,8 @@ namespace CBOR // Note(3011): In this case it includes zero, even though zero is not technically positive. template - T SignedPositive(std::uint8_t header, std::span buffer, std::size_t ¤t) + T SignedPositive(std::uint8_t header, std::span buffer, + std::size_t ¤t) { static constexpr std::uint64_t maxValue = std::numeric_limits::max(); @@ -266,7 +325,8 @@ namespace CBOR } template - T SignedNegative(std::uint8_t header, std::span buffer, std::size_t ¤t) + T SignedNegative(std::uint8_t header, std::span buffer, + std::size_t ¤t) { static constexpr auto actualMin = std::numeric_limits::min(); static constexpr std::uint64_t minValue = -std::int64_t(actualMin + 1); @@ -319,7 +379,7 @@ namespace CBOR } template - T ExtractSigned(std::span buffer, std::size_t ¤t) + T ExtractSigned(std::span buffer, std::size_t ¤t) { std::uint8_t header = Consume1B(buffer, current); MajorType major = GetMajorType(header); @@ -335,15 +395,17 @@ namespace CBOR } } - std::span ExtractBinary(std::size_t size, std::span buffer, std::size_t ¤t) + 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); + 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) + 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); @@ -356,6 +418,21 @@ namespace CBOR : mDecoder(&decoder) {} + MajorType Item::GetMajor() const + { + return mDecoder->GetMajor(); + } + + MinorType Item::GetMinor() const + { + return mDecoder->GetMinor(); + } + + std::uint64_t Item::GetArgument() const + { + return mDecoder->GetArgument(); + } + bool Item::Bool() { return mDecoder->Bool(); @@ -416,7 +493,7 @@ namespace CBOR return mDecoder->Double(); } - std::span Item::Binary() + std::span Item::Binary() { return mDecoder->Binary(); } @@ -531,7 +608,7 @@ namespace CBOR return mDone; } - std::span Binary::Next() + std::span Binary::Next() { if (!mHeaderParsed) { std::uint8_t header = Consume1B(mDecoder->mBuffer, mDecoder->mCurrent); @@ -767,10 +844,37 @@ namespace CBOR return KeyValue(*mDecoder); } - Decoder::Decoder(std::span buffer) + Decoder::Decoder(std::span buffer) : mCurrent(0), mBuffer(buffer) {} + MajorType Decoder::GetMajor() const + { + std::uint8_t header = Read1B(mBuffer, mCurrent); + return GetMajorType(header); + } + + MinorType Decoder::GetMinor() const + { + std::uint8_t header = Read1B(mBuffer, mCurrent); + if (GetMajorType(header) != MajorType::Other) { + throw InvalidUsageError("use the GetArgument function instead"); + } + return GetMinorType(header); + } + + std::uint64_t Decoder::GetArgument() const + { + std::uint8_t header = Read1B(mBuffer, mCurrent); + if (GetMajorType(header) != MajorType::Other) { + throw InvalidUsageError("use the GetMinor function instead"); + } + if (GetArgumentPosition(header) == ArgumentPosition::Indefinite) { + return Decoder::ArgumentIndefinite; + } + return ReadArgumentValue(header, mBuffer, mCurrent); + } + bool Decoder::Bool() { std::uint8_t header = Consume1B(mBuffer, mCurrent); @@ -881,7 +985,7 @@ namespace CBOR throw TypeMismatchError("double", ToString(major)); } - std::span Decoder::Binary() + std::span Decoder::Binary() { std::uint8_t header = Consume1B(mBuffer, mCurrent); MajorType major = GetMajorType(header); @@ -937,4 +1041,9 @@ namespace CBOR { return { *this }; } + + Item Decoder::AsItem() + { + return { *this }; + } } diff --git a/LibCBOR/Source/Printer.cpp b/LibCBOR/Source/Printer.cpp new file mode 100644 index 0000000..89716c3 --- /dev/null +++ b/LibCBOR/Source/Printer.cpp @@ -0,0 +1,146 @@ +#include "Printer.hpp" + +#include "Core.hpp" +#include "Decoder.hpp" + +#include +#include + +namespace CBOR +{ + namespace + { + void Print(std::ostream &out, std::size_t depth, CBOR::Item item); + + char AsChar(std::uint8_t nibble) + { + if (nibble < 10) { + return nibble + '0'; + } + if (nibble < 16) { + return nibble + 'A'; + } + return 'X'; + } + + std::string AsString(std::uint8_t byte) + { + std::string result; + result.push_back(AsChar((byte >> 4) & 15)); + result.push_back(AsChar((byte ) & 15)); + return result; + } + + void PrintBinary(std::ostream &out, CBOR::Binary binary) + { + out << "b\""; + while (!binary.Done()) { + std::span chunk = binary.Next(); + for (std::uint8_t byte: chunk) { + out << AsString(byte); + } + } + out << '\"'; + } + + void PrintString(std::ostream &out, CBOR::String string) + { + out << '\"'; + while (!string.Done()) { + out << string.Next(); + } + out << '\"'; + } + + void PrintArray(std::ostream &out, std::size_t depth, CBOR::Array array) + { + out << "[\n"; + while (!array.Done()) { + out << std::string((depth + 1) * 4, ' '); + Print(out, depth + 1, array.Next()); + out << ",\n"; + } + out << std::string(depth * 4, ' ') << "]"; + } + + void PrintMap(std::ostream &out, std::size_t depth, CBOR::Map map) + { + out << "{\n"; + while (!map.Done()) { + CBOR::KeyValue kv = map.Next(); + out << std::string((depth + 1) * 4, ' '); + Print(out, depth + 1, kv.Key()); + out << ": "; + Print(out, depth + 1, kv.Value()); + out << ",\n"; + } + out << std::string(depth * 4, ' ') << "}"; + } + + void PrintTagged(std::ostream &out, std::size_t depth, CBOR::TaggedItem item) + { + out << item.Tag(); + out << '('; + Print(out, depth, item.Item()); + out << ')'; + } + + void Print(std::ostream &out, std::size_t depth, CBOR::Item item) + { + switch (item.GetMajor()) { + case MajorType::Unsigned: + out << item.Uint64(); + break; + case MajorType::Negative: + out << item.Int64(); + break; + case MajorType::Binary: + PrintBinary(out, item.IndefiniteBinary()); + break; + case MajorType::String: + PrintString(out, item.IndefiniteString()); + break; + case MajorType::Array: + PrintArray(out, depth, item.Array()); + break; + case MajorType::Map: + PrintMap(out, depth, item.Map()); + break; + case MajorType::Tag: + PrintTagged(out, depth, item.TaggedItem()); + break; + case MajorType::Other: + switch (item.GetMinor()) { + case MinorType::False: + out << "false"; + break; + case MinorType::True: + out << "true"; + break; + case MinorType::Null: + out << "null"; + break; + case MinorType::Undefined: + out << "undefined"; + break; + case MinorType::Float: + std::print(out, "{:.6f}", item.Float()); + break; + case MinorType::Double: + std::print(out, "{:.15f}", item.Double()); + break; + default: + out << "invalid_value"; + break; + } + } + } + } + + void Print(std::ostream &out, std::span buffer) + { + CBOR::Decoder dec(buffer); + Print(out, 0, dec.AsItem()); + out << '\n'; + } +} diff --git a/Tests/Main.cpp b/Tests/Main.cpp index 5b51912..e0424ef 100644 --- a/Tests/Main.cpp +++ b/Tests/Main.cpp @@ -1,7 +1,9 @@ #include "CBOR/Decoder.hpp" #include "CBOR/Encoder.hpp" +#include "Cbor/Printer.hpp" #include #include +#include #include #include #include @@ -206,6 +208,9 @@ int main() 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.");