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