diff --git a/lib/aries/Tags.cpp b/lib/aries/Tags.cpp index b619da9..ea074fa 100644 --- a/lib/aries/Tags.cpp +++ b/lib/aries/Tags.cpp @@ -1,8 +1,9 @@ #include +#include namespace ls::aries { - bool ParseTagFieldsToMap(const std::string_view tagFieldData, TagMap& outMap) { + bool ParseTagFieldsToMap(const std::string_view tagFieldData, TagMap& outMap) { // Nothing to parse, // which isn't exclusively a failure condition. if(tagFieldData.empty()) @@ -83,35 +84,62 @@ namespace ls::aries { } void SerializeTagFields(const TagMap& map, std::string& outStr) { - std::string tagFieldBuffer{}; + std::string tagFieldBuffer {}; // Reserve a sane amount, to avoid allocations when serializing // (in most cases; larger tag count MIGHT still cause some allocation pressure). tagFieldBuffer.reserve(512); - // Serialize the tag fields + // Serialize the tag fields for(auto [key, value] : map) tagFieldBuffer += std::format("{}={}\n", key, value); - // Null terminate it. (TODO: We shouldn't have to do this anymore, std::string does this on its own) - //tagFieldBuffer.push_back('\0'); - outStr = std::move(tagFieldBuffer); } - std::vector DecodeBinaryTagData(std::string_view tagData) { + namespace impl { + static constexpr std::byte HexCharToByte(char nibble1, char nibble2) { + // mmm so sexy + constexpr auto convertOneNibble = [](char nibbleChar) -> char { + if(nibbleChar >= '0' && nibbleChar <= '9') { + return nibbleChar - '0'; + } else if(nibbleChar >= 'A' && nibbleChar <= 'F') { + return nibbleChar - 'A' + 10; + } else if(nibbleChar >= 'a' && nibbleChar <= 'f') { + return nibbleChar - 'a' + 10; + } else { + return 0; + } + }; + + return static_cast((convertOneNibble(nibble1) << 4) | (convertOneNibble(nibble2))); + } + } // namespace impl + + std::vector DecodeBinaryTagData(std::string_view tagData) { // This could be more ergonomic as an optional or something. + std::vector res; if(tagData.empty()) - return {}; + return res; - // Marker for binary data + // Aries tagfield's marker for binary data is the $ character. + // If this isn't at the start of a binary payload, it's probably invalid. if(tagData[0] != '$') - return {}; + return res; - // TODO: Implement me fully! + // remove the '$' marker from consideration + const auto hexPointLength = tagData.length() - 1; + const auto byteSize = hexPointLength / 2; - return {}; + res.resize(byteSize); + + const auto dataView = std::string_view(tagData.data() + 1, hexPointLength); + for(auto i = 0; i < byteSize; ++i) { + res[i] = impl::HexCharToByte(dataView[i * 2], dataView[i * 2 + 1]); + } + + return res; } } // namespace ls::aries \ No newline at end of file diff --git a/lib/aries/Tags.hpp b/lib/aries/Tags.hpp index 9782783..0a89f3f 100644 --- a/lib/aries/Tags.hpp +++ b/lib/aries/Tags.hpp @@ -4,21 +4,21 @@ namespace ls::aries { - using TagMap = std::unordered_map; + /// A map of tag fields and their values. + using TagMap = std::unordered_map; - /// Parses tag field data to a TagMap. - /// # Returns - /// True on success; false otherwise (TODO: Move to exceptions or error_category) - bool ParseTagFieldsToMap(const std::string_view tagFieldData, TagMap& outMap); + /// Parses tag field data to a TagMap. + /// # Returns + /// True on success; false otherwise (TODO: Move to exceptions or error_category) + bool ParseTagFieldsToMap(const std::string_view tagFieldData, TagMap& outMap); - /// Serializes a TagMap to a string. - void SerializeTagFields(const TagMap& map, std::string& outStr); + /// Serializes a TagMap to a string. + void SerializeTagFields(const TagMap& map, std::string& outStr); - /// Decodes a binary tag to binary data. - std::vector DecodeBinaryTagData(std::string_view tagData); + /// Decodes a binary tag to binary data. + std::vector DecodeBinaryTagData(std::string_view tagData); - - // TODO: Maybe also a in-Aries implementation of "CryptoSSC2"/other dirtysock crypto primitives so that we can rehash - // passwords to an actually sane password hash (e.g: argon2di). + // TODO: Maybe also a in-Aries implementation of "CryptoSSC2"/other dirtysock crypto primitives so that we can rehash + // passwords to an actually sane password hash (e.g: argon2di). -} \ No newline at end of file +} // namespace ls::aries \ No newline at end of file diff --git a/lib/aries/Tags.test.cpp b/lib/aries/Tags.test.cpp index e765b23..623099e 100644 --- a/lib/aries/Tags.test.cpp +++ b/lib/aries/Tags.test.cpp @@ -28,7 +28,13 @@ TEST_CASE("Aries tag field serde functions as expected", "[Aries] [TagFields]") TEST_CASE("DecodeBinaryTagData() works", "[Aries] [TagFields] [DecodeBinarytagData]") { // should decode to [ 0x12, 0x34, 0x56, 0x78 ] std::string testCorpus = "$12345678"; - const static std::vector expectedOutput = { 0x12, 0x34, 0x56, 0x78 }; + const static std::vector expectedOutput = { static_cast(0x12), static_cast(0x34), static_cast(0x56), static_cast(0x78) }; - REQUIRE(aries::DecodeBinaryTagData(testCorpus) == expectedOutput); + std::string testCorpus2 = "$feffffeeffffffff"; + const static std::vector expectedOutput2 = { static_cast(0xfe), static_cast(0xff), static_cast(0xff), static_cast(0xee), static_cast(0xff), static_cast(0xff), static_cast(0xff), static_cast(0xff) }; + + REQUIRE(aries::DecodeBinaryTagData(testCorpus) == expectedOutput); + REQUIRE(aries::DecodeBinaryTagData(testCorpus2) == expectedOutput2); + + REQUIRE(aries::DecodeBinaryTagData("invalid").empty()); } \ No newline at end of file