/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/Format.h>
+#include <folly/CppAttributes.h>
+#include <folly/portability/Constexpr.h>
+
+#include <double-conversion/double-conversion.h>
+
namespace folly {
namespace detail {
extern const FormatArg::Align formatAlignTable[];
extern const FormatArg::Sign formatSignTable[];
-} // namespace detail
+} // namespace detail
using namespace folly::detail;
+void FormatValue<double>::formatHelper(
+ fbstring& piece, int& prefixLen, FormatArg& arg) const {
+ using ::double_conversion::DoubleToStringConverter;
+ using ::double_conversion::StringBuilder;
+
+ arg.validate(FormatArg::Type::FLOAT);
+
+ if (arg.presentation == FormatArg::kDefaultPresentation) {
+ arg.presentation = 'g';
+ }
+
+ const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
+ const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
+ char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
+
+ if (arg.precision == FormatArg::kDefaultPrecision) {
+ arg.precision = 6;
+ }
+
+ // 2+: for null terminator and optional sign shenanigans.
+ constexpr int bufLen =
+ 2 + constexpr_max(
+ 2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
+ DoubleToStringConverter::kMaxFixedDigitsAfterPoint,
+ constexpr_max(8 + DoubleToStringConverter::kMaxExponentialDigits,
+ 7 + DoubleToStringConverter::kMaxPrecisionDigits));
+ char buf[bufLen];
+ StringBuilder builder(buf + 1, bufLen - 1);
+
+ char plusSign;
+ switch (arg.sign) {
+ case FormatArg::Sign::PLUS_OR_MINUS:
+ plusSign = '+';
+ break;
+ case FormatArg::Sign::SPACE_OR_MINUS:
+ plusSign = ' ';
+ break;
+ default:
+ plusSign = '\0';
+ break;
+ };
+
+ auto flags =
+ DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
+ (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
+ : 0);
+
+ double val = val_;
+ switch (arg.presentation) {
+ case '%':
+ val *= 100;
+ FOLLY_FALLTHROUGH;
+ case 'f':
+ case 'F':
+ {
+ if (arg.precision >
+ DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
+ arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
+ }
+ DoubleToStringConverter conv(flags,
+ infinitySymbol,
+ nanSymbol,
+ exponentSymbol,
+ -4,
+ arg.precision,
+ 0,
+ 0);
+ arg.enforce(conv.ToFixed(val, arg.precision, &builder),
+ "fixed double conversion failed");
+ }
+ break;
+ case 'e':
+ case 'E':
+ {
+ if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
+ arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
+ }
+
+ DoubleToStringConverter conv(flags,
+ infinitySymbol,
+ nanSymbol,
+ exponentSymbol,
+ -4,
+ arg.precision,
+ 0,
+ 0);
+ arg.enforce(conv.ToExponential(val, arg.precision, &builder));
+ }
+ break;
+ case 'n': // should be locale-aware, but isn't
+ case 'g':
+ case 'G':
+ {
+ if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
+ arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
+ } else if (arg.precision >
+ DoubleToStringConverter::kMaxPrecisionDigits) {
+ arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
+ }
+ DoubleToStringConverter conv(flags,
+ infinitySymbol,
+ nanSymbol,
+ exponentSymbol,
+ -4,
+ arg.precision,
+ 0,
+ 0);
+ arg.enforce(conv.ToShortest(val, &builder));
+ }
+ break;
+ default:
+ arg.error("invalid specifier '", arg.presentation, "'");
+ }
+
+ int len = builder.position();
+ builder.Finalize();
+ DCHECK_GT(len, 0);
+
+ // Add '+' or ' ' sign if needed
+ char* p = buf + 1;
+ // anything that's neither negative nor nan
+ prefixLen = 0;
+ if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
+ *--p = plusSign;
+ ++len;
+ prefixLen = 1;
+ } else if (*p == '-') {
+ prefixLen = 1;
+ }
+
+ piece = fbstring(p, size_t(len));
+}
+
+
void FormatArg::initSlow() {
auto b = fullArgString.begin();
auto end = fullArgString.end();
// Parse key
- auto p = static_cast<const char*>(memchr(b, ':', end - b));
+ auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));
if (!p) {
key_ = StringPiece(b, end);
return;
if (++p == end) return;
}
- if (*p >= '0' && *p <= '9') {
- auto b = p;
+ auto readInt = [&] {
+ auto const c = p;
do {
++p;
} while (p != end && *p >= '0' && *p <= '9');
- width = to<int>(StringPiece(b, p));
+ return to<int>(StringPiece(c, p));
+ };
+
+ if (*p == '*') {
+ width = kDynamicWidth;
+ ++p;
+
+ if (p == end) return;
+
+ if (*p >= '0' && *p <= '9') widthIndex = readInt();
+
+ if (p == end) return;
+ } else if (*p >= '0' && *p <= '9') {
+ width = readInt();
if (p == end) return;
}
}
if (*p == '.') {
- auto b = ++p;
+ auto d = ++p;
while (p != end && *p >= '0' && *p <= '9') {
++p;
}
- precision = to<int>(StringPiece(b, p));
+ if (p != d) {
+ precision = to<int>(StringPiece(d, p));
+ if (p != end && *p == '.') {
+ trailingDot = true;
+ ++p;
+ }
+ } else {
+ trailingDot = true;
+ }
if (p == end) return;
}
}
}
-} // namespace folly
+namespace detail {
+void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
+ uint32_t remaining_digits = uint32_t(*end_buffer - start_buffer);
+ uint32_t separator_size = (remaining_digits - 1) / 3;
+ uint32_t result_size = remaining_digits + separator_size;
+ *end_buffer = *end_buffer + separator_size;
+
+ // get the end of the new string with the separators
+ uint32_t buffer_write_index = result_size - 1;
+ uint32_t buffer_read_index = remaining_digits - 1;
+ start_buffer[buffer_write_index + 1] = 0;
+
+ bool done = false;
+ uint32_t next_group_size = 3;
+
+ while (!done) {
+ uint32_t current_group_size = std::max<uint32_t>(1,
+ std::min<uint32_t>(remaining_digits, next_group_size));
+
+ // write out the current group's digits to the buffer index
+ for (uint32_t i = 0; i < current_group_size; i++) {
+ start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
+ }
+
+ // if not finished, write the separator before the next group
+ if (buffer_write_index < buffer_write_index + 1) {
+ start_buffer[buffer_write_index--] = ',';
+ } else {
+ done = true;
+ }
+
+ remaining_digits -= current_group_size;
+ }
+}
+} // namespace detail
+
+} // namespace folly