| 1 | // Formatting library for C++ |
| 2 | // |
| 3 | // Copyright (c) 2012 - 2016, Victor Zverovich |
| 4 | // All rights reserved. |
| 5 | // |
| 6 | // For the license information refer to format.h. |
| 7 | |
| 8 | #include "fmt/format-inl.h" |
| 9 | |
| 10 | FMT_BEGIN_NAMESPACE |
| 11 | namespace internal { |
| 12 | |
| 13 | template <typename T> |
| 14 | int format_float(char* buf, std::size_t size, const char* format, int precision, |
| 15 | T value) { |
| 16 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
| 17 | if (precision > 100000) |
| 18 | throw std::runtime_error( |
| 19 | "fuzz mode - avoid large allocation inside snprintf" ); |
| 20 | #endif |
| 21 | // Suppress the warning about nonliteral format string. |
| 22 | auto snprintf_ptr = FMT_SNPRINTF; |
| 23 | return precision < 0 ? snprintf_ptr(buf, size, format, value) |
| 24 | : snprintf_ptr(buf, size, format, precision, value); |
| 25 | } |
| 26 | struct sprintf_specs { |
| 27 | int precision; |
| 28 | char type; |
| 29 | bool alt : 1; |
| 30 | |
| 31 | template <typename Char> |
| 32 | constexpr sprintf_specs(basic_format_specs<Char> specs) |
| 33 | : precision(specs.precision), type(specs.type), alt(specs.alt) {} |
| 34 | |
| 35 | constexpr bool has_precision() const { return precision >= 0; } |
| 36 | }; |
| 37 | |
| 38 | // This is deprecated and is kept only to preserve ABI compatibility. |
| 39 | template <typename Double> |
| 40 | char* sprintf_format(Double value, internal::buffer<char>& buf, |
| 41 | sprintf_specs specs) { |
| 42 | // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. |
| 43 | FMT_ASSERT(buf.capacity() != 0, "empty buffer" ); |
| 44 | |
| 45 | // Build format string. |
| 46 | enum { max_format_size = 10 }; // longest format: %#-*.*Lg |
| 47 | char format[max_format_size]; |
| 48 | char* format_ptr = format; |
| 49 | *format_ptr++ = '%'; |
| 50 | if (specs.alt || !specs.type) *format_ptr++ = '#'; |
| 51 | if (specs.precision >= 0) { |
| 52 | *format_ptr++ = '.'; |
| 53 | *format_ptr++ = '*'; |
| 54 | } |
| 55 | if (std::is_same<Double, long double>::value) *format_ptr++ = 'L'; |
| 56 | |
| 57 | char type = specs.type; |
| 58 | |
| 59 | if (type == '%') |
| 60 | type = 'f'; |
| 61 | else if (type == 0 || type == 'n') |
| 62 | type = 'g'; |
| 63 | #if FMT_MSC_VER |
| 64 | if (type == 'F') { |
| 65 | // MSVC's printf doesn't support 'F'. |
| 66 | type = 'f'; |
| 67 | } |
| 68 | #endif |
| 69 | *format_ptr++ = type; |
| 70 | *format_ptr = '\0'; |
| 71 | |
| 72 | // Format using snprintf. |
| 73 | char* start = nullptr; |
| 74 | char* decimal_point_pos = nullptr; |
| 75 | for (;;) { |
| 76 | std::size_t buffer_size = buf.capacity(); |
| 77 | start = &buf[0]; |
| 78 | int result = |
| 79 | format_float(start, buffer_size, format, specs.precision, value); |
| 80 | if (result >= 0) { |
| 81 | unsigned n = internal::to_unsigned(value: result); |
| 82 | if (n < buf.capacity()) { |
| 83 | // Find the decimal point. |
| 84 | auto p = buf.data(), end = p + n; |
| 85 | if (*p == '+' || *p == '-') ++p; |
| 86 | if (specs.type != 'a' && specs.type != 'A') { |
| 87 | while (p < end && *p >= '0' && *p <= '9') ++p; |
| 88 | if (p < end && *p != 'e' && *p != 'E') { |
| 89 | decimal_point_pos = p; |
| 90 | if (!specs.type) { |
| 91 | // Keep only one trailing zero after the decimal point. |
| 92 | ++p; |
| 93 | if (*p == '0') ++p; |
| 94 | while (p != end && *p >= '1' && *p <= '9') ++p; |
| 95 | char* where = p; |
| 96 | while (p != end && *p == '0') ++p; |
| 97 | if (p == end || *p < '0' || *p > '9') { |
| 98 | if (p != end) std::memmove(dest: where, src: p, n: to_unsigned(value: end - p)); |
| 99 | n -= static_cast<unsigned>(p - where); |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | buf.resize(new_size: n); |
| 105 | break; // The buffer is large enough - continue with formatting. |
| 106 | } |
| 107 | buf.reserve(new_capacity: n + 1); |
| 108 | } else { |
| 109 | // If result is negative we ask to increase the capacity by at least 1, |
| 110 | // but as std::vector, the buffer grows exponentially. |
| 111 | buf.reserve(new_capacity: buf.capacity() + 1); |
| 112 | } |
| 113 | } |
| 114 | return decimal_point_pos; |
| 115 | } |
| 116 | } // namespace internal |
| 117 | |
| 118 | template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&, |
| 119 | sprintf_specs); |
| 120 | template FMT_API char* internal::sprintf_format(long double, |
| 121 | internal::buffer<char>&, |
| 122 | sprintf_specs); |
| 123 | |
| 124 | template struct FMT_API internal::basic_data<void>; |
| 125 | |
| 126 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. |
| 127 | int (*instantiate_format_float)(double, int, internal::float_specs, |
| 128 | internal::buffer<char>&) = |
| 129 | internal::format_float; |
| 130 | |
| 131 | // Explicit instantiations for char. |
| 132 | |
| 133 | template FMT_API std::string internal::grouping_impl<char>(locale_ref); |
| 134 | template FMT_API char internal::thousands_sep_impl(locale_ref); |
| 135 | template FMT_API char internal::decimal_point_impl(locale_ref); |
| 136 | |
| 137 | template FMT_API void internal::buffer<char>::append(const char*, const char*); |
| 138 | |
| 139 | template FMT_API void internal::arg_map<format_context>::init( |
| 140 | const basic_format_args<format_context>& args); |
| 141 | |
| 142 | template FMT_API std::string internal::vformat<char>( |
| 143 | string_view, basic_format_args<format_context>); |
| 144 | |
| 145 | template FMT_API format_context::iterator internal::vformat_to( |
| 146 | internal::buffer<char>&, string_view, basic_format_args<format_context>); |
| 147 | |
| 148 | template FMT_API int internal::snprintf_float(double, int, |
| 149 | internal::float_specs, |
| 150 | internal::buffer<char>&); |
| 151 | template FMT_API int internal::snprintf_float(long double, int, |
| 152 | internal::float_specs, |
| 153 | internal::buffer<char>&); |
| 154 | template FMT_API int internal::format_float(double, int, internal::float_specs, |
| 155 | internal::buffer<char>&); |
| 156 | template FMT_API int internal::format_float(long double, int, |
| 157 | internal::float_specs, |
| 158 | internal::buffer<char>&); |
| 159 | |
| 160 | // Explicit instantiations for wchar_t. |
| 161 | |
| 162 | template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref); |
| 163 | template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); |
| 164 | template FMT_API wchar_t internal::decimal_point_impl(locale_ref); |
| 165 | |
| 166 | template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*, |
| 167 | const wchar_t*); |
| 168 | |
| 169 | template FMT_API std::wstring internal::vformat<wchar_t>( |
| 170 | wstring_view, basic_format_args<wformat_context>); |
| 171 | FMT_END_NAMESPACE |
| 172 | |