From bb205d940d8929d086eadb59705349dbdaa1a274 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Nov 2019 05:15:59 -0800 Subject: [PATCH] Fix fallback pointer formatting on big endian --- include/fmt/format-inl.h | 2 +- include/fmt/format.h | 40 ++++++++++++++++++++++++++++------------ test/format-impl-test.cc | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) Index: fmt-6.0.0/include/fmt/format-inl.h =================================================================== --- fmt-6.0.0.orig/include/fmt/format-inl.h +++ fmt-6.0.0/include/fmt/format-inl.h @@ -241,7 +241,7 @@ FMT_FUNC void system_error::init(int err namespace internal { template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) { - // Assume little endian; pointer formatting is implementation-defined anyway. + // fallback_uintptr is always stored in little endian. int i = static_cast(sizeof(void*)) - 1; while (i > 0 && n.value[i] == 0) --i; auto char_digits = std::numeric_limits::digits / 4; Index: fmt-6.0.0/include/fmt/format.h =================================================================== --- fmt-6.0.0.orig/include/fmt/format.h +++ fmt-6.0.0/include/fmt/format.h @@ -196,17 +196,7 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace internal { -// A fallback implementation of uintptr_t for systems that lack it. -struct fallback_uintptr { - unsigned char value[sizeof(void*)]; -}; -#ifdef UINTPTR_MAX -using uintptr_t = ::uintptr_t; -#else -using uintptr_t = fallback_uintptr; -#endif - -// An equivalent of `*reinterpret_cast(&source)` that doesn't produce +// An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); template @@ -217,6 +207,32 @@ inline Dest bit_cast(const Source& sourc return dest; } +inline bool is_big_endian() { + auto u = 1u; + struct bytes { char data[sizeof(u)]; }; + return bit_cast(u).data[0] == 0; +} + +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; + + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (is_big_endian()) std::memmove(value, value, sizeof(void*)); + } +}; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +#else +using uintptr_t = fallback_uintptr; +inline fallback_uintptr to_uintptr(const void* p) { + return fallback_uintptr(p); +} +#endif + // An approximation of iterator_t for pre-C++20 systems. template using iterator_t = decltype(std::begin(std::declval())); @@ -1731,7 +1747,7 @@ class arg_formatter_base { } void write_pointer(const void* p) { - writer_.write_pointer(internal::bit_cast(p), specs_); + writer_.write_pointer(internal::to_uintptr(p), specs_); } protected: Index: fmt-6.0.0/test/format-impl-test.cc =================================================================== --- fmt-6.0.0.orig/test/format-impl-test.cc +++ fmt-6.0.0/test/format-impl-test.cc @@ -259,7 +259,7 @@ TEST(UtilTest, CountDigits) { TEST(UtilTest, WriteUIntPtr) { fmt::memory_buffer buf; fmt::internal::writer writer(buf); - writer.write_pointer(fmt::internal::bit_cast( + writer.write_pointer(fmt::internal::fallback_uintptr( reinterpret_cast(0xface)), nullptr); EXPECT_EQ("0xface", to_string(buf));