1//
2// POSIX spec:
3// http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
4//
5#include "absl/strings/internal/str_format/arg.h"
6
7#include <cassert>
8#include <cerrno>
9#include <cstdlib>
10#include <string>
11#include <type_traits>
12
13#include "absl/base/port.h"
14#include "absl/strings/internal/str_format/float_conversion.h"
15
16namespace absl {
17namespace str_format_internal {
18namespace {
19
20const char kDigit[2][32] = { "0123456789abcdef", "0123456789ABCDEF" };
21
22// Reduce *capacity by s.size(), clipped to a 0 minimum.
23void ReducePadding(string_view s, size_t *capacity) {
24 *capacity = Excess(s.size(), *capacity);
25}
26
27// Reduce *capacity by n, clipped to a 0 minimum.
28void ReducePadding(size_t n, size_t *capacity) {
29 *capacity = Excess(n, *capacity);
30}
31
32template <typename T>
33struct MakeUnsigned : std::make_unsigned<T> {};
34template <>
35struct MakeUnsigned<absl::uint128> {
36 using type = absl::uint128;
37};
38
39template <typename T>
40struct IsSigned : std::is_signed<T> {};
41template <>
42struct IsSigned<absl::uint128> : std::false_type {};
43
44class ConvertedIntInfo {
45 public:
46 template <typename T>
47 ConvertedIntInfo(T v, ConversionChar conv) {
48 using Unsigned = typename MakeUnsigned<T>::type;
49 auto u = static_cast<Unsigned>(v);
50 if (IsNeg(v)) {
51 is_neg_ = true;
52 u = Unsigned{} - u;
53 } else {
54 is_neg_ = false;
55 }
56 UnsignedToStringRight(u, conv);
57 }
58
59 string_view digits() const {
60 return {end() - size_, static_cast<size_t>(size_)};
61 }
62 bool is_neg() const { return is_neg_; }
63
64 private:
65 template <typename T, bool IsSigned>
66 struct IsNegImpl {
67 static bool Eval(T v) { return v < 0; }
68 };
69 template <typename T>
70 struct IsNegImpl<T, false> {
71 static bool Eval(T) {
72 return false;
73 }
74 };
75
76 template <typename T>
77 bool IsNeg(T v) {
78 return IsNegImpl<T, IsSigned<T>::value>::Eval(v);
79 }
80
81 template <typename T>
82 void UnsignedToStringRight(T u, ConversionChar conv) {
83 char *p = end();
84 switch (conv.radix()) {
85 default:
86 case 10:
87 for (; u; u /= 10)
88 *--p = static_cast<char>('0' + static_cast<size_t>(u % 10));
89 break;
90 case 8:
91 for (; u; u /= 8)
92 *--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
93 break;
94 case 16: {
95 const char *digits = kDigit[conv.upper() ? 1 : 0];
96 for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
97 break;
98 }
99 }
100 size_ = static_cast<int>(end() - p);
101 }
102
103 const char *end() const { return storage_ + sizeof(storage_); }
104 char *end() { return storage_ + sizeof(storage_); }
105
106 bool is_neg_;
107 int size_;
108 // Max size: 128 bit value as octal -> 43 digits
109 char storage_[128 / 3 + 1];
110};
111
112// Note: 'o' conversions do not have a base indicator, it's just that
113// the '#' flag is specified to modify the precision for 'o' conversions.
114string_view BaseIndicator(const ConvertedIntInfo &info,
115 const ConversionSpec conv) {
116 bool alt = conv.flags().alt;
117 int radix = conv.conv().radix();
118 if (conv.conv().id() == ConversionChar::p)
119 alt = true; // always show 0x for %p.
120 // From the POSIX description of '#' flag:
121 // "For x or X conversion specifiers, a non-zero result shall have
122 // 0x (or 0X) prefixed to it."
123 if (alt && radix == 16 && !info.digits().empty()) {
124 if (conv.conv().upper()) return "0X";
125 return "0x";
126 }
127 return {};
128}
129
130string_view SignColumn(bool neg, const ConversionSpec conv) {
131 if (conv.conv().is_signed()) {
132 if (neg) return "-";
133 if (conv.flags().show_pos) return "+";
134 if (conv.flags().sign_col) return " ";
135 }
136 return {};
137}
138
139bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
140 FormatSinkImpl *sink) {
141 size_t fill = 0;
142 if (conv.width() >= 0) fill = conv.width();
143 ReducePadding(1, &fill);
144 if (!conv.flags().left) sink->Append(fill, ' ');
145 sink->Append(1, v);
146 if (conv.flags().left) sink->Append(fill, ' ');
147 return true;
148}
149
150bool ConvertIntImplInner(const ConvertedIntInfo &info,
151 const ConversionSpec conv, FormatSinkImpl *sink) {
152 // Print as a sequence of Substrings:
153 // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
154 size_t fill = 0;
155 if (conv.width() >= 0) fill = conv.width();
156
157 string_view formatted = info.digits();
158 ReducePadding(formatted, &fill);
159
160 string_view sign = SignColumn(info.is_neg(), conv);
161 ReducePadding(sign, &fill);
162
163 string_view base_indicator = BaseIndicator(info, conv);
164 ReducePadding(base_indicator, &fill);
165
166 int precision = conv.precision();
167 bool precision_specified = precision >= 0;
168 if (!precision_specified)
169 precision = 1;
170
171 if (conv.flags().alt && conv.conv().id() == ConversionChar::o) {
172 // From POSIX description of the '#' (alt) flag:
173 // "For o conversion, it increases the precision (if necessary) to
174 // force the first digit of the result to be zero."
175 if (formatted.empty() || *formatted.begin() != '0') {
176 int needed = static_cast<int>(formatted.size()) + 1;
177 precision = std::max(precision, needed);
178 }
179 }
180
181 size_t num_zeroes = Excess(formatted.size(), precision);
182 ReducePadding(num_zeroes, &fill);
183
184 size_t num_left_spaces = !conv.flags().left ? fill : 0;
185 size_t num_right_spaces = conv.flags().left ? fill : 0;
186
187 // From POSIX description of the '0' (zero) flag:
188 // "For d, i, o, u, x, and X conversion specifiers, if a precision
189 // is specified, the '0' flag is ignored."
190 if (!precision_specified && conv.flags().zero) {
191 num_zeroes += num_left_spaces;
192 num_left_spaces = 0;
193 }
194
195 sink->Append(num_left_spaces, ' ');
196 sink->Append(sign);
197 sink->Append(base_indicator);
198 sink->Append(num_zeroes, '0');
199 sink->Append(formatted);
200 sink->Append(num_right_spaces, ' ');
201 return true;
202}
203
204template <typename T>
205bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
206 ConvertedIntInfo info(v, conv.conv());
207 if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
208 if (info.is_neg()) sink->Append(1, '-');
209 if (info.digits().empty()) {
210 sink->Append(1, '0');
211 } else {
212 sink->Append(info.digits());
213 }
214 return true;
215 }
216 return ConvertIntImplInner(info, conv, sink);
217}
218
219template <typename T>
220bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
221 if (conv.conv().is_float()) {
222 return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
223 }
224 if (conv.conv().id() == ConversionChar::c)
225 return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
226 if (!conv.conv().is_integral())
227 return false;
228 if (!conv.conv().is_signed() && IsSigned<T>::value) {
229 using U = typename MakeUnsigned<T>::type;
230 return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
231 }
232 return ConvertIntImplInner(v, conv, sink);
233}
234
235template <typename T>
236bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
237 return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
238}
239
240inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
241 FormatSinkImpl *sink) {
242 if (conv.conv().id() != ConversionChar::s)
243 return false;
244 if (conv.flags().basic) {
245 sink->Append(v);
246 return true;
247 }
248 return sink->PutPaddedString(v, conv.width(), conv.precision(),
249 conv.flags().left);
250}
251
252} // namespace
253
254// ==================== Strings ====================
255ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
256 const ConversionSpec conv,
257 FormatSinkImpl *sink) {
258 return {ConvertStringArg(v, conv, sink)};
259}
260
261ConvertResult<Conv::s> FormatConvertImpl(string_view v,
262 const ConversionSpec conv,
263 FormatSinkImpl *sink) {
264 return {ConvertStringArg(v, conv, sink)};
265}
266
267ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
268 const ConversionSpec conv,
269 FormatSinkImpl *sink) {
270 if (conv.conv().id() == ConversionChar::p)
271 return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
272 size_t len;
273 if (v == nullptr) {
274 len = 0;
275 } else if (conv.precision() < 0) {
276 len = std::strlen(v);
277 } else {
278 // If precision is set, we look for the null terminator on the valid range.
279 len = std::find(v, v + conv.precision(), '\0') - v;
280 }
281 return {ConvertStringArg(string_view(v, len), conv, sink)};
282}
283
284// ==================== Raw pointers ====================
285ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
286 FormatSinkImpl *sink) {
287 if (conv.conv().id() != ConversionChar::p)
288 return {false};
289 if (!v.value) {
290 sink->Append("(nil)");
291 return {true};
292 }
293 return {ConvertIntImplInner(v.value, conv, sink)};
294}
295
296// ==================== Floats ====================
297FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv,
298 FormatSinkImpl *sink) {
299 return {ConvertFloatArg(v, conv, sink)};
300}
301FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv,
302 FormatSinkImpl *sink) {
303 return {ConvertFloatArg(v, conv, sink)};
304}
305FloatingConvertResult FormatConvertImpl(long double v,
306 const ConversionSpec conv,
307 FormatSinkImpl *sink) {
308 return {ConvertFloatArg(v, conv, sink)};
309}
310
311// ==================== Chars ====================
312IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv,
313 FormatSinkImpl *sink) {
314 return {ConvertIntArg(v, conv, sink)};
315}
316IntegralConvertResult FormatConvertImpl(signed char v,
317 const ConversionSpec conv,
318 FormatSinkImpl *sink) {
319 return {ConvertIntArg(v, conv, sink)};
320}
321IntegralConvertResult FormatConvertImpl(unsigned char v,
322 const ConversionSpec conv,
323 FormatSinkImpl *sink) {
324 return {ConvertIntArg(v, conv, sink)};
325}
326
327// ==================== Ints ====================
328IntegralConvertResult FormatConvertImpl(short v, // NOLINT
329 const ConversionSpec conv,
330 FormatSinkImpl *sink) {
331 return {ConvertIntArg(v, conv, sink)};
332}
333IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
334 const ConversionSpec conv,
335 FormatSinkImpl *sink) {
336 return {ConvertIntArg(v, conv, sink)};
337}
338IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv,
339 FormatSinkImpl *sink) {
340 return {ConvertIntArg(v, conv, sink)};
341}
342IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv,
343 FormatSinkImpl *sink) {
344 return {ConvertIntArg(v, conv, sink)};
345}
346IntegralConvertResult FormatConvertImpl(long v, // NOLINT
347 const ConversionSpec conv,
348 FormatSinkImpl *sink) {
349 return {ConvertIntArg(v, conv, sink)};
350}
351IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
352 const ConversionSpec conv,
353 FormatSinkImpl *sink) {
354 return {ConvertIntArg(v, conv, sink)};
355}
356IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
357 const ConversionSpec conv,
358 FormatSinkImpl *sink) {
359 return {ConvertIntArg(v, conv, sink)};
360}
361IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
362 const ConversionSpec conv,
363 FormatSinkImpl *sink) {
364 return {ConvertIntArg(v, conv, sink)};
365}
366IntegralConvertResult FormatConvertImpl(absl::uint128 v,
367 const ConversionSpec conv,
368 FormatSinkImpl *sink) {
369 return {ConvertIntArg(v, conv, sink)};
370}
371
372ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
373
374
375} // namespace str_format_internal
376
377} // namespace absl
378