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
10FMT_BEGIN_NAMESPACE
11namespace internal {
12
13template <typename T>
14int 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}
26struct 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.
39template <typename Double>
40char* 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
118template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
119 sprintf_specs);
120template FMT_API char* internal::sprintf_format(long double,
121 internal::buffer<char>&,
122 sprintf_specs);
123
124template struct FMT_API internal::basic_data<void>;
125
126// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
127int (*instantiate_format_float)(double, int, internal::float_specs,
128 internal::buffer<char>&) =
129 internal::format_float;
130
131// Explicit instantiations for char.
132
133template FMT_API std::string internal::grouping_impl<char>(locale_ref);
134template FMT_API char internal::thousands_sep_impl(locale_ref);
135template FMT_API char internal::decimal_point_impl(locale_ref);
136
137template FMT_API void internal::buffer<char>::append(const char*, const char*);
138
139template FMT_API void internal::arg_map<format_context>::init(
140 const basic_format_args<format_context>& args);
141
142template FMT_API std::string internal::vformat<char>(
143 string_view, basic_format_args<format_context>);
144
145template FMT_API format_context::iterator internal::vformat_to(
146 internal::buffer<char>&, string_view, basic_format_args<format_context>);
147
148template FMT_API int internal::snprintf_float(double, int,
149 internal::float_specs,
150 internal::buffer<char>&);
151template FMT_API int internal::snprintf_float(long double, int,
152 internal::float_specs,
153 internal::buffer<char>&);
154template FMT_API int internal::format_float(double, int, internal::float_specs,
155 internal::buffer<char>&);
156template 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
162template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
163template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
164template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
165
166template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
167 const wchar_t*);
168
169template FMT_API std::wstring internal::vformat<wchar_t>(
170 wstring_view, basic_format_args<wformat_context>);
171FMT_END_NAMESPACE
172