diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index ad82d328..3f13d816 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -244,6 +244,15 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str, } namespace internal { + +template <> FMT_FUNC int count_digits<4>(internal::uintptr_t n) { + // Assume little endian; pointer formatting is implementation-defined anyway. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + template int char_traits::format_float(char* buf, std::size_t size, const char* format, int precision, @@ -268,6 +277,9 @@ const char basic_data::DIGITS[] = "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; +template +const char basic_data::HEX_DIGITS[] = "0123456789abcdef"; + #define FMT_POWERS_OF_10(factor) \ factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, \ factor * 1000000, factor * 10000000, factor * 100000000, \ diff --git a/include/fmt/format.h b/include/fmt/format.h index 8f606e6a..134b7115 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -311,11 +311,11 @@ template <> class numeric_limits : public std::numeric_limits { public: - static uintptr_t to_uint(const void* p) { - return fmt::internal::bit_cast(p); - } - typedef uintptr_t uintptr_type; + + static uintptr_type to_uint(const void* p) { + return fmt::internal::bit_cast(p); + } }; } // namespace std @@ -743,6 +743,7 @@ template struct FMT_API basic_data { static const uint64_t POW10_SIGNIFICANDS[]; static const int16_t POW10_EXPONENTS[]; static const char DIGITS[]; + static const char HEX_DIGITS[]; static const char FOREGROUND_COLOR[]; static const char BACKGROUND_COLOR[]; static const char RESET_COLOR[5]; @@ -791,6 +792,8 @@ template inline int count_digits(UInt n) { return num_digits; } +template <> int count_digits<4>(internal::uintptr_t n); + template inline size_t count_code_points(basic_string_view s) { return s.size(); @@ -1002,7 +1005,7 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits, buffer += num_digits; Char* end = buffer; do { - const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + const char* digits = upper ? "0123456789ABCDEF" : data::HEX_DIGITS; unsigned digit = (value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); @@ -1010,6 +1013,26 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits, return end; } +template +Char* format_uint(Char* buffer, internal::uintptr_t n, int num_digits, + bool = false) { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) + buffer = format_uint(buffer, n.value[start--], start_digits); + for (; start >= 0; --start) { + auto value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast(data::HEX_DIGITS[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + template inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1) @@ -1420,7 +1443,7 @@ template class arg_formatter_base { } void write_pointer(const void* p) { - writer_.write(p, specs_); + writer_.write_pointer(internal::numutil::to_uint(p), specs_); } protected: @@ -2690,8 +2713,9 @@ template class basic_writer { } }; + template struct pointer_writer { - internal::numutil::uintptr_type value; + UIntPtr value; int num_digits; size_t size() const { return num_digits + 2; } @@ -2700,7 +2724,7 @@ template class basic_writer { template void operator()(It&& it) const { *it++ = static_cast('0'); *it++ = static_cast('x'); - it = internal::format_uint<4, char_type>(it, value, num_digits, false); + it = internal::format_uint<4, char_type>(it, value, num_digits); } }; @@ -2791,11 +2815,10 @@ template class basic_writer { write(data, size, spec); } - template ::value)> - void write(const T* p, const align_spec* spec) { - auto value = internal::numutil::to_uint(p); + template + void write_pointer(UIntPtr value, const align_spec* spec) { int num_digits = internal::count_digits<4>(value); - auto pw = pointer_writer{value, num_digits}; + auto pw = pointer_writer{value, num_digits}; if (!spec) return pw(reserve(num_digits + 2)); align_spec as = *spec; if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; diff --git a/test/format-test.cc b/test/format-test.cc index 1b5a2f71..65f9d298 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -701,6 +701,13 @@ TEST(WriterTest, WriteWideString) { // std::declval>().write("abc"); } +TEST(WriterTest, WriteUIntPtr) { + memory_buffer buf; + fmt::writer writer(buf); + writer.write_pointer(fmt::internal::bit_cast( + reinterpret_cast(0xface)), FMT_NULL); +} + TEST(FormatToTest, FormatWithoutArgs) { std::string s; fmt::format_to(std::back_inserter(s), "test");