2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/Format.h>
19 #include <folly/ConstexprMath.h>
20 #include <folly/CppAttributes.h>
22 #include <double-conversion/double-conversion.h>
27 extern const FormatArg::Align formatAlignTable[];
28 extern const FormatArg::Sign formatSignTable[];
32 using namespace folly::detail;
34 void FormatValue<double>::formatHelper(
35 fbstring& piece, int& prefixLen, FormatArg& arg) const {
36 using ::double_conversion::DoubleToStringConverter;
37 using ::double_conversion::StringBuilder;
39 arg.validate(FormatArg::Type::FLOAT);
41 if (arg.presentation == FormatArg::kDefaultPresentation) {
42 arg.presentation = 'g';
45 const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
46 const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
47 char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
49 if (arg.precision == FormatArg::kDefaultPrecision) {
53 // 2+: for null terminator and optional sign shenanigans.
54 constexpr int bufLen =
56 2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
57 DoubleToStringConverter::kMaxFixedDigitsAfterPoint,
58 constexpr_max(8 + DoubleToStringConverter::kMaxExponentialDigits,
59 7 + DoubleToStringConverter::kMaxPrecisionDigits));
61 StringBuilder builder(buf + 1, bufLen - 1);
65 case FormatArg::Sign::PLUS_OR_MINUS:
68 case FormatArg::Sign::SPACE_OR_MINUS:
77 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
78 (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
82 switch (arg.presentation) {
90 DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
91 arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
93 DoubleToStringConverter conv(flags,
101 arg.enforce(conv.ToFixed(val, arg.precision, &builder),
102 "fixed double conversion failed");
108 if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
109 arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
112 DoubleToStringConverter conv(flags,
120 arg.enforce(conv.ToExponential(val, arg.precision, &builder));
123 case 'n': // should be locale-aware, but isn't
127 if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
128 arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
129 } else if (arg.precision >
130 DoubleToStringConverter::kMaxPrecisionDigits) {
131 arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
133 DoubleToStringConverter conv(flags,
141 arg.enforce(conv.ToShortest(val, &builder));
145 arg.error("invalid specifier '", arg.presentation, "'");
148 int len = builder.position();
152 // Add '+' or ' ' sign if needed
154 // anything that's neither negative nor nan
156 if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
160 } else if (*p == '-') {
164 piece = fbstring(p, size_t(len));
168 void FormatArg::initSlow() {
169 auto b = fullArgString.begin();
170 auto end = fullArgString.end();
173 auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));
175 key_ = StringPiece(b, end);
178 key_ = StringPiece(b, p);
182 if (++p == end) return;
184 // fill/align, or just align
187 (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
192 if (p == end) return;
193 } else if ((a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
196 if (++p == end) return;
200 unsigned char uSign = static_cast<unsigned char>(*p);
201 if ((s = formatSignTable[uSign]) != Sign::INVALID) {
203 if (++p == end) return;
208 if (++p == end) return;
212 enforce(align == Align::DEFAULT, "alignment specified twice");
214 align = Align::PAD_AFTER_SIGN;
215 if (++p == end) return;
222 } while (p != end && *p >= '0' && *p <= '9');
223 return to<int>(StringPiece(c, p));
227 width = kDynamicWidth;
230 if (p == end) return;
232 if (*p >= '0' && *p <= '9') widthIndex = readInt();
234 if (p == end) return;
235 } else if (*p >= '0' && *p <= '9') {
238 if (p == end) return;
242 thousandsSeparator = true;
243 if (++p == end) return;
248 while (p != end && *p >= '0' && *p <= '9') {
252 precision = to<int>(StringPiece(d, p));
253 if (p != end && *p == '.') {
261 if (p == end) return;
265 if (++p == end) return;
268 error("extra characters in format string");
271 void FormatArg::validate(Type type) const {
272 enforce(keyEmpty(), "index not allowed");
275 enforce(precision == kDefaultPrecision,
276 "precision not allowed on integers");
280 "base prefix ('#') specifier only allowed on integers");
281 enforce(!thousandsSeparator,
282 "thousands separator (',') only allowed on integers");
285 enforce(align != Align::PAD_AFTER_SIGN,
286 "'='alignment only allowed on numbers");
287 enforce(sign == Sign::DEFAULT,
288 "sign specifier only allowed on numbers");
290 "base prefix ('#') specifier only allowed on integers");
291 enforce(!thousandsSeparator,
292 "thousands separator (',') only allowed on integers");
298 void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
299 uint32_t remaining_digits = uint32_t(*end_buffer - start_buffer);
300 uint32_t separator_size = (remaining_digits - 1) / 3;
301 uint32_t result_size = remaining_digits + separator_size;
302 *end_buffer = *end_buffer + separator_size;
304 // get the end of the new string with the separators
305 uint32_t buffer_write_index = result_size - 1;
306 uint32_t buffer_read_index = remaining_digits - 1;
307 start_buffer[buffer_write_index + 1] = 0;
310 uint32_t next_group_size = 3;
313 uint32_t current_group_size = std::max<uint32_t>(1,
314 std::min<uint32_t>(remaining_digits, next_group_size));
316 // write out the current group's digits to the buffer index
317 for (uint32_t i = 0; i < current_group_size; i++) {
318 start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
321 // if not finished, write the separator before the next group
322 if (buffer_write_index < buffer_write_index + 1) {
323 start_buffer[buffer_write_index--] = ',';
328 remaining_digits -= current_group_size;
331 } // namespace detail