proxygen
Format.cpp
Go to the documentation of this file.
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
30  static constexpr std::size_t size = 256;
31  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 :
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>
47  static_assert(Base <= 36, "Base is unrepresentable");
48  struct make_item {
49  std::size_t index{};
50  constexpr explicit make_item(std::size_t index_) : index(index_) {} // gcc49
51  constexpr char alpha(std::size_t ord) const {
52  return ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10);
53  }
54  constexpr char operator()(std::size_t offset) const {
55  return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base);
56  }
57  };
58  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
65  static constexpr std::size_t size = 256;
66  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 :
73  // clang-format on
74  }
75 };
76 
77 // the tables
79  make_array_with<256>(format_table_align_make_item{});
81  make_array_with<256>(format_table_sign_make_item{});
83  make_array_with<256>(format_table_conv_make_item<16, 2, false>{});
85  make_array_with<256>(format_table_conv_make_item<16, 2, true>{});
90 
91 } // namespace detail
92 
93 using namespace folly::detail;
94 
96  fbstring& piece,
97  int& prefixLen,
98  FormatArg& arg) const {
99  using ::double_conversion::DoubleToStringConverter;
100  using ::double_conversion::StringBuilder;
101 
103 
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 
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,
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) {
129  plusSign = '+';
130  break;
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;
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 
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 
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 {
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 
423  : std::out_of_range(kMessagePrefix.str() + key.str()) {}
424 
426 
427 } // namespace folly
flags
Definition: http_parser.h:127
FOLLY_STORAGE_CONSTEXPR auto formatAlignTable
Definition: Format.cpp:78
char b
const std::array< std::array< char, 3 >, 512 > formatOctal
Definition: Format.cpp:86
PskType type
FOLLY_STORAGE_CONSTEXPR auto formatSignTable
Definition: Format.cpp:80
STL namespace.
double val
Definition: String.cpp:273
constexpr FormatArg::Sign operator()(std::size_t index) const
Definition: Format.cpp:66
void initSlow()
Definition: Format.cpp:228
void enforce(bool v, Args &&...args) const
Definition: FormatArg.h:76
static constexpr int kDefaultPrecision
Definition: FormatArg.h:150
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
constexpr FormatArg::Align operator()(std::size_t index) const
Definition: Format.cpp:31
const std::array< std::array< char, 8 >, 256 > formatBinary
Definition: Format.cpp:88
constexpr T constexpr_max(T a)
Definition: ConstexprMath.h:68
static constexpr char kDefaultPresentation
Definition: FormatArg.h:156
constexpr char alpha(std::size_t ord) const
Definition: Format.cpp:51
constexpr T constexpr_pow(T base, std::size_t exp)
static constexpr std::size_t size
Definition: Format.cpp:30
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
const std::array< std::array< char, 2 >, 256 > formatHexUpper
Definition: Format.cpp:84
#define FOLLY_STORAGE_CONSTEXPR
Definition: Portability.h:445
AtomicCounter< T, DeterministicAtomic > Base
char a
const std::array< std::array< char, 2 >, 256 > formatHexLower
Definition: Format.cpp:82
void validate(Type type) const
Definition: Format.cpp:358
void insertThousandsGroupingUnsafe(char *start_buffer, char **end_buffer)
Definition: Format.cpp:387
void error(Args &&...args) const
Definition: FormatArg.h:217
static set< string > s
constexpr auto make_array_with(MakeItem const &make)
Definition: Array.h:75
constexpr std::array< char, Size > operator()(std::size_t index) const
Definition: Format.cpp:58
basic_fbstring< char > fbstring
Definition: FBString.h:2904
constexpr char operator()(std::size_t offset) const
Definition: Format.cpp:54
Range< const char * > StringPiece
char c
#define FOLLY_FALLTHROUGH
Definition: CppAttributes.h:63
FormatKeyNotFoundException(StringPiece key)
Definition: Format.cpp:422
static constexpr StringPiece const kMessagePrefix
Definition: Format.h:331