/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_FORMAT_SIMPLE_INTEGER_HPP #define MPT_FORMAT_SIMPLE_INTEGER_HPP #include "mpt/base/detect.hpp" #if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT) #define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 1 #else // MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT #define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 0 #endif // !MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT #include "mpt/base/algorithm.hpp" #include "mpt/base/namespace.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_spec.hpp" #include "mpt/string/types.hpp" #if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #include #endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 #include #include namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 template ::value, bool> = true> inline Tstring format_simple_integer_to_chars(const T & x, int base) { std::string str(1, '\0'); bool done = false; while (!done) { if constexpr (std::is_same::value) { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast(x), base); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } else { std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base); if (result.ec != std::errc{}) { str.resize(mpt::exponential_grow(str.size()), '\0'); } else { str.resize(result.ptr - str.data()); done = true; } } } return mpt::convert_formatted_simple(str); } #else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 template ::value, bool> = true> inline Tstring format_simple_integer_to_stream(const T & x, int base) { using stream_char_type = typename mpt::select_format_char_type::type; if ((base == 10) || ((base == 16) && std::is_unsigned::value) || ((base == 8) && std::is_unsigned::value)) { // fast path std::basic_ostringstream s; s.imbue(std::locale::classic()); if (base == 16) { s << std::hex; } else if (base == 8) { s << std::oct; } if constexpr (std::is_same::value) { s << static_cast(x); } else if constexpr (mpt::is_character::value) { s << (x + 0); // force integral promotion } else { s << x; } return mpt::convert_formatted_simple(s.str()); } else { if constexpr (std::is_same::value) { return x ? Tstring(1, mpt::unsafe_char_convert('1')) : Tstring(1, mpt::unsafe_char_convert('0')); } else if constexpr (std::is_unsigned::value) { Tstring result; T val = x; if (val == 0) { result += Tstring(1, mpt::unsafe_char_convert('0')); } else { using Tunsigned = typename std::make_unsigned::type; while (val > 0) { Tunsigned digit = static_cast(val % static_cast(base)); val = static_cast(val / static_cast(base)); if (digit >= 10) { result += Tstring(1, static_cast(mpt::unsafe_char_convert('a') - 10 + digit)); } else { result += Tstring(1, static_cast(mpt::unsafe_char_convert('0') + digit)); } } std::reverse(result.begin(), result.end()); } return result; } else { Tstring result; if (x == 0) { result += Tstring(1, mpt::unsafe_char_convert('0')); } else { using Tunsigned = typename std::make_unsigned::type; Tunsigned val = (x != -x) ? ((x >= 0) ? x : -x) : (static_cast(-(x + 1)) + 1); while (val > 0) { Tunsigned digit = static_cast(val % static_cast(base)); val = static_cast(val / static_cast(base)); if (digit >= 10) { result += Tstring(1, static_cast(mpt::unsafe_char_convert('a') - 10 + digit)); } else { result += Tstring(1, static_cast(mpt::unsafe_char_convert('0') + digit)); } } if (x < 0) { result += Tstring(1, mpt::unsafe_char_convert('-')); } std::reverse(result.begin(), result.end()); } return result; } } } #endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 template inline Tstring format_simple_integer_postprocess_case(Tstring str, const format_simple_spec & format) { format_simple_flags f = format.GetFlags(); if (f & format_simple_base::CaseUpp) { for (auto & c : str) { if (mpt::unsafe_char_convert('a') <= c && c <= mpt::unsafe_char_convert('z')) { c -= mpt::unsafe_char_convert('a') - mpt::unsafe_char_convert('A'); } } } return str; } template inline Tstring format_simple_integer_postprocess_digits(Tstring str, const format_simple_spec & format) { format_simple_flags f = format.GetFlags(); std::size_t width = format.GetWidth(); if (f & format_simple_base::FillNul) { auto pos = str.begin(); if (str.length() > 0) { if (str[0] == mpt::unsafe_char_convert('+')) { pos++; width++; } else if (str[0] == mpt::unsafe_char_convert('-')) { pos++; width++; } } if (str.length() < width) { str.insert(pos, width - str.length(), mpt::unsafe_char_convert('0')); } } return str; } #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 4723) // potential divide by 0 #endif // MPT_COMPILER_MSVC template inline Tstring format_simple_integer_postprocess_group(Tstring str, const format_simple_spec & format) { if (format.GetGroup() > 0) { const unsigned int groupSize = format.GetGroup(); const char groupSep = format.GetGroupSep(); std::size_t len = str.length(); for (std::size_t n = 0; n < len; ++n) { if (n > 0 && (n % groupSize) == 0) { if (!(n == (len - 1) && (str[0] == mpt::unsafe_char_convert('+') || str[0] == mpt::unsafe_char_convert('-')))) { str.insert(str.begin() + (len - n), 1, mpt::unsafe_char_convert(groupSep)); } } } } return str; } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC #if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 template ::value, bool> = true> inline Tstring format_simple(const T & x, const format_simple_spec & format) { int base = 10; if (format.GetFlags() & format_simple_base::BaseDec) { base = 10; } if (format.GetFlags() & format_simple_base::BaseHex) { base = 16; } using format_string_type = typename mpt::select_format_string_type::type; return mpt::transcode(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_chars(x, base), format), format), format)); } #else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 template ::value, bool> = true> inline Tstring format_simple(const T & x, const format_simple_spec & format) { int base = 10; if (format.GetFlags() & format_simple_base::BaseDec) { base = 10; } if (format.GetFlags() & format_simple_base::BaseHex) { base = 16; } using format_string_type = typename mpt::select_format_string_type::type; return mpt::transcode(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_stream(x, base), format), format), format)); } #endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_FORMAT_SIMPLE_INTEGER_HPP