diff --git a/benchmark/name.cpp b/benchmark/name.cpp index d9d0907..1cced2b 100644 --- a/benchmark/name.cpp +++ b/benchmark/name.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include static void Name_ConstructFrom_String(benchmark::State& state) diff --git a/benchmark/namespace.cpp b/benchmark/namespace.cpp index 7246ed5..ac378fb 100644 --- a/benchmark/namespace.cpp +++ b/benchmark/namespace.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include static void Namespace_ConstructFrom_Name(benchmark::State& state) diff --git a/include/quicr_name b/include/qname similarity index 100% rename from include/quicr_name rename to include/qname diff --git a/include/quicr/_utilities.h b/include/quicr/_utilities.h new file mode 100644 index 0000000..92bb88a --- /dev/null +++ b/include/quicr/_utilities.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include + +namespace quicr::utility +{ + +/** + * @brief Converts a hexadecimal character to it's decimal value. + * + * @tparam UInt_t The unsigned integer type to convert to. + * @param hex The hexadecimal character to convert. + * @returns The decimal value of the provided character. + */ +template +static constexpr UInt_t hexchar_to_unsigned(char hex) noexcept +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + else if ('A' <= hex && hex <= 'F') + return hex - 'A' + 10; + else if ('a' <= hex && hex <= 'f') + return hex - 'a' + 10; + + return 0; +} + +/** + * @brief Converts an unsigned integer decimal value into a hexidecimal + * character. + * + * @tparam UInt_t The unsigned integer type to convert from. + * @param value The decimal value to convert. + * @returns The hexadecimal character of the provided decimal value. + */ +template +static constexpr char unsigned_to_hexchar(UInt_t value) noexcept +{ + if (value > 9) return value + 'A' - 10; + return value + '0'; +} + +/** + * @brief Converts a hexidecimal string to an unsigned integer decimal value. + * + * @tparam UInt_t The unsigned integer type to convert to. + * @param hex The hexadecimal string to convert from. + * @returns The decimal value of the provided hexadecimal string. + */ +template +static constexpr UInt_t hex_to_unsigned(std::string_view hex) noexcept +{ + if (hex.starts_with("0x")) hex.remove_prefix(2); + + UInt_t value = 0; + for (std::size_t i = 0; i < hex.length(); ++i) + { + value *= 16ull; + value += hexchar_to_unsigned(hex[i]); + } + + return value; +} + +/** + * @brief Converts an unsigned integer to a hexadecimal string. + * + * @tparam UInt_t The unsigned integer type to convert from. + * @param value The decimal value to convert from. + * @returns The hexadecimal string of the provided decimal value. + */ +template +std::string unsigned_to_hex(UInt_t value) noexcept +{ + char hex[sizeof(UInt_t) * 2 + 1] = ""; + for (int i = sizeof(UInt_t) * 2 - 1; i >= 0; --i) + { + UInt_t b = value & 0x0F; + hex[i] = unsigned_to_hexchar(b); + value -= b; + value /= 16; + } + hex[sizeof(UInt_t) * 2] = '\0'; + + return hex; +} + +} diff --git a/include/quicr/hex_endec.h b/include/quicr/hex_endec.h index d8f394f..3237a02 100644 --- a/include/quicr/hex_endec.h +++ b/include/quicr/hex_endec.h @@ -1,9 +1,11 @@ #pragma once -#include +#include "_utilities.h" +#include "name.h" #include #include +#include #include #include #include @@ -12,7 +14,6 @@ namespace quicr { - /** * @brief Encodes/Decodes a hex string from/into a list of unsigned integers * values. @@ -51,7 +52,7 @@ class HexEndec * @returns Hex string containing the provided values distributed according * to Dist in order. */ - template + template static inline std::string Encode(UInt_ts... values) { static_assert(Size == (Dist + ...), "Total bits cannot exceed specified size"); @@ -62,10 +63,10 @@ class HexEndec return Encode(distribution, std::span(vals)); } - template + template static inline std::string Encode(std::span distribution, UInt_ts... values) { - if(Size < std::accumulate(distribution.begin(), distribution.end(), 0)) + if (Size < std::accumulate(distribution.begin(), distribution.end(), 0)) throw std::invalid_argument("Total bits cannot exceed specified size"); std::array vals{ values... }; @@ -88,7 +89,7 @@ class HexEndec if constexpr (Size == sizeof(quicr::Name) * 8) return bits; - return "0x" + uint_to_hex(bits.bits(0, Size)); + return "0x" + utility::unsigned_to_hex(bits.bits(0, Size)); } /** @@ -102,13 +103,13 @@ class HexEndec * @returns Structured binding of values decoded from hex string * corresponding in order to the size of Dist. */ - template + template static constexpr std::array Decode(std::string_view hex) { return Decode(quicr::Name(hex)); } - template + template static constexpr std::array Decode(quicr::Name name) { static_assert(Size >= (Dist + ...), "Total bits cannot exceed specified size"); @@ -117,7 +118,7 @@ class HexEndec return Decode(distribution, name); } - template + template static constexpr std::array Decode(std::span distribution, quicr::Name name) { const auto dist_size = distribution.size(); @@ -133,13 +134,13 @@ class HexEndec return result; } - template + template static inline std::vector Decode(std::span distribution, std::string_view hex) { return Decode(distribution, quicr::Name(hex)); } - template + template static inline std::vector Decode(std::span distribution, quicr::Name name) { const auto dist_size = distribution.size(); diff --git a/include/quicr/name.h b/include/quicr/name.h index a159e3e..cac19b0 100644 --- a/include/quicr/name.h +++ b/include/quicr/name.h @@ -1,5 +1,8 @@ #pragma once +#include "_utilities.h" + +#include #include #include #include @@ -12,113 +15,9 @@ namespace quicr { -// clang-format off - -/** - * @brief A new definition for checking is_integral in the QUICR API. - * @tparam T The type to check. - */ -template -struct is_integral : std::is_integral {}; - -/** - * @brief Unique to QUICR, defines Name to be an integral type to the QUICR API. - */ -template<> -struct is_integral : std::true_type {}; - -template -constexpr bool is_integral_v = is_integral::value; template -concept Unsigned = std::is_unsigned_v; - -template -concept Integral = quicr::is_integral_v; -// clang-format on - -namespace -{ -/** - * @brief Converts a hexadecimal character to it's decimal value. - * - * @tparam UInt_t The unsigned integer type to convert to. - * @param hex The hexadecimal character to convert. - * @returns The decimal value of the provided character. - */ -template -constexpr UInt_t hexchar_to_uint(char hex) noexcept -{ - if ('0' <= hex && hex <= '9') - return hex - '0'; - else if ('A' <= hex && hex <= 'F') - return hex - 'A' + 10; - else if ('a' <= hex && hex <= 'f') - return hex - 'a' + 10; - - return 0; -} - -/** - * @brief Converts an unsigned integer decimal value into a hexidecimal - * character. - * - * @tparam UInt_t The unsigned integer type to convert from. - * @param value The decimal value to convert. - * @returns The hexadecimal character of the provided decimal value. - */ -template -constexpr char uint_to_hexchar(UInt_t value) noexcept -{ - if (value > 9) return value + 'A' - 10; - return value + '0'; -} - -/** - * @brief Converts a hexidecimal string to an unsigned integer decimal value. - * - * @tparam UInt_t The unsigned integer type to convert to. - * @param hex The hexadecimal string to convert from. - * @returns The decimal value of the provided hexadecimal string. - */ -template -constexpr UInt_t hex_to_uint(std::string_view hex) noexcept -{ - if (hex.starts_with("0x")) hex.remove_prefix(2); - - UInt_t value = 0; - for (std::size_t i = 0; i < hex.length(); ++i) - { - value *= 16ull; - value += hexchar_to_uint(hex[i]); - } - - return value; -} - -/** - * @brief Converts an unsigned integer to a hexadecimal string. - * - * @tparam UInt_t The unsigned integer type to convert from. - * @param value The decimal value to convert from. - * @returns The hexadecimal string of the provided decimal value. - */ -template -std::string uint_to_hex(UInt_t value) noexcept -{ - char hex[sizeof(UInt_t) * 2 + 1] = ""; - for (int i = sizeof(UInt_t) * 2 - 1; i >= 0; --i) - { - UInt_t b = value & 0x0F; - hex[i] = uint_to_hexchar(b); - value -= b; - value /= 16; - } - hex[sizeof(UInt_t) * 2] = '\0'; - - return hex; -} -} // namespace +concept UnsignedOrName = std::unsigned_integral || std::is_same_v; /** * @brief Unsigned 128 bit number which can be created from strings or byte arrays. @@ -146,7 +45,7 @@ class Name constexpr Name(const Name& other) noexcept = default; constexpr Name(Name&& other) noexcept = default; - constexpr Name(std::string_view hex_value, bool align_left = false) noexcept(false) + constexpr Name(std::string_view hex_value) noexcept(false) { if (hex_value.starts_with("0x")) hex_value.remove_prefix(2); @@ -156,16 +55,14 @@ class Name if (hex_value.length() > sizeof(Name)) { - _hi = hex_to_uint(hex_value.substr(0, hex_value.length() - sizeof(Name))); - _lo = hex_to_uint(hex_value.substr(hex_value.length() - sizeof(Name), sizeof(Name))); + _hi = utility::hex_to_unsigned(hex_value.substr(0, hex_value.length() - sizeof(Name))); + _lo = utility::hex_to_unsigned(hex_value.substr(hex_value.length() - sizeof(Name), sizeof(Name))); } else { _hi = 0; - _lo = hex_to_uint(hex_value.substr(0, hex_value.length())); + _lo = utility::hex_to_unsigned(hex_value.substr(0, hex_value.length())); } - - if (align_left) *this <<= (sizeof(Name) - (hex_value.length() / 2)) * 8; } /** @@ -173,43 +70,34 @@ class Name * * @param data The byte array pointer to read from. * @param length The length of the byte array pointer. Must NOT be greater. - * @param align_left Aligns the bytes to the left (higher bytes). * * Note: The ordering of the byte array MUST conform to the endianness of the machine. */ - Name(const std::uint8_t* data, std::size_t length, bool align_left = false) noexcept(false) - : _lo{ 0 }, _hi{ 0 } + Name(const std::uint8_t* data, std::size_t size) noexcept(false) : _lo{ 0 }, _hi{ 0 } { if (!data) throw std::invalid_argument("Byte array data must not be null"); - if (length > sizeof(Name)) + if (size > sizeof(Name)) { - throw std::invalid_argument("Byte array length (" + std::to_string(length) + - ") cannot be longer than length of Name (" + std::to_string(sizeof(Name)) + - ")"); + throw std::invalid_argument("Byte array size (" + std::to_string(size) + ") cannot exceed size of Name (" + + std::to_string(sizeof(Name)) + ")"); } - if (length > sizeof(uint_t)) + if (size > sizeof(uint_t)) { - std::memcpy(&_hi, data + sizeof(uint_t), length - sizeof(uint_t)); - std::memcpy(&_lo, data , sizeof(uint_t)); + std::memcpy(&_hi, data + sizeof(uint_t), size - sizeof(uint_t)); + std::memcpy(&_lo, data, sizeof(uint_t)); } else - std::memcpy(&_lo, data, length); - - if (align_left) *this <<= (sizeof(Name) - length) * 8; + std::memcpy(&_lo, data, size); } /** * @brief Constructs a Name from a byte range. * * @param data The byte range to read from. - * @param align_left Aligns the bytes to the left (higher bytes). */ - Name(std::span data, bool align_left = false) noexcept(false) - : Name(data.data(), data.size(), align_left) - { - } + Name(std::span data) noexcept(false) : Name(data.data(), data.size()) {} /*=======================================================================*/ // Assignment Operators @@ -228,16 +116,12 @@ class Name */ operator std::string() const noexcept { - std::string hex = "0x"; - hex += uint_to_hex(_hi); - hex += uint_to_hex(_lo); - - return hex; + return "0x" + utility::unsigned_to_hex(_hi) + utility::unsigned_to_hex(_lo); } - explicit constexpr operator std::uint8_t() const noexcept { return std::uint8_t(_lo); } - explicit constexpr operator std::uint16_t() const noexcept { return std::uint16_t(_lo); } - explicit constexpr operator std::uint32_t() const noexcept { return std::uint32_t(_lo); } + explicit constexpr operator std::uint8_t() const noexcept { return static_cast(_lo); } + explicit constexpr operator std::uint16_t() const noexcept { return static_cast(_lo); } + explicit constexpr operator std::uint32_t() const noexcept { return static_cast(_lo); } explicit constexpr operator std::uint64_t() const noexcept { return _lo; } /*=======================================================================*/ @@ -368,9 +252,6 @@ class Name friend constexpr bool operator>=(const Name& a, const Name& b) noexcept { return !(a < b); } friend constexpr bool operator<=(const Name& a, const Name& b) noexcept { return !(a > b); } - friend constexpr bool operator==(const Name& a, std::string_view b) noexcept { return a == Name(b); } - friend constexpr bool operator!=(const Name& a, std::string_view b) noexcept { return a != Name(b); } - /*=======================================================================*/ // Access Operators /*=======================================================================*/ @@ -396,7 +277,7 @@ class Name * @param length The number of bits to access. If length == 0, returns 1 bit. * @returns The requested bits. */ - template + template constexpr T bits(std::uint16_t from, std::uint16_t length = 1) const { if (length == 0) return 0; @@ -407,16 +288,6 @@ class Name return T((*this & (((Name(uint_t(0), uint_t(1)) << length) - 1) << from)) >> from); } - [[deprecated("quicr::Name::to_hex is deprecated, use std::string or std::ostream operators")]] - std::string to_hex() const noexcept - { - std::string hex = "0x"; - hex += uint_to_hex(_hi); - hex += uint_to_hex(_lo); - - return hex; - } - /*=======================================================================*/ // Stream Operators /*=======================================================================*/ diff --git a/include/quicr/namespace.h b/include/quicr/namespace.h index c195db9..c964150 100644 --- a/include/quicr/namespace.h +++ b/include/quicr/namespace.h @@ -15,6 +15,34 @@ namespace quicr */ class Namespace { + /** + * @brief Static helper method for converting charater to decimal value at compile-time. + * @param dec The decimal character to convert. + * @returns The decimal value of the digit character. + */ + static constexpr uint8_t char_to_uint(char dec) + { + if ('0' <= dec && dec <= '9') return dec - '0'; + return 0; + } + + /** + * @brief Static helper function to convert a string to a decimal value at compile-time. + * @param dec The decimal string to convert. + * @returns The decimal value of the given string. + */ + static constexpr uint8_t str_to_uint(std::string_view dec) + { + uint8_t value = 0; + for (std::size_t i = 0; i < dec.length(); ++i) + { + value *= 10u; + value += char_to_uint(dec[i]); + } + + return value; + } + public: Namespace() noexcept = default; constexpr Namespace(const Namespace& ns) noexcept = default; @@ -79,12 +107,6 @@ class Namespace */ constexpr uint8_t length() const noexcept { return _sig_bits; } - [[deprecated("Namespace::to_hex is deprecated, use std::string or std::ostream operators")]] - std::string to_hex() const - { - return std::string(_name) + "/" + std::to_string(_sig_bits); - } - /*=======================================================================*/ // Conversion Operators /*=======================================================================*/ @@ -151,43 +173,14 @@ class Namespace /** * Inputs a string in the form '0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/X' into Name */ - friend std::istream& operator>>(std::istream& is, Namespace& name) + friend std::istream& operator>>(std::istream& is, Namespace& ns) { std::string hex; is >> hex; - name = hex; + ns = hex; return is; } - private: - /** - * @brief Static helper method for converting charater to decimal value at compile-time. - * @param dec The decimal character to convert. - * @returns The decimal value of the digit character. - */ - static constexpr uint8_t char_to_uint(char dec) - { - if ('0' <= dec && dec <= '9') return dec - '0'; - return 0; - } - - /** - * @brief Static helper function to convert a string to a decimal value at compile-time. - * @param dec The decimal string to convert. - * @returns The decimal value of the given string. - */ - static constexpr uint8_t str_to_uint(std::string_view dec) - { - uint8_t value = 0; - for (std::size_t i = 0; i < dec.length(); ++i) - { - value *= 10u; - value += char_to_uint(dec[i]); - } - - return value; - } - private: Name _name; uint8_t _sig_bits; diff --git a/test/hex_endec.cpp b/test/hex_endec.cpp index bad4ef4..02a5662 100644 --- a/test/hex_endec.cpp +++ b/test/hex_endec.cpp @@ -1,6 +1,6 @@ #include -#include +#include TEST_CASE("quicr::HexEndec 128bit Encode/Decode Test") { @@ -84,7 +84,7 @@ TEST_CASE("quicr::HexEndec Name Encode/Decode Test") quicr::HexEndec<128, 64, 56, 8> formatter_128bit; const std::string mask = formatter_128bit.Encode(first_part, second_part, third_part); - CHECK_EQ(mask, name); + CHECK_EQ(mask, std::string(name)); const auto [one, two, three] = formatter_128bit.Decode(name); CHECK_EQ(one, first_part); diff --git a/test/name.cpp b/test/name.cpp index ad93152..7f017dc 100644 --- a/test/name.cpp +++ b/test/name.cpp @@ -15,9 +15,7 @@ TEST_CASE("quicr::Name Type Tests") CHECK(std::is_trivially_copy_assignable_v); CHECK(std::is_trivially_move_constructible_v); CHECK(std::is_trivially_move_assignable_v); - - CHECK_FALSE(std::is_integral_v); - CHECK(quicr::is_integral_v); + CHECK_EQ(sizeof(quicr::Name), sizeof(std::uint64_t) * 2); } TEST_CASE("quicr::Name Constructor Tests") @@ -191,13 +189,9 @@ TEST_CASE("quicr::Name Medium Byte Array Tests") std::vector byte_arr = { 0x01, 0x00, 0x00, 0x00, 0x10 }; - quicr::Name right_aligned_name_from_bytes(byte_arr, false); - CHECK_NE(right_aligned_name_from_bytes, long_name); - CHECK_EQ(right_aligned_name_from_bytes, short_name); - - quicr::Name left_aligned_name_from_bytes(byte_arr, true); - CHECK_EQ(left_aligned_name_from_bytes, long_name); - CHECK_NE(left_aligned_name_from_bytes, short_name); + quicr::Name name_from_bytes(byte_arr); + CHECK_NE(name_from_bytes, long_name); + CHECK_EQ(name_from_bytes, short_name); } TEST_CASE("quicr::Name Short Byte Array Tests") @@ -207,37 +201,24 @@ TEST_CASE("quicr::Name Short Byte Array Tests") std::vector byte_arr = { 0x10 }; - quicr::Name right_aligned_name_from_bytes(byte_arr, false); - CHECK_NE(right_aligned_name_from_bytes, long_name); - CHECK_EQ(right_aligned_name_from_bytes, short_name); - - quicr::Name left_aligned_name_from_bytes(byte_arr, true); - CHECK_EQ(left_aligned_name_from_bytes, long_name); - CHECK_NE(left_aligned_name_from_bytes, short_name); + quicr::Name name_from_bytes(byte_arr); + CHECK_NE(name_from_bytes, long_name); + CHECK_EQ(name_from_bytes, short_name); } TEST_CASE("quicr::Name Integer Byte Array Tests") { - uint64_t i = 0x123456; - uint8_t* i_ptr = reinterpret_cast(&i); - std::vector byte_arr = { 0x56, 0x34, 0x12 }; - - { - quicr::Name right_aligned_name_from_bytes(i_ptr, 3, false); - CHECK_EQ(right_aligned_name_from_bytes, 0x00000000000000000000000000123456_name); - } { - quicr::Name right_aligned_name_from_bytes(byte_arr, false); - CHECK_EQ(right_aligned_name_from_bytes, 0x00000000000000000000000000123456_name); + uint64_t i = 0x123456; + uint8_t* i_ptr = reinterpret_cast(&i); + quicr::Name name_from_bytes(i_ptr, 3); + CHECK_EQ(name_from_bytes, 0x00000000000000000000000000123456_name); } { - quicr::Name left_aligned_name_from_bytes(i_ptr, 3, true); - CHECK_EQ(left_aligned_name_from_bytes, 0x12345600000000000000000000000000_name); - } - { - quicr::Name left_aligned_name_from_bytes(byte_arr, true); - CHECK_EQ(left_aligned_name_from_bytes, 0x12345600000000000000000000000000_name); + std::vector byte_arr = { 0x56, 0x34, 0x12 }; + quicr::Name name_from_bytes(byte_arr); + CHECK_EQ(name_from_bytes, 0x00000000000000000000000000123456_name); } } @@ -287,7 +268,7 @@ TEST_CASE("quicr::Name Conversion Tests") CHECK_EQ(std::uint32_t(name), 0xFFFFFFFF); CHECK_EQ(std::uint64_t(name), 0xFFFFFFFFFFFFFFFF); - CHECK_EQ(name, std::string("0x000000000000FFFFFFFFFFFFFFFFFFFF")); + CHECK_EQ(std::string(name), "0x000000000000FFFFFFFFFFFFFFFFFFFF"); } TEST_CASE("quicr::Name Extract Bits Tests")