From 6e1eb8fc92617dee132112b42b1d83075ea05a89 Mon Sep 17 00:00:00 2001 From: Marcin Kowalczyk Date: Fri, 27 Sep 2024 16:24:43 +0200 Subject: [PATCH] Add `HexUpperCase()`. PiperOrigin-RevId: 679581299 --- riegeli/text/write_int.cc | 347 ++++++++++++++++++++++++++++---------- riegeli/text/write_int.h | 281 ++++++++++++++++++++++-------- 2 files changed, 463 insertions(+), 165 deletions(-) diff --git a/riegeli/text/write_int.cc b/riegeli/text/write_int.cc index 75ecbeea..7d6c9eaf 100644 --- a/riegeli/text/write_int.cc +++ b/riegeli/text/write_int.cc @@ -29,7 +29,7 @@ #include "absl/numeric/bits.h" // IWYU pragma: keep #include "absl/numeric/int128.h" #include "riegeli/base/arithmetic.h" -#include "riegeli/base/assert.h" // IWYU pragma: keep +#include "riegeli/base/assert.h" #include "riegeli/endian/endian_writing.h" namespace riegeli { @@ -39,6 +39,22 @@ namespace { #ifdef __SSSE3__ +template +__m128i HexDigits(); + +template <> +inline __m128i HexDigits() { + return _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', + 'b', 'c', 'd', 'e', 'f'); +} + +template <> +inline __m128i HexDigits() { + return _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', + 'B', 'C', 'D', 'E', 'F'); +} + +template inline __m128i WriteHex2Impl(uint8_t src) { // Load 8-bit value to 128-bit register. const __m128i value = _mm_cvtsi32_si128(src); @@ -49,11 +65,10 @@ inline __m128i WriteHex2Impl(uint8_t src) { // Mask out high nibbles of bytes. const __m128i masked = _mm_and_si128(interleaved, _mm_set1_epi8(0xf)); // Convert to characters. - return _mm_shuffle_epi8(_mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'), - masked); + return _mm_shuffle_epi8(HexDigits(), masked); } +template inline __m128i WriteHex4Impl(uint16_t src) { // Convert to Big Endian. char encoded[2]; @@ -67,11 +82,10 @@ inline __m128i WriteHex4Impl(uint16_t src) { // Mask out high nibbles of bytes. const __m128i masked = _mm_and_si128(interleaved, _mm_set1_epi8(0xf)); // Convert to characters. - return _mm_shuffle_epi8(_mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'), - masked); + return _mm_shuffle_epi8(HexDigits(), masked); } +template inline __m128i WriteHex8Impl(uint32_t src) { // Convert to Big Endian. char encoded[4]; @@ -85,11 +99,10 @@ inline __m128i WriteHex8Impl(uint32_t src) { // Mask out high nibbles of bytes. const __m128i masked = _mm_and_si128(interleaved, _mm_set1_epi8(0xf)); // Convert to characters. - return _mm_shuffle_epi8(_mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'), - masked); + return _mm_shuffle_epi8(HexDigits(), masked); } +template inline __m128i WriteHex16Impl(uint64_t src) { // Convert to Big Endian. char encoded[8]; @@ -103,34 +116,47 @@ inline __m128i WriteHex16Impl(uint64_t src) { // Mask out high nibbles of bytes. const __m128i masked = _mm_and_si128(interleaved, _mm_set1_epi8(0xf)); // Convert to characters. - return _mm_shuffle_epi8(_mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'), - masked); + return _mm_shuffle_epi8(HexDigits(), masked); } #endif +template +inline T DigitCaseDependent(T for_lower, T for_upper) { + switch (digit_case) { + case DigitCase::kLower: + return for_lower; + case DigitCase::kUpper: + return for_upper; + } +} + // `WriteHex{1,2,4,8,16,32}Impl()` write a fixed number of digits. +template inline char* WriteHex1Impl(uint8_t src, char* dest) { RIEGELI_ASSERT_LT(src, 0x10) << "Failed precondition of WriteHex1Impl(): value too large"; - *dest = static_cast(src) + (src < 10 ? '0' : 'a' - 10); + *dest = static_cast(src) + + (src < 10 ? '0' : DigitCaseDependent('a', 'A') - 10); return dest + 1; } +template inline char* WriteHex2Impl(uint8_t src, char* dest) { #ifdef __SSSE3__ - _mm_storeu_si16(dest, WriteHex2Impl(src)); + _mm_storeu_si16(dest, WriteHex2Impl(src)); #else uint16_t out = src; // Spread out nibbles to bytes (00AB -> 0AXB -> 0A0B). out = (out | (out << 4)) & 0x0f0f; // Convert each byte [0..9] to [6..15], and [10..15] to [16..21]. out += 0x0606; - // Keep bytes [6..15] unchanged. Convert each byte [16..21] to [55..60]. - out += 39 * ((out & 0x1010) >> 4); - // Convert each byte [6..15] to ['0'..'9'], and [55..60] to ['a'..'f']. + // Keep bytes [6..15] unchanged. Convert each byte [16..21] to [55..60] + // for `DigitCase::kLower`, or [23..28] for `DigitCase::kUpper`. + out += DigitCaseDependent(39, 7) * ((out & 0x1010) >> 4); + // Convert each byte [6..15] to ['0'..'9'], and [55..60] to ['a'..'f'] for + // `DigitCase::kLower`, or [23..28] to ['A'..'F'] for `DigitCase::kUpper`. out += 0x2a2a; // Write the result, swapping the bytes. riegeli::WriteBigEndian16(out, dest); @@ -138,9 +164,10 @@ inline char* WriteHex2Impl(uint8_t src, char* dest) { return dest + 2; } +template inline char* WriteHex4Impl(uint16_t src, char* dest) { #ifdef __SSSE3__ - _mm_storeu_si32(dest, WriteHex4Impl(src)); + _mm_storeu_si32(dest, WriteHex4Impl(src)); #else uint32_t out = src; // Spread out nibbles to bytes, swapping the middle ones @@ -148,9 +175,11 @@ inline char* WriteHex4Impl(uint16_t src, char* dest) { out = (out | (out << 12)) & 0x0f0f0f0f; // Convert each byte [0..9] to [6..15], and [10..15] to [16..21]. out += 0x06060606; - // Keep bytes [6..15] unchanged. Convert each byte [16..21] to [55..60]. - out += 39 * ((out & 0x10101010) >> 4); - // Convert each byte [6..15] to ['0'..'9'], and [55..60] to ['a'..'f']. + // Keep bytes [6..15] unchanged. Convert each byte [16..21] to [55..60] + // for `DigitCase::kLower`, or [23..28] for `DigitCase::kUpper`. + out += DigitCaseDependent(39, 7) * ((out & 0x10101010) >> 4); + // Convert each byte [6..15] to ['0'..'9'], and [55..60] to ['a'..'f'] for + // `DigitCase::kLower`, or [23..28] to ['A'..'F'] for `DigitCase::kUpper`. out += 0x2a2a2a2a; // Swap the first and the last byte. out = (out << 24) | (out >> 24) | (out & 0x00ffff00); @@ -160,50 +189,58 @@ inline char* WriteHex4Impl(uint16_t src, char* dest) { return dest + 4; } +template inline char* WriteHex8Impl(uint32_t src, char* dest) { #ifdef __SSSE3__ - _mm_storeu_si64(dest, WriteHex8Impl(src)); + _mm_storeu_si64(dest, WriteHex8Impl(src)); return dest + 8; #else - dest = WriteHex4Impl(IntCast(src >> 16), dest); - return WriteHex4Impl(static_cast(src), dest); + dest = WriteHex4Impl(IntCast(src >> 16), dest); + return WriteHex4Impl(static_cast(src), dest); #endif } +template inline char* WriteHex16Impl(uint64_t src, char* dest) { #ifdef __SSSE3__ - _mm_storeu_si128(reinterpret_cast<__m128i*>(dest), WriteHex16Impl(src)); + _mm_storeu_si128(reinterpret_cast<__m128i*>(dest), + WriteHex16Impl(src)); return dest + 16; #else - dest = WriteHex8Impl(IntCast(src >> 32), dest); - return WriteHex8Impl(static_cast(src), dest); + dest = WriteHex8Impl(IntCast(src >> 32), dest); + return WriteHex8Impl(static_cast(src), dest); #endif } +template inline char* WriteHex32Impl(absl::uint128 src, char* dest) { - dest = WriteHex16Impl(absl::Uint128High64(src), dest); - return WriteHex16Impl(absl::Uint128Low64(src), dest); + dest = WriteHex16Impl(absl::Uint128High64(src), dest); + return WriteHex16Impl(absl::Uint128Low64(src), dest); } // `WriteHexImpl()` writes at least `width` digits. // Inline to optimize for a constant `width`. -ABSL_ATTRIBUTE_ALWAYS_INLINE -inline char* WriteHexImpl(uint8_t src, char* dest, size_t width) { - if (src < uint8_t{1} << 4 && width <= 1) return WriteHex1Impl(src, dest); +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline char* WriteHexImpl(uint8_t src, char* dest, + size_t width) { + if (src < uint8_t{1} << 4 && width <= 1) { + return WriteHex1Impl(src, dest); + } if (width > 2) { // Redundant condition suppresses gcc warning `-Wstringop-overflow`. std::memset(dest, '0', width > 2 ? width - 2 : 0); dest += width - 2; } - return WriteHex2Impl(src, dest); + return WriteHex2Impl(src, dest); } // Inline to optimize for a constant `width`. -ABSL_ATTRIBUTE_ALWAYS_INLINE -inline char* WriteHexImpl(uint16_t src, char* dest, size_t width) { +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline char* WriteHexImpl(uint16_t src, char* dest, + size_t width) { #ifdef __SSSE3__ - const __m128i out = WriteHex4Impl(src); + const __m128i out = WriteHex4Impl(src); if (src >= uint32_t{1} << 12 || width >= 4) { if (width > 4) { // Redundant condition suppresses gcc warning `-Wstringop-overflow`. @@ -222,19 +259,20 @@ inline char* WriteHexImpl(uint16_t src, char* dest, size_t width) { return dest + width; #else if (src <= std::numeric_limits::max()) { - return WriteHexImpl(IntCast(src), dest, width); + return WriteHexImpl(IntCast(src), dest, width); } - dest = WriteHexImpl(IntCast(src >> 8), dest, - SaturatingSub(width, size_t{2})); - return WriteHex2Impl(static_cast(src), dest); + dest = WriteHexImpl(IntCast(src >> 8), dest, + SaturatingSub(width, size_t{2})); + return WriteHex2Impl(static_cast(src), dest); #endif } // Inline to optimize for a constant `width`. -ABSL_ATTRIBUTE_ALWAYS_INLINE -inline char* WriteHexImpl(uint32_t src, char* dest, size_t width) { +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline char* WriteHexImpl(uint32_t src, char* dest, + size_t width) { #ifdef __SSSE3__ - const __m128i out = WriteHex8Impl(src); + const __m128i out = WriteHex8Impl(src); if (src >= uint32_t{1} << 28 || width >= 8) { if (width > 8) { // Redundant condition suppresses gcc warning `-Wstringop-overflow`. @@ -252,19 +290,20 @@ inline char* WriteHexImpl(uint32_t src, char* dest, size_t width) { return dest + width; #else if (src <= std::numeric_limits::max()) { - return WriteHexImpl(IntCast(src), dest, width); + return WriteHexImpl(IntCast(src), dest, width); } - dest = WriteHexImpl(IntCast(src >> 16), dest, - SaturatingSub(width, size_t{4})); - return WriteHex4Impl(static_cast(src), dest); + dest = WriteHexImpl(IntCast(src >> 16), dest, + SaturatingSub(width, size_t{4})); + return WriteHex4Impl(static_cast(src), dest); #endif } // Inline to optimize for a constant `width`. -ABSL_ATTRIBUTE_ALWAYS_INLINE -inline char* WriteHexImpl(uint64_t src, char* dest, size_t width) { +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline char* WriteHexImpl(uint64_t src, char* dest, + size_t width) { #ifdef __SSSE3__ - const __m128i out = WriteHex16Impl(src); + const __m128i out = WriteHex16Impl(src); if (src >= uint64_t{1} << 60 || width >= 16) { if (width > 16) { // Redundant condition suppresses gcc warning `-Wstringop-overflow`. @@ -282,73 +321,195 @@ inline char* WriteHexImpl(uint64_t src, char* dest, size_t width) { return dest + width; #else if (src <= std::numeric_limits::max()) { - return WriteHexImpl(IntCast(src), dest, width); + return WriteHexImpl(IntCast(src), dest, width); } - dest = WriteHexImpl(IntCast(src >> 32), dest, - SaturatingSub(width, size_t{8})); - return WriteHex8Impl(static_cast(src), dest); + dest = WriteHexImpl(IntCast(src >> 32), dest, + SaturatingSub(width, size_t{8})); + return WriteHex8Impl(static_cast(src), dest); #endif } // Inline to optimize for a constant `width`. -ABSL_ATTRIBUTE_ALWAYS_INLINE -inline char* WriteHexImpl(absl::uint128 src, char* dest, size_t width) { +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline char* WriteHexImpl(absl::uint128 src, + char* dest, + size_t width) { if (absl::Uint128High64(src) == 0) { - return WriteHexImpl(absl::Uint128Low64(src), dest, width); + return WriteHexImpl(absl::Uint128Low64(src), dest, width); } - dest = WriteHexImpl(absl::Uint128High64(src), dest, - SaturatingSub(width, size_t{16})); - return WriteHex16Impl(absl::Uint128Low64(src), dest); + dest = WriteHexImpl(absl::Uint128High64(src), dest, + SaturatingSub(width, size_t{16})); + return WriteHex16Impl(absl::Uint128Low64(src), dest); } } // namespace -char* WriteHex2(uint8_t src, char* dest) { return WriteHex2Impl(src, dest); } -char* WriteHex4(uint16_t src, char* dest) { return WriteHex4Impl(src, dest); } -char* WriteHex8(uint32_t src, char* dest) { return WriteHex8Impl(src, dest); } -char* WriteHex16(uint64_t src, char* dest) { return WriteHex16Impl(src, dest); } -char* WriteHex32(absl::uint128 src, char* dest) { - return WriteHex32Impl(src, dest); +template <> +char* WriteHex2(uint8_t src, char* dest) { + return WriteHex2Impl(src, dest); +} +template <> +char* WriteHex4(uint16_t src, char* dest) { + return WriteHex4Impl(src, dest); +} +template <> +char* WriteHex8(uint32_t src, char* dest) { + return WriteHex8Impl(src, dest); +} +template <> +char* WriteHex16(uint64_t src, char* dest) { + return WriteHex16Impl(src, dest); +} +template <> +char* WriteHex32(absl::uint128 src, char* dest) { + return WriteHex32Impl(src, dest); +} + +template <> +char* WriteHex2(uint8_t src, char* dest) { + return WriteHex2Impl(src, dest); +} +template <> +char* WriteHex4(uint16_t src, char* dest) { + return WriteHex4Impl(src, dest); +} +template <> +char* WriteHex8(uint32_t src, char* dest) { + return WriteHex8Impl(src, dest); +} +template <> +char* WriteHex16(uint64_t src, char* dest) { + return WriteHex16Impl(src, dest); +} +template <> +char* WriteHex32(absl::uint128 src, char* dest) { + return WriteHex32Impl(src, dest); } -char* WriteHex(uint8_t src, char* dest) { return WriteHexImpl(src, dest, 0); } -char* WriteHex(uint16_t src, char* dest) { return WriteHexImpl(src, dest, 0); } -char* WriteHex(uint32_t src, char* dest) { return WriteHexImpl(src, dest, 0); } -char* WriteHex(uint64_t src, char* dest) { return WriteHexImpl(src, dest, 0); } -char* WriteHex(absl::uint128 src, char* dest) { - return WriteHexImpl(src, dest, 0); +template <> +char* WriteHex(uint8_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint16_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint32_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint64_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(absl::uint128 src, char* dest) { + return WriteHexImpl(src, dest, 0); +} + +template <> +char* WriteHex(uint8_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint16_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint32_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(uint64_t src, char* dest) { + return WriteHexImpl(src, dest, 0); +} +template <> +char* WriteHex(absl::uint128 src, char* dest) { + return WriteHexImpl(src, dest, 0); +} + +template <> +char* WriteHex(uint8_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint16_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint32_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint64_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(absl::uint128 src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} + +template <> +char* WriteHex(uint8_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint16_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint32_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(uint64_t src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); +} +template <> +char* WriteHex(absl::uint128 src, char* dest, size_t width) { + return WriteHexImpl(src, dest, width); } -char* WriteHex(uint8_t src, char* dest, size_t width) { - return WriteHexImpl(src, dest, width); +template <> +void WriteHexBackward2(uint8_t src, char* dest) { + WriteHex2Impl(src, dest - 2); } -char* WriteHex(uint16_t src, char* dest, size_t width) { - return WriteHexImpl(src, dest, width); +template <> +void WriteHexBackward4(uint16_t src, char* dest) { + WriteHex4Impl(src, dest - 4); } -char* WriteHex(uint32_t src, char* dest, size_t width) { - return WriteHexImpl(src, dest, width); +template <> +void WriteHexBackward8(uint32_t src, char* dest) { + WriteHex8Impl(src, dest - 8); } -char* WriteHex(uint64_t src, char* dest, size_t width) { - return WriteHexImpl(src, dest, width); +template <> +void WriteHexBackward16(uint64_t src, char* dest) { + WriteHex16Impl(src, dest - 16); } -char* WriteHex(absl::uint128 src, char* dest, size_t width) { - return WriteHexImpl(src, dest, width); +template <> +void WriteHexBackward32(absl::uint128 src, char* dest) { + WriteHex32Impl(src, dest - 32); } -void WriteHexBackward2(uint8_t src, char* dest) { - WriteHex2Impl(src, dest - 2); +template <> +void WriteHexBackward2(uint8_t src, char* dest) { + WriteHex2Impl(src, dest - 2); } -void WriteHexBackward4(uint16_t src, char* dest) { - WriteHex4Impl(src, dest - 4); +template <> +void WriteHexBackward4(uint16_t src, char* dest) { + WriteHex4Impl(src, dest - 4); } -void WriteHexBackward8(uint32_t src, char* dest) { - WriteHex8Impl(src, dest - 8); +template <> +void WriteHexBackward8(uint32_t src, char* dest) { + WriteHex8Impl(src, dest - 8); } -void WriteHexBackward16(uint64_t src, char* dest) { - WriteHex16Impl(src, dest - 16); +template <> +void WriteHexBackward16(uint64_t src, char* dest) { + WriteHex16Impl(src, dest - 16); } -void WriteHexBackward32(absl::uint128 src, char* dest) { - WriteHex32Impl(src, dest - 32); +template <> +void WriteHexBackward32(absl::uint128 src, char* dest) { + WriteHex32Impl(src, dest - 32); } } // namespace write_int_internal diff --git a/riegeli/text/write_int.h b/riegeli/text/write_int.h index 2780419b..3eabe2d6 100644 --- a/riegeli/text/write_int.h +++ b/riegeli/text/write_int.h @@ -125,8 +125,13 @@ inline DecType Dec(T value, size_t width = 0) { return DecType(value, width); } +enum class DigitCase { + kLower, // ['0'..'9'], ['a'..'f'] + kUpper, // ['0'..'9'], ['A'..'F'] +}; + // The type returned by `Hex()`. -template +template class HexType { public: explicit HexType(T value, size_t width) @@ -172,8 +177,8 @@ class HexType { }; // Specialization of `HexType` for `char` which is written as unsigned. -template <> -class HexType { +template +class HexType { public: explicit HexType(char value, size_t width) : value_(value), width_(width) {} @@ -182,13 +187,13 @@ class HexType { template friend void AbslStringify(Sink& sink, const HexType& self) { - AbslStringify( - sink, HexType(static_cast(self.value()), - self.width())); + AbslStringify(sink, + HexType( + static_cast(self.value()), self.width())); } friend std::ostream& operator<<(std::ostream& out, const HexType& self) { - return out << HexType( + return out << HexType( static_cast(self.value()), self.width()); } @@ -198,7 +203,7 @@ class HexType { }; // Wraps an integer such that its stringified representation is hexadecimal, -// padded with zeros to at least the given width. +// with lower case digits, padded with zeros to at least the given width. // // For negative numbers the width includes the minus sign. // @@ -208,88 +213,216 @@ inline HexType Hex(T value, size_t width = 0) { return HexType(value, width); } +// Wraps an integer such that its stringified representation is hexadecimal, +// with upper case digits, padded with zeros to at least the given width. +// +// For negative numbers the width includes the minus sign. +// +// `char` is written as unsigned. +template +inline HexType HexUpperCase(T value, size_t width = 0) { + return HexType(value, width); +} + // Implementation details follow. namespace write_int_internal { // `WriteHex{2,4,8,16,32}()` writes a fixed number of digits. +template char* WriteHex2(uint8_t src, char* dest); +template char* WriteHex4(uint16_t src, char* dest); +template char* WriteHex8(uint32_t src, char* dest); +template char* WriteHex16(uint64_t src, char* dest); +template char* WriteHex32(absl::uint128 src, char* dest); +template <> +char* WriteHex2(uint8_t src, char* dest); +template <> +char* WriteHex4(uint16_t src, char* dest); +template <> +char* WriteHex8(uint32_t src, char* dest); +template <> +char* WriteHex16(uint64_t src, char* dest); +template <> +char* WriteHex32(absl::uint128 src, char* dest); + +template <> +char* WriteHex2(uint8_t src, char* dest); +template <> +char* WriteHex4(uint16_t src, char* dest); +template <> +char* WriteHex8(uint32_t src, char* dest); +template <> +char* WriteHex16(uint64_t src, char* dest); +template <> +char* WriteHex32(absl::uint128 src, char* dest); + // `WriteHex()` with no width parameter writes no leading zeros, except for 0 // itself. +template char* WriteHex(uint8_t src, char* dest); +template char* WriteHex(uint16_t src, char* dest); +template char* WriteHex(uint32_t src, char* dest); +template char* WriteHex(uint64_t src, char* dest); +template char* WriteHex(absl::uint128 src, char* dest); +template <> +char* WriteHex(uint8_t src, char* dest); +template <> +char* WriteHex(uint16_t src, char* dest); +template <> +char* WriteHex(uint32_t src, char* dest); +template <> +char* WriteHex(uint64_t src, char* dest); +template <> +char* WriteHex(absl::uint128 src, char* dest); + +template <> +char* WriteHex(uint8_t src, char* dest); +template <> +char* WriteHex(uint16_t src, char* dest); +template <> +char* WriteHex(uint32_t src, char* dest); +template <> +char* WriteHex(uint64_t src, char* dest); +template <> +char* WriteHex(absl::uint128 src, char* dest); + // `WriteHex()` with a width parameter writes at least `width` digits. +template char* WriteHex(uint8_t src, char* dest, size_t width); +template char* WriteHex(uint16_t src, char* dest, size_t width); +template char* WriteHex(uint32_t src, char* dest, size_t width); +template char* WriteHex(uint64_t src, char* dest, size_t width); +template char* WriteHex(absl::uint128 src, char* dest, size_t width); +template <> +char* WriteHex(uint8_t src, char* dest, size_t width); +template <> +char* WriteHex(uint16_t src, char* dest, size_t width); +template <> +char* WriteHex(uint32_t src, char* dest, size_t width); +template <> +char* WriteHex(uint64_t src, char* dest, size_t width); +template <> +char* WriteHex(absl::uint128 src, char* dest, size_t width); + +template <> +char* WriteHex(uint8_t src, char* dest, size_t width); +template <> +char* WriteHex(uint16_t src, char* dest, size_t width); +template <> +char* WriteHex(uint32_t src, char* dest, size_t width); +template <> +char* WriteHex(uint64_t src, char* dest, size_t width); +template <> +char* WriteHex(absl::uint128 src, char* dest, size_t width); + // `WriteHexUnsigned()` writes at least `width` digits. -template ::value, int> = 0> +template ::value, int> = 0> inline char* WriteHexUnsigned(T src, char* dest, size_t width) { - return width == 2 ? WriteHex2(IntCast(src), dest) - : width <= 1 ? WriteHex(IntCast(src), dest) - : WriteHex(IntCast(src), dest, width); + return width == 2 ? WriteHex2(IntCast(src), dest) + : width <= 1 + ? WriteHex(IntCast(src), dest) + : WriteHex(IntCast(src), dest, width); } -template >, FitsIn>::value, int> = 0> inline char* WriteHexUnsigned(T src, char* dest, size_t width) { - return width == 4 ? WriteHex4(IntCast(src), dest) - : width <= 1 ? WriteHex(IntCast(src), dest) - : WriteHex(IntCast(src), dest, width); + return width == 4 ? WriteHex4(IntCast(src), dest) + : width <= 1 + ? WriteHex(IntCast(src), dest) + : WriteHex(IntCast(src), dest, width); } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsigned(T src, char* dest, size_t width) { - return width == 8 ? WriteHex8(IntCast(src), dest) - : width <= 1 ? WriteHex(IntCast(src), dest) - : WriteHex(IntCast(src), dest, width); + return width == 8 ? WriteHex8(IntCast(src), dest) + : width <= 1 + ? WriteHex(IntCast(src), dest) + : WriteHex(IntCast(src), dest, width); } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsigned(T src, char* dest, size_t width) { - return width == 16 ? WriteHex16(IntCast(src), dest) - : width <= 1 ? WriteHex(IntCast(src), dest) - : WriteHex(IntCast(src), dest, width); + return width == 16 ? WriteHex16(IntCast(src), dest) + : width <= 1 + ? WriteHex(IntCast(src), dest) + : WriteHex(IntCast(src), dest, width); } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsigned(T src, char* dest, size_t width) { - return width == 32 ? WriteHex32(IntCast(src), dest) - : width <= 1 ? WriteHex(IntCast(src), dest) - : WriteHex(IntCast(src), dest, width); + return width == 32 ? WriteHex32(IntCast(src), dest) + : width <= 1 + ? WriteHex(IntCast(src), dest) + : WriteHex(IntCast(src), dest, width); } // `WriteHexBackward{2,4,8,16,32}()` writes a fixed number of digits. +template void WriteHexBackward2(uint8_t src, char* dest); +template void WriteHexBackward4(uint16_t src, char* dest); +template void WriteHexBackward8(uint32_t src, char* dest); +template void WriteHexBackward16(uint64_t src, char* dest); +template void WriteHexBackward32(absl::uint128 src, char* dest); +template <> +void WriteHexBackward2(uint8_t src, char* dest); +template <> +void WriteHexBackward4(uint16_t src, char* dest); +template <> +void WriteHexBackward8(uint32_t src, char* dest); +template <> +void WriteHexBackward16(uint64_t src, char* dest); +template <> +void WriteHexBackward32(absl::uint128 src, char* dest); + +template <> +void WriteHexBackward2(uint8_t src, char* dest); +template <> +void WriteHexBackward4(uint16_t src, char* dest); +template <> +void WriteHexBackward8(uint32_t src, char* dest); +template <> +void WriteHexBackward16(uint64_t src, char* dest); +template <> +void WriteHexBackward32(absl::uint128 src, char* dest); + template constexpr size_t MaxLengthWriteHexUnsignedBackward() { return FitsIn::value ? 2 @@ -304,24 +437,25 @@ constexpr size_t MaxLengthWriteHexUnsignedBackward() { // `width` must be at most `MaxLengthWriteHexUnsignedBackward()`, and that // much space must be available before `dest`. -template ::value, int> = 0> +template ::value, int> = 0> inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { RIEGELI_ASSERT_LE(width, 2u) << "Failed precondition of WriteHexUnsignedBackward(): width too large"; - WriteHexBackward2(IntCast(src), dest); + WriteHexBackward2(IntCast(src), dest); width = UnsignedMax(width, IntCast(src) < 0x10 ? size_t{1} : size_t{2}); return dest - width; } -template >, FitsIn>::value, int> = 0> inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { RIEGELI_ASSERT_LE(width, 4u) << "Failed precondition of WriteHexUnsignedBackward(): width too large"; - WriteHexBackward4(IntCast(src), dest); + WriteHexBackward4(IntCast(src), dest); width = UnsignedMax(width, (IntCast(absl::bit_width(IntCast( IntCast(src) | 1))) + 3) / @@ -329,42 +463,45 @@ inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { return dest - width; } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { RIEGELI_ASSERT_LE(width, 8u) << "Failed precondition of WriteHexUnsignedBackward(): width too large"; - WriteHexBackward8(IntCast(src), dest); + WriteHexBackward8(IntCast(src), dest); width = UnsignedMax( width, (IntCast(absl::bit_width(IntCast(src) | 1)) + 3) / 4); return dest - width; } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { RIEGELI_ASSERT_LE(width, 16u) << "Failed precondition of WriteHexUnsignedBackward(): width too large"; - WriteHexBackward16(IntCast(src), dest); + WriteHexBackward16(IntCast(src), dest); width = UnsignedMax( width, (IntCast(absl::bit_width(IntCast(src) | 1)) + 3) / 4); return dest - width; } -template >, - FitsIn>::value, - int> = 0> +template < + DigitCase digit_case, typename T, + std::enable_if_t>, + FitsIn>::value, + int> = 0> inline char* WriteHexUnsignedBackward(T src, char* dest, size_t width) { RIEGELI_ASSERT_LE(width, 32u) << "Failed precondition of WriteHexUnsignedBackward(): width too large"; - WriteHexBackward32(IntCast(src), dest); + WriteHexBackward32(IntCast(src), dest); width = UnsignedMax( width, (IntCast(absl::Uint128High64(src) == 0 @@ -451,10 +588,10 @@ inline void DecType::WriteTo(Writer& dest) const { write_int_internal::WriteDecSigned(value_, dest.cursor(), width_)); } -template +template template ::value, int>> -inline void HexType::Stringify(Sink& sink) const { +inline void HexType::Stringify(Sink& sink) const { constexpr size_t kMaxNumDigits = write_int_internal::MaxLengthWriteHexUnsignedBackward(); size_t width = width_; @@ -463,16 +600,16 @@ inline void HexType::Stringify(Sink& sink) const { width = kMaxNumDigits; } char str[kMaxNumDigits]; - char* const begin = write_int_internal::WriteHexUnsignedBackward( + char* const begin = write_int_internal::WriteHexUnsignedBackward( value_, str + kMaxNumDigits, width); sink.Append( absl::string_view(begin, PtrDistance(begin, str + kMaxNumDigits))); } -template +template template ::value, int>> -inline void HexType::Stringify(Sink& sink) const { +inline void HexType::Stringify(Sink& sink) const { constexpr size_t kMaxNumDigits = write_int_internal::MaxLengthWriteHexUnsignedBackward>(); size_t width = width_; @@ -484,15 +621,15 @@ inline void HexType::Stringify(Sink& sink) const { sink.Append(width - kMaxNumDigits, '0'); width = kMaxNumDigits; } - begin = write_int_internal::WriteHexUnsignedBackward( + begin = write_int_internal::WriteHexUnsignedBackward( UnsignedCast(value_), str + (kMaxNumDigits + 1), width); } else if (width > kMaxNumDigits + 1) { sink.Append("-"); sink.Append(width - (kMaxNumDigits + 1), '0'); - begin = write_int_internal::WriteHexUnsignedBackward( + begin = write_int_internal::WriteHexUnsignedBackward( NegatingUnsignedCast(value_), str + (kMaxNumDigits + 1), kMaxNumDigits); } else { - begin = write_int_internal::WriteHexUnsignedBackward( + begin = write_int_internal::WriteHexUnsignedBackward( NegatingUnsignedCast(value_), str + (kMaxNumDigits + 1), SaturatingSub(width, size_t{1})); --begin; @@ -502,22 +639,22 @@ inline void HexType::Stringify(Sink& sink) const { absl::string_view(begin, PtrDistance(begin, str + (kMaxNumDigits + 1)))); } -template +template template ::value, int>> -inline void HexType::WriteTo(Writer& dest) const { +inline void HexType::WriteTo(Writer& dest) const { constexpr size_t kMaxNumDigits = (std::numeric_limits::digits + 3) / 4; if (ABSL_PREDICT_FALSE(!dest.Push(UnsignedMax(width_, kMaxNumDigits)))) { return; } - dest.set_cursor( - write_int_internal::WriteHexUnsigned(value_, dest.cursor(), width_)); + dest.set_cursor(write_int_internal::WriteHexUnsigned( + value_, dest.cursor(), width_)); } -template +template template ::value, int>> -inline void HexType::WriteTo(Writer& dest) const { +inline void HexType::WriteTo(Writer& dest) const { constexpr size_t kMaxNumDigits = (std::numeric_limits::digits + 3) / 4; // `+ 1` for the minus sign. if (ABSL_PREDICT_FALSE(!dest.Push(UnsignedMax(width_, kMaxNumDigits + 1)))) { @@ -534,8 +671,8 @@ inline void HexType::WriteTo(Writer& dest) const { abs_value = NegatingUnsignedCast(value_); width = SaturatingSub(width, size_t{1}); } - dest.set_cursor( - write_int_internal::WriteHexUnsigned(abs_value, cursor, width)); + dest.set_cursor(write_int_internal::WriteHexUnsigned( + abs_value, cursor, width)); } } // namespace riegeli