X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat.cpp;h=db95ea24088f34aa4c57234cf37b90a76854253e;hb=5cef863eee8a03e1b6b137fb283d6fe703f35d2d;hp=53a218622d812a7bb6fe63b2baf5e99d0e5b4682;hpb=ce64f0f685111ac24c7a321ea56d0c3524621df1;p=folly.git diff --git a/folly/Format.cpp b/folly/Format.cpp index 53a21862..db95ea24 100644 --- a/folly/Format.cpp +++ b/folly/Format.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -16,6 +16,10 @@ #include +#include + +#include + namespace folly { namespace detail { @@ -26,12 +30,145 @@ extern const FormatArg::Sign formatSignTable[]; using namespace folly::detail; +void FormatValue::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; + 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(memchr(b, ':', end - b)); + auto p = static_cast(memchr(b, ':', size_t(end - b))); if (!p) { key_ = StringPiece(b, end); return; @@ -76,12 +213,25 @@ void FormatArg::initSlow() { 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(StringPiece(b, p)); + return to(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; } @@ -92,11 +242,19 @@ void FormatArg::initSlow() { } if (*p == '.') { - auto b = ++p; + auto d = ++p; while (p != end && *p >= '0' && *p <= '9') { ++p; } - precision = to(StringPiece(b, p)); + if (p != d) { + precision = to(StringPiece(d, p)); + if (p != end && *p == '.') { + trailingDot = true; + ++p; + } + } else { + trailingDot = true; + } if (p == end) return; } @@ -134,4 +292,40 @@ void FormatArg::validate(Type type) const { } } +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(1, + std::min(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; + } +} +} // detail + } // namespace folly