1 /*
2  * Copyright 2012-present 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/ConstexprMath.h>
20 #include <folly/CppAttributes.h>
21 #include <folly/container/Array.h>
22 
23 #include <double-conversion/double-conversion.h>
24 
25 namespace folly {
26 namespace detail {
27 
28 //  ctor for items in the align table
29 struct format_table_align_make_item {
30   static constexpr std::size_t size = 256;
operator ()folly::detail::format_table_align_make_item31   constexpr FormatArg::Align operator()(std::size_t index) const {
32     // clang-format off
33     return
34         index == '<' ? FormatArg::Align::LEFT:
35         index == '>' ? FormatArg::Align::RIGHT :
36         index == '=' ? FormatArg::Align::PAD_AFTER_SIGN :
37         index == '^' ? FormatArg::Align::CENTER :
38         FormatArg::Align::INVALID;
39     // clang-format on
40   }
41 };
42 
43 //  ctor for items in the conv tables for representing parts of nonnegative
44 //  integers into ascii digits of length Size, over a given base Base
45 template <std::size_t Base, std::size_t Size, bool Upper = false>
46 struct format_table_conv_make_item {
47   static_assert(Base <= 36, "Base is unrepresentable");
48   struct make_item {
49     std::size_t index{};
make_itemfolly::detail::format_table_conv_make_item::make_item50     constexpr explicit make_item(std::size_t index_) : index(index_) {} // gcc49
alphafolly::detail::format_table_conv_make_item::make_item51     constexpr char alpha(std::size_t ord) const {
52       return ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10);
53     }
operator ()folly::detail::format_table_conv_make_item::make_item54     constexpr char operator()(std::size_t offset) const {
55       return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base);
56     }
57   };
operator ()folly::detail::format_table_conv_make_item58   constexpr std::array<char, Size> operator()(std::size_t index) const {
59     return make_array_with<Size>(make_item{index});
60   }
61 };
62 
63 //  ctor for items in the sign table
64 struct format_table_sign_make_item {
65   static constexpr std::size_t size = 256;
operator ()folly::detail::format_table_sign_make_item66   constexpr FormatArg::Sign operator()(std::size_t index) const {
67     // clang-format off
68     return
69         index == '+' ? FormatArg::Sign::PLUS_OR_MINUS :
70         index == '-' ? FormatArg::Sign::MINUS :
71         index == ' ' ? FormatArg::Sign::SPACE_OR_MINUS :
72         FormatArg::Sign::INVALID;
73     // clang-format on
74   }
75 };
76 
77 //  the tables
78 FOLLY_STORAGE_CONSTEXPR auto formatAlignTable =
79     make_array_with<256>(format_table_align_make_item{});
80 FOLLY_STORAGE_CONSTEXPR auto formatSignTable =
81     make_array_with<256>(format_table_sign_make_item{});
82 FOLLY_STORAGE_CONSTEXPR decltype(formatHexLower) formatHexLower =
83     make_array_with<256>(format_table_conv_make_item<16, 2, false>{});
84 FOLLY_STORAGE_CONSTEXPR decltype(formatHexUpper) formatHexUpper =
85     make_array_with<256>(format_table_conv_make_item<16, 2, true>{});
86 FOLLY_STORAGE_CONSTEXPR decltype(formatOctal) formatOctal =
87     make_array_with<512>(format_table_conv_make_item<8, 3>{});
88 FOLLY_STORAGE_CONSTEXPR decltype(formatBinary) formatBinary =
89     make_array_with<256>(format_table_conv_make_item<2, 8>{});
90 
91 } // namespace detail
92 
93 using namespace folly::detail;
94 
formatHelper(fbstring & piece,int & prefixLen,FormatArg & arg) const95 void FormatValue<double>::formatHelper(
96     fbstring& piece,
97     int& prefixLen,
98     FormatArg& arg) const {
99   using ::double_conversion::DoubleToStringConverter;
100   using ::double_conversion::StringBuilder;
101 
102   arg.validate(FormatArg::Type::FLOAT);
103 
104   if (arg.presentation == FormatArg::kDefaultPresentation) {
105     arg.presentation = 'g';
106   }
107 
108   const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
109   const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
110   char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
111 
112   if (arg.precision == FormatArg::kDefaultPrecision) {
113     arg.precision = 6;
114   }
115 
116   // 2+: for null terminator and optional sign shenanigans.
117   constexpr int bufLen = 2 +
118       constexpr_max(2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
119                         DoubleToStringConverter::kMaxFixedDigitsAfterPoint,
120                     constexpr_max(
121                         8 + DoubleToStringConverter::kMaxExponentialDigits,
122                         7 + DoubleToStringConverter::kMaxPrecisionDigits));
123   char buf[bufLen];
124   StringBuilder builder(buf + 1, bufLen - 1);
125 
126   char plusSign;
127   switch (arg.sign) {
128     case FormatArg::Sign::PLUS_OR_MINUS:
129       plusSign = '+';
130       break;
131     case FormatArg::Sign::SPACE_OR_MINUS:
132       plusSign = ' ';
133       break;
134     default:
135       plusSign = '\0';
136       break;
137   };
138 
139   auto flags = DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
140       (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
141                        : 0);
142 
143   double val = val_;
144   switch (arg.presentation) {
145     case '%':
146       val *= 100;
147       FOLLY_FALLTHROUGH;
148     case 'f':
149     case 'F': {
150       if (arg.precision > DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
151         arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
152       }
153       DoubleToStringConverter conv(
154           flags,
155           infinitySymbol,
156           nanSymbol,
157           exponentSymbol,
158           -4,
159           arg.precision,
160           0,
161           0);
162       arg.enforce(
163           conv.ToFixed(val, arg.precision, &builder),
164           "fixed double conversion failed");
165       break;
166     }
167     case 'e':
168     case 'E': {
169       if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
170         arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
171       }
172 
173       DoubleToStringConverter conv(
174           flags,
175           infinitySymbol,
176           nanSymbol,
177           exponentSymbol,
178           -4,
179           arg.precision,
180           0,
181           0);
182       arg.enforce(conv.ToExponential(val, arg.precision, &builder));
183       break;
184     }
185     case 'n': // should be locale-aware, but isn't
186     case 'g':
187     case 'G': {
188       if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
189         arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
190       } else if (arg.precision > DoubleToStringConverter::kMaxPrecisionDigits) {
191         arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
192       }
193       DoubleToStringConverter conv(
194           flags,
195           infinitySymbol,
196           nanSymbol,
197           exponentSymbol,
198           -4,
199           arg.precision,
200           0,
201           0);
202       arg.enforce(conv.ToShortest(val, &builder));
203       break;
204     }
205     default:
206       arg.error("invalid specifier '", arg.presentation, "'");
207   }
208 
209   int len = builder.position();
210   builder.Finalize();
211   DCHECK_GT(len, 0);
212 
213   // Add '+' or ' ' sign if needed
214   char* p = buf + 1;
215   // anything that's neither negative nor nan
216   prefixLen = 0;
217   if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
218     *--p = plusSign;
219     ++len;
220     prefixLen = 1;
221   } else if (*p == '-') {
222     prefixLen = 1;
223   }
224 
225   piece = fbstring(p, size_t(len));
226 }
227 
initSlow()228 void FormatArg::initSlow() {
229   auto b = fullArgString.begin();
230   auto end = fullArgString.end();
231 
232   // Parse key
233   auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));
234   if (!p) {
235     key_ = StringPiece(b, end);
236     return;
237   }
238   key_ = StringPiece(b, p);
239 
240   if (*p == ':') {
241     // parse format spec
242     if (++p == end) {
243       return;
244     }
245 
246     // fill/align, or just align
247     Align a;
248     if (p + 1 != end &&
249         (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
250             Align::INVALID) {
251       fill = *p;
252       align = a;
253       p += 2;
254       if (p == end) {
255         return;
256       }
257     } else if (
258         (a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
259         Align::INVALID) {
260       align = a;
261       if (++p == end) {
262         return;
263       }
264     }
265 
266     Sign s;
267     unsigned char uSign = static_cast<unsigned char>(*p);
268     if ((s = formatSignTable[uSign]) != Sign::INVALID) {
269       sign = s;
270       if (++p == end) {
271         return;
272       }
273     }
274 
275     if (*p == '#') {
276       basePrefix = true;
277       if (++p == end) {
278         return;
279       }
280     }
281 
282     if (*p == '0') {
283       enforce(align == Align::DEFAULT, "alignment specified twice");
284       fill = '0';
285       align = Align::PAD_AFTER_SIGN;
286       if (++p == end) {
287         return;
288       }
289     }
290 
291     auto readInt = [&] {
292       auto const c = p;
293       do {
294         ++p;
295       } while (p != end && *p >= '0' && *p <= '9');
296       return to<int>(StringPiece(c, p));
297     };
298 
299     if (*p == '*') {
300       width = kDynamicWidth;
301       ++p;
302 
303       if (p == end) {
304         return;
305       }
306 
307       if (*p >= '0' && *p <= '9') {
308         widthIndex = readInt();
309       }
310 
311       if (p == end) {
312         return;
313       }
314     } else if (*p >= '0' && *p <= '9') {
315       width = readInt();
316 
317       if (p == end) {
318         return;
319       }
320     }
321 
322     if (*p == ',') {
323       thousandsSeparator = true;
324       if (++p == end) {
325         return;
326       }
327     }
328 
329     if (*p == '.') {
330       auto d = ++p;
331       while (p != end && *p >= '0' && *p <= '9') {
332         ++p;
333       }
334       if (p != d) {
335         precision = to<int>(StringPiece(d, p));
336         if (p != end && *p == '.') {
337           trailingDot = true;
338           ++p;
339         }
340       } else {
341         trailingDot = true;
342       }
343 
344       if (p == end) {
345         return;
346       }
347     }
348 
349     presentation = *p;
350     if (++p == end) {
351       return;
352     }
353   }
354 
355   error("extra characters in format string");
356 }
357 
validate(Type type) const358 void FormatArg::validate(Type type) const {
359   enforce(keyEmpty(), "index not allowed");
360   switch (type) {
361     case Type::INTEGER:
362       enforce(
363           precision == kDefaultPrecision, "precision not allowed on integers");
364       break;
365     case Type::FLOAT:
366       enforce(
367           !basePrefix, "base prefix ('#') specifier only allowed on integers");
368       enforce(
369           !thousandsSeparator,
370           "thousands separator (',') only allowed on integers");
371       break;
372     case Type::OTHER:
373       enforce(
374           align != Align::PAD_AFTER_SIGN,
375           "'='alignment only allowed on numbers");
376       enforce(sign == Sign::DEFAULT, "sign specifier only allowed on numbers");
377       enforce(
378           !basePrefix, "base prefix ('#') specifier only allowed on integers");
379       enforce(
380           !thousandsSeparator,
381           "thousands separator (',') only allowed on integers");
382       break;
383   }
384 }
385 
386 namespace detail {
insertThousandsGroupingUnsafe(char * start_buffer,char ** end_buffer)387 void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
388   uint32_t remaining_digits = uint32_t(*end_buffer - start_buffer);
389   uint32_t separator_size = (remaining_digits - 1) / 3;
390   uint32_t result_size = remaining_digits + separator_size;
391   *end_buffer = *end_buffer + separator_size;
392 
393   // get the end of the new string with the separators
394   uint32_t buffer_write_index = result_size - 1;
395   uint32_t buffer_read_index = remaining_digits - 1;
396   start_buffer[buffer_write_index + 1] = 0;
397 
398   bool done = false;
399   uint32_t next_group_size = 3;
400 
401   while (!done) {
402     uint32_t current_group_size = std::max<uint32_t>(
403         1, std::min<uint32_t>(remaining_digits, next_group_size));
404 
405     // write out the current group's digits to the buffer index
406     for (uint32_t i = 0; i < current_group_size; i++) {
407       start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
408     }
409 
410     // if not finished, write the separator before the next group
411     if (buffer_write_index < buffer_write_index + 1) {
412       start_buffer[buffer_write_index--] = ',';
413     } else {
414       done = true;
415     }
416 
417     remaining_digits -= current_group_size;
418   }
419 }
420 } // namespace detail
421 
FormatKeyNotFoundException(StringPiece key)422 FormatKeyNotFoundException::FormatKeyNotFoundException(StringPiece key)
423     : std::out_of_range(kMessagePrefix.str() + key.str()) {}
424 
425 constexpr StringPiece const FormatKeyNotFoundException::kMessagePrefix;
426 
427 } // namespace folly
428