X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat.cpp;h=9a711e4601e8a47c6872580f7733f0f1100d9c8d;hb=ea815395588bf55ce57f505e05f5df469edf3436;hp=6bba0aade6aaf7b2ac81db8062804e443c9b9b55;hpb=74651ade10a241046ff85206acf77010edffc039;p=folly.git diff --git a/folly/Format.cpp b/folly/Format.cpp index 6bba0aad..9a711e46 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,22 +16,161 @@ #include +#include +#include + +#include + namespace folly { namespace detail { extern const FormatArg::Align formatAlignTable[]; extern const FormatArg::Sign formatSignTable[]; -} // namespace detail +} // namespace detail 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; + 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(memchr(b, ':', end - b)); + auto p = static_cast(memchr(b, ':', size_t(end - b))); if (!p) { key_ = StringPiece(b, end); return; @@ -76,12 +215,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,12 +244,12 @@ void FormatArg::initSlow() { } if (*p == '.') { - auto b = ++p; + auto d = ++p; while (p != end && *p >= '0' && *p <= '9') { ++p; } - if (p != b) { - precision = to(StringPiece(b, p)); + if (p != d) { + precision = to(StringPiece(d, p)); if (p != end && *p == '.') { trailingDot = true; ++p; @@ -144,7 +296,7 @@ void FormatArg::validate(Type type) const { namespace detail { void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) { - uint32_t remaining_digits = *end_buffer - start_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; @@ -154,7 +306,6 @@ void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) { uint32_t buffer_read_index = remaining_digits - 1; start_buffer[buffer_write_index + 1] = 0; - uint32_t count = 0; bool done = false; uint32_t next_group_size = 3; @@ -177,6 +328,6 @@ void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) { remaining_digits -= current_group_size; } } -} // detail +} // namespace detail -} // namespace folly +} // namespace folly