apply clang-tidy modernize-use-override
[folly.git] / folly / Format.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/Format.h>
18
19 #include <folly/CppAttributes.h>
20 #include <folly/portability/Constexpr.h>
21
22 #include <double-conversion/double-conversion.h>
23
24 namespace folly {
25 namespace detail {
26
27 extern const FormatArg::Align formatAlignTable[];
28 extern const FormatArg::Sign formatSignTable[];
29
30 }  // namespace detail
31
32 using namespace folly::detail;
33
34 void FormatValue<double>::formatHelper(
35     fbstring& piece, int& prefixLen, FormatArg& arg) const {
36   using ::double_conversion::DoubleToStringConverter;
37   using ::double_conversion::StringBuilder;
38
39   arg.validate(FormatArg::Type::FLOAT);
40
41   if (arg.presentation == FormatArg::kDefaultPresentation) {
42     arg.presentation = 'g';
43   }
44
45   const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
46   const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
47   char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
48
49   if (arg.precision == FormatArg::kDefaultPrecision) {
50     arg.precision = 6;
51   }
52
53   // 2+: for null terminator and optional sign shenanigans.
54   constexpr int bufLen =
55       2 + constexpr_max(
56               2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
57                   DoubleToStringConverter::kMaxFixedDigitsAfterPoint,
58               constexpr_max(8 + DoubleToStringConverter::kMaxExponentialDigits,
59                             7 + DoubleToStringConverter::kMaxPrecisionDigits));
60   char buf[bufLen];
61   StringBuilder builder(buf + 1, bufLen - 1);
62
63   char plusSign;
64   switch (arg.sign) {
65   case FormatArg::Sign::PLUS_OR_MINUS:
66     plusSign = '+';
67     break;
68   case FormatArg::Sign::SPACE_OR_MINUS:
69     plusSign = ' ';
70     break;
71   default:
72     plusSign = '\0';
73     break;
74   };
75
76   auto flags =
77       DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
78       (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
79                        : 0);
80
81   double val = val_;
82   switch (arg.presentation) {
83   case '%':
84     val *= 100;
85     FOLLY_FALLTHROUGH;
86   case 'f':
87   case 'F':
88     {
89       if (arg.precision >
90           DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
91         arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
92       }
93       DoubleToStringConverter conv(flags,
94                                    infinitySymbol,
95                                    nanSymbol,
96                                    exponentSymbol,
97                                    -4,
98                                    arg.precision,
99                                    0,
100                                    0);
101       arg.enforce(conv.ToFixed(val, arg.precision, &builder),
102                   "fixed double conversion failed");
103     }
104     break;
105   case 'e':
106   case 'E':
107     {
108       if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
109         arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
110       }
111
112       DoubleToStringConverter conv(flags,
113                                    infinitySymbol,
114                                    nanSymbol,
115                                    exponentSymbol,
116                                    -4,
117                                    arg.precision,
118                                    0,
119                                    0);
120       arg.enforce(conv.ToExponential(val, arg.precision, &builder));
121     }
122     break;
123   case 'n':  // should be locale-aware, but isn't
124   case 'g':
125   case 'G':
126     {
127       if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
128         arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
129       } else if (arg.precision >
130                  DoubleToStringConverter::kMaxPrecisionDigits) {
131         arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
132       }
133       DoubleToStringConverter conv(flags,
134                                    infinitySymbol,
135                                    nanSymbol,
136                                    exponentSymbol,
137                                    -4,
138                                    arg.precision,
139                                    0,
140                                    0);
141       arg.enforce(conv.ToShortest(val, &builder));
142     }
143     break;
144   default:
145     arg.error("invalid specifier '", arg.presentation, "'");
146   }
147
148   int len = builder.position();
149   builder.Finalize();
150   DCHECK_GT(len, 0);
151
152   // Add '+' or ' ' sign if needed
153   char* p = buf + 1;
154   // anything that's neither negative nor nan
155   prefixLen = 0;
156   if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
157     *--p = plusSign;
158     ++len;
159     prefixLen = 1;
160   } else if (*p == '-') {
161     prefixLen = 1;
162   }
163
164   piece = fbstring(p, size_t(len));
165 }
166
167
168 void FormatArg::initSlow() {
169   auto b = fullArgString.begin();
170   auto end = fullArgString.end();
171
172   // Parse key
173   auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));
174   if (!p) {
175     key_ = StringPiece(b, end);
176     return;
177   }
178   key_ = StringPiece(b, p);
179
180   if (*p == ':') {
181     // parse format spec
182     if (++p == end) return;
183
184     // fill/align, or just align
185     Align a;
186     if (p + 1 != end &&
187         (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
188         Align::INVALID) {
189       fill = *p;
190       align = a;
191       p += 2;
192       if (p == end) return;
193     } else if ((a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
194                Align::INVALID) {
195       align = a;
196       if (++p == end) return;
197     }
198
199     Sign s;
200     unsigned char uSign = static_cast<unsigned char>(*p);
201     if ((s = formatSignTable[uSign]) != Sign::INVALID) {
202       sign = s;
203       if (++p == end) return;
204     }
205
206     if (*p == '#') {
207       basePrefix = true;
208       if (++p == end) return;
209     }
210
211     if (*p == '0') {
212       enforce(align == Align::DEFAULT, "alignment specified twice");
213       fill = '0';
214       align = Align::PAD_AFTER_SIGN;
215       if (++p == end) return;
216     }
217
218     auto readInt = [&] {
219       auto const c = p;
220       do {
221         ++p;
222       } while (p != end && *p >= '0' && *p <= '9');
223       return to<int>(StringPiece(c, p));
224     };
225
226     if (*p == '*') {
227       width = kDynamicWidth;
228       ++p;
229
230       if (p == end) return;
231
232       if (*p >= '0' && *p <= '9') widthIndex = readInt();
233
234       if (p == end) return;
235     } else if (*p >= '0' && *p <= '9') {
236       width = readInt();
237
238       if (p == end) return;
239     }
240
241     if (*p == ',') {
242       thousandsSeparator = true;
243       if (++p == end) return;
244     }
245
246     if (*p == '.') {
247       auto d = ++p;
248       while (p != end && *p >= '0' && *p <= '9') {
249         ++p;
250       }
251       if (p != d) {
252         precision = to<int>(StringPiece(d, p));
253         if (p != end && *p == '.') {
254           trailingDot = true;
255           ++p;
256         }
257       } else {
258         trailingDot = true;
259       }
260
261       if (p == end) return;
262     }
263
264     presentation = *p;
265     if (++p == end) return;
266   }
267
268   error("extra characters in format string");
269 }
270
271 void FormatArg::validate(Type type) const {
272   enforce(keyEmpty(), "index not allowed");
273   switch (type) {
274   case Type::INTEGER:
275     enforce(precision == kDefaultPrecision,
276             "precision not allowed on integers");
277     break;
278   case Type::FLOAT:
279     enforce(!basePrefix,
280             "base prefix ('#') specifier only allowed on integers");
281     enforce(!thousandsSeparator,
282             "thousands separator (',') only allowed on integers");
283     break;
284   case Type::OTHER:
285     enforce(align != Align::PAD_AFTER_SIGN,
286             "'='alignment only allowed on numbers");
287     enforce(sign == Sign::DEFAULT,
288             "sign specifier only allowed on numbers");
289     enforce(!basePrefix,
290             "base prefix ('#') specifier only allowed on integers");
291     enforce(!thousandsSeparator,
292             "thousands separator (',') only allowed on integers");
293     break;
294   }
295 }
296
297 namespace detail {
298 void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
299   uint32_t remaining_digits = uint32_t(*end_buffer - start_buffer);
300   uint32_t separator_size = (remaining_digits - 1) / 3;
301   uint32_t result_size = remaining_digits + separator_size;
302   *end_buffer = *end_buffer + separator_size;
303
304   // get the end of the new string with the separators
305   uint32_t buffer_write_index = result_size - 1;
306   uint32_t buffer_read_index = remaining_digits - 1;
307   start_buffer[buffer_write_index + 1] = 0;
308
309   bool done = false;
310   uint32_t next_group_size = 3;
311
312   while (!done) {
313     uint32_t current_group_size = std::max<uint32_t>(1,
314       std::min<uint32_t>(remaining_digits, next_group_size));
315
316     // write out the current group's digits to the buffer index
317     for (uint32_t i = 0; i < current_group_size; i++) {
318       start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
319     }
320
321     // if not finished, write the separator before the next group
322     if (buffer_write_index < buffer_write_index + 1) {
323       start_buffer[buffer_write_index--] = ',';
324     } else {
325       done = true;
326     }
327
328     remaining_digits -= current_group_size;
329   }
330 }
331 } // detail
332
333 }  // namespace folly