Add a default timeout parameter to HHWheelTimer.
[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     auto readInt = [&] {
213       auto const b = p;
214       do {
215         ++p;
216       } while (p != end && *p >= '0' && *p <= '9');
217       return to<int>(StringPiece(b, p));
218     };
219
220     if (*p == '*') {
221       width = kDynamicWidth;
222       ++p;
223
224       if (p == end) return;
225
226       if (*p >= '0' && *p <= '9') widthIndex = readInt();
227
228       if (p == end) return;
229     } else if (*p >= '0' && *p <= '9') {
230       width = readInt();
231
232       if (p == end) return;
233     }
234
235     if (*p == ',') {
236       thousandsSeparator = true;
237       if (++p == end) return;
238     }
239
240     if (*p == '.') {
241       auto b = ++p;
242       while (p != end && *p >= '0' && *p <= '9') {
243         ++p;
244       }
245       if (p != b) {
246         precision = to<int>(StringPiece(b, p));
247         if (p != end && *p == '.') {
248           trailingDot = true;
249           ++p;
250         }
251       } else {
252         trailingDot = true;
253       }
254
255       if (p == end) return;
256     }
257
258     presentation = *p;
259     if (++p == end) return;
260   }
261
262   error("extra characters in format string");
263 }
264
265 void FormatArg::validate(Type type) const {
266   enforce(keyEmpty(), "index not allowed");
267   switch (type) {
268   case Type::INTEGER:
269     enforce(precision == kDefaultPrecision,
270             "precision not allowed on integers");
271     break;
272   case Type::FLOAT:
273     enforce(!basePrefix,
274             "base prefix ('#') specifier only allowed on integers");
275     enforce(!thousandsSeparator,
276             "thousands separator (',') only allowed on integers");
277     break;
278   case Type::OTHER:
279     enforce(align != Align::PAD_AFTER_SIGN,
280             "'='alignment only allowed on numbers");
281     enforce(sign == Sign::DEFAULT,
282             "sign specifier only allowed on numbers");
283     enforce(!basePrefix,
284             "base prefix ('#') specifier only allowed on integers");
285     enforce(!thousandsSeparator,
286             "thousands separator (',') only allowed on integers");
287     break;
288   }
289 }
290
291 namespace detail {
292 void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
293   uint32_t remaining_digits = *end_buffer - start_buffer;
294   uint32_t separator_size = (remaining_digits - 1) / 3;
295   uint32_t result_size = remaining_digits + separator_size;
296   *end_buffer = *end_buffer + separator_size;
297
298   // get the end of the new string with the separators
299   uint32_t buffer_write_index = result_size - 1;
300   uint32_t buffer_read_index = remaining_digits - 1;
301   start_buffer[buffer_write_index + 1] = 0;
302
303   bool done = false;
304   uint32_t next_group_size = 3;
305
306   while (!done) {
307     uint32_t current_group_size = std::max<uint32_t>(1,
308       std::min<uint32_t>(remaining_digits, next_group_size));
309
310     // write out the current group's digits to the buffer index
311     for (uint32_t i = 0; i < current_group_size; i++) {
312       start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
313     }
314
315     // if not finished, write the separator before the next group
316     if (buffer_write_index < buffer_write_index + 1) {
317       start_buffer[buffer_write_index--] = ',';
318     } else {
319       done = true;
320     }
321
322     remaining_digits -= current_group_size;
323   }
324 }
325 } // detail
326
327 }  // namespace folly