diff options
author | Vincent Ambo <mail@tazj.in> | 2022-02-07T23·05+0300 |
---|---|---|
committer | clbot <clbot@tvl.fyi> | 2022-02-07T23·09+0000 |
commit | 5aa5d282eac56a21e74611c1cdbaa97bb5db2dca (patch) | |
tree | 8cc5dce8157a1470ff76719dd15d65f648a05522 /third_party/abseil_cpp/absl/strings/internal/str_format | |
parent | a25675804c4f429fab5ee5201fe25e89865dfd13 (diff) |
chore(3p/abseil_cpp): unvendor abseil_cpp r/3786
we weren't actually using these sources anymore, okay? Change-Id: If701571d9716de308d3512e1eb22c35db0877a66 Reviewed-on: https://cl.tvl.fyi/c/depot/+/5248 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi> Autosubmit: tazjin <tazjin@tvl.su>
Diffstat (limited to 'third_party/abseil_cpp/absl/strings/internal/str_format')
20 files changed, 0 insertions, 6943 deletions
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc deleted file mode 100644 index e28a29b17169..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// -// POSIX spec: -// http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html -// -#include "absl/strings/internal/str_format/arg.h" - -#include <cassert> -#include <cerrno> -#include <cstdlib> -#include <string> -#include <type_traits> - -#include "absl/base/port.h" -#include "absl/strings/internal/str_format/float_conversion.h" -#include "absl/strings/numbers.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { -namespace { - -// Reduce *capacity by s.size(), clipped to a 0 minimum. -void ReducePadding(string_view s, size_t *capacity) { - *capacity = Excess(s.size(), *capacity); -} - -// Reduce *capacity by n, clipped to a 0 minimum. -void ReducePadding(size_t n, size_t *capacity) { - *capacity = Excess(n, *capacity); -} - -template <typename T> -struct MakeUnsigned : std::make_unsigned<T> {}; -template <> -struct MakeUnsigned<absl::int128> { - using type = absl::uint128; -}; -template <> -struct MakeUnsigned<absl::uint128> { - using type = absl::uint128; -}; - -template <typename T> -struct IsSigned : std::is_signed<T> {}; -template <> -struct IsSigned<absl::int128> : std::true_type {}; -template <> -struct IsSigned<absl::uint128> : std::false_type {}; - -// Integral digit printer. -// Call one of the PrintAs* routines after construction once. -// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. -class IntDigits { - public: - // Print the unsigned integer as octal. - // Supports unsigned integral types and uint128. - template <typename T> - void PrintAsOct(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - do { - *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); - v >>= 3; - } while (v); - start_ = p; - size_ = storage_ + sizeof(storage_) - p; - } - - // Print the signed or unsigned integer as decimal. - // Supports all integral types. - template <typename T> - void PrintAsDec(T v) { - static_assert(std::is_integral<T>::value, ""); - start_ = storage_; - size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; - } - - void PrintAsDec(int128 v) { - auto u = static_cast<uint128>(v); - bool add_neg = false; - if (v < 0) { - add_neg = true; - u = uint128{} - u; - } - PrintAsDec(u, add_neg); - } - - void PrintAsDec(uint128 v, bool add_neg = false) { - // This function can be sped up if needed. We can call FastIntToBuffer - // twice, or fix FastIntToBuffer to support uint128. - char *p = storage_ + sizeof(storage_); - do { - p -= 2; - numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p); - v /= 100; - } while (v); - if (p[0] == '0') { - // We printed one too many hexits. - ++p; - } - if (add_neg) { - *--p = '-'; - } - size_ = storage_ + sizeof(storage_) - p; - start_ = p; - } - - // Print the unsigned integer as hex using lowercase. - // Supports unsigned integral types and uint128. - template <typename T> - void PrintAsHexLower(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - - do { - p -= 2; - constexpr const char* table = numbers_internal::kHexTable; - std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); - if (sizeof(T) == 1) break; - v >>= 8; - } while (v); - if (p[0] == '0') { - // We printed one too many digits. - ++p; - } - start_ = p; - size_ = storage_ + sizeof(storage_) - p; - } - - // Print the unsigned integer as hex using uppercase. - // Supports unsigned integral types and uint128. - template <typename T> - void PrintAsHexUpper(T v) { - static_assert(!IsSigned<T>::value, ""); - char *p = storage_ + sizeof(storage_); - - // kHexTable is only lowercase, so do it manually for uppercase. - do { - *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; - v >>= 4; - } while (v); - start_ = p; - size_ = storage_ + sizeof(storage_) - p; - } - - // The printed value including the '-' sign if available. - // For inputs of value `0`, this will return "0" - string_view with_neg_and_zero() const { return {start_, size_}; } - - // The printed value not including the '-' sign. - // For inputs of value `0`, this will return "". - string_view without_neg_or_zero() const { - static_assert('-' < '0', "The check below verifies both."); - size_t advance = start_[0] <= '0' ? 1 : 0; - return {start_ + advance, size_ - advance}; - } - - bool is_negative() const { return start_[0] == '-'; } - - private: - const char *start_; - size_t size_; - // Max size: 128 bit value as octal -> 43 digits, plus sign char - char storage_[128 / 3 + 1 + 1]; -}; - -// Note: 'o' conversions do not have a base indicator, it's just that -// the '#' flag is specified to modify the precision for 'o' conversions. -string_view BaseIndicator(const IntDigits &as_digits, - const FormatConversionSpecImpl conv) { - // always show 0x for %p. - bool alt = conv.has_alt_flag() || - conv.conversion_char() == FormatConversionCharInternal::p; - bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || - conv.conversion_char() == FormatConversionCharInternal::X || - conv.conversion_char() == FormatConversionCharInternal::p); - // From the POSIX description of '#' flag: - // "For x or X conversion specifiers, a non-zero result shall have - // 0x (or 0X) prefixed to it." - if (alt && hex && !as_digits.without_neg_or_zero().empty()) { - return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" - : "0x"; - } - return {}; -} - -string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { - if (conv.conversion_char() == FormatConversionCharInternal::d || - conv.conversion_char() == FormatConversionCharInternal::i) { - if (neg) return "-"; - if (conv.has_show_pos_flag()) return "+"; - if (conv.has_sign_col_flag()) return " "; - } - return {}; -} - -bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - size_t fill = 0; - if (conv.width() >= 0) fill = conv.width(); - ReducePadding(1, &fill); - if (!conv.has_left_flag()) sink->Append(fill, ' '); - sink->Append(1, v); - if (conv.has_left_flag()) sink->Append(fill, ' '); - return true; -} - -bool ConvertIntImplInnerSlow(const IntDigits &as_digits, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - // Print as a sequence of Substrings: - // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] - size_t fill = 0; - if (conv.width() >= 0) fill = conv.width(); - - string_view formatted = as_digits.without_neg_or_zero(); - ReducePadding(formatted, &fill); - - string_view sign = SignColumn(as_digits.is_negative(), conv); - ReducePadding(sign, &fill); - - string_view base_indicator = BaseIndicator(as_digits, conv); - ReducePadding(base_indicator, &fill); - - int precision = conv.precision(); - bool precision_specified = precision >= 0; - if (!precision_specified) - precision = 1; - - if (conv.has_alt_flag() && - conv.conversion_char() == FormatConversionCharInternal::o) { - // From POSIX description of the '#' (alt) flag: - // "For o conversion, it increases the precision (if necessary) to - // force the first digit of the result to be zero." - if (formatted.empty() || *formatted.begin() != '0') { - int needed = static_cast<int>(formatted.size()) + 1; - precision = std::max(precision, needed); - } - } - - size_t num_zeroes = Excess(formatted.size(), precision); - ReducePadding(num_zeroes, &fill); - - size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; - size_t num_right_spaces = conv.has_left_flag() ? fill : 0; - - // From POSIX description of the '0' (zero) flag: - // "For d, i, o, u, x, and X conversion specifiers, if a precision - // is specified, the '0' flag is ignored." - if (!precision_specified && conv.has_zero_flag()) { - num_zeroes += num_left_spaces; - num_left_spaces = 0; - } - - sink->Append(num_left_spaces, ' '); - sink->Append(sign); - sink->Append(base_indicator); - sink->Append(num_zeroes, '0'); - sink->Append(formatted); - sink->Append(num_right_spaces, ' '); - return true; -} - -template <typename T> -bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - using U = typename MakeUnsigned<T>::type; - IntDigits as_digits; - - // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes - // it to complain about a switch/case type mismatch, even though both are - // FormatConverionChar. Likely this is because at this point - // FormatConversionChar is declared, but not defined. - switch (static_cast<uint8_t>(conv.conversion_char())) { - case static_cast<uint8_t>(FormatConversionCharInternal::c): - return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); - - case static_cast<uint8_t>(FormatConversionCharInternal::o): - as_digits.PrintAsOct(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::x): - as_digits.PrintAsHexLower(static_cast<U>(v)); - break; - case static_cast<uint8_t>(FormatConversionCharInternal::X): - as_digits.PrintAsHexUpper(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::u): - as_digits.PrintAsDec(static_cast<U>(v)); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::d): - case static_cast<uint8_t>(FormatConversionCharInternal::i): - as_digits.PrintAsDec(v); - break; - - case static_cast<uint8_t>(FormatConversionCharInternal::a): - case static_cast<uint8_t>(FormatConversionCharInternal::e): - case static_cast<uint8_t>(FormatConversionCharInternal::f): - case static_cast<uint8_t>(FormatConversionCharInternal::g): - case static_cast<uint8_t>(FormatConversionCharInternal::A): - case static_cast<uint8_t>(FormatConversionCharInternal::E): - case static_cast<uint8_t>(FormatConversionCharInternal::F): - case static_cast<uint8_t>(FormatConversionCharInternal::G): - return ConvertFloatImpl(static_cast<double>(v), conv, sink); - - default: - ABSL_INTERNAL_ASSUME(false); - } - - if (conv.is_basic()) { - sink->Append(as_digits.with_neg_and_zero()); - return true; - } - return ConvertIntImplInnerSlow(as_digits, conv, sink); -} - -template <typename T> -bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return FormatConversionCharIsFloat(conv.conversion_char()) && - ConvertFloatImpl(v, conv, sink); -} - -inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - if (conv.is_basic()) { - sink->Append(v); - return true; - } - return sink->PutPaddedString(v, conv.width(), conv.precision(), - conv.has_left_flag()); -} - -} // namespace - -// ==================== Strings ==================== -StringConvertResult FormatConvertImpl(const std::string &v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertStringArg(v, conv, sink)}; -} - -StringConvertResult FormatConvertImpl(string_view v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertStringArg(v, conv, sink)}; -} - -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - if (conv.conversion_char() == FormatConversionCharInternal::p) - return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; - size_t len; - if (v == nullptr) { - len = 0; - } else if (conv.precision() < 0) { - len = std::strlen(v); - } else { - // If precision is set, we look for the NUL-terminator on the valid range. - len = std::find(v, v + conv.precision(), '\0') - v; - } - return {ConvertStringArg(string_view(v, len), conv, sink)}; -} - -// ==================== Raw pointers ==================== -ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( - VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { - if (!v.value) { - sink->Append("(nil)"); - return {true}; - } - IntDigits as_digits; - as_digits.PrintAsHexLower(v.value); - return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; -} - -// ==================== Floats ==================== -FloatingConvertResult FormatConvertImpl(float v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertFloatArg(v, conv, sink)}; -} -FloatingConvertResult FormatConvertImpl(double v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertFloatArg(v, conv, sink)}; -} -FloatingConvertResult FormatConvertImpl(long double v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertFloatArg(v, conv, sink)}; -} - -// ==================== Chars ==================== -IntegralConvertResult FormatConvertImpl(char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(signed char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(unsigned char v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} - -// ==================== Ints ==================== -IntegralConvertResult FormatConvertImpl(short v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(int v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(unsigned v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(long v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(absl::int128 v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} -IntegralConvertResult FormatConvertImpl(absl::uint128 v, - const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { - return {ConvertIntArg(v, conv, sink)}; -} - -ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); - - - -} // namespace str_format_internal - -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h b/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h deleted file mode 100644 index 7040c866778e..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ - -#include <string.h> -#include <wchar.h> - -#include <cstdio> -#include <iomanip> -#include <limits> -#include <memory> -#include <sstream> -#include <string> -#include <type_traits> - -#include "absl/base/port.h" -#include "absl/meta/type_traits.h" -#include "absl/numeric/int128.h" -#include "absl/strings/internal/str_format/extension.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -class Cord; -class FormatCountCapture; -class FormatSink; - -template <absl::FormatConversionCharSet C> -struct FormatConvertResult; -class FormatConversionSpec; - -namespace str_format_internal { - -template <typename T, typename = void> -struct HasUserDefinedConvert : std::false_type {}; - -template <typename T> -struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( - std::declval<const T&>(), - std::declval<const FormatConversionSpec&>(), - std::declval<FormatSink*>()))>> - : std::true_type {}; - -void AbslFormatConvert(); // Stops the lexical name lookup -template <typename T> -auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) - -> decltype(AbslFormatConvert(v, - std::declval<const FormatConversionSpec&>(), - std::declval<FormatSink*>())) { - using FormatConversionSpecT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; - using FormatSinkT = - absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; - auto fcs = conv.Wrap<FormatConversionSpecT>(); - auto fs = sink->Wrap<FormatSinkT>(); - return AbslFormatConvert(v, fcs, &fs); -} - -template <typename T> -class StreamedWrapper; - -// If 'v' can be converted (in the printf sense) according to 'conv', -// then convert it, appending to `sink` and return `true`. -// Otherwise fail and return `false`. - -// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' -// as an extension mechanism. These FormatConvertImpl functions are the default -// implementations. -// The ADL search is augmented via the 'Sink*' parameter, which also -// serves as a disambiguator to reject possible unintended 'AbslFormatConvert' -// functions in the namespaces associated with 'v'. - -// Raw pointers. -struct VoidPtr { - VoidPtr() = default; - template <typename T, - decltype(reinterpret_cast<uintptr_t>(std::declval<T*>())) = 0> - VoidPtr(T* ptr) // NOLINT - : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {} - uintptr_t value; -}; - -template <FormatConversionCharSet C> -struct ArgConvertResult { - bool value; -}; - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { - return C; -} - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { - return C; -} - -using StringConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::s>; -ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( - VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); - -// Strings. -StringConvertResult FormatConvertImpl(const std::string& v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -StringConvertResult FormatConvertImpl(string_view v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, - FormatSinkImpl* sink); - -template <class AbslCord, typename std::enable_if<std::is_same< - AbslCord, absl::Cord>::value>::type* = nullptr> -StringConvertResult FormatConvertImpl(const AbslCord& value, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { - bool is_left = conv.has_left_flag(); - size_t space_remaining = 0; - - int width = conv.width(); - if (width >= 0) space_remaining = width; - - size_t to_write = value.size(); - - int precision = conv.precision(); - if (precision >= 0) - to_write = (std::min)(to_write, static_cast<size_t>(precision)); - - space_remaining = Excess(to_write, space_remaining); - - if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' '); - - for (string_view piece : value.Chunks()) { - if (piece.size() > to_write) { - piece.remove_suffix(piece.size() - to_write); - to_write = 0; - } else { - to_write -= piece.size(); - } - sink->Append(piece); - if (to_write == 0) { - break; - } - } - - if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' '); - return {true}; -} - -using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::c, - FormatConversionCharSetInternal::kNumeric, - FormatConversionCharSetInternal::kStar)>; -using FloatingConvertResult = - ArgConvertResult<FormatConversionCharSetInternal::kFloating>; - -// Floats. -FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(long double v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); - -// Chars. -IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(signed char v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned char v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); - -// Ints. -IntegralConvertResult FormatConvertImpl(short v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(long v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(uint128 v, - FormatConversionSpecImpl conv, - FormatSinkImpl* sink); -template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> -IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { - return FormatConvertImpl(static_cast<int>(v), conv, sink); -} - -// We provide this function to help the checker, but it is never defined. -// FormatArgImpl will use the underlying Convert functions instead. -template <typename T> -typename std::enable_if<std::is_enum<T>::value && - !HasUserDefinedConvert<T>::value, - IntegralConvertResult>::type -FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); - -template <typename T> -StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, - FormatConversionSpecImpl conv, - FormatSinkImpl* out) { - std::ostringstream oss; - oss << v.v_; - if (!oss) return {false}; - return str_format_internal::FormatConvertImpl(oss.str(), conv, out); -} - -// Use templates and dependent types to delay evaluation of the function -// until after FormatCountCapture is fully defined. -struct FormatCountCaptureHelper { - template <class T = int> - static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( - const FormatCountCapture& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { - const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - - if (conv.conversion_char() != - str_format_internal::FormatConversionCharInternal::n) { - return {false}; - } - *v2.p_ = static_cast<int>(sink->size()); - return {true}; - } -}; - -template <class T = int> -ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( - const FormatCountCapture& v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink) { - return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); -} - -// Helper friend struct to hide implementation details from the public API of -// FormatArgImpl. -struct FormatArgImplFriend { - template <typename Arg> - static bool ToInt(Arg arg, int* out) { - // A value initialized FormatConversionSpecImpl has a `none` conv, which - // tells the dispatcher to run the `int` conversion. - return arg.dispatcher_(arg.data_, {}, out); - } - - template <typename Arg> - static bool Convert(Arg arg, FormatConversionSpecImpl conv, - FormatSinkImpl* out) { - return arg.dispatcher_(arg.data_, conv, out); - } - - template <typename Arg> - static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) { - return arg.dispatcher_; - } -}; - -template <typename Arg> -constexpr FormatConversionCharSet ArgumentToConv() { - return absl::str_format_internal::ExtractCharSet( - decltype(str_format_internal::FormatConvertImpl( - std::declval<const Arg&>(), - std::declval<const FormatConversionSpecImpl&>(), - std::declval<FormatSinkImpl*>())){}); -} - -// A type-erased handle to a format argument. -class FormatArgImpl { - private: - enum { kInlinedSpace = 8 }; - - using VoidPtr = str_format_internal::VoidPtr; - - union Data { - const void* ptr; - const volatile void* volatile_ptr; - char buf[kInlinedSpace]; - }; - - using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); - - template <typename T> - struct store_by_value - : std::integral_constant<bool, (sizeof(T) <= kInlinedSpace) && - (std::is_integral<T>::value || - std::is_floating_point<T>::value || - std::is_pointer<T>::value || - std::is_same<VoidPtr, T>::value)> {}; - - enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue }; - template <typename T> - struct storage_policy - : std::integral_constant<StoragePolicy, - (std::is_volatile<T>::value - ? ByVolatilePointer - : (store_by_value<T>::value ? ByValue - : ByPointer))> { - }; - - // To reduce the number of vtables we will decay values before hand. - // Anything with a user-defined Convert will get its own vtable. - // For everything else: - // - Decay char* and char arrays into `const char*` - // - Decay any other pointer to `const void*` - // - Decay all enums to their underlying type. - // - Decay function pointers to void*. - template <typename T, typename = void> - struct DecayType { - static constexpr bool kHasUserDefined = - str_format_internal::HasUserDefinedConvert<T>::value; - using type = typename std::conditional< - !kHasUserDefined && std::is_convertible<T, const char*>::value, - const char*, - typename std::conditional<!kHasUserDefined && - std::is_convertible<T, VoidPtr>::value, - VoidPtr, const T&>::type>::type; - }; - template <typename T> - struct DecayType<T, - typename std::enable_if< - !str_format_internal::HasUserDefinedConvert<T>::value && - std::is_enum<T>::value>::type> { - using type = typename std::underlying_type<T>::type; - }; - - public: - template <typename T> - explicit FormatArgImpl(const T& value) { - using D = typename DecayType<T>::type; - static_assert( - std::is_same<D, const T&>::value || storage_policy<D>::value == ByValue, - "Decayed types must be stored by value"); - Init(static_cast<D>(value)); - } - - private: - friend struct str_format_internal::FormatArgImplFriend; - template <typename T, StoragePolicy = storage_policy<T>::value> - struct Manager; - - template <typename T> - struct Manager<T, ByPointer> { - static Data SetValue(const T& value) { - Data data; - data.ptr = std::addressof(value); - return data; - } - - static const T& Value(Data arg) { return *static_cast<const T*>(arg.ptr); } - }; - - template <typename T> - struct Manager<T, ByVolatilePointer> { - static Data SetValue(const T& value) { - Data data; - data.volatile_ptr = &value; - return data; - } - - static const T& Value(Data arg) { - return *static_cast<const T*>(arg.volatile_ptr); - } - }; - - template <typename T> - struct Manager<T, ByValue> { - static Data SetValue(const T& value) { - Data data; - memcpy(data.buf, &value, sizeof(value)); - return data; - } - - static T Value(Data arg) { - T value; - memcpy(&value, arg.buf, sizeof(T)); - return value; - } - }; - - template <typename T> - void Init(const T& value) { - data_ = Manager<T>::SetValue(value); - dispatcher_ = &Dispatch<T>; - } - - template <typename T> - static int ToIntVal(const T& val) { - using CommonType = typename std::conditional<std::is_signed<T>::value, - int64_t, uint64_t>::type; - if (static_cast<CommonType>(val) > - static_cast<CommonType>((std::numeric_limits<int>::max)())) { - return (std::numeric_limits<int>::max)(); - } else if (std::is_signed<T>::value && - static_cast<CommonType>(val) < - static_cast<CommonType>((std::numeric_limits<int>::min)())) { - return (std::numeric_limits<int>::min)(); - } - return static_cast<int>(val); - } - - template <typename T> - static bool ToInt(Data arg, int* out, std::true_type /* is_integral */, - std::false_type) { - *out = ToIntVal(Manager<T>::Value(arg)); - return true; - } - - template <typename T> - static bool ToInt(Data arg, int* out, std::false_type, - std::true_type /* is_enum */) { - *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>( - Manager<T>::Value(arg))); - return true; - } - - template <typename T> - static bool ToInt(Data, int*, std::false_type, std::false_type) { - return false; - } - - template <typename T> - static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { - // A `none` conv indicates that we want the `int` conversion. - if (ABSL_PREDICT_FALSE(spec.conversion_char() == - FormatConversionCharInternal::kNone)) { - return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), - std::is_enum<T>()); - } - if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(), - spec.conversion_char()))) { - return false; - } - return str_format_internal::FormatConvertImpl( - Manager<T>::Value(arg), spec, - static_cast<FormatSinkImpl*>(out)) - .value; - } - - Data data_; - Dispatcher dispatcher_; -}; - -#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ - E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ - void*) - -#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ - __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short, /* NOLINT */ \ - __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */ \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long, /* NOLINT */ \ - __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */ \ - __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \ - __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__) - -ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); - - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc deleted file mode 100644 index 1261937c3097..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/arg.h" - -#include <ostream> -#include <string> -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { -namespace { - -class FormatArgImplTest : public ::testing::Test { - public: - enum Color { kRed, kGreen, kBlue }; - - static const char *hi() { return "hi"; } - - struct X {}; - - X x_; -}; - -inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert( - const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) { - return {false}; -} - -TEST_F(FormatArgImplTest, ToInt) { - int out = 0; - EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out)); - EXPECT_EQ(1, out); - EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(-1), &out)); - EXPECT_EQ(-1, out); - EXPECT_TRUE( - FormatArgImplFriend::ToInt(FormatArgImpl(static_cast<char>(64)), &out)); - EXPECT_EQ(64, out); - EXPECT_TRUE(FormatArgImplFriend::ToInt( - FormatArgImpl(static_cast<unsigned long long>(123456)), &out)); // NOLINT - EXPECT_EQ(123456, out); - EXPECT_TRUE(FormatArgImplFriend::ToInt( - FormatArgImpl(static_cast<unsigned long long>( // NOLINT - std::numeric_limits<int>::max()) + - 1), - &out)); - EXPECT_EQ(std::numeric_limits<int>::max(), out); - EXPECT_TRUE(FormatArgImplFriend::ToInt( - FormatArgImpl(static_cast<long long>( // NOLINT - std::numeric_limits<int>::min()) - - 10), - &out)); - EXPECT_EQ(std::numeric_limits<int>::min(), out); - EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(false), &out)); - EXPECT_EQ(0, out); - EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(true), &out)); - EXPECT_EQ(1, out); - EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(2.2), &out)); - EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(3.2f), &out)); - EXPECT_FALSE(FormatArgImplFriend::ToInt( - FormatArgImpl(static_cast<int *>(nullptr)), &out)); - EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out)); - EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out)); - EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out)); - EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out)); - EXPECT_EQ(2, out); -} - -extern const char kMyArray[]; - -TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) { - const char* a = ""; - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), - FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(""))); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), - FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("A"))); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), - FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("ABC"))); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), - FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray))); -} - -TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) { - auto expected = FormatArgImplFriend::GetVTablePtrForTest( - FormatArgImpl(static_cast<void *>(nullptr))); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest( - FormatArgImpl(static_cast<int *>(nullptr))), - expected); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest( - FormatArgImpl(static_cast<volatile int *>(nullptr))), - expected); - - auto p = static_cast<void (*)()>([] {}); - EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(p)), - expected); -} - -TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) { - std::string s; - FormatSinkImpl sink(&s); - FormatConversionSpecImpl conv; - FormatConversionSpecImplFriend::SetConversionChar( - FormatConversionCharInternal::s, &conv); - FormatConversionSpecImplFriend::SetFlags(Flags(), &conv); - FormatConversionSpecImplFriend::SetWidth(-1, &conv); - FormatConversionSpecImplFriend::SetPrecision(-1, &conv); - EXPECT_TRUE( - FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink)); - sink.Flush(); - EXPECT_EQ("ABCDE", s); -} -const char kMyArray[] = "ABCDE"; - -} // namespace -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc deleted file mode 100644 index 4e68b90b5ce8..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/bind.h" - -#include <cerrno> -#include <limits> -#include <sstream> -#include <string> - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -namespace { - -inline bool BindFromPosition(int position, int* value, - absl::Span<const FormatArgImpl> pack) { - assert(position > 0); - if (static_cast<size_t>(position) > pack.size()) { - return false; - } - // -1 because positions are 1-based - return FormatArgImplFriend::ToInt(pack[position - 1], value); -} - -class ArgContext { - public: - explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {} - - // Fill 'bound' with the results of applying the context's argument pack - // to the specified 'unbound'. We synthesize a BoundConversion by - // lining up a UnboundConversion with a user argument. We also - // resolve any '*' specifiers for width and precision, so after - // this call, 'bound' has all the information it needs to be formatted. - // Returns false on failure. - bool Bind(const UnboundConversion* unbound, BoundConversion* bound); - - private: - absl::Span<const FormatArgImpl> pack_; -}; - -inline bool ArgContext::Bind(const UnboundConversion* unbound, - BoundConversion* bound) { - const FormatArgImpl* arg = nullptr; - int arg_position = unbound->arg_position; - if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false; - arg = &pack_[arg_position - 1]; // 1-based - - if (!unbound->flags.basic) { - int width = unbound->width.value(); - bool force_left = false; - if (unbound->width.is_from_arg()) { - if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_)) - return false; - if (width < 0) { - // "A negative field width is taken as a '-' flag followed by a - // positive field width." - force_left = true; - // Make sure we don't overflow the width when negating it. - width = -std::max(width, -std::numeric_limits<int>::max()); - } - } - - int precision = unbound->precision.value(); - if (unbound->precision.is_from_arg()) { - if (!BindFromPosition(unbound->precision.get_from_arg(), &precision, - pack_)) - return false; - } - - FormatConversionSpecImplFriend::SetWidth(width, bound); - FormatConversionSpecImplFriend::SetPrecision(precision, bound); - - if (force_left) { - Flags flags = unbound->flags; - flags.left = true; - FormatConversionSpecImplFriend::SetFlags(flags, bound); - } else { - FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); - } - } else { - FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); - FormatConversionSpecImplFriend::SetWidth(-1, bound); - FormatConversionSpecImplFriend::SetPrecision(-1, bound); - } - FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound); - bound->set_arg(arg); - return true; -} - -template <typename Converter> -class ConverterConsumer { - public: - ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack) - : converter_(converter), arg_context_(pack) {} - - bool Append(string_view s) { - converter_.Append(s); - return true; - } - bool ConvertOne(const UnboundConversion& conv, string_view conv_string) { - BoundConversion bound; - if (!arg_context_.Bind(&conv, &bound)) return false; - return converter_.ConvertOne(bound, conv_string); - } - - private: - Converter converter_; - ArgContext arg_context_; -}; - -template <typename Converter> -bool ConvertAll(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args, Converter converter) { - if (format.has_parsed_conversion()) { - return format.parsed_conversion()->ProcessFormat( - ConverterConsumer<Converter>(converter, args)); - } else { - return ParseFormatString(format.str(), - ConverterConsumer<Converter>(converter, args)); - } -} - -class DefaultConverter { - public: - explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {} - - void Append(string_view s) const { sink_->Append(s); } - - bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { - return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_); - } - - private: - FormatSinkImpl* sink_; -}; - -class SummarizingConverter { - public: - explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {} - - void Append(string_view s) const { sink_->Append(s); } - - bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { - UntypedFormatSpecImpl spec("%d"); - - std::ostringstream ss; - ss << "{" << Streamable(spec, {*bound.arg()}) << ":" - << FormatConversionSpecImplFriend::FlagsToString(bound); - if (bound.width() >= 0) ss << bound.width(); - if (bound.precision() >= 0) ss << "." << bound.precision(); - ss << bound.conversion_char() << "}"; - Append(ss.str()); - return true; - } - - private: - FormatSinkImpl* sink_; -}; - -} // namespace - -bool BindWithPack(const UnboundConversion* props, - absl::Span<const FormatArgImpl> pack, - BoundConversion* bound) { - return ArgContext(pack).Bind(props, bound); -} - -std::string Summarize(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - typedef SummarizingConverter Converter; - std::string out; - { - // inner block to destroy sink before returning out. It ensures a last - // flush. - FormatSinkImpl sink(&out); - if (!ConvertAll(format, args, Converter(&sink))) { - return ""; - } - } - return out; -} - -bool FormatUntyped(FormatRawSinkImpl raw_sink, - const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - FormatSinkImpl sink(raw_sink); - using Converter = DefaultConverter; - return ConvertAll(format, args, Converter(&sink)); -} - -std::ostream& Streamable::Print(std::ostream& os) const { - if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit); - return os; -} - -std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - size_t orig = out->size(); - if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) { - out->erase(orig); - } - return *out; -} - -std::string FormatPack(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - std::string out; - if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) { - out.clear(); - } - return out; -} - -int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - FILERawSink sink(output); - if (!FormatUntyped(&sink, format, args)) { - errno = EINVAL; - return -1; - } - if (sink.error()) { - errno = sink.error(); - return -1; - } - if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) { - errno = EFBIG; - return -1; - } - return static_cast<int>(sink.count()); -} - -int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { - BufferRawSink sink(output, size ? size - 1 : 0); - if (!FormatUntyped(&sink, format, args)) { - errno = EINVAL; - return -1; - } - size_t total = sink.total_written(); - if (size) output[std::min(total, size - 1)] = 0; - return static_cast<int>(total); -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h b/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h deleted file mode 100644 index 267cc0ef6928..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ - -#include <array> -#include <cstdio> -#include <sstream> -#include <string> - -#include "absl/base/port.h" -#include "absl/strings/internal/str_format/arg.h" -#include "absl/strings/internal/str_format/checker.h" -#include "absl/strings/internal/str_format/parser.h" -#include "absl/types/span.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -class UntypedFormatSpec; - -namespace str_format_internal { - -class BoundConversion : public FormatConversionSpecImpl { - public: - const FormatArgImpl* arg() const { return arg_; } - void set_arg(const FormatArgImpl* a) { arg_ = a; } - - private: - const FormatArgImpl* arg_; -}; - -// This is the type-erased class that the implementation uses. -class UntypedFormatSpecImpl { - public: - UntypedFormatSpecImpl() = delete; - - explicit UntypedFormatSpecImpl(string_view s) - : data_(s.data()), size_(s.size()) {} - explicit UntypedFormatSpecImpl( - const str_format_internal::ParsedFormatBase* pc) - : data_(pc), size_(~size_t{}) {} - - bool has_parsed_conversion() const { return size_ == ~size_t{}; } - - string_view str() const { - assert(!has_parsed_conversion()); - return string_view(static_cast<const char*>(data_), size_); - } - const str_format_internal::ParsedFormatBase* parsed_conversion() const { - assert(has_parsed_conversion()); - return static_cast<const str_format_internal::ParsedFormatBase*>(data_); - } - - template <typename T> - static const UntypedFormatSpecImpl& Extract(const T& s) { - return s.spec_; - } - - private: - const void* data_; - size_t size_; -}; - -template <typename T, FormatConversionCharSet...> -struct MakeDependent { - using type = T; -}; - -// Implicitly convertible from `const char*`, `string_view`, and the -// `ExtendedParsedFormat` type. This abstraction allows all format functions to -// operate on any without providing too many overloads. -template <FormatConversionCharSet... Args> -class FormatSpecTemplate - : public MakeDependent<UntypedFormatSpec, Args...>::type { - using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type; - - public: -#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - - // Honeypot overload for when the string is not constexpr. - // We use the 'unavailable' attribute to give a better compiler error than - // just 'method is deleted'. - FormatSpecTemplate(...) // NOLINT - __attribute__((unavailable("Format string is not constexpr."))); - - // Honeypot overload for when the format is constexpr and invalid. - // We use the 'unavailable' attribute to give a better compiler error than - // just 'method is deleted'. - // To avoid checking the format twice, we just check that the format is - // constexpr. If is it valid, then the overload below will kick in. - // We add the template here to make this overload have lower priority. - template <typename = void> - FormatSpecTemplate(const char* s) // NOLINT - __attribute__(( - enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"), - unavailable( - "Format specified does not match the arguments passed."))); - - template <typename T = void> - FormatSpecTemplate(string_view s) // NOLINT - __attribute__((enable_if(str_format_internal::EnsureConstexpr(s), - "constexpr trap"))) { - static_assert(sizeof(T*) == 0, - "Format specified does not match the arguments passed."); - } - - // Good format overload. - FormatSpecTemplate(const char* s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) - : Base(s) {} - - FormatSpecTemplate(string_view s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) - : Base(s) {} - -#else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - - FormatSpecTemplate(const char* s) : Base(s) {} // NOLINT - FormatSpecTemplate(string_view s) : Base(s) {} // NOLINT - -#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - - template < - FormatConversionCharSet... C, - typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type, - typename = typename std::enable_if<AllOf(Contains(Args, - C)...)>::type> - FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT - : Base(&pc) {} -}; - -class Streamable { - public: - Streamable(const UntypedFormatSpecImpl& format, - absl::Span<const FormatArgImpl> args) - : format_(format) { - if (args.size() <= ABSL_ARRAYSIZE(few_args_)) { - for (size_t i = 0; i < args.size(); ++i) { - few_args_[i] = args[i]; - } - args_ = absl::MakeSpan(few_args_, args.size()); - } else { - many_args_.assign(args.begin(), args.end()); - args_ = many_args_; - } - } - - std::ostream& Print(std::ostream& os) const; - - friend std::ostream& operator<<(std::ostream& os, const Streamable& l) { - return l.Print(os); - } - - private: - const UntypedFormatSpecImpl& format_; - absl::Span<const FormatArgImpl> args_; - // if args_.size() is 4 or less: - FormatArgImpl few_args_[4] = {FormatArgImpl(0), FormatArgImpl(0), - FormatArgImpl(0), FormatArgImpl(0)}; - // if args_.size() is more than 4: - std::vector<FormatArgImpl> many_args_; -}; - -// for testing -std::string Summarize(UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); -bool BindWithPack(const UnboundConversion* props, - absl::Span<const FormatArgImpl> pack, BoundConversion* bound); - -bool FormatUntyped(FormatRawSinkImpl raw_sink, - UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); - -std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); - -std::string FormatPack(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); - -int FprintF(std::FILE* output, UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); -int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); - -// Returned by Streamed(v). Converts via '%s' to the std::string created -// by std::ostream << v. -template <typename T> -class StreamedWrapper { - public: - explicit StreamedWrapper(const T& v) : v_(v) { } - - private: - template <typename S> - friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( - const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, - FormatSinkImpl* out); - const T& v_; -}; - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc deleted file mode 100644 index 1eef9c4326e2..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/bind.h" - -#include <string.h> -#include <limits> - -#include "gtest/gtest.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { -namespace { - -class FormatBindTest : public ::testing::Test { - public: - bool Extract(const char *s, UnboundConversion *props, int *next) const { - return ConsumeUnboundConversion(s, s + strlen(s), props, next) == - s + strlen(s); - } -}; - -TEST_F(FormatBindTest, BindSingle) { - struct Expectation { - int line; - const char *fmt; - int ok_phases; - const FormatArgImpl *arg; - int width; - int precision; - int next_arg; - }; - const int no = -1; - const int ia[] = { 10, 20, 30, 40}; - const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]), - FormatArgImpl(ia[2]), FormatArgImpl(ia[3])}; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" - const Expectation kExpect[] = { - {__LINE__, "d", 2, &args[0], no, no, 2}, - {__LINE__, "4d", 2, &args[0], 4, no, 2}, - {__LINE__, ".5d", 2, &args[0], no, 5, 2}, - {__LINE__, "4.5d", 2, &args[0], 4, 5, 2}, - {__LINE__, "*d", 2, &args[1], 10, no, 3}, - {__LINE__, ".*d", 2, &args[1], no, 10, 3}, - {__LINE__, "*.*d", 2, &args[2], 10, 20, 4}, - {__LINE__, "1$d", 2, &args[0], no, no, 0}, - {__LINE__, "2$d", 2, &args[1], no, no, 0}, - {__LINE__, "3$d", 2, &args[2], no, no, 0}, - {__LINE__, "4$d", 2, &args[3], no, no, 0}, - {__LINE__, "2$*1$d", 2, &args[1], 10, no, 0}, - {__LINE__, "2$*2$d", 2, &args[1], 20, no, 0}, - {__LINE__, "2$*3$d", 2, &args[1], 30, no, 0}, - {__LINE__, "2$.*1$d", 2, &args[1], no, 10, 0}, - {__LINE__, "2$.*2$d", 2, &args[1], no, 20, 0}, - {__LINE__, "2$.*3$d", 2, &args[1], no, 30, 0}, - {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0}, - {__LINE__, "2$*2$.*2$d", 2, &args[1], 20, 20, 0}, - {__LINE__, "2$*1$.*3$d", 2, &args[1], 10, 30, 0}, - {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0}, - {__LINE__, "1$*d", 0}, // indexed, then positional - {__LINE__, "*2$d", 0}, // positional, then indexed - {__LINE__, "6$d", 1}, // arg position out of bounds - {__LINE__, "1$6$d", 0}, // width position incorrectly specified - {__LINE__, "1$.6$d", 0}, // precision position incorrectly specified - {__LINE__, "1$*6$d", 1}, // width position out of bounds - {__LINE__, "1$.*6$d", 1}, // precision position out of bounds - }; -#pragma GCC diagnostic pop - for (const Expectation &e : kExpect) { - SCOPED_TRACE(e.line); - SCOPED_TRACE(e.fmt); - UnboundConversion props; - BoundConversion bound; - int ok_phases = 0; - int next = 0; - if (Extract(e.fmt, &props, &next)) { - ++ok_phases; - if (BindWithPack(&props, args, &bound)) { - ++ok_phases; - } - } - EXPECT_EQ(e.ok_phases, ok_phases); - if (e.ok_phases < 2) continue; - if (e.arg != nullptr) { - EXPECT_EQ(e.arg, bound.arg()); - } - EXPECT_EQ(e.width, bound.width()); - EXPECT_EQ(e.precision, bound.precision()); - } -} - -TEST_F(FormatBindTest, WidthUnderflowRegression) { - UnboundConversion props; - BoundConversion bound; - int next = 0; - const int args_i[] = {std::numeric_limits<int>::min(), 17}; - const FormatArgImpl args[] = {FormatArgImpl(args_i[0]), - FormatArgImpl(args_i[1])}; - ASSERT_TRUE(Extract("*d", &props, &next)); - ASSERT_TRUE(BindWithPack(&props, args, &bound)); - - EXPECT_EQ(bound.width(), std::numeric_limits<int>::max()); - EXPECT_EQ(bound.arg(), args + 1); -} - -TEST_F(FormatBindTest, FormatPack) { - struct Expectation { - int line; - const char *fmt; - const char *summary; - }; - const int ia[] = { 10, 20, 30, 40, -10 }; - const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]), - FormatArgImpl(ia[2]), FormatArgImpl(ia[3]), - FormatArgImpl(ia[4])}; - const Expectation kExpect[] = { - {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"}, - {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"}, - {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"}, - {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"}, - {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"}, - {__LINE__, "a%.*fb", "a{20:.10f}b"}, - {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"}, - {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"}, - {__LINE__, "a%04ldb", "a{10:04d}b"}, - {__LINE__, "a%-#04lldb", "a{10:-#04d}b"}, - {__LINE__, "a%1$*5$db", "a{10:-10d}b"}, - {__LINE__, "a%1$.*5$db", "a{10:d}b"}, - }; - for (const Expectation &e : kExpect) { - absl::string_view fmt = e.fmt; - SCOPED_TRACE(e.line); - SCOPED_TRACE(e.fmt); - UntypedFormatSpecImpl format(fmt); - EXPECT_EQ(e.summary, - str_format_internal::Summarize(format, absl::MakeSpan(args))) - << "line:" << e.line; - } -} - -} // namespace -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h b/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h deleted file mode 100644 index 2a2601eccfd8..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ - -#include "absl/base/attributes.h" -#include "absl/strings/internal/str_format/arg.h" -#include "absl/strings/internal/str_format/extension.h" - -// Compile time check support for entry points. - -#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) -#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 -#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) -#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -constexpr bool AllOf() { return true; } - -template <typename... T> -constexpr bool AllOf(bool b, T... t) { - return b && AllOf(t...); -} - -#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - -constexpr bool ContainsChar(const char* chars, char c) { - return *chars == c || (*chars && ContainsChar(chars + 1, c)); -} - -// A constexpr compatible list of Convs. -struct ConvList { - const FormatConversionCharSet* array; - int count; - - // We do the bound check here to avoid having to do it on the callers. - // Returning an empty FormatConversionCharSet has the same effect as - // short circuiting because it will never match any conversion. - constexpr FormatConversionCharSet operator[](int i) const { - return i < count ? array[i] : FormatConversionCharSet{}; - } - - constexpr ConvList without_front() const { - return count != 0 ? ConvList{array + 1, count - 1} : *this; - } -}; - -template <size_t count> -struct ConvListT { - // Make sure the array has size > 0. - FormatConversionCharSet list[count ? count : 1]; -}; - -constexpr char GetChar(string_view str, size_t index) { - return index < str.size() ? str[index] : char{}; -} - -constexpr string_view ConsumeFront(string_view str, size_t len = 1) { - return len <= str.size() ? string_view(str.data() + len, str.size() - len) - : string_view(); -} - -constexpr string_view ConsumeAnyOf(string_view format, const char* chars) { - return ContainsChar(chars, GetChar(format, 0)) - ? ConsumeAnyOf(ConsumeFront(format), chars) - : format; -} - -constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; } - -// Helper class for the ParseDigits function. -// It encapsulates the two return values we need there. -struct Integer { - string_view format; - int value; - - // If the next character is a '$', consume it. - // Otherwise, make `this` an invalid positional argument. - constexpr Integer ConsumePositionalDollar() const { - return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value} - : Integer{format, 0}; - } -}; - -constexpr Integer ParseDigits(string_view format, int value = 0) { - return IsDigit(GetChar(format, 0)) - ? ParseDigits(ConsumeFront(format), - 10 * value + GetChar(format, 0) - '0') - : Integer{format, value}; -} - -// Parse digits for a positional argument. -// The parsing also consumes the '$'. -constexpr Integer ParsePositional(string_view format) { - return ParseDigits(format).ConsumePositionalDollar(); -} - -// Parses a single conversion specifier. -// See ConvParser::Run() for post conditions. -class ConvParser { - constexpr ConvParser SetFormat(string_view format) const { - return ConvParser(format, args_, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetArgs(ConvList args) const { - return ConvParser(format_, args, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetError(bool error) const { - return ConvParser(format_, args_, error_ || error, arg_position_, - is_positional_); - } - - constexpr ConvParser SetArgPosition(int arg_position) const { - return ConvParser(format_, args_, error_, arg_position, is_positional_); - } - - // Consumes the next arg and verifies that it matches `conv`. - // `error_` is set if there is no next arg or if it doesn't match `conv`. - constexpr ConvParser ConsumeNextArg(char conv) const { - return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv)); - } - - // Verify that positional argument `i.value` matches `conv`. - // `error_` is set if `i.value` is not a valid argument or if it doesn't - // match. - constexpr ConvParser VerifyPositional(Integer i, char conv) const { - return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv)); - } - - // Parse the position of the arg and store it in `arg_position_`. - constexpr ConvParser ParseArgPosition(Integer arg) const { - return SetFormat(arg.format).SetArgPosition(arg.value); - } - - // Consume the flags. - constexpr ConvParser ParseFlags() const { - return SetFormat(ConsumeAnyOf(format_, "-+ #0")); - } - - // Consume the width. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParseWidth() const { - return IsDigit(GetChar(format_, 0)) - ? SetFormat(ParseDigits(format_).format) - : GetChar(format_, 0) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_)), '*') - : SetFormat(ConsumeFront(format_)) - .ConsumeNextArg('*') - : *this; - } - - // Consume the precision. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParsePrecision() const { - return GetChar(format_, 0) != '.' - ? *this - : GetChar(format_, 1) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_, 2)), '*') - : SetFormat(ConsumeFront(format_, 2)) - .ConsumeNextArg('*') - : SetFormat(ParseDigits(ConsumeFront(format_)).format); - } - - // Consume the length characters. - constexpr ConvParser ParseLength() const { - return SetFormat(ConsumeAnyOf(format_, "lLhjztq")); - } - - // Consume the conversion character and verify that it matches `args_`. - // `error_` is set if it doesn't match. - constexpr ConvParser ParseConversion() const { - return is_positional_ - ? VerifyPositional({ConsumeFront(format_), arg_position_}, - GetChar(format_, 0)) - : ConsumeNextArg(GetChar(format_, 0)) - .SetFormat(ConsumeFront(format_)); - } - - constexpr ConvParser(string_view format, ConvList args, bool error, - int arg_position, bool is_positional) - : format_(format), - args_(args), - error_(error), - arg_position_(arg_position), - is_positional_(is_positional) {} - - public: - constexpr ConvParser(string_view format, ConvList args, bool is_positional) - : format_(format), - args_(args), - error_(false), - arg_position_(0), - is_positional_(is_positional) {} - - // Consume the whole conversion specifier. - // `format()` will be set to the character after the conversion character. - // `error()` will be set if any of the arguments do not match. - constexpr ConvParser Run() const { - return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this) - .ParseFlags() - .ParseWidth() - .ParsePrecision() - .ParseLength() - .ParseConversion(); - } - - constexpr string_view format() const { return format_; } - constexpr ConvList args() const { return args_; } - constexpr bool error() const { return error_; } - constexpr bool is_positional() const { return is_positional_; } - - private: - string_view format_; - // Current list of arguments. If we are not in positional mode we will consume - // from the front. - ConvList args_; - bool error_; - // Holds the argument position of the conversion character, if we are in - // positional mode. Otherwise, it is unspecified. - int arg_position_; - // Whether we are in positional mode. - // It changes the behavior of '*' and where to find the converted argument. - bool is_positional_; -}; - -// Parses a whole format expression. -// See FormatParser::Run(). -class FormatParser { - static constexpr bool FoundPercent(string_view format) { - return format.empty() || - (GetChar(format, 0) == '%' && GetChar(format, 1) != '%'); - } - - // We use an inner function to increase the recursion limit. - // The inner function consumes up to `limit` characters on every run. - // This increases the limit from 512 to ~512*limit. - static constexpr string_view ConsumeNonPercentInner(string_view format, - int limit = 20) { - return FoundPercent(format) || !limit - ? format - : ConsumeNonPercentInner( - ConsumeFront(format, GetChar(format, 0) == '%' && - GetChar(format, 1) == '%' - ? 2 - : 1), - limit - 1); - } - - // Consume characters until the next conversion spec %. - // It skips %%. - static constexpr string_view ConsumeNonPercent(string_view format) { - return FoundPercent(format) - ? format - : ConsumeNonPercent(ConsumeNonPercentInner(format)); - } - - static constexpr bool IsPositional(string_view format) { - return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format)) - : GetChar(format, 0) == '$'; - } - - constexpr bool RunImpl(bool is_positional) const { - // In non-positional mode we require all arguments to be consumed. - // In positional mode just reaching the end of the format without errors is - // enough. - return (format_.empty() && (is_positional || args_.count == 0)) || - (!format_.empty() && - ValidateArg( - ConvParser(ConsumeFront(format_), args_, is_positional).Run())); - } - - constexpr bool ValidateArg(ConvParser conv) const { - return !conv.error() && FormatParser(conv.format(), conv.args()) - .RunImpl(conv.is_positional()); - } - - public: - constexpr FormatParser(string_view format, ConvList args) - : format_(ConsumeNonPercent(format)), args_(args) {} - - // Runs the parser for `format` and `args`. - // It verifies that the format is valid and that all conversion specifiers - // match the arguments passed. - // In non-positional mode it also verfies that all arguments are consumed. - constexpr bool Run() const { - return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_))); - } - - private: - string_view format_; - // Current list of arguments. - // If we are not in positional mode we will consume from the front and will - // have to be empty in the end. - ConvList args_; -}; - -template <FormatConversionCharSet... C> -constexpr bool ValidFormatImpl(string_view format) { - return FormatParser(format, - {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)}) - .Run(); -} - -#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc deleted file mode 100644 index 7c70f47d682a..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { -namespace { - -std::string ConvToString(FormatConversionCharSet conv) { - std::string out; -#define CONV_SET_CASE(c) \ - if (Contains(conv, FormatConversionCharSetInternal::c)) { \ - out += #c; \ - } - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) -#undef CONV_SET_CASE - if (Contains(conv, FormatConversionCharSetInternal::kStar)) { - out += "*"; - } - return out; -} - -TEST(StrFormatChecker, ArgumentToConv) { - FormatConversionCharSet conv = ArgumentToConv<std::string>(); - EXPECT_EQ(ConvToString(conv), "s"); - - conv = ArgumentToConv<const char*>(); - EXPECT_EQ(ConvToString(conv), "sp"); - - conv = ArgumentToConv<double>(); - EXPECT_EQ(ConvToString(conv), "fFeEgGaA"); - - conv = ArgumentToConv<int>(); - EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*"); - - conv = ArgumentToConv<std::string*>(); - EXPECT_EQ(ConvToString(conv), "p"); -} - -#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - -struct Case { - bool result; - const char* format; -}; - -template <typename... Args> -constexpr Case ValidFormat(const char* format) { - return {ValidFormatImpl<ArgumentToConv<Args>()...>(format), format}; -} - -TEST(StrFormatChecker, ValidFormat) { - // We want to make sure these expressions are constexpr and they have the - // expected value. - // If they are not constexpr the attribute will just ignore them and not give - // a compile time error. - enum e {}; - enum class e2 {}; - constexpr Case trues[] = { - ValidFormat<>("abc"), // - - ValidFormat<e>("%d"), // - ValidFormat<e2>("%d"), // - ValidFormat<int>("%% %d"), // - ValidFormat<int>("%ld"), // - ValidFormat<int>("%lld"), // - ValidFormat<std::string>("%s"), // - ValidFormat<std::string>("%10s"), // - ValidFormat<int>("%.10x"), // - ValidFormat<int, int>("%*.3x"), // - ValidFormat<int>("%1.d"), // - ValidFormat<int>("%.d"), // - ValidFormat<int, double>("%d %g"), // - ValidFormat<int, std::string>("%*s"), // - ValidFormat<int, double>("%.*f"), // - ValidFormat<void (*)(), volatile int*>("%p %p"), // - ValidFormat<string_view, const char*, double, void*>( - "string_view=%s const char*=%s double=%f void*=%p)"), - - ValidFormat<int>("%% %1$d"), // - ValidFormat<int>("%1$ld"), // - ValidFormat<int>("%1$lld"), // - ValidFormat<std::string>("%1$s"), // - ValidFormat<std::string>("%1$10s"), // - ValidFormat<int>("%1$.10x"), // - ValidFormat<int>("%1$*1$.*1$d"), // - ValidFormat<int, int>("%1$*2$.3x"), // - ValidFormat<int>("%1$1.d"), // - ValidFormat<int>("%1$.d"), // - ValidFormat<double, int>("%2$d %1$g"), // - ValidFormat<int, std::string>("%2$*1$s"), // - ValidFormat<int, double>("%2$.*1$f"), // - ValidFormat<void*, string_view, const char*, double>( - "string_view=%2$s const char*=%3$s double=%4$f void*=%1$p " - "repeat=%3$s)")}; - - for (Case c : trues) { - EXPECT_TRUE(c.result) << c.format; - } - - constexpr Case falses[] = { - ValidFormat<int>(""), // - - ValidFormat<e>("%s"), // - ValidFormat<e2>("%s"), // - ValidFormat<>("%s"), // - ValidFormat<>("%r"), // - ValidFormat<int>("%s"), // - ValidFormat<int>("%.1.d"), // - ValidFormat<int>("%*1d"), // - ValidFormat<int>("%1-d"), // - ValidFormat<std::string, int>("%*s"), // - ValidFormat<int>("%*d"), // - ValidFormat<std::string>("%p"), // - ValidFormat<int (*)(int)>("%d"), // - - ValidFormat<>("%3$d"), // - ValidFormat<>("%1$r"), // - ValidFormat<int>("%1$s"), // - ValidFormat<int>("%1$.1.d"), // - ValidFormat<int>("%1$*2$1d"), // - ValidFormat<int>("%1$1-d"), // - ValidFormat<std::string, int>("%2$*1$s"), // - ValidFormat<std::string>("%1$p"), - - ValidFormat<int, int>("%d %2$d"), // - }; - - for (Case c : falses) { - EXPECT_FALSE(c.result) << c.format; - } -} - -TEST(StrFormatChecker, LongFormat) { -#define CHARS_X_40 "1234567890123456789012345678901234567890" -#define CHARS_X_400 \ - CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 \ - CHARS_X_40 CHARS_X_40 CHARS_X_40 -#define CHARS_X_4000 \ - CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 \ - CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 - constexpr char long_format[] = - CHARS_X_4000 "%d" CHARS_X_4000 "%s" CHARS_X_4000; - constexpr bool is_valid = ValidFormat<int, std::string>(long_format).result; - EXPECT_TRUE(is_valid); -} - -#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - -} // namespace -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc deleted file mode 100644 index 375db0a0592c..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc +++ /dev/null @@ -1,1242 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> - -#include <cctype> -#include <cmath> -#include <limits> -#include <string> -#include <thread> // NOLINT - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/internal/raw_logging.h" -#include "absl/strings/internal/str_format/bind.h" -#include "absl/strings/match.h" -#include "absl/types/optional.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { -namespace { - -struct NativePrintfTraits { - bool hex_float_has_glibc_rounding; - bool hex_float_prefers_denormal_repr; - bool hex_float_uses_minimal_precision_when_not_specified; - bool hex_float_optimizes_leading_digit_bit_count; -}; - -template <typename T, size_t N> -size_t ArraySize(T (&)[N]) { - return N; -} - -std::string LengthModFor(float) { return ""; } -std::string LengthModFor(double) { return ""; } -std::string LengthModFor(long double) { return "L"; } -std::string LengthModFor(char) { return "hh"; } -std::string LengthModFor(signed char) { return "hh"; } -std::string LengthModFor(unsigned char) { return "hh"; } -std::string LengthModFor(short) { return "h"; } // NOLINT -std::string LengthModFor(unsigned short) { return "h"; } // NOLINT -std::string LengthModFor(int) { return ""; } -std::string LengthModFor(unsigned) { return ""; } -std::string LengthModFor(long) { return "l"; } // NOLINT -std::string LengthModFor(unsigned long) { return "l"; } // NOLINT -std::string LengthModFor(long long) { return "ll"; } // NOLINT -std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT - -std::string EscCharImpl(int v) { - if (std::isprint(static_cast<unsigned char>(v))) { - return std::string(1, static_cast<char>(v)); - } - char buf[64]; - int n = snprintf(buf, sizeof(buf), "\\%#.2x", - static_cast<unsigned>(v & 0xff)); - assert(n > 0 && n < sizeof(buf)); - return std::string(buf, n); -} - -std::string Esc(char v) { return EscCharImpl(v); } -std::string Esc(signed char v) { return EscCharImpl(v); } -std::string Esc(unsigned char v) { return EscCharImpl(v); } - -template <typename T> -std::string Esc(const T &v) { - std::ostringstream oss; - oss << v; - return oss.str(); -} - -void StrAppendV(std::string *dst, const char *format, va_list ap) { - // First try with a small fixed size buffer - static const int kSpaceLength = 1024; - char space[kSpaceLength]; - - // It's possible for methods that use a va_list to invalidate - // the data in it upon use. The fix is to make a copy - // of the structure before using it and use that copy instead. - va_list backup_ap; - va_copy(backup_ap, ap); - int result = vsnprintf(space, kSpaceLength, format, backup_ap); - va_end(backup_ap); - if (result < kSpaceLength) { - if (result >= 0) { - // Normal case -- everything fit. - dst->append(space, result); - return; - } - if (result < 0) { - // Just an error. - return; - } - } - - // Increase the buffer size to the size requested by vsnprintf, - // plus one for the closing \0. - int length = result + 1; - char *buf = new char[length]; - - // Restore the va_list before we use it again - va_copy(backup_ap, ap); - result = vsnprintf(buf, length, format, backup_ap); - va_end(backup_ap); - - if (result >= 0 && result < length) { - // It fit - dst->append(buf, result); - } - delete[] buf; -} - -void StrAppend(std::string *out, const char *format, ...) { - va_list ap; - va_start(ap, format); - StrAppendV(out, format, ap); - va_end(ap); -} - -std::string StrPrint(const char *format, ...) { - va_list ap; - va_start(ap, format); - std::string result; - StrAppendV(&result, format, ap); - va_end(ap); - return result; -} - -NativePrintfTraits VerifyNativeImplementationImpl() { - NativePrintfTraits result; - - // >>> hex_float_has_glibc_rounding. To have glibc's rounding behavior we need - // to meet three requirements: - // - // - The threshold for rounding up is 8 (for e.g. MSVC uses 9). - // - If the digits lower than than the 8 are non-zero then we round up. - // - If the digits lower than the 8 are all zero then we round toward even. - // - // The numbers below represent all the cases covering {below,at,above} the - // threshold (8) with both {zero,non-zero} lower bits and both {even,odd} - // preceding digits. - const double d0079 = 65657.0; // 0x1.0079p+16 - const double d0179 = 65913.0; // 0x1.0179p+16 - const double d0080 = 65664.0; // 0x1.0080p+16 - const double d0180 = 65920.0; // 0x1.0180p+16 - const double d0081 = 65665.0; // 0x1.0081p+16 - const double d0181 = 65921.0; // 0x1.0181p+16 - result.hex_float_has_glibc_rounding = - StartsWith(StrPrint("%.2a", d0079), "0x1.00") && - StartsWith(StrPrint("%.2a", d0179), "0x1.01") && - StartsWith(StrPrint("%.2a", d0080), "0x1.00") && - StartsWith(StrPrint("%.2a", d0180), "0x1.02") && - StartsWith(StrPrint("%.2a", d0081), "0x1.01") && - StartsWith(StrPrint("%.2a", d0181), "0x1.02"); - - // >>> hex_float_prefers_denormal_repr. Formatting `denormal` on glibc yields - // "0x0.0000000000001p-1022", whereas on std libs that don't use denormal - // representation it would either be 0x1p-1074 or 0x1.0000000000000-1074. - const double denormal = std::numeric_limits<double>::denorm_min(); - result.hex_float_prefers_denormal_repr = - StartsWith(StrPrint("%a", denormal), "0x0.0000000000001"); - - // >>> hex_float_uses_minimal_precision_when_not_specified. Some (non-glibc) - // libs will format the following as "0x1.0079000000000p+16". - result.hex_float_uses_minimal_precision_when_not_specified = - (StrPrint("%a", d0079) == "0x1.0079p+16"); - - // >>> hex_float_optimizes_leading_digit_bit_count. The number 1.5, when - // formatted by glibc should yield "0x1.8p+0" for `double` and "0xcp-3" for - // `long double`, i.e., number of bits in the leading digit is adapted to the - // number of bits in the mantissa. - const double d_15 = 1.5; - const long double ld_15 = 1.5; - result.hex_float_optimizes_leading_digit_bit_count = - StartsWith(StrPrint("%a", d_15), "0x1.8") && - StartsWith(StrPrint("%La", ld_15), "0xc"); - - return result; -} - -const NativePrintfTraits &VerifyNativeImplementation() { - static NativePrintfTraits native_traits = VerifyNativeImplementationImpl(); - return native_traits; -} - -class FormatConvertTest : public ::testing::Test { }; - -template <typename T> -void TestStringConvert(const T& str) { - const FormatArgImpl args[] = {FormatArgImpl(str)}; - struct Expectation { - const char *out; - const char *fmt; - }; - const Expectation kExpect[] = { - {"hello", "%1$s" }, - {"", "%1$.s" }, - {"", "%1$.0s" }, - {"h", "%1$.1s" }, - {"he", "%1$.2s" }, - {"hello", "%1$.10s" }, - {" hello", "%1$6s" }, - {" he", "%1$5.2s" }, - {"he ", "%1$-5.2s" }, - {"hello ", "%1$-6.10s" }, - }; - for (const Expectation &e : kExpect) { - UntypedFormatSpecImpl format(e.fmt); - EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))); - } -} - -TEST_F(FormatConvertTest, BasicString) { - TestStringConvert("hello"); // As char array. - TestStringConvert(static_cast<const char*>("hello")); - TestStringConvert(std::string("hello")); - TestStringConvert(string_view("hello")); -} - -TEST_F(FormatConvertTest, NullString) { - const char* p = nullptr; - UntypedFormatSpecImpl format("%s"); - EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)})); -} - -TEST_F(FormatConvertTest, StringPrecision) { - // We cap at the precision. - char c = 'a'; - const char* p = &c; - UntypedFormatSpecImpl format("%.1s"); - EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)})); - - // We cap at the NUL-terminator. - p = "ABC"; - UntypedFormatSpecImpl format2("%.10s"); - EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)})); -} - -// Pointer formatting is implementation defined. This checks that the argument -// can be matched to `ptr`. -MATCHER_P(MatchesPointerString, ptr, "") { - if (ptr == nullptr && arg == "(nil)") { - return true; - } - void* parsed = nullptr; - if (sscanf(arg.c_str(), "%p", &parsed) != 1) { - ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str()); - } - return ptr == parsed; -} - -TEST_F(FormatConvertTest, Pointer) { - static int x = 0; - const int *xp = &x; - char c = 'h'; - char *mcp = &c; - const char *cp = "hi"; - const char *cnil = nullptr; - const int *inil = nullptr; - using VoidF = void (*)(); - VoidF fp = [] {}, fnil = nullptr; - volatile char vc; - volatile char *vcp = &vc; - volatile char *vcnil = nullptr; - const FormatArgImpl args_array[] = { - FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil), - FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp), - FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil), - }; - auto args = absl::MakeConstSpan(args_array); - - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args), - MatchesPointerString(&x)); - - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), - MatchesPointerString(&x)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args), - MatchesPointerString(&x)); - - // const char* - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args), - MatchesPointerString(cp)); - // null const int* - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args), - MatchesPointerString(nullptr)); - // null const char* - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args), - MatchesPointerString(nullptr)); - // nonconst char* - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args), - MatchesPointerString(mcp)); - - // function pointers - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), - MatchesPointerString(reinterpret_cast<const void*>(fp))); - EXPECT_THAT( - FormatPack(UntypedFormatSpecImpl("%8$p"), args), - MatchesPointerString(reinterpret_cast<volatile const void *>(vcp))); - - // null function pointers - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), - MatchesPointerString(nullptr)); - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args), - MatchesPointerString(nullptr)); -} - -struct Cardinal { - enum Pos { k1 = 1, k2 = 2, k3 = 3 }; - enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 }; -}; - -TEST_F(FormatConvertTest, Enum) { - const Cardinal::Pos k3 = Cardinal::k3; - const Cardinal::Neg km3 = Cardinal::kM3; - const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)}; - UntypedFormatSpecImpl format("%1$d"); - UntypedFormatSpecImpl format2("%2$d"); - EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args))); - EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args))); -} - -template <typename T> -class TypedFormatConvertTest : public FormatConvertTest { }; - -TYPED_TEST_SUITE_P(TypedFormatConvertTest); - -std::vector<std::string> AllFlagCombinations() { - const char kFlags[] = {'-', '#', '0', '+', ' '}; - std::vector<std::string> result; - for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) { - std::string flag_set; - for (size_t fi = 0; fi < ArraySize(kFlags); ++fi) - if (fsi & (1ull << fi)) - flag_set += kFlags[fi]; - result.push_back(flag_set); - } - return result; -} - -TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) { - typedef TypeParam T; - typedef typename std::make_unsigned<T>::type UnsignedT; - using remove_volatile_t = typename std::remove_volatile<T>::type; - const T kMin = std::numeric_limits<remove_volatile_t>::min(); - const T kMax = std::numeric_limits<remove_volatile_t>::max(); - const T kVals[] = { - remove_volatile_t(1), - remove_volatile_t(2), - remove_volatile_t(3), - remove_volatile_t(123), - remove_volatile_t(-1), - remove_volatile_t(-2), - remove_volatile_t(-3), - remove_volatile_t(-123), - remove_volatile_t(0), - kMax - remove_volatile_t(1), - kMax, - kMin + remove_volatile_t(1), - kMin, - }; - const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'}; - const std::string kWid[] = {"", "4", "10"}; - const std::string kPrec[] = {"", ".", ".0", ".4", ".10"}; - - const std::vector<std::string> flag_sets = AllFlagCombinations(); - - for (size_t vi = 0; vi < ArraySize(kVals); ++vi) { - const T val = kVals[vi]; - SCOPED_TRACE(Esc(val)); - const FormatArgImpl args[] = {FormatArgImpl(val)}; - for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) { - const char conv_char = kConvChars[ci]; - for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) { - const std::string &flag_set = flag_sets[fsi]; - for (size_t wi = 0; wi < ArraySize(kWid); ++wi) { - const std::string &wid = kWid[wi]; - for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) { - const std::string &prec = kPrec[pi]; - - const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i'); - const bool is_unsigned_to_signed = - !std::is_signed<T>::value && is_signed_conv; - // Don't consider sign-related flags '+' and ' ' when doing - // unsigned to signed conversions. - if (is_unsigned_to_signed && - flag_set.find_first_of("+ ") != std::string::npos) { - continue; - } - - std::string new_fmt("%"); - new_fmt += flag_set; - new_fmt += wid; - new_fmt += prec; - // old and new always agree up to here. - std::string old_fmt = new_fmt; - new_fmt += conv_char; - std::string old_result; - if (is_unsigned_to_signed) { - // don't expect agreement on unsigned formatted as signed, - // as printf can't do that conversion properly. For those - // cases, we do expect agreement with printf with a "%u" - // and the unsigned equivalent of 'val'. - UnsignedT uval = val; - old_fmt += LengthModFor(uval); - old_fmt += "u"; - old_result = StrPrint(old_fmt.c_str(), uval); - } else { - old_fmt += LengthModFor(val); - old_fmt += conv_char; - old_result = StrPrint(old_fmt.c_str(), val); - } - - SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt + - "\"'" - " new_fmt: \"" + - new_fmt + "\""); - UntypedFormatSpecImpl format(new_fmt); - EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args))); - } - } - } - } - } -} - -TYPED_TEST_P(TypedFormatConvertTest, Char) { - typedef TypeParam T; - using remove_volatile_t = typename std::remove_volatile<T>::type; - static const T kMin = std::numeric_limits<remove_volatile_t>::min(); - static const T kMax = std::numeric_limits<remove_volatile_t>::max(); - T kVals[] = { - remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), - remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), - remove_volatile_t(0), - kMin + remove_volatile_t(1), kMin, - kMax - remove_volatile_t(1), kMax - }; - for (const T &c : kVals) { - const FormatArgImpl args[] = {FormatArgImpl(c)}; - UntypedFormatSpecImpl format("%c"); - EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args))); - } -} - -REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char); - -typedef ::testing::Types< - int, unsigned, volatile int, - short, unsigned short, - long, unsigned long, - long long, unsigned long long, - signed char, unsigned char, char> - AllIntTypes; -INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes, - TypedFormatConvertTest, AllIntTypes); -TEST_F(FormatConvertTest, VectorBool) { - // Make sure vector<bool>'s values behave as bools. - std::vector<bool> v = {true, false}; - const std::vector<bool> cv = {true, false}; - EXPECT_EQ("1,0,1,0", - FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"), - absl::Span<const FormatArgImpl>( - {FormatArgImpl(v[0]), FormatArgImpl(v[1]), - FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); -} - - -TEST_F(FormatConvertTest, Int128) { - absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979; - absl::int128 negative = -positive; - absl::int128 max = absl::Int128Max(), min = absl::Int128Min(); - const FormatArgImpl args[] = {FormatArgImpl(positive), - FormatArgImpl(negative), FormatArgImpl(max), - FormatArgImpl(min)}; - - struct Case { - const char* format; - const char* expected; - } cases[] = { - {"%1$d", "2595989796776606496405"}, - {"%1$30d", " 2595989796776606496405"}, - {"%1$-30d", "2595989796776606496405 "}, - {"%1$u", "2595989796776606496405"}, - {"%1$x", "8cba9876066020f695"}, - {"%2$d", "-2595989796776606496405"}, - {"%2$30d", " -2595989796776606496405"}, - {"%2$-30d", "-2595989796776606496405 "}, - {"%2$u", "340282366920938460867384810655161715051"}, - {"%2$x", "ffffffffffffff73456789f99fdf096b"}, - {"%3$d", "170141183460469231731687303715884105727"}, - {"%3$u", "170141183460469231731687303715884105727"}, - {"%3$x", "7fffffffffffffffffffffffffffffff"}, - {"%4$d", "-170141183460469231731687303715884105728"}, - {"%4$x", "80000000000000000000000000000000"}, - }; - - for (auto c : cases) { - UntypedFormatSpecImpl format(c.format); - EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args))); - } -} - -TEST_F(FormatConvertTest, Uint128) { - absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979; - absl::uint128 max = absl::Uint128Max(); - const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)}; - - struct Case { - const char* format; - const char* expected; - } cases[] = { - {"%1$d", "2595989796776606496405"}, - {"%1$30d", " 2595989796776606496405"}, - {"%1$-30d", "2595989796776606496405 "}, - {"%1$u", "2595989796776606496405"}, - {"%1$x", "8cba9876066020f695"}, - {"%2$d", "340282366920938463463374607431768211455"}, - {"%2$u", "340282366920938463463374607431768211455"}, - {"%2$x", "ffffffffffffffffffffffffffffffff"}, - }; - - for (auto c : cases) { - UntypedFormatSpecImpl format(c.format); - EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args))); - } -} - -template <typename Floating> -void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) { - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - // Reserve the space to ensure we don't allocate memory in the output itself. - std::string str_format_result; - str_format_result.reserve(1 << 20); - std::string string_printf_result; - string_printf_result.reserve(1 << 20); - - const char *const kFormats[] = { - "%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03", - "%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; - - for (const char *fmt : kFormats) { - for (char f : {'f', 'F', // - 'g', 'G', // - 'a', 'A', // - 'e', 'E'}) { - std::string fmt_str = std::string(fmt) + f; - - if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' && - f != 'a' && f != 'A') { - // This particular test takes way too long with snprintf. - // Disable for the case we are not implementing natively. - continue; - } - - if ((f == 'a' || f == 'A') && - !native_traits.hex_float_has_glibc_rounding) { - continue; - } - - for (Floating d : floats) { - if (!native_traits.hex_float_prefers_denormal_repr && - (f == 'a' || f == 'A') && std::fpclassify(d) == FP_SUBNORMAL) { - continue; - } - int i = -10; - FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)}; - UntypedFormatSpecImpl format(fmt_str); - - string_printf_result.clear(); - StrAppend(&string_printf_result, fmt_str.c_str(), d, i); - str_format_result.clear(); - - { - AppendPack(&str_format_result, format, absl::MakeSpan(args)); - } - - if (string_printf_result != str_format_result) { - // We use ASSERT_EQ here because failures are usually correlated and a - // bug would print way too many failed expectations causing the test - // to time out. - ASSERT_EQ(string_printf_result, str_format_result) - << fmt_str << " " << StrPrint("%.18g", d) << " " - << StrPrint("%a", d) << " " << StrPrint("%.50f", d); - } - } - } - } -} - -TEST_F(FormatConvertTest, Float) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER - - std::vector<float> floats = {0.0f, - -0.0f, - .9999999f, - 9999999.f, - std::numeric_limits<float>::max(), - -std::numeric_limits<float>::max(), - std::numeric_limits<float>::min(), - -std::numeric_limits<float>::min(), - std::numeric_limits<float>::lowest(), - -std::numeric_limits<float>::lowest(), - std::numeric_limits<float>::epsilon(), - std::numeric_limits<float>::epsilon() + 1.0f, - std::numeric_limits<float>::infinity(), - -std::numeric_limits<float>::infinity()}; - - // Some regression tests. - floats.push_back(0.999999989f); - - if (std::numeric_limits<float>::has_denorm != std::denorm_absent) { - floats.push_back(std::numeric_limits<float>::denorm_min()); - floats.push_back(-std::numeric_limits<float>::denorm_min()); - } - - for (float base : - {1.f, 12.f, 123.f, 1234.f, 12345.f, 123456.f, 1234567.f, 12345678.f, - 123456789.f, 1234567890.f, 12345678901.f, 12345678.f, 12345678.f}) { - for (int exp = -123; exp <= 123; ++exp) { - for (int sign : {1, -1}) { - floats.push_back(sign * std::ldexp(base, exp)); - } - } - } - - for (int exp = -300; exp <= 300; ++exp) { - const float all_ones_mantissa = 0xffffff; - floats.push_back(std::ldexp(all_ones_mantissa, exp)); - } - - // Remove duplicates to speed up the logic below. - std::sort(floats.begin(), floats.end()); - floats.erase(std::unique(floats.begin(), floats.end()), floats.end()); - -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - floats.push_back(std::nan("")); -#endif - - TestWithMultipleFormatsHelper(floats); -} - -TEST_F(FormatConvertTest, Double) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER - - std::vector<double> doubles = {0.0, - -0.0, - .99999999999999, - 99999999999999., - std::numeric_limits<double>::max(), - -std::numeric_limits<double>::max(), - std::numeric_limits<double>::min(), - -std::numeric_limits<double>::min(), - std::numeric_limits<double>::lowest(), - -std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::epsilon(), - std::numeric_limits<double>::epsilon() + 1, - std::numeric_limits<double>::infinity(), - -std::numeric_limits<double>::infinity()}; - - // Some regression tests. - doubles.push_back(0.99999999999999989); - - if (std::numeric_limits<double>::has_denorm != std::denorm_absent) { - doubles.push_back(std::numeric_limits<double>::denorm_min()); - doubles.push_back(-std::numeric_limits<double>::denorm_min()); - } - - for (double base : - {1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789., - 1234567890., 12345678901., 123456789012., 1234567890123.}) { - for (int exp = -123; exp <= 123; ++exp) { - for (int sign : {1, -1}) { - doubles.push_back(sign * std::ldexp(base, exp)); - } - } - } - - // Workaround libc bug. - // https://sourceware.org/bugzilla/show_bug.cgi?id=22142 - const bool gcc_bug_22142 = - StrPrint("%f", std::numeric_limits<double>::max()) != - "1797693134862315708145274237317043567980705675258449965989174768031" - "5726078002853876058955863276687817154045895351438246423432132688946" - "4182768467546703537516986049910576551282076245490090389328944075868" - "5084551339423045832369032229481658085593321233482747978262041447231" - "68738177180919299881250404026184124858368.000000"; - - if (!gcc_bug_22142) { - for (int exp = -300; exp <= 300; ++exp) { - const double all_ones_mantissa = 0x1fffffffffffff; - doubles.push_back(std::ldexp(all_ones_mantissa, exp)); - } - } - - if (gcc_bug_22142) { - for (auto &d : doubles) { - using L = std::numeric_limits<double>; - double d2 = std::abs(d); - if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) { - d = 0; - } - } - } - - // Remove duplicates to speed up the logic below. - std::sort(doubles.begin(), doubles.end()); - doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end()); - -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - doubles.push_back(std::nan("")); -#endif - - TestWithMultipleFormatsHelper(doubles); -} - -TEST_F(FormatConvertTest, DoubleRound) { - std::string s; - const auto format = [&](const char *fmt, double d) -> std::string & { - s.clear(); - FormatArgImpl args[1] = {FormatArgImpl(d)}; - AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); -#if !defined(_MSC_VER) - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - EXPECT_EQ(StrPrint(fmt, d), s); -#endif // _MSC_VER - - return s; - }; - // All of these values have to be exactly represented. - // Otherwise we might not be testing what we think we are testing. - - // These values can fit in a 64bit "fast" representation. - const double exact_value = 0.00000000000005684341886080801486968994140625; - assert(exact_value == std::pow(2, -44)); - // Round up at a 5xx. - EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001"); - // Round up at a >5 - EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006"); - // Round down at a <5 - EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568"); - // Nine handling - EXPECT_EQ(format("%.35f", exact_value), - "0.00000000000005684341886080801486969"); - EXPECT_EQ(format("%.36f", exact_value), - "0.000000000000056843418860808014869690"); - // Round down the last nine. - EXPECT_EQ(format("%.37f", exact_value), - "0.0000000000000568434188608080148696899"); - EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147"); - // Round up the last nine - EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470"); - EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697"); - - // Round to even (down) - EXPECT_EQ(format("%.43f", exact_value), - "0.0000000000000568434188608080148696899414062"); - // Exact - EXPECT_EQ(format("%.44f", exact_value), - "0.00000000000005684341886080801486968994140625"); - // Round to even (up), let make the last digits 75 instead of 25 - EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)), - "0.0000000000001705302565824240446090698242188"); - // Exact, just to check. - EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)), - "0.00000000000017053025658242404460906982421875"); - - // This value has to be small enough that it won't fit in the uint128 - // representation for printing. - const double small_exact_value = - 0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625; // NOLINT - assert(small_exact_value == std::pow(2, -120)); - // Round up at a 5xx. - EXPECT_EQ(format("%.37f", small_exact_value), - "0.0000000000000000000000000000000000008"); - // Round down at a <5 - EXPECT_EQ(format("%.38f", small_exact_value), - "0.00000000000000000000000000000000000075"); - // Round up at a >5 - EXPECT_EQ(format("%.41f", small_exact_value), - "0.00000000000000000000000000000000000075232"); - // Nine handling - EXPECT_EQ(format("%.55f", small_exact_value), - "0.0000000000000000000000000000000000007523163845262640051"); - EXPECT_EQ(format("%.56f", small_exact_value), - "0.00000000000000000000000000000000000075231638452626400510"); - EXPECT_EQ(format("%.57f", small_exact_value), - "0.000000000000000000000000000000000000752316384526264005100"); - EXPECT_EQ(format("%.58f", small_exact_value), - "0.0000000000000000000000000000000000007523163845262640051000"); - // Round down the last nine - EXPECT_EQ(format("%.59f", small_exact_value), - "0.00000000000000000000000000000000000075231638452626400509999"); - // Round up the last nine - EXPECT_EQ(format("%.79f", small_exact_value), - "0.000000000000000000000000000000000000" - "7523163845262640050999913838222372338039460"); - - // Round to even (down) - EXPECT_EQ(format("%.119f", small_exact_value), - "0.000000000000000000000000000000000000" - "75231638452626400509999138382223723380" - "394595633413601376560109201818704605102539062"); - // Exact - EXPECT_EQ(format("%.120f", small_exact_value), - "0.000000000000000000000000000000000000" - "75231638452626400509999138382223723380" - "3945956334136013765601092018187046051025390625"); - // Round to even (up), let make the last digits 75 instead of 25 - EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)), - "0.000000000000000000000000000000000002" - "25694915357879201529997415146671170141" - "183786900240804129680327605456113815307617188"); - // Exact, just to check. - EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)), - "0.000000000000000000000000000000000002" - "25694915357879201529997415146671170141" - "1837869002408041296803276054561138153076171875"); -} - -TEST_F(FormatConvertTest, DoubleRoundA) { - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - std::string s; - const auto format = [&](const char *fmt, double d) -> std::string & { - s.clear(); - FormatArgImpl args[1] = {FormatArgImpl(d)}; - AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); - if (native_traits.hex_float_has_glibc_rounding) { - EXPECT_EQ(StrPrint(fmt, d), s); - } - return s; - }; - - // 0x1.00018000p+100 - const double on_boundary_odd = 1267679614447900152596896153600.0; - EXPECT_EQ(format("%.0a", on_boundary_odd), "0x1p+100"); - EXPECT_EQ(format("%.1a", on_boundary_odd), "0x1.0p+100"); - EXPECT_EQ(format("%.2a", on_boundary_odd), "0x1.00p+100"); - EXPECT_EQ(format("%.3a", on_boundary_odd), "0x1.000p+100"); - EXPECT_EQ(format("%.4a", on_boundary_odd), "0x1.0002p+100"); // round - EXPECT_EQ(format("%.5a", on_boundary_odd), "0x1.00018p+100"); - EXPECT_EQ(format("%.6a", on_boundary_odd), "0x1.000180p+100"); - - // 0x1.00028000p-2 - const double on_boundary_even = 0.250009536743164062500; - EXPECT_EQ(format("%.0a", on_boundary_even), "0x1p-2"); - EXPECT_EQ(format("%.1a", on_boundary_even), "0x1.0p-2"); - EXPECT_EQ(format("%.2a", on_boundary_even), "0x1.00p-2"); - EXPECT_EQ(format("%.3a", on_boundary_even), "0x1.000p-2"); - EXPECT_EQ(format("%.4a", on_boundary_even), "0x1.0002p-2"); // no round - EXPECT_EQ(format("%.5a", on_boundary_even), "0x1.00028p-2"); - EXPECT_EQ(format("%.6a", on_boundary_even), "0x1.000280p-2"); - - // 0x1.00018001p+1 - const double slightly_over = 2.00004577683284878730773925781250; - EXPECT_EQ(format("%.0a", slightly_over), "0x1p+1"); - EXPECT_EQ(format("%.1a", slightly_over), "0x1.0p+1"); - EXPECT_EQ(format("%.2a", slightly_over), "0x1.00p+1"); - EXPECT_EQ(format("%.3a", slightly_over), "0x1.000p+1"); - EXPECT_EQ(format("%.4a", slightly_over), "0x1.0002p+1"); - EXPECT_EQ(format("%.5a", slightly_over), "0x1.00018p+1"); - EXPECT_EQ(format("%.6a", slightly_over), "0x1.000180p+1"); - - // 0x1.00017fffp+0 - const double slightly_under = 1.000022887950763106346130371093750; - EXPECT_EQ(format("%.0a", slightly_under), "0x1p+0"); - EXPECT_EQ(format("%.1a", slightly_under), "0x1.0p+0"); - EXPECT_EQ(format("%.2a", slightly_under), "0x1.00p+0"); - EXPECT_EQ(format("%.3a", slightly_under), "0x1.000p+0"); - EXPECT_EQ(format("%.4a", slightly_under), "0x1.0001p+0"); - EXPECT_EQ(format("%.5a", slightly_under), "0x1.00018p+0"); - EXPECT_EQ(format("%.6a", slightly_under), "0x1.000180p+0"); - EXPECT_EQ(format("%.7a", slightly_under), "0x1.0001800p+0"); - - // 0x1.1b3829ac28058p+3 - const double hex_value = 8.85060580848964661981881363317370414733886718750; - EXPECT_EQ(format("%.0a", hex_value), "0x1p+3"); - EXPECT_EQ(format("%.1a", hex_value), "0x1.2p+3"); - EXPECT_EQ(format("%.2a", hex_value), "0x1.1bp+3"); - EXPECT_EQ(format("%.3a", hex_value), "0x1.1b4p+3"); - EXPECT_EQ(format("%.4a", hex_value), "0x1.1b38p+3"); - EXPECT_EQ(format("%.5a", hex_value), "0x1.1b383p+3"); - EXPECT_EQ(format("%.6a", hex_value), "0x1.1b382ap+3"); - EXPECT_EQ(format("%.7a", hex_value), "0x1.1b3829bp+3"); - EXPECT_EQ(format("%.8a", hex_value), "0x1.1b3829acp+3"); - EXPECT_EQ(format("%.9a", hex_value), "0x1.1b3829ac3p+3"); - EXPECT_EQ(format("%.10a", hex_value), "0x1.1b3829ac28p+3"); - EXPECT_EQ(format("%.11a", hex_value), "0x1.1b3829ac280p+3"); - EXPECT_EQ(format("%.12a", hex_value), "0x1.1b3829ac2806p+3"); - EXPECT_EQ(format("%.13a", hex_value), "0x1.1b3829ac28058p+3"); - EXPECT_EQ(format("%.14a", hex_value), "0x1.1b3829ac280580p+3"); - EXPECT_EQ(format("%.15a", hex_value), "0x1.1b3829ac2805800p+3"); - EXPECT_EQ(format("%.16a", hex_value), "0x1.1b3829ac28058000p+3"); - EXPECT_EQ(format("%.17a", hex_value), "0x1.1b3829ac280580000p+3"); - EXPECT_EQ(format("%.18a", hex_value), "0x1.1b3829ac2805800000p+3"); - EXPECT_EQ(format("%.19a", hex_value), "0x1.1b3829ac28058000000p+3"); - EXPECT_EQ(format("%.20a", hex_value), "0x1.1b3829ac280580000000p+3"); - EXPECT_EQ(format("%.21a", hex_value), "0x1.1b3829ac2805800000000p+3"); - - // 0x1.0818283848586p+3 - const double hex_value2 = 8.2529488658208371987257123691961169242858886718750; - EXPECT_EQ(format("%.0a", hex_value2), "0x1p+3"); - EXPECT_EQ(format("%.1a", hex_value2), "0x1.1p+3"); - EXPECT_EQ(format("%.2a", hex_value2), "0x1.08p+3"); - EXPECT_EQ(format("%.3a", hex_value2), "0x1.082p+3"); - EXPECT_EQ(format("%.4a", hex_value2), "0x1.0818p+3"); - EXPECT_EQ(format("%.5a", hex_value2), "0x1.08183p+3"); - EXPECT_EQ(format("%.6a", hex_value2), "0x1.081828p+3"); - EXPECT_EQ(format("%.7a", hex_value2), "0x1.0818284p+3"); - EXPECT_EQ(format("%.8a", hex_value2), "0x1.08182838p+3"); - EXPECT_EQ(format("%.9a", hex_value2), "0x1.081828385p+3"); - EXPECT_EQ(format("%.10a", hex_value2), "0x1.0818283848p+3"); - EXPECT_EQ(format("%.11a", hex_value2), "0x1.08182838486p+3"); - EXPECT_EQ(format("%.12a", hex_value2), "0x1.081828384858p+3"); - EXPECT_EQ(format("%.13a", hex_value2), "0x1.0818283848586p+3"); - EXPECT_EQ(format("%.14a", hex_value2), "0x1.08182838485860p+3"); - EXPECT_EQ(format("%.15a", hex_value2), "0x1.081828384858600p+3"); - EXPECT_EQ(format("%.16a", hex_value2), "0x1.0818283848586000p+3"); - EXPECT_EQ(format("%.17a", hex_value2), "0x1.08182838485860000p+3"); - EXPECT_EQ(format("%.18a", hex_value2), "0x1.081828384858600000p+3"); - EXPECT_EQ(format("%.19a", hex_value2), "0x1.0818283848586000000p+3"); - EXPECT_EQ(format("%.20a", hex_value2), "0x1.08182838485860000000p+3"); - EXPECT_EQ(format("%.21a", hex_value2), "0x1.081828384858600000000p+3"); -} - -TEST_F(FormatConvertTest, LongDoubleRoundA) { - if (std::numeric_limits<long double>::digits % 4 != 0) { - // This test doesn't really make sense to run on platforms where a long - // double has a different mantissa size (mod 4) than Prod, since then the - // leading digit will be formatted differently. - return; - } - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - std::string s; - const auto format = [&](const char *fmt, long double d) -> std::string & { - s.clear(); - FormatArgImpl args[1] = {FormatArgImpl(d)}; - AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); - if (native_traits.hex_float_has_glibc_rounding && - native_traits.hex_float_optimizes_leading_digit_bit_count) { - EXPECT_EQ(StrPrint(fmt, d), s); - } - return s; - }; - - // 0x8.8p+4 - const long double on_boundary_even = 136.0; - EXPECT_EQ(format("%.0La", on_boundary_even), "0x8p+4"); - EXPECT_EQ(format("%.1La", on_boundary_even), "0x8.8p+4"); - EXPECT_EQ(format("%.2La", on_boundary_even), "0x8.80p+4"); - EXPECT_EQ(format("%.3La", on_boundary_even), "0x8.800p+4"); - EXPECT_EQ(format("%.4La", on_boundary_even), "0x8.8000p+4"); - EXPECT_EQ(format("%.5La", on_boundary_even), "0x8.80000p+4"); - EXPECT_EQ(format("%.6La", on_boundary_even), "0x8.800000p+4"); - - // 0x9.8p+4 - const long double on_boundary_odd = 152.0; - EXPECT_EQ(format("%.0La", on_boundary_odd), "0xap+4"); - EXPECT_EQ(format("%.1La", on_boundary_odd), "0x9.8p+4"); - EXPECT_EQ(format("%.2La", on_boundary_odd), "0x9.80p+4"); - EXPECT_EQ(format("%.3La", on_boundary_odd), "0x9.800p+4"); - EXPECT_EQ(format("%.4La", on_boundary_odd), "0x9.8000p+4"); - EXPECT_EQ(format("%.5La", on_boundary_odd), "0x9.80000p+4"); - EXPECT_EQ(format("%.6La", on_boundary_odd), "0x9.800000p+4"); - - // 0x8.80001p+24 - const long double slightly_over = 142606352.0; - EXPECT_EQ(format("%.0La", slightly_over), "0x9p+24"); - EXPECT_EQ(format("%.1La", slightly_over), "0x8.8p+24"); - EXPECT_EQ(format("%.2La", slightly_over), "0x8.80p+24"); - EXPECT_EQ(format("%.3La", slightly_over), "0x8.800p+24"); - EXPECT_EQ(format("%.4La", slightly_over), "0x8.8000p+24"); - EXPECT_EQ(format("%.5La", slightly_over), "0x8.80001p+24"); - EXPECT_EQ(format("%.6La", slightly_over), "0x8.800010p+24"); - - // 0x8.7ffffp+24 - const long double slightly_under = 142606320.0; - EXPECT_EQ(format("%.0La", slightly_under), "0x8p+24"); - EXPECT_EQ(format("%.1La", slightly_under), "0x8.8p+24"); - EXPECT_EQ(format("%.2La", slightly_under), "0x8.80p+24"); - EXPECT_EQ(format("%.3La", slightly_under), "0x8.800p+24"); - EXPECT_EQ(format("%.4La", slightly_under), "0x8.8000p+24"); - EXPECT_EQ(format("%.5La", slightly_under), "0x8.7ffffp+24"); - EXPECT_EQ(format("%.6La", slightly_under), "0x8.7ffff0p+24"); - EXPECT_EQ(format("%.7La", slightly_under), "0x8.7ffff00p+24"); - - // 0xc.0828384858688000p+128 - const long double eights = 4094231060438608800781871108094404067328.0; - EXPECT_EQ(format("%.0La", eights), "0xcp+128"); - EXPECT_EQ(format("%.1La", eights), "0xc.1p+128"); - EXPECT_EQ(format("%.2La", eights), "0xc.08p+128"); - EXPECT_EQ(format("%.3La", eights), "0xc.083p+128"); - EXPECT_EQ(format("%.4La", eights), "0xc.0828p+128"); - EXPECT_EQ(format("%.5La", eights), "0xc.08284p+128"); - EXPECT_EQ(format("%.6La", eights), "0xc.082838p+128"); - EXPECT_EQ(format("%.7La", eights), "0xc.0828385p+128"); - EXPECT_EQ(format("%.8La", eights), "0xc.08283848p+128"); - EXPECT_EQ(format("%.9La", eights), "0xc.082838486p+128"); - EXPECT_EQ(format("%.10La", eights), "0xc.0828384858p+128"); - EXPECT_EQ(format("%.11La", eights), "0xc.08283848587p+128"); - EXPECT_EQ(format("%.12La", eights), "0xc.082838485868p+128"); - EXPECT_EQ(format("%.13La", eights), "0xc.0828384858688p+128"); - EXPECT_EQ(format("%.14La", eights), "0xc.08283848586880p+128"); - EXPECT_EQ(format("%.15La", eights), "0xc.082838485868800p+128"); - EXPECT_EQ(format("%.16La", eights), "0xc.0828384858688000p+128"); -} - -// We don't actually store the results. This is just to exercise the rest of the -// machinery. -struct NullSink { - friend void AbslFormatFlush(NullSink *sink, string_view str) {} -}; - -template <typename... T> -bool FormatWithNullSink(absl::string_view fmt, const T &... a) { - NullSink sink; - FormatArgImpl args[] = {FormatArgImpl(a)...}; - return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); -} - -TEST_F(FormatConvertTest, ExtremeWidthPrecision) { - for (const char *fmt : {"f"}) { - for (double d : {1e-100, 1.0, 1e100}) { - constexpr int max = std::numeric_limits<int>::max(); - EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d)); - EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d)); - EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d)); - EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d)); - } - } -} - -TEST_F(FormatConvertTest, LongDouble) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000", - "%.60", "%+", "% ", "%-10"}; - - std::vector<long double> doubles = { - 0.0, - -0.0, - std::numeric_limits<long double>::max(), - -std::numeric_limits<long double>::max(), - std::numeric_limits<long double>::min(), - -std::numeric_limits<long double>::min(), - std::numeric_limits<long double>::infinity(), - -std::numeric_limits<long double>::infinity()}; - - for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L, - 1234567.L, 12345678.L, 123456789.L, 1234567890.L, - 12345678901.L, 123456789012.L, 1234567890123.L, - // This value is not representable in double, but it - // is in long double that uses the extended format. - // This is to verify that we are not truncating the - // value mistakenly through a double. - 10000000000000000.25L}) { - for (int exp : {-1000, -500, 0, 500, 1000}) { - for (int sign : {1, -1}) { - doubles.push_back(sign * std::ldexp(base, exp)); - doubles.push_back(sign / std::ldexp(base, exp)); - } - } - } - - // Regression tests - // - // Using a string literal because not all platforms support hex literals or it - // might be out of range. - doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr)); - - for (const char *fmt : kFormats) { - for (char f : {'f', 'F', // - 'g', 'G', // - 'a', 'A', // - 'e', 'E'}) { - std::string fmt_str = std::string(fmt) + 'L' + f; - - if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' && - f != 'a' && f != 'A') { - // This particular test takes way too long with snprintf. - // Disable for the case we are not implementing natively. - continue; - } - - if (f == 'a' || f == 'A') { - if (!native_traits.hex_float_has_glibc_rounding || - !native_traits.hex_float_optimizes_leading_digit_bit_count) { - continue; - } - } - - for (auto d : doubles) { - FormatArgImpl arg(d); - UntypedFormatSpecImpl format(fmt_str); - // We use ASSERT_EQ here because failures are usually correlated and a - // bug would print way too many failed expectations causing the test to - // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1})) - << fmt_str << " " << StrPrint("%.18Lg", d) << " " - << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d); - } - } - } -} - -TEST_F(FormatConvertTest, IntAsDouble) { - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - const int kMin = std::numeric_limits<int>::min(); - const int kMax = std::numeric_limits<int>::max(); - const int ia[] = { - 1, 2, 3, 123, - -1, -2, -3, -123, - 0, kMax - 1, kMax, kMin + 1, kMin }; - for (const int fx : ia) { - SCOPED_TRACE(fx); - const FormatArgImpl args[] = {FormatArgImpl(fx)}; - struct Expectation { - int line; - std::string out; - const char *fmt; - }; - const double dx = static_cast<double>(fx); - std::vector<Expectation> expect = { - {__LINE__, StrPrint("%f", dx), "%f"}, - {__LINE__, StrPrint("%12f", dx), "%12f"}, - {__LINE__, StrPrint("%.12f", dx), "%.12f"}, - {__LINE__, StrPrint("%.12a", dx), "%.12a"}, - }; - if (native_traits.hex_float_uses_minimal_precision_when_not_specified) { - Expectation ex = {__LINE__, StrPrint("%12a", dx), "%12a"}; - expect.push_back(ex); - } - for (const Expectation &e : expect) { - SCOPED_TRACE(e.line); - SCOPED_TRACE(e.fmt); - UntypedFormatSpecImpl format(e.fmt); - EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))); - } - } -} - -template <typename T> -bool FormatFails(const char* test_format, T value) { - std::string format_string = std::string("<<") + test_format + ">>"; - UntypedFormatSpecImpl format(format_string); - - int one = 1; - const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)}; - EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "") - << "format=" << test_format << " value=" << value; - return FormatPack(format, absl::MakeSpan(args)).empty(); -} - -TEST_F(FormatConvertTest, ExpectedFailures) { - // Int input - EXPECT_TRUE(FormatFails("%p", 1)); - EXPECT_TRUE(FormatFails("%s", 1)); - EXPECT_TRUE(FormatFails("%n", 1)); - - // Double input - EXPECT_TRUE(FormatFails("%p", 1.)); - EXPECT_TRUE(FormatFails("%s", 1.)); - EXPECT_TRUE(FormatFails("%n", 1.)); - EXPECT_TRUE(FormatFails("%c", 1.)); - EXPECT_TRUE(FormatFails("%d", 1.)); - EXPECT_TRUE(FormatFails("%x", 1.)); - EXPECT_TRUE(FormatFails("%*d", 1.)); - - // String input - EXPECT_TRUE(FormatFails("%n", "")); - EXPECT_TRUE(FormatFails("%c", "")); - EXPECT_TRUE(FormatFails("%d", "")); - EXPECT_TRUE(FormatFails("%x", "")); - EXPECT_TRUE(FormatFails("%f", "")); - EXPECT_TRUE(FormatFails("%*d", "")); -} - -// Sanity check to make sure that we are testing what we think we're testing on -// e.g. the x86_64+glibc platform. -TEST_F(FormatConvertTest, GlibcHasCorrectTraits) { -#if !defined(__GLIBC__) || !defined(__x86_64__) - return; -#endif - const NativePrintfTraits &native_traits = VerifyNativeImplementation(); - // If one of the following tests break then it is either because the above PP - // macro guards failed to exclude a new platform (likely) or because something - // has changed in the implemention of glibc sprintf float formatting behavior. - // If the latter, then the code that computes these flags needs to be - // revisited and/or possibly the StrFormat implementation. - EXPECT_TRUE(native_traits.hex_float_has_glibc_rounding); - EXPECT_TRUE(native_traits.hex_float_prefers_denormal_repr); - EXPECT_TRUE( - native_traits.hex_float_uses_minimal_precision_when_not_specified); - EXPECT_TRUE(native_traits.hex_float_optimizes_leading_digit_bit_count); -} - -} // namespace -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc deleted file mode 100644 index bb0d96cf3216..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/extension.h" - -#include <errno.h> -#include <algorithm> -#include <string> - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -std::string Flags::ToString() const { - std::string s; - s.append(left ? "-" : ""); - s.append(show_pos ? "+" : ""); - s.append(sign_col ? " " : ""); - s.append(alt ? "#" : ""); - s.append(zero ? "0" : ""); - return s; -} - -#define ABSL_INTERNAL_X_VAL(id) \ - constexpr absl::FormatConversionChar FormatConversionCharInternal::id; -ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) -#undef ABSL_INTERNAL_X_VAL -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone; - -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - constexpr FormatConversionCharSet FormatConversionCharSetInternal::c; -ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) -#undef ABSL_INTERNAL_CHAR_SET_CASE - -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; -// NOLINTNEXTLINE(readability-redundant-declaration) -constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; - -bool FormatSinkImpl::PutPaddedString(string_view value, int width, - int precision, bool left) { - size_t space_remaining = 0; - if (width >= 0) space_remaining = width; - size_t n = value.size(); - if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); - string_view shown(value.data(), n); - space_remaining = Excess(shown.size(), space_remaining); - if (!left) Append(space_remaining, ' '); - Append(shown); - if (left) Append(space_remaining, ' '); - return true; -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h b/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h deleted file mode 100644 index a9b9e137deb2..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h +++ /dev/null @@ -1,427 +0,0 @@ -// -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ - -#include <limits.h> - -#include <cstddef> -#include <cstring> -#include <ostream> - -#include "absl/base/config.h" -#include "absl/base/port.h" -#include "absl/meta/type_traits.h" -#include "absl/strings/internal/str_format/output.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -enum class FormatConversionChar : uint8_t; -enum class FormatConversionCharSet : uint64_t; - -namespace str_format_internal { - -class FormatRawSinkImpl { - public: - // Implicitly convert from any type that provides the hook function as - // described above. - template <typename T, decltype(str_format_internal::InvokeFlush( - std::declval<T*>(), string_view()))* = nullptr> - FormatRawSinkImpl(T* raw) // NOLINT - : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {} - - void Write(string_view s) { write_(sink_, s); } - - template <typename T> - static FormatRawSinkImpl Extract(T s) { - return s.sink_; - } - - private: - template <typename T> - static void Flush(void* r, string_view s) { - str_format_internal::InvokeFlush(static_cast<T*>(r), s); - } - - void* sink_; - void (*write_)(void*, string_view); -}; - -// An abstraction to which conversions write their string data. -class FormatSinkImpl { - public: - explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {} - - ~FormatSinkImpl() { Flush(); } - - void Flush() { - raw_.Write(string_view(buf_, pos_ - buf_)); - pos_ = buf_; - } - - void Append(size_t n, char c) { - if (n == 0) return; - size_ += n; - auto raw_append = [&](size_t count) { - memset(pos_, c, count); - pos_ += count; - }; - while (n > Avail()) { - n -= Avail(); - if (Avail() > 0) { - raw_append(Avail()); - } - Flush(); - } - raw_append(n); - } - - void Append(string_view v) { - size_t n = v.size(); - if (n == 0) return; - size_ += n; - if (n >= Avail()) { - Flush(); - raw_.Write(v); - return; - } - memcpy(pos_, v.data(), n); - pos_ += n; - } - - size_t size() const { return size_; } - - // Put 'v' to 'sink' with specified width, precision, and left flag. - bool PutPaddedString(string_view v, int width, int precision, bool left); - - template <typename T> - T Wrap() { - return T(this); - } - - template <typename T> - static FormatSinkImpl* Extract(T* s) { - return s->sink_; - } - - private: - size_t Avail() const { return buf_ + sizeof(buf_) - pos_; } - - FormatRawSinkImpl raw_; - size_t size_ = 0; - char* pos_ = buf_; - char buf_[1024]; -}; - -struct Flags { - bool basic : 1; // fastest conversion: no flags, width, or precision - bool left : 1; // "-" - bool show_pos : 1; // "+" - bool sign_col : 1; // " " - bool alt : 1; // "#" - bool zero : 1; // "0" - std::string ToString() const; - friend std::ostream& operator<<(std::ostream& os, const Flags& v) { - return os << v.ToString(); - } -}; - -// clang-format off -#define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ - /* text */ \ - X_VAL(c) X_SEP X_VAL(s) X_SEP \ - /* ints */ \ - X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ - X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ - /* floats */ \ - X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \ - X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ - /* misc */ \ - X_VAL(n) X_SEP X_VAL(p) -// clang-format on - -// This type should not be referenced, it exists only to provide labels -// internally that match the values declared in FormatConversionChar in -// str_format.h. This is meant to allow internal libraries to use the same -// declared interface type as the public interface -// (absl::StrFormatConversionChar) while keeping the definition in a public -// header. -// Internal libraries should use the form -// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for -// comparisons. Use in switch statements is not recommended due to a bug in how -// gcc 4.9 -Wswitch handles declared but undefined enums. -struct FormatConversionCharInternal { - FormatConversionCharInternal() = delete; - - private: - // clang-format off - enum class Enum : uint8_t { - c, s, // text - d, i, o, u, x, X, // int - f, F, e, E, g, G, a, A, // float - n, p, // misc - kNone - }; - // clang-format on - public: -#define ABSL_INTERNAL_X_VAL(id) \ - static constexpr FormatConversionChar id = \ - static_cast<FormatConversionChar>(Enum::id); - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) -#undef ABSL_INTERNAL_X_VAL - static constexpr FormatConversionChar kNone = - static_cast<FormatConversionChar>(Enum::kNone); -}; -// clang-format on - -inline FormatConversionChar FormatConversionCharFromChar(char c) { - switch (c) { -#define ABSL_INTERNAL_X_VAL(id) \ - case #id[0]: \ - return FormatConversionCharInternal::id; - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) -#undef ABSL_INTERNAL_X_VAL - } - return FormatConversionCharInternal::kNone; -} - -inline bool FormatConversionCharIsUpper(FormatConversionChar c) { - if (c == FormatConversionCharInternal::X || - c == FormatConversionCharInternal::F || - c == FormatConversionCharInternal::E || - c == FormatConversionCharInternal::G || - c == FormatConversionCharInternal::A) { - return true; - } else { - return false; - } -} - -inline bool FormatConversionCharIsFloat(FormatConversionChar c) { - if (c == FormatConversionCharInternal::a || - c == FormatConversionCharInternal::e || - c == FormatConversionCharInternal::f || - c == FormatConversionCharInternal::g || - c == FormatConversionCharInternal::A || - c == FormatConversionCharInternal::E || - c == FormatConversionCharInternal::F || - c == FormatConversionCharInternal::G) { - return true; - } else { - return false; - } -} - -inline char FormatConversionCharToChar(FormatConversionChar c) { - if (c == FormatConversionCharInternal::kNone) { - return '\0'; - -#define ABSL_INTERNAL_X_VAL(e) \ - } else if (c == FormatConversionCharInternal::e) { \ - return #e[0]; -#define ABSL_INTERNAL_X_SEP - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, - ABSL_INTERNAL_X_SEP) - } else { - return '\0'; - } - -#undef ABSL_INTERNAL_X_VAL -#undef ABSL_INTERNAL_X_SEP -} - -// The associated char. -inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) { - char c = FormatConversionCharToChar(v); - if (!c) c = '?'; - return os << c; -} - -struct FormatConversionSpecImplFriend; - -class FormatConversionSpecImpl { - public: - // Width and precison are not specified, no flags are set. - bool is_basic() const { return flags_.basic; } - bool has_left_flag() const { return flags_.left; } - bool has_show_pos_flag() const { return flags_.show_pos; } - bool has_sign_col_flag() const { return flags_.sign_col; } - bool has_alt_flag() const { return flags_.alt; } - bool has_zero_flag() const { return flags_.zero; } - - FormatConversionChar conversion_char() const { - // Keep this field first in the struct . It generates better code when - // accessing it when ConversionSpec is passed by value in registers. - static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, ""); - return conv_; - } - - // Returns the specified width. If width is unspecfied, it returns a negative - // value. - int width() const { return width_; } - // Returns the specified precision. If precision is unspecfied, it returns a - // negative value. - int precision() const { return precision_; } - - template <typename T> - T Wrap() { - return T(*this); - } - - private: - friend struct str_format_internal::FormatConversionSpecImplFriend; - FormatConversionChar conv_ = FormatConversionCharInternal::kNone; - Flags flags_; - int width_; - int precision_; -}; - -struct FormatConversionSpecImplFriend final { - static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { - conv->flags_ = f; - } - static void SetConversionChar(FormatConversionChar c, - FormatConversionSpecImpl* conv) { - conv->conv_ = c; - } - static void SetWidth(int w, FormatConversionSpecImpl* conv) { - conv->width_ = w; - } - static void SetPrecision(int p, FormatConversionSpecImpl* conv) { - conv->precision_ = p; - } - static std::string FlagsToString(const FormatConversionSpecImpl& spec) { - return spec.flags_.ToString(); - } -}; - -// Type safe OR operator. -// We need this for two reasons: -// 1. operator| on enums makes them decay to integers and the result is an -// integer. We need the result to stay as an enum. -// 2. We use "enum class" which would not work even if we accepted the decay. -constexpr FormatConversionCharSet FormatConversionCharSetUnion( - FormatConversionCharSet a) { - return a; -} - -template <typename... CharSet> -constexpr FormatConversionCharSet FormatConversionCharSetUnion( - FormatConversionCharSet a, CharSet... rest) { - return static_cast<FormatConversionCharSet>( - static_cast<uint64_t>(a) | - static_cast<uint64_t>(FormatConversionCharSetUnion(rest...))); -} - -constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) { - return uint64_t{1} << (1 + static_cast<uint8_t>(c)); -} - -constexpr uint64_t FormatConversionCharToConvInt(char conv) { - return -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - conv == #c[0] \ - ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \ - : - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) -#undef ABSL_INTERNAL_CHAR_SET_CASE - conv == '*' - ? 1 - : 0; -} - -constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) { - return static_cast<FormatConversionCharSet>( - FormatConversionCharToConvInt(conv)); -} - -struct FormatConversionCharSetInternal { -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - static constexpr FormatConversionCharSet c = \ - FormatConversionCharToConvValue(#c[0]); - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) -#undef ABSL_INTERNAL_CHAR_SET_CASE - - // Used for width/precision '*' specification. - static constexpr FormatConversionCharSet kStar = - FormatConversionCharToConvValue('*'); - - static constexpr FormatConversionCharSet kIntegral = - FormatConversionCharSetUnion(d, i, u, o, x, X); - static constexpr FormatConversionCharSet kFloating = - FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); - static constexpr FormatConversionCharSet kNumeric = - FormatConversionCharSetUnion(kIntegral, kFloating); - static constexpr FormatConversionCharSet kPointer = p; -}; - -// Type safe OR operator. -// We need this for two reasons: -// 1. operator| on enums makes them decay to integers and the result is an -// integer. We need the result to stay as an enum. -// 2. We use "enum class" which would not work even if we accepted the decay. -constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, - FormatConversionCharSet b) { - return FormatConversionCharSetUnion(a, b); -} - -// Overloaded conversion functions to support absl::ParsedFormat. -// Get a conversion with a single character in it. -constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) { - return static_cast<FormatConversionCharSet>( - FormatConversionCharToConvValue(c)); -} - -// Get a conversion with a single character in it. -constexpr FormatConversionCharSet ToFormatConversionCharSet( - FormatConversionCharSet c) { - return c; -} - -template <typename T> -void ToFormatConversionCharSet(T) = delete; - -// Checks whether `c` exists in `set`. -constexpr bool Contains(FormatConversionCharSet set, char c) { - return (static_cast<uint64_t>(set) & - static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0; -} - -// Checks whether all the characters in `c` are contained in `set` -constexpr bool Contains(FormatConversionCharSet set, - FormatConversionCharSet c) { - return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) == - static_cast<uint64_t>(c); -} - -// Checks whether all the characters in `c` are contained in `set` -constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) { - return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0; -} - -// Return capacity - used, clipped to a minimum of 0. -inline size_t Excess(size_t used, size_t capacity) { - return used < capacity ? capacity - used : 0; -} - -} // namespace str_format_internal - -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc deleted file mode 100644 index 1c93fdb1c75b..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "absl/strings/internal/str_format/extension.h" - -#include <random> -#include <string> - -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" - -namespace my_namespace { -class UserDefinedType { - public: - UserDefinedType() = default; - - void Append(absl::string_view str) { value_.append(str.data(), str.size()); } - const std::string& Value() const { return value_; } - - friend void AbslFormatFlush(UserDefinedType* x, absl::string_view str) { - x->Append(str); - } - - private: - std::string value_; -}; -} // namespace my_namespace - -namespace { - -std::string MakeRandomString(size_t len) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis('a', 'z'); - std::string s(len, '0'); - for (char& c : s) { - c = dis(gen); - } - return s; -} - -TEST(FormatExtensionTest, SinkAppendSubstring) { - for (size_t chunk_size : {1, 10, 100, 1000, 10000}) { - std::string expected, actual; - absl::str_format_internal::FormatSinkImpl sink(&actual); - for (size_t chunks = 0; chunks < 10; ++chunks) { - std::string rand = MakeRandomString(chunk_size); - expected += rand; - sink.Append(rand); - } - sink.Flush(); - EXPECT_EQ(actual, expected); - } -} - -TEST(FormatExtensionTest, SinkAppendChars) { - for (size_t chunk_size : {1, 10, 100, 1000, 10000}) { - std::string expected, actual; - absl::str_format_internal::FormatSinkImpl sink(&actual); - for (size_t chunks = 0; chunks < 10; ++chunks) { - std::string rand = MakeRandomString(1); - expected.append(chunk_size, rand[0]); - sink.Append(chunk_size, rand[0]); - } - sink.Flush(); - EXPECT_EQ(actual, expected); - } -} - -TEST(FormatExtensionTest, VerifyEnumEquality) { -#define X_VAL(id) \ - EXPECT_EQ(absl::FormatConversionChar::id, \ - absl::str_format_internal::FormatConversionCharInternal::id); - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); -#undef X_VAL - -#define X_VAL(id) \ - EXPECT_EQ(absl::FormatConversionCharSet::id, \ - absl::str_format_internal::FormatConversionCharSetInternal::id); - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); -#undef X_VAL -} - -} // namespace diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc deleted file mode 100644 index 0ded0a66afa9..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc +++ /dev/null @@ -1,1419 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/float_conversion.h" - -#include <string.h> - -#include <algorithm> -#include <cassert> -#include <cmath> -#include <limits> -#include <string> - -#include "absl/base/attributes.h" -#include "absl/base/config.h" -#include "absl/base/internal/bits.h" -#include "absl/base/optimization.h" -#include "absl/functional/function_ref.h" -#include "absl/meta/type_traits.h" -#include "absl/numeric/int128.h" -#include "absl/strings/numbers.h" -#include "absl/types/optional.h" -#include "absl/types/span.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -namespace { - -// The code below wants to avoid heap allocations. -// To do so it needs to allocate memory on the stack. -// `StackArray` will allocate memory on the stack in the form of a uint32_t -// array and call the provided callback with said memory. -// It will allocate memory in increments of 512 bytes. We could allocate the -// largest needed unconditionally, but that is more than we need in most of -// cases. This way we use less stack in the common cases. -class StackArray { - using Func = absl::FunctionRef<void(absl::Span<uint32_t>)>; - static constexpr size_t kStep = 512 / sizeof(uint32_t); - // 5 steps is 2560 bytes, which is enough to hold a long double with the - // largest/smallest exponents. - // The operations below will static_assert their particular maximum. - static constexpr size_t kNumSteps = 5; - - // We do not want this function to be inlined. - // Otherwise the caller will allocate the stack space unnecessarily for all - // the variants even though it only calls one. - template <size_t steps> - ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) { - uint32_t values[steps * kStep]{}; - f(absl::MakeSpan(values)); - } - - public: - static constexpr size_t kMaxCapacity = kStep * kNumSteps; - - static void RunWithCapacity(size_t capacity, Func f) { - assert(capacity <= kMaxCapacity); - const size_t step = (capacity + kStep - 1) / kStep; - assert(step <= kNumSteps); - switch (step) { - case 1: - return RunWithCapacityImpl<1>(f); - case 2: - return RunWithCapacityImpl<2>(f); - case 3: - return RunWithCapacityImpl<3>(f); - case 4: - return RunWithCapacityImpl<4>(f); - case 5: - return RunWithCapacityImpl<5>(f); - } - - assert(false && "Invalid capacity"); - } -}; - -// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns -// the carry. -template <typename Int> -inline Int MultiplyBy10WithCarry(Int *v, Int carry) { - using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; - BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; - *v = static_cast<Int>(tmp); - return static_cast<Int>(tmp >> (sizeof(Int) * 8)); -} - -// Calculates `(2^64 * carry + *v) / 10`. -// Stores the quotient in `*v` and returns the remainder. -// Requires: `0 <= carry <= 9` -inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) { - constexpr uint64_t divisor = 10; - // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor - constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2); - constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor; - - const uint64_t mod = *v % divisor; - const uint64_t next_carry = chunk_remainder * carry + mod; - *v = *v / divisor + carry * chunk_quotient + next_carry / divisor; - return next_carry % divisor; -} - -// Generates the decimal representation for an integer of the form `v * 2^exp`, -// where `v` and `exp` are both positive integers. -// It generates the digits from the left (ie the most significant digit first) -// to allow for direct printing into the sink. -// -// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`. -class BinaryToDecimal { - static constexpr int ChunksNeeded(int exp) { - // We will left shift a uint128 by `exp` bits, so we need `128+exp` total - // bits. Round up to 32. - // See constructor for details about adding `10%` to the value. - return (128 + exp + 31) / 32 * 11 / 10; - } - - public: - // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`. - // This function will allocate enough stack space to perform the conversion. - static void RunConversion(uint128 v, int exp, - absl::FunctionRef<void(BinaryToDecimal)> f) { - assert(exp > 0); - assert(exp <= std::numeric_limits<long double>::max_exponent); - static_assert( - static_cast<int>(StackArray::kMaxCapacity) >= - ChunksNeeded(std::numeric_limits<long double>::max_exponent), - ""); - - StackArray::RunWithCapacity( - ChunksNeeded(exp), - [=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); }); - } - - int TotalDigits() const { - return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk + - CurrentDigits().size()); - } - - // See the current block of digits. - absl::string_view CurrentDigits() const { - return absl::string_view(digits_ + kDigitsPerChunk - size_, size_); - } - - // Advance the current view of digits. - // Returns `false` when no more digits are available. - bool AdvanceDigits() { - if (decimal_start_ >= decimal_end_) return false; - - uint32_t w = data_[decimal_start_++]; - for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) { - digits_[kDigitsPerChunk - ++size_] = w % 10 + '0'; - } - return true; - } - - private: - BinaryToDecimal(absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) { - // We need to print the digits directly into the sink object without - // buffering them all first. To do this we need two things: - // - to know the total number of digits to do padding when necessary - // - to generate the decimal digits from the left. - // - // In order to do this, we do a two pass conversion. - // On the first pass we convert the binary representation of the value into - // a decimal representation in which each uint32_t chunk holds up to 9 - // decimal digits. In the second pass we take each decimal-holding-uint32_t - // value and generate the ascii decimal digits into `digits_`. - // - // The binary and decimal representations actually share the same memory - // region. As we go converting the chunks from binary to decimal we free - // them up and reuse them for the decimal representation. One caveat is that - // the decimal representation is around 7% less efficient in space than the - // binary one. We allocate an extra 10% memory to account for this. See - // ChunksNeeded for this calculation. - int chunk_index = exp / 32; - decimal_start_ = decimal_end_ = ChunksNeeded(exp); - const int offset = exp % 32; - // Left shift v by exp bits. - data_[chunk_index] = static_cast<uint32_t>(v << offset); - for (v >>= (32 - offset); v; v >>= 32) - data_[++chunk_index] = static_cast<uint32_t>(v); - - while (chunk_index >= 0) { - // While we have more than one chunk available, go in steps of 1e9. - // `data_[chunk_index]` holds the highest non-zero binary chunk, so keep - // the variable updated. - uint32_t carry = 0; - for (int i = chunk_index; i >= 0; --i) { - uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32); - data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); - carry = static_cast<uint32_t>(tmp % uint64_t{1000000000}); - } - - // If the highest chunk is now empty, remove it from view. - if (data_[chunk_index] == 0) --chunk_index; - - --decimal_start_; - assert(decimal_start_ != chunk_index); - data_[decimal_start_] = carry; - } - - // Fill the first set of digits. The first chunk might not be complete, so - // handle differently. - for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) { - digits_[kDigitsPerChunk - ++size_] = first % 10 + '0'; - } - } - - private: - static constexpr int kDigitsPerChunk = 9; - - int decimal_start_; - int decimal_end_; - - char digits_[kDigitsPerChunk]; - int size_ = 0; - - absl::Span<uint32_t> data_; -}; - -// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. -// Requires `-exp < 0` and -// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`. -class FractionalDigitGenerator { - public: - // Run the conversion for `v * 2^exp` and call `f(generator)`. - // This function will allocate enough stack space to perform the conversion. - static void RunConversion( - uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) { - using Limits = std::numeric_limits<long double>; - assert(-exp < 0); - assert(-exp >= Limits::min_exponent - 128); - static_assert(StackArray::kMaxCapacity >= - (Limits::digits + 128 - Limits::min_exponent + 31) / 32, - ""); - StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32, - [=](absl::Span<uint32_t> input) { - f(FractionalDigitGenerator(input, v, exp)); - }); - } - - // Returns true if there are any more non-zero digits left. - bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } - - // Returns true if the remainder digits are greater than 5000... - bool IsGreaterThanHalf() const { - return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); - } - // Returns true if the remainder digits are exactly 5000... - bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } - - struct Digits { - int digit_before_nine; - int num_nines; - }; - - // Get the next set of digits. - // They are composed by a non-9 digit followed by a runs of zero or more 9s. - Digits GetDigits() { - Digits digits{next_digit_, 0}; - - next_digit_ = GetOneDigit(); - while (next_digit_ == 9) { - ++digits.num_nines; - next_digit_ = GetOneDigit(); - } - - return digits; - } - - private: - // Return the next digit. - int GetOneDigit() { - if (chunk_index_ < 0) return 0; - - uint32_t carry = 0; - for (int i = chunk_index_; i >= 0; --i) { - carry = MultiplyBy10WithCarry(&data_[i], carry); - } - // If the lowest chunk is now empty, remove it from view. - if (data_[chunk_index_] == 0) --chunk_index_; - return carry; - } - - FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp) - : chunk_index_(exp / 32), data_(data) { - const int offset = exp % 32; - // Right shift `v` by `exp` bits. - data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset)); - v >>= offset; - // Make sure we don't overflow the data. We already calculated that - // non-zero bits fit, so we might not have space for leading zero bits. - for (int pos = chunk_index_; v; v >>= 32) - data_[--pos] = static_cast<uint32_t>(v); - - // Fill next_digit_, as GetDigits expects it to be populated always. - next_digit_ = GetOneDigit(); - } - - int next_digit_; - int chunk_index_; - absl::Span<uint32_t> data_; -}; - -// Count the number of leading zero bits. -int LeadingZeros(uint64_t v) { return base_internal::CountLeadingZeros64(v); } -int LeadingZeros(uint128 v) { - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - return high != 0 ? base_internal::CountLeadingZeros64(high) - : 64 + base_internal::CountLeadingZeros64(low); -} - -// Round up the text digits starting at `p`. -// The buffer must have an extra digit that is known to not need rounding. -// This is done below by having an extra '0' digit on the left. -void RoundUp(char *p) { - while (*p == '9' || *p == '.') { - if (*p == '9') *p = '0'; - --p; - } - ++*p; -} - -// Check the previous digit and round up or down to follow the round-to-even -// policy. -void RoundToEven(char *p) { - if (*p == '.') --p; - if (*p % 2 == 1) RoundUp(p); -} - -// Simple integral decimal digit printing for values that fit in 64-bits. -// Returns the pointer to the last written digit. -char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) { - do { - *--p = DivideBy10WithCarry(&v, 0) + '0'; - } while (v != 0); - return p; -} - -// Simple integral decimal digit printing for values that fit in 128-bits. -// Returns the pointer to the last written digit. -char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - - while (high != 0) { - uint64_t carry = DivideBy10WithCarry(&high, 0); - carry = DivideBy10WithCarry(&low, carry); - *--p = carry + '0'; - } - return PrintIntegralDigitsFromRightFast(low, p); -} - -// Simple fractional decimal digit printing for values that fir in 64-bits after -// shifting. -// Performs rounding if necessary to fit within `precision`. -// Returns the pointer to one after the last character written. -char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, - int precision) { - char *p = start; - v <<= (64 - exp); - while (precision > 0) { - if (!v) return p; - *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; - --precision; - } - - // We need to round. - if (v < 0x8000000000000000) { - // We round down, so nothing to do. - } else if (v > 0x8000000000000000) { - // We round up. - RoundUp(p - 1); - } else { - RoundToEven(p - 1); - } - - assert(precision == 0); - // Precision can only be zero here. - return p; -} - -// Simple fractional decimal digit printing for values that fir in 128-bits -// after shifting. -// Performs rounding if necessary to fit within `precision`. -// Returns the pointer to one after the last character written. -char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, - int precision) { - char *p = start; - v <<= (128 - exp); - auto high = static_cast<uint64_t>(v >> 64); - auto low = static_cast<uint64_t>(v); - - // While we have digits to print and `low` is not empty, do the long - // multiplication. - while (precision > 0 && low != 0) { - uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0}); - carry = MultiplyBy10WithCarry(&high, carry); - - *p++ = carry + '0'; - --precision; - } - - // Now `low` is empty, so use a faster approach for the rest of the digits. - // This block is pretty much the same as the main loop for the 64-bit case - // above. - while (precision > 0) { - if (!high) return p; - *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; - --precision; - } - - // We need to round. - if (high < 0x8000000000000000) { - // We round down, so nothing to do. - } else if (high > 0x8000000000000000 || low != 0) { - // We round up. - RoundUp(p - 1); - } else { - RoundToEven(p - 1); - } - - assert(precision == 0); - // Precision can only be zero here. - return p; -} - -struct FormatState { - char sign_char; - int precision; - const FormatConversionSpecImpl &conv; - FormatSinkImpl *sink; - - // In `alt` mode (flag #) we keep the `.` even if there are no fractional - // digits. In non-alt mode, we strip it. - bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); } -}; - -struct Padding { - int left_spaces; - int zeros; - int right_spaces; -}; - -Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { - if (state.conv.width() < 0 || - static_cast<size_t>(state.conv.width()) <= total_size) { - return {0, 0, 0}; - } - int missing_chars = state.conv.width() - total_size; - if (state.conv.has_left_flag()) { - return {0, 0, missing_chars}; - } else if (state.conv.has_zero_flag()) { - return {0, missing_chars, 0}; - } else { - return {missing_chars, 0, 0}; - } -} - -void FinalPrint(const FormatState &state, absl::string_view data, - int padding_offset, int trailing_zeros, - absl::string_view data_postfix) { - if (state.conv.width() < 0) { - // No width specified. Fast-path. - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - state.sink->Append(data); - state.sink->Append(trailing_zeros, '0'); - state.sink->Append(data_postfix); - return; - } - - auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + - data.size() + data_postfix.size() + - static_cast<size_t>(trailing_zeros), - state); - - state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - // Padding in general needs to be inserted somewhere in the middle of `data`. - state.sink->Append(data.substr(0, padding_offset)); - state.sink->Append(padding.zeros, '0'); - state.sink->Append(data.substr(padding_offset)); - state.sink->Append(trailing_zeros, '0'); - state.sink->Append(data_postfix); - state.sink->Append(padding.right_spaces, ' '); -} - -// Fastpath %f formatter for when the shifted value fits in a simple integral -// type. -// Prints `v*2^exp` with the options from `state`. -template <typename Int> -void FormatFFast(Int v, int exp, const FormatState &state) { - constexpr int input_bits = sizeof(Int) * 8; - - static constexpr size_t integral_size = - /* in case we need to round up an extra digit */ 1 + - /* decimal digits for uint128 */ 40 + 1; - char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128]; - buffer[integral_size] = '.'; - char *const integral_digits_end = buffer + integral_size; - char *integral_digits_start; - char *const fractional_digits_start = buffer + integral_size + 1; - char *fractional_digits_end = fractional_digits_start; - - if (exp >= 0) { - const int total_bits = input_bits - LeadingZeros(v) + exp; - integral_digits_start = - total_bits <= 64 - ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp, - integral_digits_end) - : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp, - integral_digits_end); - } else { - exp = -exp; - - integral_digits_start = PrintIntegralDigitsFromRightFast( - exp < input_bits ? v >> exp : 0, integral_digits_end); - // PrintFractionalDigits may pull a carried 1 all the way up through the - // integral portion. - integral_digits_start[-1] = '0'; - - fractional_digits_end = - exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp, - state.precision) - : PrintFractionalDigitsFast(static_cast<uint128>(v), - fractional_digits_start, exp, - state.precision); - // There was a carry, so include the first digit too. - if (integral_digits_start[-1] != '0') --integral_digits_start; - } - - size_t size = fractional_digits_end - integral_digits_start; - - // In `alt` mode (flag #) we keep the `.` even if there are no fractional - // digits. In non-alt mode, we strip it. - if (!state.ShouldPrintDot()) --size; - FinalPrint(state, absl::string_view(integral_digits_start, size), - /*padding_offset=*/0, - static_cast<int>(state.precision - (fractional_digits_end - - fractional_digits_start)), - /*data_postfix=*/""); -} - -// Slow %f formatter for when the shifted value does not fit in a uint128, and -// `exp > 0`. -// Prints `v*2^exp` with the options from `state`. -// This one is guaranteed to not have fractional digits, so we don't have to -// worry about anything after the `.`. -void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { - BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) { - const size_t total_digits = - btd.TotalDigits() + - (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); - - const auto padding = ExtraWidthToPadding( - total_digits + (state.sign_char != '\0' ? 1 : 0), state); - - state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - state.sink->Append(padding.zeros, '0'); - - do { - state.sink->Append(btd.CurrentDigits()); - } while (btd.AdvanceDigits()); - - if (state.ShouldPrintDot()) state.sink->Append(1, '.'); - state.sink->Append(state.precision, '0'); - state.sink->Append(padding.right_spaces, ' '); - }); -} - -// Slow %f formatter for when the shifted value does not fit in a uint128, and -// `exp < 0`. -// Prints `v*2^exp` with the options from `state`. -// This one is guaranteed to be < 1.0, so we don't have to worry about integral -// digits. -void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { - const size_t total_digits = - /* 0 */ 1 + - (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); - auto padding = - ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); - padding.zeros += 1; - state.sink->Append(padding.left_spaces, ' '); - if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); - state.sink->Append(padding.zeros, '0'); - - if (state.ShouldPrintDot()) state.sink->Append(1, '.'); - - // Print digits - int digits_to_go = state.precision; - - FractionalDigitGenerator::RunConversion( - v, exp, [&](FractionalDigitGenerator digit_gen) { - // There are no digits to print here. - if (state.precision == 0) return; - - // We go one digit at a time, while keeping track of runs of nines. - // The runs of nines are used to perform rounding when necessary. - - while (digits_to_go > 0 && digit_gen.HasMoreDigits()) { - auto digits = digit_gen.GetDigits(); - - // Now we have a digit and a run of nines. - // See if we can print them all. - if (digits.num_nines + 1 < digits_to_go) { - // We don't have to round yet, so print them. - state.sink->Append(1, digits.digit_before_nine + '0'); - state.sink->Append(digits.num_nines, '9'); - digits_to_go -= digits.num_nines + 1; - - } else { - // We can't print all the nines, see where we have to truncate. - - bool round_up = false; - if (digits.num_nines + 1 > digits_to_go) { - // We round up at a nine. No need to print them. - round_up = true; - } else { - // We can fit all the nines, but truncate just after it. - if (digit_gen.IsGreaterThanHalf()) { - round_up = true; - } else if (digit_gen.IsExactlyHalf()) { - // Round to even - round_up = - digits.num_nines != 0 || digits.digit_before_nine % 2 == 1; - } - } - - if (round_up) { - state.sink->Append(1, digits.digit_before_nine + '1'); - --digits_to_go; - // The rest will be zeros. - } else { - state.sink->Append(1, digits.digit_before_nine + '0'); - state.sink->Append(digits_to_go - 1, '9'); - digits_to_go = 0; - } - return; - } - } - }); - - state.sink->Append(digits_to_go, '0'); - state.sink->Append(padding.right_spaces, ' '); -} - -template <typename Int> -void FormatF(Int mantissa, int exp, const FormatState &state) { - if (exp >= 0) { - const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp; - - // Fallback to the slow stack-based approach if we can't do it in a 64 or - // 128 bit state. - if (ABSL_PREDICT_FALSE(total_bits > 128)) { - return FormatFPositiveExpSlow(mantissa, exp, state); - } - } else { - // Fallback to the slow stack-based approach if we can't do it in a 64 or - // 128 bit state. - if (ABSL_PREDICT_FALSE(exp < -128)) { - return FormatFNegativeExpSlow(mantissa, -exp, state); - } - } - return FormatFFast(mantissa, exp, state); -} - -// Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to -// bits 4-7. -template <typename Int> -uint8_t GetNibble(Int n, int nibble_index) { - constexpr Int mask_low_nibble = Int{0xf}; - int shift = nibble_index * 4; - n &= mask_low_nibble << shift; - return static_cast<uint8_t>((n >> shift) & 0xf); -} - -// Add one to the given nibble, applying carry to higher nibbles. Returns true -// if overflow, false otherwise. -template <typename Int> -bool IncrementNibble(int nibble_index, Int *n) { - constexpr int kShift = sizeof(Int) * 8 - 1; - constexpr int kNumNibbles = sizeof(Int) * 8 / 4; - Int before = *n >> kShift; - // Here we essentially want to take the number 1 and move it into the requsted - // nibble, then add it to *n to effectively increment the nibble. However, - // ASan will complain if we try to shift the 1 beyond the limits of the Int, - // i.e., if the nibble_index is out of range. So therefore we check for this - // and if we are out of range we just add 0 which leaves *n unchanged, which - // seems like the reasonable thing to do in that case. - *n += ((nibble_index >= kNumNibbles) ? 0 : (Int{1} << (nibble_index * 4))); - Int after = *n >> kShift; - return (before && !after) || (nibble_index >= kNumNibbles); -} - -// Return a mask with 1's in the given nibble and all lower nibbles. -template <typename Int> -Int MaskUpToNibbleInclusive(int nibble_index) { - constexpr int kNumNibbles = sizeof(Int) * 8 / 4; - static const Int ones = ~Int{0}; - return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); -} - -// Return a mask with 1's below the given nibble. -template <typename Int> -Int MaskUpToNibbleExclusive(int nibble_index) { - return nibble_index <= 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1); -} - -template <typename Int> -Int MoveToNibble(uint8_t nibble, int nibble_index) { - return Int{nibble} << (4 * nibble_index); -} - -// Given mantissa size, find optimal # of mantissa bits to put in initial digit. -// -// In the hex representation we keep a single hex digit to the left of the dot. -// However, the question as to how many bits of the mantissa should be put into -// that hex digit in theory is arbitrary, but in practice it is optimal to -// choose based on the size of the mantissa. E.g., for a `double`, there are 53 -// mantissa bits, so that means that we should put 1 bit to the left of the dot, -// thereby leaving 52 bits to the right, which is evenly divisible by four and -// thus all fractional digits represent actual precision. For a `long double`, -// on the other hand, there are 64 bits of mantissa, thus we can use all four -// bits for the initial hex digit and still have a number left over (60) that is -// a multiple of four. Once again, the goal is to have all fractional digits -// represent real precision. -template <typename Float> -constexpr int HexFloatLeadingDigitSizeInBits() { - return std::numeric_limits<Float>::digits % 4 > 0 - ? std::numeric_limits<Float>::digits % 4 - : 4; -} - -// This function captures the rounding behavior of glibc for hex float -// representations. E.g. when rounding 0x1.ab800000 to a precision of .2 -// ("%.2a") glibc will round up because it rounds toward the even number (since -// 0xb is an odd number, it will round up to 0xc). However, when rounding at a -// point that is not followed by 800000..., it disregards the parity and rounds -// up if > 8 and rounds down if < 8. -template <typename Int> -bool HexFloatNeedsRoundUp(Int mantissa, int final_nibble_displayed, - uint8_t leading) { - // If the last nibble (hex digit) to be displayed is the lowest on in the - // mantissa then that means that we don't have any further nibbles to inform - // rounding, so don't round. - if (final_nibble_displayed <= 0) { - return false; - } - int rounding_nibble_idx = final_nibble_displayed - 1; - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; - assert(final_nibble_displayed <= kTotalNibbles); - Int mantissa_up_to_rounding_nibble_inclusive = - mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); - Int eight = MoveToNibble<Int>(8, rounding_nibble_idx); - if (mantissa_up_to_rounding_nibble_inclusive != eight) { - return mantissa_up_to_rounding_nibble_inclusive > eight; - } - // Nibble in question == 8. - uint8_t round_if_odd = (final_nibble_displayed == kTotalNibbles) - ? leading - : GetNibble(mantissa, final_nibble_displayed); - return round_if_odd % 2 == 1; -} - -// Stores values associated with a Float type needed by the FormatA -// implementation in order to avoid templatizing that function by the Float -// type. -struct HexFloatTypeParams { - template <typename Float> - explicit HexFloatTypeParams(Float) - : min_exponent(std::numeric_limits<Float>::min_exponent - 1), - leading_digit_size_bits(HexFloatLeadingDigitSizeInBits<Float>()) { - assert(leading_digit_size_bits >= 1 && leading_digit_size_bits <= 4); - } - - int min_exponent; - int leading_digit_size_bits; -}; - -// Hex Float Rounding. First check if we need to round; if so, then we do that -// by manipulating (incrementing) the mantissa, that way we can later print the -// mantissa digits by iterating through them in the same way regardless of -// whether a rounding happened. -template <typename Int> -void FormatARound(bool precision_specified, const FormatState &state, - uint8_t *leading, Int *mantissa, int *exp) { - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; - // Index of the last nibble that we could display given precision. - int final_nibble_displayed = - precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; - if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { - // Need to round up. - bool overflow = IncrementNibble(final_nibble_displayed, mantissa); - *leading += (overflow ? 1 : 0); - if (ABSL_PREDICT_FALSE(*leading > 15)) { - // We have overflowed the leading digit. This would mean that we would - // need two hex digits to the left of the dot, which is not allowed. So - // adjust the mantissa and exponent so that the result is always 1.0eXXX. - *leading = 1; - *mantissa = 0; - *exp += 4; - } - } - // Now that we have handled a possible round-up we can go ahead and zero out - // all the nibbles of the mantissa that we won't need. - if (precision_specified) { - *mantissa &= ~MaskUpToNibbleExclusive<Int>(final_nibble_displayed); - } -} - -template <typename Int> -void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, - Int *mantissa, int *exp) { - constexpr int kIntBits = sizeof(Int) * 8; - static const Int kHighIntBit = Int{1} << (kIntBits - 1); - const int kLeadDigitBitsCount = float_traits.leading_digit_size_bits; - // Normalize mantissa so that highest bit set is in MSB position, unless we - // get interrupted by the exponent threshold. - while (*mantissa && !(*mantissa & kHighIntBit)) { - if (ABSL_PREDICT_FALSE(*exp - 1 < float_traits.min_exponent)) { - *mantissa >>= (float_traits.min_exponent - *exp); - *exp = float_traits.min_exponent; - return; - } - *mantissa <<= 1; - --*exp; - } - // Extract bits for leading digit then shift them away leaving the - // fractional part. - *leading = - static_cast<uint8_t>(*mantissa >> (kIntBits - kLeadDigitBitsCount)); - *exp -= (*mantissa != 0) ? kLeadDigitBitsCount : *exp; - *mantissa <<= kLeadDigitBitsCount; -} - -template <typename Int> -void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, - bool uppercase, const FormatState &state) { - // Int properties. - constexpr int kIntBits = sizeof(Int) * 8; - constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; - // Did the user specify a precision explicitly? - const bool precision_specified = state.conv.precision() >= 0; - - // ========== Normalize/Denormalize ========== - exp += kIntBits; // make all digits fractional digits. - // This holds the (up to four) bits of leading digit, i.e., the '1' in the - // number 0x1.e6fp+2. It's always > 0 unless number is zero or denormal. - uint8_t leading = 0; - FormatANormalize(float_traits, &leading, &mantissa, &exp); - - // =============== Rounding ================== - // Check if we need to round; if so, then we do that by manipulating - // (incrementing) the mantissa before beginning to print characters. - FormatARound(precision_specified, state, &leading, &mantissa, &exp); - - // ============= Format Result =============== - // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the - // size with long double which is the largest of the floats. - constexpr size_t kBufSizeForHexFloatRepr = - 2 // 0x - + std::numeric_limits<long double>::digits / 4 // number of hex digits - + 1 // round up - + 1; // "." (dot) - char digits_buffer[kBufSizeForHexFloatRepr]; - char *digits_iter = digits_buffer; - const char *const digits = - static_cast<const char *>("0123456789ABCDEF0123456789abcdef") + - (uppercase ? 0 : 16); - - // =============== Hex Prefix ================ - *digits_iter++ = '0'; - *digits_iter++ = uppercase ? 'X' : 'x'; - - // ========== Non-Fractional Digit =========== - *digits_iter++ = digits[leading]; - - // ================== Dot ==================== - // There are three reasons we might need a dot. Keep in mind that, at this - // point, the mantissa holds only the fractional part. - if ((precision_specified && state.precision > 0) || - (!precision_specified && mantissa > 0) || state.conv.has_alt_flag()) { - *digits_iter++ = '.'; - } - - // ============ Fractional Digits ============ - int digits_emitted = 0; - while (mantissa > 0) { - *digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)]; - mantissa <<= 4; - ++digits_emitted; - } - int trailing_zeros = - precision_specified ? state.precision - digits_emitted : 0; - assert(trailing_zeros >= 0); - auto digits_result = string_view(digits_buffer, digits_iter - digits_buffer); - - // =============== Exponent ================== - constexpr size_t kBufSizeForExpDecRepr = - numbers_internal::kFastToBufferSize // requred for FastIntToBuffer - + 1 // 'p' or 'P' - + 1; // '+' or '-' - char exp_buffer[kBufSizeForExpDecRepr]; - exp_buffer[0] = uppercase ? 'P' : 'p'; - exp_buffer[1] = exp >= 0 ? '+' : '-'; - numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2); - - // ============ Assemble Result ============== - FinalPrint(state, // - digits_result, // 0xN.NNN... - 2, // offset in `data` to start padding if needed. - trailing_zeros, // num remaining mantissa padding zeros - exp_buffer); // exponent -} - -char *CopyStringTo(absl::string_view v, char *out) { - std::memcpy(out, v.data(), v.size()); - return out + v.size(); -} - -template <typename Float> -bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - int w = conv.width() >= 0 ? conv.width() : 0; - int p = conv.precision() >= 0 ? conv.precision() : -1; - char fmt[32]; - { - char *fp = fmt; - *fp++ = '%'; - fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp); - fp = CopyStringTo("*.*", fp); - if (std::is_same<long double, Float>()) { - *fp++ = 'L'; - } - *fp++ = FormatConversionCharToChar(conv.conversion_char()); - *fp = 0; - assert(fp < fmt + sizeof(fmt)); - } - std::string space(512, '\0'); - absl::string_view result; - while (true) { - int n = snprintf(&space[0], space.size(), fmt, w, p, v); - if (n < 0) return false; - if (static_cast<size_t>(n) < space.size()) { - result = absl::string_view(space.data(), n); - break; - } - space.resize(n + 1); - } - sink->Append(result); - return true; -} - -// 128-bits in decimal: ceil(128*log(2)/log(10)) -// or std::numeric_limits<__uint128_t>::digits10 -constexpr int kMaxFixedPrecision = 39; - -constexpr int kBufferLength = /*sign*/ 1 + - /*integer*/ kMaxFixedPrecision + - /*point*/ 1 + - /*fraction*/ kMaxFixedPrecision + - /*exponent e+123*/ 5; - -struct Buffer { - void push_front(char c) { - assert(begin > data); - *--begin = c; - } - void push_back(char c) { - assert(end < data + sizeof(data)); - *end++ = c; - } - void pop_back() { - assert(begin < end); - --end; - } - - char &back() { - assert(begin < end); - return end[-1]; - } - - char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; } - - int size() const { return static_cast<int>(end - begin); } - - char data[kBufferLength]; - char *begin; - char *end; -}; - -enum class FormatStyle { Fixed, Precision }; - -// If the value is Inf or Nan, print it and return true. -// Otherwise, return false. -template <typename Float> -bool ConvertNonNumericFloats(char sign_char, Float v, - const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - char text[4], *ptr = text; - if (sign_char != '\0') *ptr++ = sign_char; - if (std::isnan(v)) { - ptr = std::copy_n( - FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, - ptr); - } else if (std::isinf(v)) { - ptr = std::copy_n( - FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, - ptr); - } else { - return false; - } - - return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, - conv.has_left_flag()); -} - -// Round up the last digit of the value. -// It will carry over and potentially overflow. 'exp' will be adjusted in that -// case. -template <FormatStyle mode> -void RoundUp(Buffer *buffer, int *exp) { - char *p = &buffer->back(); - while (p >= buffer->begin && (*p == '9' || *p == '.')) { - if (*p == '9') *p = '0'; - --p; - } - - if (p < buffer->begin) { - *p = '1'; - buffer->begin = p; - if (mode == FormatStyle::Precision) { - std::swap(p[1], p[2]); // move the . - ++*exp; - buffer->pop_back(); - } - } else { - ++*p; - } -} - -void PrintExponent(int exp, char e, Buffer *out) { - out->push_back(e); - if (exp < 0) { - out->push_back('-'); - exp = -exp; - } else { - out->push_back('+'); - } - // Exponent digits. - if (exp > 99) { - out->push_back(exp / 100 + '0'); - out->push_back(exp / 10 % 10 + '0'); - out->push_back(exp % 10 + '0'); - } else { - out->push_back(exp / 10 + '0'); - out->push_back(exp % 10 + '0'); - } -} - -template <typename Float, typename Int> -constexpr bool CanFitMantissa() { - return -#if defined(__clang__) && !defined(__SSE3__) - // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289 - // Casting from long double to uint64_t is miscompiled and drops bits. - (!std::is_same<Float, long double>::value || - !std::is_same<Int, uint64_t>::value) && -#endif - std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits; -} - -template <typename Float> -struct Decomposed { - using MantissaType = - absl::conditional_t<std::is_same<long double, Float>::value, uint128, - uint64_t>; - static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, - ""); - MantissaType mantissa; - int exponent; -}; - -// Decompose the double into an integer mantissa and an exponent. -template <typename Float> -Decomposed<Float> Decompose(Float v) { - int exp; - Float m = std::frexp(v, &exp); - m = std::ldexp(m, std::numeric_limits<Float>::digits); - exp -= std::numeric_limits<Float>::digits; - - return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp}; -} - -// Print 'digits' as decimal. -// In Fixed mode, we add a '.' at the end. -// In Precision mode, we add a '.' after the first digit. -template <FormatStyle mode, typename Int> -int PrintIntegralDigits(Int digits, Buffer *out) { - int printed = 0; - if (digits) { - for (; digits; digits /= 10) out->push_front(digits % 10 + '0'); - printed = out->size(); - if (mode == FormatStyle::Precision) { - out->push_front(*out->begin); - out->begin[1] = '.'; - } else { - out->push_back('.'); - } - } else if (mode == FormatStyle::Fixed) { - out->push_front('0'); - out->push_back('.'); - printed = 1; - } - return printed; -} - -// Back out 'extra_digits' digits and round up if necessary. -bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value, - Buffer *out, int *exp_out) { - if (extra_digits <= 0) return false; - - // Back out the extra digits - out->end -= extra_digits; - - bool needs_to_round_up = [&] { - // We look at the digit just past the end. - // There must be 'extra_digits' extra valid digits after end. - if (*out->end > '5') return true; - if (*out->end < '5') return false; - if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits, - [](char c) { return c != '0'; })) - return true; - - // Ends in ...50*, round to even. - return out->last_digit() % 2 == 1; - }(); - - if (needs_to_round_up) { - RoundUp<FormatStyle::Precision>(out, exp_out); - } - return true; -} - -// Print the value into the buffer. -// This will not include the exponent, which will be returned in 'exp_out' for -// Precision mode. -template <typename Int, typename Float, FormatStyle mode> -bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, - int *exp_out) { - assert((CanFitMantissa<Float, Int>())); - - const int int_bits = std::numeric_limits<Int>::digits; - - // In precision mode, we start printing one char to the right because it will - // also include the '.' - // In fixed mode we put the dot afterwards on the right. - out->begin = out->end = - out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision); - - if (exp >= 0) { - if (std::numeric_limits<Float>::digits + exp > int_bits) { - // The value will overflow the Int - return false; - } - int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out); - int digits_to_zero_pad = precision; - if (mode == FormatStyle::Precision) { - *exp_out = digits_printed - 1; - digits_to_zero_pad -= digits_printed - 1; - if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) { - return true; - } - } - for (; digits_to_zero_pad-- > 0;) out->push_back('0'); - return true; - } - - exp = -exp; - // We need at least 4 empty bits for the next decimal digit. - // We will multiply by 10. - if (exp > int_bits - 4) return false; - - const Int mask = (Int{1} << exp) - 1; - - // Print the integral part first. - int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out); - int_mantissa &= mask; - - int fractional_count = precision; - if (mode == FormatStyle::Precision) { - if (digits_printed == 0) { - // Find the first non-zero digit, when in Precision mode. - *exp_out = 0; - if (int_mantissa) { - while (int_mantissa <= mask) { - int_mantissa *= 10; - --*exp_out; - } - } - out->push_front(static_cast<char>(int_mantissa >> exp) + '0'); - out->push_back('.'); - int_mantissa &= mask; - } else { - // We already have a digit, and a '.' - *exp_out = digits_printed - 1; - fractional_count -= *exp_out; - if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out, - exp_out)) { - // If we had enough digits, return right away. - // The code below will try to round again otherwise. - return true; - } - } - } - - auto get_next_digit = [&] { - int_mantissa *= 10; - int digit = static_cast<int>(int_mantissa >> exp); - int_mantissa &= mask; - return digit; - }; - - // Print fractional_count more digits, if available. - for (; fractional_count > 0; --fractional_count) { - out->push_back(get_next_digit() + '0'); - } - - int next_digit = get_next_digit(); - if (next_digit > 5 || - (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) { - RoundUp<mode>(out, exp_out); - } - - return true; -} - -template <FormatStyle mode, typename Float> -bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, - int *exp) { - if (precision > kMaxFixedPrecision) return false; - - // Try with uint64_t. - if (CanFitMantissa<Float, std::uint64_t>() && - FloatToBufferImpl<std::uint64_t, Float, mode>( - static_cast<std::uint64_t>(decomposed.mantissa), - static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp)) - return true; - -#if defined(ABSL_HAVE_INTRINSIC_INT128) - // If that is not enough, try with __uint128_t. - return CanFitMantissa<Float, __uint128_t>() && - FloatToBufferImpl<__uint128_t, Float, mode>( - static_cast<__uint128_t>(decomposed.mantissa), - static_cast<__uint128_t>(decomposed.exponent), precision, out, - exp); -#endif - return false; -} - -void WriteBufferToSink(char sign_char, absl::string_view str, - const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - int left_spaces = 0, zeros = 0, right_spaces = 0; - int missing_chars = - conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) - - static_cast<int>(sign_char != 0), - 0) - : 0; - if (conv.has_left_flag()) { - right_spaces = missing_chars; - } else if (conv.has_zero_flag()) { - zeros = missing_chars; - } else { - left_spaces = missing_chars; - } - - sink->Append(left_spaces, ' '); - if (sign_char != '\0') sink->Append(1, sign_char); - sink->Append(zeros, '0'); - sink->Append(str); - sink->Append(right_spaces, ' '); -} - -template <typename Float> -bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - // Print the sign or the sign column. - Float abs_v = v; - char sign_char = 0; - if (std::signbit(abs_v)) { - sign_char = '-'; - abs_v = -abs_v; - } else if (conv.has_show_pos_flag()) { - sign_char = '+'; - } else if (conv.has_sign_col_flag()) { - sign_char = ' '; - } - - // Print nan/inf. - if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) { - return true; - } - - int precision = conv.precision() < 0 ? 6 : conv.precision(); - - int exp = 0; - - auto decomposed = Decompose(abs_v); - - Buffer buffer; - - FormatConversionChar c = conv.conversion_char(); - - if (c == FormatConversionCharInternal::f || - c == FormatConversionCharInternal::F) { - FormatF(decomposed.mantissa, decomposed.exponent, - {sign_char, precision, conv, sink}); - return true; - } else if (c == FormatConversionCharInternal::e || - c == FormatConversionCharInternal::E) { - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); - PrintExponent( - exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', - &buffer); - } else if (c == FormatConversionCharInternal::g || - c == FormatConversionCharInternal::G) { - precision = std::max(0, precision - 1); - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (precision + 1 > exp && exp >= -4) { - if (exp < 0) { - // Have 1.23456, needs 0.00123456 - // Move the first digit - buffer.begin[1] = *buffer.begin; - // Add some zeros - for (; exp < -1; ++exp) *buffer.begin-- = '0'; - *buffer.begin-- = '.'; - *buffer.begin = '0'; - } else if (exp > 0) { - // Have 1.23456, needs 1234.56 - // Move the '.' exp positions to the right. - std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); - } - exp = 0; - } - if (!conv.has_alt_flag()) { - while (buffer.back() == '0') buffer.pop_back(); - if (buffer.back() == '.') buffer.pop_back(); - } - if (exp) { - PrintExponent( - exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', - &buffer); - } - } else if (c == FormatConversionCharInternal::a || - c == FormatConversionCharInternal::A) { - bool uppercase = (c == FormatConversionCharInternal::A); - FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa, - decomposed.exponent, uppercase, {sign_char, precision, conv, sink}); - return true; - } else { - return false; - } - - WriteBufferToSink(sign_char, - absl::string_view(buffer.begin, buffer.end - buffer.begin), - conv, sink); - - return true; -} - -} // namespace - -bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - if (std::numeric_limits<long double>::digits == - 2 * std::numeric_limits<double>::digits) { - // This is the `double-double` representation of `long double`. - // We do not handle it natively. Fallback to snprintf. - return FallbackToSnprintf(v, conv, sink); - } - - return FloatToSink(v, conv, sink); -} - -bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - return FloatToSink(static_cast<double>(v), conv, sink); -} - -bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink) { - return FloatToSink(v, conv, sink); -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h b/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h deleted file mode 100644 index 71100e714257..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ - -#include "absl/strings/internal/str_format/extension.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink); - -bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink); - -bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, - FormatSinkImpl *sink); - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc deleted file mode 100644 index c4b24706132c..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/output.h" - -#include <errno.h> -#include <cstring> - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -namespace { -struct ClearErrnoGuard { - ClearErrnoGuard() : old_value(errno) { errno = 0; } - ~ClearErrnoGuard() { - if (!errno) errno = old_value; - } - int old_value; -}; -} // namespace - -void BufferRawSink::Write(string_view v) { - size_t to_write = std::min(v.size(), size_); - std::memcpy(buffer_, v.data(), to_write); - buffer_ += to_write; - size_ -= to_write; - total_written_ += v.size(); -} - -void FILERawSink::Write(string_view v) { - while (!v.empty() && !error_) { - // Reset errno to zero in case the libc implementation doesn't set errno - // when a failure occurs. - ClearErrnoGuard guard; - - if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) { - // Some progress was made. - count_ += result; - v.remove_prefix(result); - } else { - if (errno == EINTR) { - continue; - } else if (errno) { - error_ = errno; - } else if (std::ferror(output_)) { - // Non-POSIX compliant libc implementations may not set errno, so we - // have check the streams error indicator. - error_ = EBADF; - } else { - // We're likely on a non-POSIX system that encountered EINTR but had no - // way of reporting it. - continue; - } - } - } -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output.h b/third_party/abseil_cpp/absl/strings/internal/str_format/output.h deleted file mode 100644 index 8030dae00f4f..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/output.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Output extension hooks for the Format library. -// `internal::InvokeFlush` calls the appropriate flush function for the -// specified output argument. -// `BufferRawSink` is a simple output sink for a char buffer. Used by SnprintF. -// `FILERawSink` is a std::FILE* based sink. Used by PrintF and FprintF. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ - -#include <cstdio> -#include <ostream> -#include <string> - -#include "absl/base/port.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -// RawSink implementation that writes into a char* buffer. -// It will not overflow the buffer, but will keep the total count of chars -// that would have been written. -class BufferRawSink { - public: - BufferRawSink(char* buffer, size_t size) : buffer_(buffer), size_(size) {} - - size_t total_written() const { return total_written_; } - void Write(string_view v); - - private: - char* buffer_; - size_t size_; - size_t total_written_ = 0; -}; - -// RawSink implementation that writes into a FILE*. -// It keeps track of the total number of bytes written and any error encountered -// during the writes. -class FILERawSink { - public: - explicit FILERawSink(std::FILE* output) : output_(output) {} - - void Write(string_view v); - - size_t count() const { return count_; } - int error() const { return error_; } - - private: - std::FILE* output_; - int error_ = 0; - size_t count_ = 0; -}; - -// Provide RawSink integration with common types from the STL. -inline void AbslFormatFlush(std::string* out, string_view s) { - out->append(s.data(), s.size()); -} -inline void AbslFormatFlush(std::ostream* out, string_view s) { - out->write(s.data(), s.size()); -} - -inline void AbslFormatFlush(FILERawSink* sink, string_view v) { - sink->Write(v); -} - -inline void AbslFormatFlush(BufferRawSink* sink, string_view v) { - sink->Write(v); -} - -// This is a SFINAE to get a better compiler error message when the type -// is not supported. -template <typename T> -auto InvokeFlush(T* out, string_view s) -> decltype(AbslFormatFlush(out, s)) { - AbslFormatFlush(out, s); -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc deleted file mode 100644 index ce2e91a0bbe8..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/output.h" - -#include <sstream> -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/cord.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace { - -TEST(InvokeFlush, String) { - std::string str = "ABC"; - str_format_internal::InvokeFlush(&str, "DEF"); - EXPECT_EQ(str, "ABCDEF"); -} - -TEST(InvokeFlush, Stream) { - std::stringstream str; - str << "ABC"; - str_format_internal::InvokeFlush(&str, "DEF"); - EXPECT_EQ(str.str(), "ABCDEF"); -} - -TEST(InvokeFlush, Cord) { - absl::Cord str("ABC"); - str_format_internal::InvokeFlush(&str, "DEF"); - EXPECT_EQ(str, "ABCDEF"); -} - -TEST(BufferRawSink, Limits) { - char buf[16]; - { - std::fill(std::begin(buf), std::end(buf), 'x'); - str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1); - str_format_internal::InvokeFlush(&bufsink, "Hello World237"); - EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx"); - } - { - std::fill(std::begin(buf), std::end(buf), 'x'); - str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1); - str_format_internal::InvokeFlush(&bufsink, "Hello World237237"); - EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x"); - } - { - std::fill(std::begin(buf), std::end(buf), 'x'); - str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1); - str_format_internal::InvokeFlush(&bufsink, "Hello World"); - str_format_internal::InvokeFlush(&bufsink, "237"); - EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx"); - } - { - std::fill(std::begin(buf), std::end(buf), 'x'); - str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1); - str_format_internal::InvokeFlush(&bufsink, "Hello World"); - str_format_internal::InvokeFlush(&bufsink, "237237"); - EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x"); - } -} - -} // namespace -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc deleted file mode 100644 index f308d0235120..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/parser.h" - -#include <assert.h> -#include <string.h> -#include <wchar.h> -#include <cctype> -#include <cstdint> - -#include <algorithm> -#include <initializer_list> -#include <limits> -#include <ostream> -#include <string> -#include <unordered_set> - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -using CC = FormatConversionCharInternal; -using LM = LengthMod; - -ABSL_CONST_INIT const ConvTag kTags[256] = { - {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 - {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f - {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 - {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f - {}, {}, {}, {}, {}, {}, {}, {}, // 20-27 - {}, {}, {}, {}, {}, {}, {}, {}, // 28-2f - {}, {}, {}, {}, {}, {}, {}, {}, // 30-37 - {}, {}, {}, {}, {}, {}, {}, {}, // 38-3f - {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG - {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO - {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW - CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ - {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg - LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno - CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw - CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! - {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 - {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f - {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 - {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f - {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 - {}, {}, {}, {}, {}, {}, {}, {}, // a8-af - {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 - {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf - {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 - {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf - {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 - {}, {}, {}, {}, {}, {}, {}, {}, // d8-df - {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 - {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef - {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 - {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff -}; - -namespace { - -bool CheckFastPathSetting(const UnboundConversion& conv) { - bool should_be_basic = !conv.flags.left && // - !conv.flags.show_pos && // - !conv.flags.sign_col && // - !conv.flags.alt && // - !conv.flags.zero && // - (conv.width.value() == -1) && - (conv.precision.value() == -1); - if (should_be_basic != conv.flags.basic) { - fprintf(stderr, - "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " - "width=%d precision=%d\n", - conv.flags.basic, conv.flags.left, conv.flags.show_pos, - conv.flags.sign_col, conv.flags.alt, conv.flags.zero, - conv.width.value(), conv.precision.value()); - } - return should_be_basic == conv.flags.basic; -} - -template <bool is_positional> -const char *ConsumeConversion(const char *pos, const char *const end, - UnboundConversion *conv, int *next_arg) { - const char* const original_pos = pos; - char c; - // Read the next char into `c` and update `pos`. Returns false if there are - // no more chars to read. -#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ - do { \ - if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \ - c = *pos++; \ - } while (0) - - const auto parse_digits = [&] { - int digits = c - '0'; - // We do not want to overflow `digits` so we consume at most digits10 - // digits. If there are more digits the parsing will fail later on when the - // digit doesn't match the expected characters. - int num_digits = std::numeric_limits<int>::digits10; - for (;;) { - if (ABSL_PREDICT_FALSE(pos == end)) break; - c = *pos++; - if (!std::isdigit(c)) break; - --num_digits; - if (ABSL_PREDICT_FALSE(!num_digits)) break; - digits = 10 * digits + c - '0'; - } - return digits; - }; - - if (is_positional) { - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->arg_position = parse_digits(); - assert(conv->arg_position > 0); - if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; - } - - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - - // We should start with the basic flag on. - assert(conv->flags.basic); - - // Any non alpha character makes this conversion not basic. - // This includes flags (-+ #0), width (1-9, *) or precision (.). - // All conversion characters and length modifiers are alpha characters. - if (c < 'A') { - conv->flags.basic = false; - - for (; c <= '0';) { - // FIXME: We might be able to speed this up reusing the lookup table from - // above. It might require changing Flags to be a plain integer where we - // can |= a value. - switch (c) { - case '-': - conv->flags.left = true; - break; - case '+': - conv->flags.show_pos = true; - break; - case ' ': - conv->flags.sign_col = true; - break; - case '#': - conv->flags.alt = true; - break; - case '0': - conv->flags.zero = true; - break; - default: - goto flags_done; - } - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } -flags_done: - - if (c <= '9') { - if (c >= '0') { - int maybe_width = parse_digits(); - if (!is_positional && c == '$') { - if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; - // Positional conversion. - *next_arg = -1; - conv->flags = Flags(); - conv->flags.basic = true; - return ConsumeConversion<true>(original_pos, end, conv, next_arg); - } - conv->width.set_value(maybe_width); - } else if (c == '*') { - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (is_positional) { - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->width.set_from_arg(parse_digits()); - if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->width.set_from_arg(++*next_arg); - } - } - } - - if (c == '.') { - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (std::isdigit(c)) { - conv->precision.set_value(parse_digits()); - } else if (c == '*') { - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (is_positional) { - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; - conv->precision.set_from_arg(parse_digits()); - if (c != '$') return nullptr; - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->precision.set_from_arg(++*next_arg); - } - } else { - conv->precision.set_value(0); - } - } - } - - auto tag = GetTagForChar(c); - - if (ABSL_PREDICT_FALSE(!tag.is_conv())) { - if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; - - // It is a length modifier. - using str_format_internal::LengthMod; - LengthMod length_mod = tag.as_length(); - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (c == 'h' && length_mod == LengthMod::h) { - conv->length_mod = LengthMod::hh; - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else if (c == 'l' && length_mod == LengthMod::l) { - conv->length_mod = LengthMod::ll; - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - } else { - conv->length_mod = length_mod; - } - tag = GetTagForChar(c); - if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; - } - - assert(CheckFastPathSetting(*conv)); - (void)(&CheckFastPathSetting); - - conv->conv = tag.as_conv(); - if (!is_positional) conv->arg_position = ++*next_arg; - return pos; -} - -} // namespace - -std::string LengthModToString(LengthMod v) { - switch (v) { - case LengthMod::h: - return "h"; - case LengthMod::hh: - return "hh"; - case LengthMod::l: - return "l"; - case LengthMod::ll: - return "ll"; - case LengthMod::L: - return "L"; - case LengthMod::j: - return "j"; - case LengthMod::z: - return "z"; - case LengthMod::t: - return "t"; - case LengthMod::q: - return "q"; - case LengthMod::none: - return ""; - } - return ""; -} - -const char *ConsumeUnboundConversion(const char *p, const char *end, - UnboundConversion *conv, int *next_arg) { - if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg); - return ConsumeConversion<false>(p, end, conv, next_arg); -} - -struct ParsedFormatBase::ParsedFormatConsumer { - explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat) - : parsed(parsedformat), data_pos(parsedformat->data_.get()) {} - - bool Append(string_view s) { - if (s.empty()) return true; - - size_t text_end = AppendText(s); - - if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) { - // Let's extend the existing text run. - parsed->items_.back().text_end = text_end; - } else { - // Let's make a new text run. - parsed->items_.push_back({false, text_end, {}}); - } - return true; - } - - bool ConvertOne(const UnboundConversion &conv, string_view s) { - size_t text_end = AppendText(s); - parsed->items_.push_back({true, text_end, conv}); - return true; - } - - size_t AppendText(string_view s) { - memcpy(data_pos, s.data(), s.size()); - data_pos += s.size(); - return static_cast<size_t>(data_pos - parsed->data_.get()); - } - - ParsedFormatBase *parsed; - char* data_pos; -}; - -ParsedFormatBase::ParsedFormatBase( - string_view format, bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) - : data_(format.empty() ? nullptr : new char[format.size()]) { - has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) || - !MatchesConversions(allow_ignored, convs); -} - -bool ParsedFormatBase::MatchesConversions( - bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) const { - std::unordered_set<int> used; - auto add_if_valid_conv = [&](int pos, char c) { - if (static_cast<size_t>(pos) > convs.size() || - !Contains(convs.begin()[pos - 1], c)) - return false; - used.insert(pos); - return true; - }; - for (const ConversionItem &item : items_) { - if (!item.is_conversion) continue; - auto &conv = item.conv; - if (conv.precision.is_from_arg() && - !add_if_valid_conv(conv.precision.get_from_arg(), '*')) - return false; - if (conv.width.is_from_arg() && - !add_if_valid_conv(conv.width.get_from_arg(), '*')) - return false; - if (!add_if_valid_conv(conv.arg_position, - FormatConversionCharToChar(conv.conv))) - return false; - } - return used.size() == convs.size() || allow_ignored; -} - -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h b/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h deleted file mode 100644 index 6504dd3ddc20..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ -#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ - -#include <limits.h> -#include <stddef.h> -#include <stdlib.h> - -#include <cassert> -#include <cstdint> -#include <initializer_list> -#include <iosfwd> -#include <iterator> -#include <memory> -#include <string> -#include <vector> - -#include "absl/strings/internal/str_format/checker.h" -#include "absl/strings/internal/str_format/extension.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; - -std::string LengthModToString(LengthMod v); - -// The analyzed properties of a single specified conversion. -struct UnboundConversion { - UnboundConversion() - : flags() /* This is required to zero all the fields of flags. */ { - flags.basic = true; - } - - class InputValue { - public: - void set_value(int value) { - assert(value >= 0); - value_ = value; - } - int value() const { return value_; } - - // Marks the value as "from arg". aka the '*' format. - // Requires `value >= 1`. - // When set, is_from_arg() return true and get_from_arg() returns the - // original value. - // `value()`'s return value is unspecfied in this state. - void set_from_arg(int value) { - assert(value > 0); - value_ = -value - 1; - } - bool is_from_arg() const { return value_ < -1; } - int get_from_arg() const { - assert(is_from_arg()); - return -value_ - 1; - } - - private: - int value_ = -1; - }; - - // No need to initialize. It will always be set in the parser. - int arg_position; - - InputValue width; - InputValue precision; - - Flags flags; - LengthMod length_mod = LengthMod::none; - FormatConversionChar conv = FormatConversionCharInternal::kNone; -}; - -// Consume conversion spec prefix (not including '%') of [p, end) if valid. -// Examples of valid specs would be e.g.: "s", "d", "-12.6f". -// If valid, it returns the first character following the conversion spec, -// and the spec part is broken down and returned in 'conv'. -// If invalid, returns nullptr. -const char* ConsumeUnboundConversion(const char* p, const char* end, - UnboundConversion* conv, int* next_arg); - -// Helper tag class for the table below. -// It allows fast `char -> ConversionChar/LengthMod` checking and -// conversions. -class ConvTag { - public: - constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT - : tag_(static_cast<int8_t>(conversion_char)) {} - // We invert the length modifiers to make them negative so that we can easily - // test for them. - constexpr ConvTag(LengthMod length_mod) // NOLINT - : tag_(~static_cast<std::int8_t>(length_mod)) {} - // Everything else is -128, which is negative to make is_conv() simpler. - constexpr ConvTag() : tag_(-128) {} - - bool is_conv() const { return tag_ >= 0; } - bool is_length() const { return tag_ < 0 && tag_ != -128; } - FormatConversionChar as_conv() const { - assert(is_conv()); - return static_cast<FormatConversionChar>(tag_); - } - LengthMod as_length() const { - assert(is_length()); - return static_cast<LengthMod>(~tag_); - } - - private: - std::int8_t tag_; -}; - -extern const ConvTag kTags[256]; -// Keep a single table for all the conversion chars and length modifiers. -inline ConvTag GetTagForChar(char c) { - return kTags[static_cast<unsigned char>(c)]; -} - -// Parse the format string provided in 'src' and pass the identified items into -// 'consumer'. -// Text runs will be passed by calling -// Consumer::Append(string_view); -// ConversionItems will be passed by calling -// Consumer::ConvertOne(UnboundConversion, string_view); -// In the case of ConvertOne, the string_view that is passed is the -// portion of the format string corresponding to the conversion, not including -// the leading %. On success, it returns true. On failure, it stops and returns -// false. -template <typename Consumer> -bool ParseFormatString(string_view src, Consumer consumer) { - int next_arg = 0; - const char* p = src.data(); - const char* const end = p + src.size(); - while (p != end) { - const char* percent = static_cast<const char*>(memchr(p, '%', end - p)); - if (!percent) { - // We found the last substring. - return consumer.Append(string_view(p, end - p)); - } - // We found a percent, so push the text run then process the percent. - if (ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) { - return false; - } - if (ABSL_PREDICT_FALSE(percent + 1 >= end)) return false; - - auto tag = GetTagForChar(percent[1]); - if (tag.is_conv()) { - if (ABSL_PREDICT_FALSE(next_arg < 0)) { - // This indicates an error in the format string. - // The only way to get `next_arg < 0` here is to have a positional - // argument first which sets next_arg to -1 and then a non-positional - // argument. - return false; - } - p = percent + 2; - - // Keep this case separate from the one below. - // ConvertOne is more efficient when the compiler can see that the `basic` - // flag is set. - UnboundConversion conv; - conv.conv = tag.as_conv(); - conv.arg_position = ++next_arg; - if (ABSL_PREDICT_FALSE( - !consumer.ConvertOne(conv, string_view(percent + 1, 1)))) { - return false; - } - } else if (percent[1] != '%') { - UnboundConversion conv; - p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg); - if (ABSL_PREDICT_FALSE(p == nullptr)) return false; - if (ABSL_PREDICT_FALSE(!consumer.ConvertOne( - conv, string_view(percent + 1, p - (percent + 1))))) { - return false; - } - } else { - if (ABSL_PREDICT_FALSE(!consumer.Append("%"))) return false; - p = percent + 2; - continue; - } - } - return true; -} - -// Always returns true, or fails to compile in a constexpr context if s does not -// point to a constexpr char array. -constexpr bool EnsureConstexpr(string_view s) { - return s.empty() || s[0] == s[0]; -} - -class ParsedFormatBase { - public: - explicit ParsedFormatBase( - string_view format, bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs); - - ParsedFormatBase(const ParsedFormatBase& other) { *this = other; } - - ParsedFormatBase(ParsedFormatBase&& other) { *this = std::move(other); } - - ParsedFormatBase& operator=(const ParsedFormatBase& other) { - if (this == &other) return *this; - has_error_ = other.has_error_; - items_ = other.items_; - size_t text_size = items_.empty() ? 0 : items_.back().text_end; - data_.reset(new char[text_size]); - memcpy(data_.get(), other.data_.get(), text_size); - return *this; - } - - ParsedFormatBase& operator=(ParsedFormatBase&& other) { - if (this == &other) return *this; - has_error_ = other.has_error_; - data_ = std::move(other.data_); - items_ = std::move(other.items_); - // Reset the vector to make sure the invariants hold. - other.items_.clear(); - return *this; - } - - template <typename Consumer> - bool ProcessFormat(Consumer consumer) const { - const char* const base = data_.get(); - string_view text(base, 0); - for (const auto& item : items_) { - const char* const end = text.data() + text.size(); - text = string_view(end, (base + item.text_end) - end); - if (item.is_conversion) { - if (!consumer.ConvertOne(item.conv, text)) return false; - } else { - if (!consumer.Append(text)) return false; - } - } - return !has_error_; - } - - bool has_error() const { return has_error_; } - - private: - // Returns whether the conversions match and if !allow_ignored it verifies - // that all conversions are used by the format. - bool MatchesConversions( - bool allow_ignored, - std::initializer_list<FormatConversionCharSet> convs) const; - - struct ParsedFormatConsumer; - - struct ConversionItem { - bool is_conversion; - // Points to the past-the-end location of this element in the data_ array. - size_t text_end; - UnboundConversion conv; - }; - - bool has_error_; - std::unique_ptr<char[]> data_; - std::vector<ConversionItem> items_; -}; - - -// A value type representing a preparsed format. These can be created, copied -// around, and reused to speed up formatting loops. -// The user must specify through the template arguments the conversion -// characters used in the format. This will be checked at compile time. -// -// This class uses Conv enum values to specify each argument. -// This allows for more flexibility as you can specify multiple possible -// conversion characters for each argument. -// ParsedFormat<char...> is a simplified alias for when the user only -// needs to specify a single conversion character for each argument. -// -// Example: -// // Extended format supports multiple characters per argument: -// using MyFormat = ExtendedParsedFormat<Conv::d | Conv::x>; -// MyFormat GetFormat(bool use_hex) { -// if (use_hex) return MyFormat("foo %x bar"); -// return MyFormat("foo %d bar"); -// } -// // 'format' can be used with any value that supports 'd' and 'x', -// // like `int`. -// auto format = GetFormat(use_hex); -// value = StringF(format, i); -// -// This class also supports runtime format checking with the ::New() and -// ::NewAllowIgnored() factory functions. -// This is the only API that allows the user to pass a runtime specified format -// string. These factory functions will return NULL if the format does not match -// the conversions requested by the user. -template <FormatConversionCharSet... C> -class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { - public: - explicit ExtendedParsedFormat(string_view format) -#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - __attribute__(( - enable_if(str_format_internal::EnsureConstexpr(format), - "Format string is not constexpr."), - enable_if(str_format_internal::ValidFormatImpl<C...>(format), - "Format specified does not match the template arguments."))) -#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - : ExtendedParsedFormat(format, false) { - } - - // ExtendedParsedFormat factory function. - // The user still has to specify the conversion characters, but they will not - // be checked at compile time. Instead, it will be checked at runtime. - // This delays the checking to runtime, but allows the user to pass - // dynamically sourced formats. - // It returns NULL if the format does not match the conversion characters. - // The user is responsible for checking the return value before using it. - // - // The 'New' variant will check that all the specified arguments are being - // consumed by the format and return NULL if any argument is being ignored. - // The 'NewAllowIgnored' variant will not verify this and will allow formats - // that ignore arguments. - static std::unique_ptr<ExtendedParsedFormat> New(string_view format) { - return New(format, false); - } - static std::unique_ptr<ExtendedParsedFormat> NewAllowIgnored( - string_view format) { - return New(format, true); - } - - private: - static std::unique_ptr<ExtendedParsedFormat> New(string_view format, - bool allow_ignored) { - std::unique_ptr<ExtendedParsedFormat> conv( - new ExtendedParsedFormat(format, allow_ignored)); - if (conv->has_error()) return nullptr; - return conv; - } - - ExtendedParsedFormat(string_view s, bool allow_ignored) - : ParsedFormatBase(s, allow_ignored, {C...}) {} -}; -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc deleted file mode 100644 index a5fa1c79aaf4..000000000000 --- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2020 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/str_format/parser.h" - -#include <string.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/macros.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace str_format_internal { - -namespace { - -using testing::Pair; - -TEST(LengthModTest, Names) { - struct Expectation { - int line; - LengthMod mod; - const char *name; - }; - const Expectation kExpect[] = { - {__LINE__, LengthMod::none, "" }, - {__LINE__, LengthMod::h, "h" }, - {__LINE__, LengthMod::hh, "hh"}, - {__LINE__, LengthMod::l, "l" }, - {__LINE__, LengthMod::ll, "ll"}, - {__LINE__, LengthMod::L, "L" }, - {__LINE__, LengthMod::j, "j" }, - {__LINE__, LengthMod::z, "z" }, - {__LINE__, LengthMod::t, "t" }, - {__LINE__, LengthMod::q, "q" }, - }; - EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), 10); - for (auto e : kExpect) { - SCOPED_TRACE(e.line); - EXPECT_EQ(e.name, LengthModToString(e.mod)); - } -} - -TEST(ConversionCharTest, Names) { - struct Expectation { - FormatConversionChar id; - char name; - }; - // clang-format off - const Expectation kExpect[] = { -#define X(c) {FormatConversionCharInternal::c, #c[0]} - X(c), X(s), // text - X(d), X(i), X(o), X(u), X(x), X(X), // int - X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float - X(n), X(p), // misc -#undef X - {FormatConversionCharInternal::kNone, '\0'}, - }; - // clang-format on - for (auto e : kExpect) { - SCOPED_TRACE(e.name); - FormatConversionChar v = e.id; - EXPECT_EQ(e.name, FormatConversionCharToChar(v)); - } -} - -class ConsumeUnboundConversionTest : public ::testing::Test { - public: - std::pair<string_view, string_view> Consume(string_view src) { - int next = 0; - o = UnboundConversion(); // refresh - const char* p = ConsumeUnboundConversion( - src.data(), src.data() + src.size(), &o, &next); - if (!p) return {{}, src}; - return {string_view(src.data(), p - src.data()), - string_view(p, src.data() + src.size() - p)}; - } - - bool Run(const char *fmt, bool force_positional = false) { - int next = force_positional ? -1 : 0; - o = UnboundConversion(); // refresh - return ConsumeUnboundConversion(fmt, fmt + strlen(fmt), &o, &next) == - fmt + strlen(fmt); - } - UnboundConversion o; -}; - -TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) { - struct Expectation { - int line; - string_view src; - string_view out; - string_view src_post; - }; - const Expectation kExpect[] = { - {__LINE__, "", "", "" }, - {__LINE__, "b", "", "b" }, // 'b' is invalid - {__LINE__, "ba", "", "ba"}, // 'b' is invalid - {__LINE__, "l", "", "l" }, // just length mod isn't okay - {__LINE__, "d", "d", "" }, // basic - {__LINE__, "d ", "d", " " }, // leave suffix - {__LINE__, "dd", "d", "d" }, // don't be greedy - {__LINE__, "d9", "d", "9" }, // leave non-space suffix - {__LINE__, "dzz", "d", "zz"}, // length mod as suffix - {__LINE__, "1$*2$d", "1$*2$d", "" }, // arg indexing and * allowed. - {__LINE__, "0-14.3hhd", "0-14.3hhd", ""}, // precision, width - {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""}, // flags - }; - for (const auto& e : kExpect) { - SCOPED_TRACE(e.line); - EXPECT_THAT(Consume(e.src), Pair(e.out, e.src_post)); - } -} - -TEST_F(ConsumeUnboundConversionTest, BasicConversion) { - EXPECT_FALSE(Run("")); - EXPECT_FALSE(Run("z")); - - EXPECT_FALSE(Run("dd")); // no excess allowed - - EXPECT_TRUE(Run("d")); - EXPECT_EQ('d', FormatConversionCharToChar(o.conv)); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_LT(o.width.value(), 0); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_LT(o.precision.value(), 0); - EXPECT_EQ(1, o.arg_position); -} - -TEST_F(ConsumeUnboundConversionTest, ArgPosition) { - EXPECT_TRUE(Run("d")); - EXPECT_EQ(1, o.arg_position); - EXPECT_TRUE(Run("3$d")); - EXPECT_EQ(3, o.arg_position); - EXPECT_TRUE(Run("1$d")); - EXPECT_EQ(1, o.arg_position); - EXPECT_TRUE(Run("1$d", true)); - EXPECT_EQ(1, o.arg_position); - EXPECT_TRUE(Run("123$d")); - EXPECT_EQ(123, o.arg_position); - EXPECT_TRUE(Run("123$d", true)); - EXPECT_EQ(123, o.arg_position); - EXPECT_TRUE(Run("10$d")); - EXPECT_EQ(10, o.arg_position); - EXPECT_TRUE(Run("10$d", true)); - EXPECT_EQ(10, o.arg_position); - - // Position can't be zero. - EXPECT_FALSE(Run("0$d")); - EXPECT_FALSE(Run("0$d", true)); - EXPECT_FALSE(Run("1$*0$d")); - EXPECT_FALSE(Run("1$.*0$d")); - - // Position can't start with a zero digit at all. That is not a 'decimal'. - EXPECT_FALSE(Run("01$p")); - EXPECT_FALSE(Run("01$p", true)); - EXPECT_FALSE(Run("1$*01$p")); - EXPECT_FALSE(Run("1$.*01$p")); -} - -TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) { - EXPECT_TRUE(Run("14d")); - EXPECT_EQ('d', FormatConversionCharToChar(o.conv)); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_EQ(14, o.width.value()); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_LT(o.precision.value(), 0); - - EXPECT_TRUE(Run("14.d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(14, o.width.value()); - EXPECT_EQ(0, o.precision.value()); - - EXPECT_TRUE(Run(".d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_LT(o.width.value(), 0); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(0, o.precision.value()); - - EXPECT_TRUE(Run(".5d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_LT(o.width.value(), 0); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(5, o.precision.value()); - - EXPECT_TRUE(Run(".0d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_LT(o.width.value(), 0); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(0, o.precision.value()); - - EXPECT_TRUE(Run("14.5d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(14, o.width.value()); - EXPECT_EQ(5, o.precision.value()); - - EXPECT_TRUE(Run("*.*d")); - EXPECT_TRUE(o.width.is_from_arg()); - EXPECT_EQ(1, o.width.get_from_arg()); - EXPECT_TRUE(o.precision.is_from_arg()); - EXPECT_EQ(2, o.precision.get_from_arg()); - EXPECT_EQ(3, o.arg_position); - - EXPECT_TRUE(Run("*d")); - EXPECT_TRUE(o.width.is_from_arg()); - EXPECT_EQ(1, o.width.get_from_arg()); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_LT(o.precision.value(), 0); - EXPECT_EQ(2, o.arg_position); - - EXPECT_TRUE(Run(".*d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_LT(o.width.value(), 0); - EXPECT_TRUE(o.precision.is_from_arg()); - EXPECT_EQ(1, o.precision.get_from_arg()); - EXPECT_EQ(2, o.arg_position); - - // mixed implicit and explicit: didn't specify arg position. - EXPECT_FALSE(Run("*23$.*34$d")); - - EXPECT_TRUE(Run("12$*23$.*34$d")); - EXPECT_EQ(12, o.arg_position); - EXPECT_TRUE(o.width.is_from_arg()); - EXPECT_EQ(23, o.width.get_from_arg()); - EXPECT_TRUE(o.precision.is_from_arg()); - EXPECT_EQ(34, o.precision.get_from_arg()); - - EXPECT_TRUE(Run("2$*5$.*9$d")); - EXPECT_EQ(2, o.arg_position); - EXPECT_TRUE(o.width.is_from_arg()); - EXPECT_EQ(5, o.width.get_from_arg()); - EXPECT_TRUE(o.precision.is_from_arg()); - EXPECT_EQ(9, o.precision.get_from_arg()); - - EXPECT_FALSE(Run(".*0$d")) << "no arg 0"; - - // Large values - EXPECT_TRUE(Run("999999999.999999999d")); - EXPECT_FALSE(o.width.is_from_arg()); - EXPECT_EQ(999999999, o.width.value()); - EXPECT_FALSE(o.precision.is_from_arg()); - EXPECT_EQ(999999999, o.precision.value()); - - EXPECT_FALSE(Run("1000000000.999999999d")); - EXPECT_FALSE(Run("999999999.1000000000d")); - EXPECT_FALSE(Run("9999999999d")); - EXPECT_FALSE(Run(".9999999999d")); -} - -TEST_F(ConsumeUnboundConversionTest, Flags) { - static const char kAllFlags[] = "-+ #0"; - static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1; - for (int rev = 0; rev < 2; ++rev) { - for (int i = 0; i < 1 << kNumFlags; ++i) { - std::string fmt; - for (int k = 0; k < kNumFlags; ++k) - if ((i >> k) & 1) fmt += kAllFlags[k]; - // flag order shouldn't matter - if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); } - fmt += 'd'; - SCOPED_TRACE(fmt); - EXPECT_TRUE(Run(fmt.c_str())); - EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left); - EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos); - EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col); - EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt); - EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero); - } - } -} - -TEST_F(ConsumeUnboundConversionTest, BasicFlag) { - // Flag is on - for (const char* fmt : {"d", "llx", "G", "1$X"}) { - SCOPED_TRACE(fmt); - EXPECT_TRUE(Run(fmt)); - EXPECT_TRUE(o.flags.basic); - } - - // Flag is off - for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) { - SCOPED_TRACE(fmt); - EXPECT_TRUE(Run(fmt)); - EXPECT_FALSE(o.flags.basic); - } -} - -TEST_F(ConsumeUnboundConversionTest, LengthMod) { - EXPECT_TRUE(Run("d")); - EXPECT_EQ(LengthMod::none, o.length_mod); - EXPECT_TRUE(Run("hd")); - EXPECT_EQ(LengthMod::h, o.length_mod); - EXPECT_TRUE(Run("hhd")); - EXPECT_EQ(LengthMod::hh, o.length_mod); - EXPECT_TRUE(Run("ld")); - EXPECT_EQ(LengthMod::l, o.length_mod); - EXPECT_TRUE(Run("lld")); - EXPECT_EQ(LengthMod::ll, o.length_mod); - EXPECT_TRUE(Run("Lf")); - EXPECT_EQ(LengthMod::L, o.length_mod); - EXPECT_TRUE(Run("qf")); - EXPECT_EQ(LengthMod::q, o.length_mod); - EXPECT_TRUE(Run("jd")); - EXPECT_EQ(LengthMod::j, o.length_mod); - EXPECT_TRUE(Run("zd")); - EXPECT_EQ(LengthMod::z, o.length_mod); - EXPECT_TRUE(Run("td")); - EXPECT_EQ(LengthMod::t, o.length_mod); -} - -struct SummarizeConsumer { - std::string* out; - explicit SummarizeConsumer(std::string* out) : out(out) {} - - bool Append(string_view s) { - *out += "[" + std::string(s) + "]"; - return true; - } - - bool ConvertOne(const UnboundConversion& conv, string_view s) { - *out += "{"; - *out += std::string(s); - *out += ":"; - *out += std::to_string(conv.arg_position) + "$"; - if (conv.width.is_from_arg()) { - *out += std::to_string(conv.width.get_from_arg()) + "$*"; - } - if (conv.precision.is_from_arg()) { - *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*"; - } - *out += FormatConversionCharToChar(conv.conv); - *out += "}"; - return true; - } -}; - -std::string SummarizeParsedFormat(const ParsedFormatBase& pc) { - std::string out; - if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!"; - return out; -} - -class ParsedFormatTest : public testing::Test {}; - -TEST_F(ParsedFormatTest, ValueSemantics) { - ParsedFormatBase p1({}, true, {}); // empty format - EXPECT_EQ("", SummarizeParsedFormat(p1)); - - ParsedFormatBase p2 = p1; // copy construct (empty) - EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2)); - - p1 = ParsedFormatBase("hello%s", true, - {FormatConversionCharSetInternal::s}); // move assign - EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1)); - - ParsedFormatBase p3 = p1; // copy construct (nonempty) - EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3)); - - using std::swap; - swap(p1, p2); - EXPECT_EQ("", SummarizeParsedFormat(p1)); - EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2)); - swap(p1, p2); // undo - - p2 = p1; // copy assign - EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2)); -} - -struct ExpectParse { - const char* in; - std::initializer_list<FormatConversionCharSet> conv_set; - const char* out; -}; - -TEST_F(ParsedFormatTest, Parsing) { - // Parse should be equivalent to that obtained by ConversionParseIterator. - // No need to retest the parsing edge cases here. - const ExpectParse kExpect[] = { - {"", {}, ""}, - {"ab", {}, "[ab]"}, - {"a%d", {FormatConversionCharSetInternal::d}, "[a]{d:1$d}"}, - {"a%+d", {FormatConversionCharSetInternal::d}, "[a]{+d:1$d}"}, - {"a% d", {FormatConversionCharSetInternal::d}, "[a]{ d:1$d}"}, - {"a%b %d", {}, "[a]!"}, // stop after error - }; - for (const auto& e : kExpect) { - SCOPED_TRACE(e.in); - EXPECT_EQ(e.out, - SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set))); - } -} - -TEST_F(ParsedFormatTest, ParsingFlagOrder) { - const ExpectParse kExpect[] = { - {"a%+ 0d", {FormatConversionCharSetInternal::d}, "[a]{+ 0d:1$d}"}, - {"a%+0 d", {FormatConversionCharSetInternal::d}, "[a]{+0 d:1$d}"}, - {"a%0+ d", {FormatConversionCharSetInternal::d}, "[a]{0+ d:1$d}"}, - {"a% +0d", {FormatConversionCharSetInternal::d}, "[a]{ +0d:1$d}"}, - {"a%0 +d", {FormatConversionCharSetInternal::d}, "[a]{0 +d:1$d}"}, - {"a% 0+d", {FormatConversionCharSetInternal::d}, "[a]{ 0+d:1$d}"}, - {"a%+ 0+d", {FormatConversionCharSetInternal::d}, "[a]{+ 0+d:1$d}"}, - }; - for (const auto& e : kExpect) { - SCOPED_TRACE(e.in); - EXPECT_EQ(e.out, - SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set))); - } -} - -} // namespace -} // namespace str_format_internal -ABSL_NAMESPACE_END -} // namespace absl |