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