create
This commit is contained in:
671
Pods/abseil/absl/strings/internal/str_format/arg.cc
generated
Normal file
671
Pods/abseil/absl/strings/internal/str_format/arg.cc
generated
Normal file
@@ -0,0 +1,671 @@
|
||||
// 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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/container/fixed_array.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
#include "absl/strings/internal/str_format/float_conversion.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#if defined(ABSL_HAVE_STD_STRING_VIEW)
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
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_ = static_cast<size_t>(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_ = static_cast<size_t>(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<uint32_t>(v % 100), p);
|
||||
v /= 100;
|
||||
} while (v);
|
||||
if (p[0] == '0') {
|
||||
// We printed one too many hexits.
|
||||
++p;
|
||||
}
|
||||
if (add_neg) {
|
||||
*--p = '-';
|
||||
}
|
||||
size_ = static_cast<size_t>(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_ = static_cast<size_t>(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_ = static_cast<size_t>(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(char v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
size_t fill = 0;
|
||||
if (conv.width() >= 0)
|
||||
fill = static_cast<size_t>(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 = static_cast<size_t>(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);
|
||||
|
||||
bool precision_specified = conv.precision() >= 0;
|
||||
size_t precision =
|
||||
precision_specified ? static_cast<size_t>(conv.precision()) : size_t{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') {
|
||||
size_t needed = 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 ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
|
||||
if (conv.conversion_char() == FormatConversionCharInternal::v) {
|
||||
conv.set_conversion_char(FormatConversionCharInternal::g);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
struct ShiftState {
|
||||
bool saw_high_surrogate = false;
|
||||
uint8_t bits = 0;
|
||||
};
|
||||
|
||||
// Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
|
||||
// assumed to have enough space for the output. `s` is used to carry state
|
||||
// between successive calls with a UTF-16 surrogate pair. Returns the number of
|
||||
// chars written, or `static_cast<size_t>(-1)` on failure.
|
||||
//
|
||||
// This is basically std::wcrtomb(), but always outputting UTF-8 instead of
|
||||
// respecting the current locale.
|
||||
inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
|
||||
const auto v = static_cast<uint32_t>(wc);
|
||||
if (v < 0x80) {
|
||||
*buf = static_cast<char>(v);
|
||||
return 1;
|
||||
} else if (v < 0x800) {
|
||||
*buf++ = static_cast<char>(0xc0 | (v >> 6));
|
||||
*buf = static_cast<char>(0x80 | (v & 0x3f));
|
||||
return 2;
|
||||
} else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
|
||||
*buf++ = static_cast<char>(0xe0 | (v >> 12));
|
||||
*buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
|
||||
*buf = static_cast<char>(0x80 | (v & 0x3f));
|
||||
return 3;
|
||||
} else if ((v - 0x10000) < 0x100000) {
|
||||
*buf++ = static_cast<char>(0xf0 | (v >> 18));
|
||||
*buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
|
||||
*buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
|
||||
*buf = static_cast<char>(0x80 | (v & 0x3f));
|
||||
return 4;
|
||||
} else if (v < 0xdc00) {
|
||||
s.saw_high_surrogate = true;
|
||||
s.bits = static_cast<uint8_t>(v & 0x3);
|
||||
const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
|
||||
*buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
|
||||
*buf =
|
||||
static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
|
||||
static_cast<uint8_t>((v >> 2) & 0xf));
|
||||
return 2;
|
||||
} else if (v < 0xe000 && s.saw_high_surrogate) {
|
||||
*buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
|
||||
static_cast<uint8_t>((v >> 6) & 0xf));
|
||||
*buf = static_cast<char>(0x80 | (v & 0x3f));
|
||||
s.saw_high_surrogate = false;
|
||||
s.bits = 0;
|
||||
return 2;
|
||||
} else {
|
||||
return static_cast<size_t>(-1);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ConvertStringArg(const wchar_t *v,
|
||||
size_t len,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
FixedArray<char> mb(len * 4);
|
||||
ShiftState s;
|
||||
size_t chars_written = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
const size_t chars = WideToUtf8(v[i], &mb[chars_written], s);
|
||||
if (chars == static_cast<size_t>(-1)) { return false; }
|
||||
chars_written += chars;
|
||||
}
|
||||
return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink);
|
||||
}
|
||||
|
||||
bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
char mb[4];
|
||||
ShiftState s;
|
||||
const size_t chars_written = WideToUtf8(v, mb, s);
|
||||
return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate &&
|
||||
ConvertStringArg(string_view(mb, chars_written), conv, sink);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
|
||||
if (v) {
|
||||
sink->Append("true");
|
||||
} else {
|
||||
sink->Append("false");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool ConvertIntArg(T v, 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
|
||||
// FormatConversionChar. 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 (std::is_same<T, wchar_t>::value ||
|
||||
(conv.length_mod() == LengthMod::l))
|
||||
? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink)
|
||||
: ConvertCharImpl(static_cast<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):
|
||||
case static_cast<uint8_t>(FormatConversionCharInternal::v):
|
||||
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_ASSUME(false);
|
||||
}
|
||||
|
||||
if (conv.is_basic()) {
|
||||
sink->Append(as_digits.with_neg_and_zero());
|
||||
return true;
|
||||
}
|
||||
return ConvertIntImplInnerSlow(as_digits, conv, sink);
|
||||
}
|
||||
|
||||
template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<signed char>(signed char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<unsigned char>(unsigned char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<short>(short v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<unsigned short>(unsigned short v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<unsigned int>(unsigned int v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<long>(long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<long long>(long long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
template bool ConvertIntArg<unsigned long long>(unsigned long long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink);
|
||||
|
||||
// ==================== Strings ====================
|
||||
StringConvertResult FormatConvertImpl(const std::string &v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
return {ConvertStringArg(v, conv, sink)};
|
||||
}
|
||||
|
||||
StringConvertResult FormatConvertImpl(const std::wstring &v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
return {ConvertStringArg(v.data(), v.size(), conv, sink)};
|
||||
}
|
||||
|
||||
StringConvertResult FormatConvertImpl(string_view v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
return {ConvertStringArg(v, conv, sink)};
|
||||
}
|
||||
|
||||
#if defined(ABSL_HAVE_STD_STRING_VIEW)
|
||||
StringConvertResult FormatConvertImpl(std::wstring_view v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
return {ConvertStringArg(v.data(), v.size(), conv, sink)};
|
||||
}
|
||||
#endif
|
||||
|
||||
StringPtrConvertResult 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 = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v);
|
||||
}
|
||||
return {ConvertStringArg(string_view(v, len), conv, sink)};
|
||||
}
|
||||
|
||||
StringPtrConvertResult FormatConvertImpl(const wchar_t* 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::wcslen(v);
|
||||
} else {
|
||||
// If precision is set, we look for the NUL-terminator on the valid range.
|
||||
len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v);
|
||||
}
|
||||
return {ConvertStringArg(v, len, conv, sink)};
|
||||
}
|
||||
|
||||
StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
return FormatConvertImpl(static_cast<const char*>(nullptr), 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 ====================
|
||||
CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl *sink) {
|
||||
return {ConvertIntArg(v, conv, sink)};
|
||||
}
|
||||
CharConvertResult FormatConvertImpl(wchar_t v,
|
||||
const FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
return {ConvertIntArg(v, conv, sink)};
|
||||
}
|
||||
|
||||
// ==================== Ints ====================
|
||||
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)};
|
||||
}
|
||||
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
|
||||
671
Pods/abseil/absl/strings/internal/str_format/arg.h
generated
Normal file
671
Pods/abseil/absl/strings/internal/str_format/arg.h
generated
Normal file
@@ -0,0 +1,671 @@
|
||||
// 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 <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/strings/has_absl_stringify.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#if defined(ABSL_HAVE_STD_STRING_VIEW)
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
class Cord;
|
||||
class FormatCountCapture;
|
||||
class FormatSink;
|
||||
|
||||
template <absl::FormatConversionCharSet C>
|
||||
struct FormatConvertResult;
|
||||
class FormatConversionSpec;
|
||||
|
||||
namespace str_format_internal {
|
||||
|
||||
template <FormatConversionCharSet C>
|
||||
struct ArgConvertResult {
|
||||
bool value;
|
||||
};
|
||||
|
||||
using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::c,
|
||||
FormatConversionCharSetInternal::kNumeric,
|
||||
FormatConversionCharSetInternal::kStar,
|
||||
FormatConversionCharSetInternal::v)>;
|
||||
using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::kFloating,
|
||||
FormatConversionCharSetInternal::v)>;
|
||||
using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::c,
|
||||
FormatConversionCharSetInternal::kNumeric,
|
||||
FormatConversionCharSetInternal::kStar)>;
|
||||
|
||||
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 {};
|
||||
|
||||
// These declarations prevent ADL lookup from continuing in absl namespaces,
|
||||
// we are deliberately using these as ADL hooks and want them to consider
|
||||
// non-absl namespaces only.
|
||||
void AbslFormatConvert();
|
||||
void AbslStringify();
|
||||
|
||||
template <typename T>
|
||||
bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
|
||||
|
||||
// Forward declarations of internal `ConvertIntArg` function template
|
||||
// instantiations are here to avoid including the template body in the headers
|
||||
// and instantiating it in large numbers of translation units. Explicit
|
||||
// instantiations can be found in "absl/strings/internal/str_format/arg.cc"
|
||||
extern template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<signed char>(signed char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<unsigned char>(unsigned char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<wchar_t>(wchar_t v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<short>(short v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<unsigned short>( // NOLINT
|
||||
unsigned short v, FormatConversionSpecImpl conv, // NOLINT
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<unsigned int>(unsigned int v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<long>( // NOLINT
|
||||
long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT
|
||||
extern template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<long long>(long long v, // NOLINT
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
extern template bool ConvertIntArg<unsigned long long>( // NOLINT
|
||||
unsigned long long v, FormatConversionSpecImpl conv, // NOLINT
|
||||
FormatSinkImpl* sink);
|
||||
|
||||
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>
|
||||
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink)
|
||||
-> std::enable_if_t<std::is_enum<T>::value &&
|
||||
std::is_void<decltype(AbslStringify(
|
||||
std::declval<FormatSink&>(), v))>::value,
|
||||
IntegralConvertResult> {
|
||||
if (conv.conversion_char() == FormatConversionCharInternal::v) {
|
||||
using FormatSinkT =
|
||||
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
|
||||
auto fs = sink->Wrap<FormatSinkT>();
|
||||
AbslStringify(fs, v);
|
||||
return {true};
|
||||
} else {
|
||||
return {ConvertIntArg(
|
||||
static_cast<typename std::underlying_type<T>::type>(v), conv, sink)};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
|
||||
FormatSinkImpl* sink)
|
||||
-> std::enable_if_t<!std::is_enum<T>::value &&
|
||||
!std::is_same<T, absl::Cord>::value &&
|
||||
std::is_void<decltype(AbslStringify(
|
||||
std::declval<FormatSink&>(), v))>::value,
|
||||
ArgConvertResult<FormatConversionCharSetInternal::v>> {
|
||||
using FormatSinkT =
|
||||
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
|
||||
auto fs = sink->Wrap<FormatSinkT>();
|
||||
AbslStringify(fs, v);
|
||||
return {true};
|
||||
}
|
||||
|
||||
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>
|
||||
constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) {
|
||||
return C;
|
||||
}
|
||||
|
||||
template <FormatConversionCharSet C>
|
||||
constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) {
|
||||
return C;
|
||||
}
|
||||
|
||||
ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
|
||||
VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
|
||||
|
||||
// Strings.
|
||||
using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::s,
|
||||
FormatConversionCharSetInternal::v)>;
|
||||
StringConvertResult FormatConvertImpl(const std::string& v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
StringConvertResult FormatConvertImpl(const std::wstring& v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
StringConvertResult FormatConvertImpl(string_view v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
#if defined(ABSL_HAVE_STD_STRING_VIEW)
|
||||
StringConvertResult FormatConvertImpl(std::wstring_view v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
#if !defined(ABSL_USES_STD_STRING_VIEW)
|
||||
inline StringConvertResult FormatConvertImpl(std::string_view v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink);
|
||||
}
|
||||
#endif // !ABSL_USES_STD_STRING_VIEW
|
||||
#endif // ABSL_HAVE_STD_STRING_VIEW
|
||||
|
||||
using StringPtrConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::s,
|
||||
FormatConversionCharSetInternal::p)>;
|
||||
StringPtrConvertResult FormatConvertImpl(const char* v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
StringPtrConvertResult FormatConvertImpl(const wchar_t* v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
// This overload is needed to disambiguate, since `nullptr` could match either
|
||||
// of the other overloads equally well.
|
||||
StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
|
||||
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 = static_cast<size_t>(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};
|
||||
}
|
||||
|
||||
bool ConvertBoolArg(bool v, FormatSinkImpl* sink);
|
||||
|
||||
// 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.
|
||||
CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
CharConvertResult FormatConvertImpl(wchar_t v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
|
||||
// Ints.
|
||||
IntegralConvertResult FormatConvertImpl(signed char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
IntegralConvertResult FormatConvertImpl(unsigned char v,
|
||||
FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink);
|
||||
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);
|
||||
|
||||
// This function needs to be a template due to ambiguity regarding type
|
||||
// conversions.
|
||||
template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
|
||||
IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv,
|
||||
FormatSinkImpl* sink) {
|
||||
if (conv.conversion_char() == FormatConversionCharInternal::v) {
|
||||
return {ConvertBoolArg(v, 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 &&
|
||||
!HasAbslStringify<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() {
|
||||
using ConvResult = decltype(str_format_internal::FormatConvertImpl(
|
||||
std::declval<const Arg&>(),
|
||||
std::declval<const FormatConversionSpecImpl&>(),
|
||||
std::declval<FormatSinkImpl*>()));
|
||||
return absl::str_format_internal::ExtractCharSet(ConvResult{});
|
||||
}
|
||||
|
||||
// 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 wchar_t* and wchar_t arrays into `const wchar_t*`
|
||||
// - Decay any other pointer to `const void*`
|
||||
// - Decay all enums to the integral promotion of 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 ||
|
||||
HasAbslStringify<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, const wchar_t*>::value,
|
||||
const wchar_t*,
|
||||
typename std::conditional<
|
||||
!kHasUserDefined && std::is_convertible<T, VoidPtr>::value,
|
||||
VoidPtr,
|
||||
const T&>::type>::type>::type;
|
||||
};
|
||||
template <typename T>
|
||||
struct DecayType<
|
||||
T, typename std::enable_if<
|
||||
!str_format_internal::HasUserDefinedConvert<T>::value &&
|
||||
!HasAbslStringify<T>::value && std::is_enum<T>::value>::type> {
|
||||
using type = decltype(+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_NO_WSTRING_VIEW_(...) \
|
||||
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_INSTANTIATE_(const wchar_t*, __VA_ARGS__); \
|
||||
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__)
|
||||
|
||||
#if defined(ABSL_HAVE_STD_STRING_VIEW)
|
||||
#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
|
||||
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \
|
||||
__VA_ARGS__); \
|
||||
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__)
|
||||
#else
|
||||
#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
|
||||
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
|
||||
|
||||
|
||||
} // namespace str_format_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
|
||||
275
Pods/abseil/absl/strings/internal/str_format/bind.cc
generated
Normal file
275
Pods/abseil/absl/strings/internal/str_format/bind.cc
generated
Normal file
@@ -0,0 +1,275 @@
|
||||
// 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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/strings/internal/str_format/arg.h"
|
||||
#include "absl/strings/internal/str_format/constexpr_parser.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
#include "absl/strings/internal/str_format/output.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
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[static_cast<size_t>(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_[static_cast<size_t>(arg_position - 1)]; // 1-based
|
||||
|
||||
if (unbound->flags != Flags::kBasic) {
|
||||
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) {
|
||||
FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft,
|
||||
bound);
|
||||
} else {
|
||||
FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
|
||||
}
|
||||
|
||||
FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, 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(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
|
||||
237
Pods/abseil/absl/strings/internal/str_format/bind.h
generated
Normal file
237
Pods/abseil/absl/strings/internal/str_format/bind.h
generated
Normal file
@@ -0,0 +1,237 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdio>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "absl/strings/internal/str_format/arg.h"
|
||||
#include "absl/strings/internal/str_format/checker.h"
|
||||
#include "absl/strings/internal/str_format/constexpr_parser.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
#include "absl/strings/internal/str_format/parser.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "absl/utility/utility.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;
|
||||
|
||||
template <bool res>
|
||||
struct ErrorMaker {
|
||||
constexpr bool operator()(int) const { return res; }
|
||||
};
|
||||
|
||||
template <int i, int j>
|
||||
static constexpr bool CheckArity(ErrorMaker<true> SpecifierCount = {},
|
||||
ErrorMaker<i == j> ParametersPassed = {}) {
|
||||
static_assert(SpecifierCount(i) == ParametersPassed(j),
|
||||
"Number of arguments passed must match the number of "
|
||||
"conversion specifiers.");
|
||||
return true;
|
||||
}
|
||||
|
||||
template <FormatConversionCharSet specified, FormatConversionCharSet passed,
|
||||
int arg>
|
||||
static constexpr bool CheckMatch(
|
||||
ErrorMaker<Contains(specified, passed)> MismatchedArgumentNumber = {}) {
|
||||
static_assert(MismatchedArgumentNumber(arg),
|
||||
"Passed argument must match specified format.");
|
||||
return true;
|
||||
}
|
||||
|
||||
template <FormatConversionCharSet... C, size_t... I>
|
||||
static bool CheckMatches(absl::index_sequence<I...>) {
|
||||
bool res[] = {true, CheckMatch<Args, C, I + 1>()...};
|
||||
(void)res;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 it is 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")))
|
||||
: Base("to avoid noise in the compiler error") {
|
||||
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>
|
||||
FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT
|
||||
: Base(&pc) {
|
||||
CheckArity<sizeof...(C), sizeof...(Args)>();
|
||||
CheckMatches<C...>(absl::make_index_sequence<sizeof...(C)>{});
|
||||
}
|
||||
};
|
||||
|
||||
class Streamable {
|
||||
public:
|
||||
Streamable(const UntypedFormatSpecImpl& format,
|
||||
absl::Span<const FormatArgImpl> args)
|
||||
: format_(format), args_(args.begin(), args.end()) {}
|
||||
|
||||
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::InlinedVector<FormatArgImpl, 4> 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(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<FormatConversionCharSetUnion(
|
||||
FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>
|
||||
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_
|
||||
100
Pods/abseil/absl/strings/internal/str_format/checker.h
generated
Normal file
100
Pods/abseil/absl/strings/internal/str_format/checker.h
generated
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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 <algorithm>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/strings/internal/str_format/arg.h"
|
||||
#include "absl/strings/internal/str_format/constexpr_parser.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
|
||||
// Compile time check support for entry points.
|
||||
|
||||
#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
|
||||
// We disable format checker under vscode intellisense compilation.
|
||||
// See https://github.com/microsoft/vscode-cpptools/issues/3683 for
|
||||
// more details.
|
||||
#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && \
|
||||
!defined(__INTELLISENSE__)
|
||||
#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
|
||||
#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) &&
|
||||
// !defined(__INTELLISENSE__)
|
||||
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace str_format_internal {
|
||||
|
||||
#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
|
||||
|
||||
template <FormatConversionCharSet... C>
|
||||
constexpr bool ValidFormatImpl(string_view format) {
|
||||
int next_arg = 0;
|
||||
const char* p = format.data();
|
||||
const char* const end = p + format.size();
|
||||
constexpr FormatConversionCharSet
|
||||
kAllowedConvs[(std::max)(sizeof...(C), size_t{1})] = {C...};
|
||||
bool used[(std::max)(sizeof...(C), size_t{1})]{};
|
||||
constexpr int kNumArgs = sizeof...(C);
|
||||
while (p != end) {
|
||||
while (p != end && *p != '%') ++p;
|
||||
if (p == end) {
|
||||
break;
|
||||
}
|
||||
if (p + 1 >= end) return false;
|
||||
if (p[1] == '%') {
|
||||
// %%
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
UnboundConversion conv(absl::kConstInit);
|
||||
p = ConsumeUnboundConversion(p + 1, end, &conv, &next_arg);
|
||||
if (p == nullptr) return false;
|
||||
if (conv.arg_position <= 0 || conv.arg_position > kNumArgs) {
|
||||
return false;
|
||||
}
|
||||
if (!Contains(kAllowedConvs[conv.arg_position - 1], conv.conv)) {
|
||||
return false;
|
||||
}
|
||||
used[conv.arg_position - 1] = true;
|
||||
for (auto extra : {conv.width, conv.precision}) {
|
||||
if (extra.is_from_arg()) {
|
||||
int pos = extra.get_from_arg();
|
||||
if (pos <= 0 || pos > kNumArgs) return false;
|
||||
used[pos - 1] = true;
|
||||
if (!Contains(kAllowedConvs[pos - 1], '*')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sizeof...(C) != 0) {
|
||||
for (bool b : used) {
|
||||
if (!b) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
|
||||
|
||||
} // namespace str_format_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
|
||||
357
Pods/abseil/absl/strings/internal/str_format/constexpr_parser.h
generated
Normal file
357
Pods/abseil/absl/strings/internal/str_format/constexpr_parser.h
generated
Normal file
@@ -0,0 +1,357 @@
|
||||
// Copyright 2022 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_CONSTEXPR_PARSER_H_
|
||||
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/const_init.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace str_format_internal {
|
||||
|
||||
// The analyzed properties of a single specified conversion.
|
||||
struct UnboundConversion {
|
||||
// This is a user defined default constructor on purpose to skip the
|
||||
// initialization of parts of the object that are not necessary.
|
||||
UnboundConversion() {} // NOLINT
|
||||
|
||||
// This constructor is provided for the static checker. We don't want to do
|
||||
// the unnecessary initialization in the normal case.
|
||||
explicit constexpr UnboundConversion(absl::ConstInitType)
|
||||
: arg_position{}, width{}, precision{} {}
|
||||
|
||||
class InputValue {
|
||||
public:
|
||||
constexpr void set_value(int value) {
|
||||
assert(value >= 0);
|
||||
value_ = value;
|
||||
}
|
||||
constexpr 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 unspecified in this state.
|
||||
constexpr void set_from_arg(int value) {
|
||||
assert(value > 0);
|
||||
value_ = -value - 1;
|
||||
}
|
||||
constexpr bool is_from_arg() const { return value_ < -1; }
|
||||
constexpr 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 = Flags::kBasic;
|
||||
LengthMod length_mod = LengthMod::none;
|
||||
FormatConversionChar conv = FormatConversionCharInternal::kNone;
|
||||
};
|
||||
|
||||
// Helper tag class for the table below.
|
||||
// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and
|
||||
// conversions.
|
||||
class ConvTag {
|
||||
public:
|
||||
constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT
|
||||
: tag_(static_cast<uint8_t>(conversion_char)) {}
|
||||
constexpr ConvTag(LengthMod length_mod) // NOLINT
|
||||
: tag_(0x80 | static_cast<uint8_t>(length_mod)) {}
|
||||
constexpr ConvTag(Flags flags) // NOLINT
|
||||
: tag_(0xc0 | static_cast<uint8_t>(flags)) {}
|
||||
constexpr ConvTag() : tag_(0xFF) {}
|
||||
|
||||
constexpr bool is_conv() const { return (tag_ & 0x80) == 0; }
|
||||
constexpr bool is_length() const { return (tag_ & 0xC0) == 0x80; }
|
||||
constexpr bool is_flags() const { return (tag_ & 0xE0) == 0xC0; }
|
||||
|
||||
constexpr FormatConversionChar as_conv() const {
|
||||
assert(is_conv());
|
||||
assert(!is_length());
|
||||
assert(!is_flags());
|
||||
return static_cast<FormatConversionChar>(tag_);
|
||||
}
|
||||
constexpr LengthMod as_length() const {
|
||||
assert(!is_conv());
|
||||
assert(is_length());
|
||||
assert(!is_flags());
|
||||
return static_cast<LengthMod>(tag_ & 0x3F);
|
||||
}
|
||||
constexpr Flags as_flags() const {
|
||||
assert(!is_conv());
|
||||
assert(!is_length());
|
||||
assert(is_flags());
|
||||
return static_cast<Flags>(tag_ & 0x1F);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t tag_;
|
||||
};
|
||||
|
||||
struct ConvTagHolder {
|
||||
using CC = FormatConversionCharInternal;
|
||||
using LM = LengthMod;
|
||||
|
||||
// Abbreviations to fit in the table below.
|
||||
static constexpr auto kFSign = Flags::kSignCol;
|
||||
static constexpr auto kFAlt = Flags::kAlt;
|
||||
static constexpr auto kFPos = Flags::kShowPos;
|
||||
static constexpr auto kFLeft = Flags::kLeft;
|
||||
static constexpr auto kFZero = Flags::kZero;
|
||||
|
||||
static constexpr ConvTag value[256] = {
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 00-07
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 08-0f
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 10-17
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 18-1f
|
||||
kFSign, {}, {}, kFAlt, {}, {}, {}, {}, // !"#$%&'
|
||||
{}, {}, {}, kFPos, {}, kFLeft, {}, {}, // ()*+,-./
|
||||
kFZero, {}, {}, {}, {}, {}, {}, {}, // 01234567
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>?
|
||||
{}, 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, CC::v, {}, // 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
|
||||
};
|
||||
};
|
||||
|
||||
// Keep a single table for all the conversion chars and length modifiers.
|
||||
constexpr ConvTag GetTagForChar(char c) {
|
||||
return ConvTagHolder::value[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
constexpr bool CheckFastPathSetting(const UnboundConversion& conv) {
|
||||
bool width_precision_needed =
|
||||
conv.width.value() >= 0 || conv.precision.value() >= 0;
|
||||
if (width_precision_needed && conv.flags == Flags::kBasic) {
|
||||
#if defined(__clang__)
|
||||
// Some compilers complain about this in constexpr even when not executed,
|
||||
// so only enable the error dump in clang.
|
||||
fprintf(stderr,
|
||||
"basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
|
||||
"width=%d precision=%d\n",
|
||||
conv.flags == Flags::kBasic ? 1 : 0,
|
||||
FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0,
|
||||
FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0,
|
||||
FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0,
|
||||
FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0,
|
||||
FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(),
|
||||
conv.precision.value());
|
||||
#endif // defined(__clang__)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr int ParseDigits(char& c, const char*& pos, const char* const end) {
|
||||
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 ('0' > c || c > '9') break;
|
||||
--num_digits;
|
||||
if (ABSL_PREDICT_FALSE(!num_digits)) break;
|
||||
digits = 10 * digits + c - '0';
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
template <bool is_positional>
|
||||
constexpr const char* ConsumeConversion(const char* pos, const char* const end,
|
||||
UnboundConversion* conv,
|
||||
int* next_arg) {
|
||||
const char* const original_pos = pos;
|
||||
char c = 0;
|
||||
// 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)
|
||||
|
||||
if (is_positional) {
|
||||
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
|
||||
if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
|
||||
conv->arg_position = ParseDigits(c, pos, end);
|
||||
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 == Flags::kBasic);
|
||||
|
||||
// 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') {
|
||||
while (c <= '0') {
|
||||
auto tag = GetTagForChar(c);
|
||||
if (tag.is_flags()) {
|
||||
conv->flags = conv->flags | tag.as_flags();
|
||||
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c <= '9') {
|
||||
if (c >= '0') {
|
||||
int maybe_width = ParseDigits(c, pos, end);
|
||||
if (!is_positional && c == '$') {
|
||||
if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr;
|
||||
// Positional conversion.
|
||||
*next_arg = -1;
|
||||
return ConsumeConversion<true>(original_pos, end, conv, next_arg);
|
||||
}
|
||||
conv->flags = conv->flags | Flags::kNonBasic;
|
||||
conv->width.set_value(maybe_width);
|
||||
} else if (c == '*') {
|
||||
conv->flags = conv->flags | Flags::kNonBasic;
|
||||
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
|
||||
if (is_positional) {
|
||||
if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
|
||||
conv->width.set_from_arg(ParseDigits(c, pos, end));
|
||||
if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
|
||||
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
|
||||
} else {
|
||||
conv->width.set_from_arg(++*next_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '.') {
|
||||
conv->flags = conv->flags | Flags::kNonBasic;
|
||||
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
|
||||
if ('0' <= c && c <= '9') {
|
||||
conv->precision.set_value(ParseDigits(c, pos, end));
|
||||
} 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(ParseDigits(c, pos, end));
|
||||
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(c == 'v' && conv->flags != Flags::kBasic)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ABSL_PREDICT_FALSE(!tag.is_conv())) {
|
||||
if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
|
||||
|
||||
// It is a length modifier.
|
||||
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(c == 'v')) return nullptr;
|
||||
if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
|
||||
|
||||
// `wchar_t` args are marked non-basic so `Bind()` will copy the length mod.
|
||||
if (conv->length_mod == LengthMod::l && c == 'c') {
|
||||
conv->flags = conv->flags | Flags::kNonBasic;
|
||||
}
|
||||
}
|
||||
#undef ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR
|
||||
|
||||
assert(CheckFastPathSetting(*conv));
|
||||
(void)(&CheckFastPathSetting);
|
||||
|
||||
conv->conv = tag.as_conv();
|
||||
if (!is_positional) conv->arg_position = ++*next_arg;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// 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.
|
||||
constexpr 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);
|
||||
}
|
||||
|
||||
} // namespace str_format_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_
|
||||
75
Pods/abseil/absl/strings/internal/str_format/extension.cc
generated
Normal file
75
Pods/abseil/absl/strings/internal/str_format/extension.cc
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// 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 FlagsToString(Flags v) {
|
||||
std::string s;
|
||||
s.append(FlagsContains(v, Flags::kLeft) ? "-" : "");
|
||||
s.append(FlagsContains(v, Flags::kShowPos) ? "+" : "");
|
||||
s.append(FlagsContains(v, Flags::kSignCol) ? " " : "");
|
||||
s.append(FlagsContains(v, Flags::kAlt) ? "#" : "");
|
||||
s.append(FlagsContains(v, Flags::kZero) ? "0" : "");
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
|
||||
#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
|
||||
|
||||
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar;
|
||||
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral;
|
||||
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating;
|
||||
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric;
|
||||
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer;
|
||||
|
||||
#endif // ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
|
||||
bool FormatSinkImpl::PutPaddedString(string_view value, int width,
|
||||
int precision, bool left) {
|
||||
size_t space_remaining = 0;
|
||||
if (width >= 0)
|
||||
space_remaining = static_cast<size_t>(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
|
||||
456
Pods/abseil/absl/strings/internal/str_format/extension.h
generated
Normal file
456
Pods/abseil/absl/strings/internal/str_format/extension.h
generated
Normal file
@@ -0,0 +1,456 @@
|
||||
//
|
||||
// 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.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;
|
||||
enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
|
||||
|
||||
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_, static_cast<size_t>(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 static_cast<size_t>(buf_ + sizeof(buf_) - pos_);
|
||||
}
|
||||
|
||||
FormatRawSinkImpl raw_;
|
||||
size_t size_ = 0;
|
||||
char* pos_ = buf_;
|
||||
char buf_[1024];
|
||||
};
|
||||
|
||||
enum class Flags : uint8_t {
|
||||
kBasic = 0,
|
||||
kLeft = 1 << 0,
|
||||
kShowPos = 1 << 1,
|
||||
kSignCol = 1 << 2,
|
||||
kAlt = 1 << 3,
|
||||
kZero = 1 << 4,
|
||||
// This is not a real flag. It just exists to turn off kBasic when no other
|
||||
// flags are set. This is for when width/precision are specified, or a length
|
||||
// modifier affects the behavior ("%lc").
|
||||
kNonBasic = 1 << 5,
|
||||
};
|
||||
|
||||
constexpr Flags operator|(Flags a, Flags b) {
|
||||
return static_cast<Flags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
|
||||
}
|
||||
|
||||
constexpr bool FlagsContains(Flags haystack, Flags needle) {
|
||||
return (static_cast<uint8_t>(haystack) & static_cast<uint8_t>(needle)) ==
|
||||
static_cast<uint8_t>(needle);
|
||||
}
|
||||
|
||||
std::string FlagsToString(Flags v);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, Flags v) {
|
||||
return os << FlagsToString(v);
|
||||
}
|
||||
|
||||
// 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) X_SEP X_VAL(v)
|
||||
// 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, v, // 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 precision are not specified, no flags are set.
|
||||
bool is_basic() const { return flags_ == Flags::kBasic; }
|
||||
bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); }
|
||||
bool has_show_pos_flag() const {
|
||||
return FlagsContains(flags_, Flags::kShowPos);
|
||||
}
|
||||
bool has_sign_col_flag() const {
|
||||
return FlagsContains(flags_, Flags::kSignCol);
|
||||
}
|
||||
bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); }
|
||||
bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); }
|
||||
|
||||
LengthMod length_mod() const { return length_mod_; }
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
void set_conversion_char(FormatConversionChar c) { conv_ = c; }
|
||||
|
||||
// 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_;
|
||||
LengthMod length_mod_ = LengthMod::none;
|
||||
int width_;
|
||||
int precision_;
|
||||
};
|
||||
|
||||
struct FormatConversionSpecImplFriend final {
|
||||
static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
|
||||
conv->flags_ = f;
|
||||
}
|
||||
static void SetLengthMod(LengthMod l, FormatConversionSpecImpl* conv) {
|
||||
conv->length_mod_ = l;
|
||||
}
|
||||
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 str_format_internal::FlagsToString(spec.flags_);
|
||||
}
|
||||
};
|
||||
|
||||
// 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_
|
||||
1457
Pods/abseil/absl/strings/internal/str_format/float_conversion.cc
generated
Normal file
1457
Pods/abseil/absl/strings/internal/str_format/float_conversion.cc
generated
Normal file
@@ -0,0 +1,1457 @@
|
||||
// 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/optimization.h"
|
||||
#include "absl/functional/function_ref.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/numeric/internal/representation.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 {
|
||||
|
||||
using ::absl::numeric_internal::IsDoubleDouble;
|
||||
|
||||
// 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.
|
||||
// Requires: `0 <= carry <= 9`
|
||||
template <typename Int>
|
||||
inline char MultiplyBy10WithCarry(Int* v, char carry) {
|
||||
using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>;
|
||||
BiggerInt tmp =
|
||||
10 * static_cast<BiggerInt>(*v) + static_cast<BiggerInt>(carry);
|
||||
*v = static_cast<Int>(tmp);
|
||||
return static_cast<char>(tmp >> (sizeof(Int) * 8));
|
||||
}
|
||||
|
||||
// Calculates `(2^64 * carry + *v) / 10`.
|
||||
// Stores the quotient in `*v` and returns the remainder.
|
||||
// Requires: `0 <= carry <= 9`
|
||||
inline char DivideBy10WithCarry(uint64_t* v, char 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 carry_u64 = static_cast<uint64_t>(carry);
|
||||
const uint64_t mod = *v % divisor;
|
||||
const uint64_t next_carry = chunk_remainder * carry_u64 + mod;
|
||||
*v = *v / divisor + carry_u64 * chunk_quotient + next_carry / divisor;
|
||||
return static_cast<char>(next_carry % divisor);
|
||||
}
|
||||
|
||||
using MaxFloatType =
|
||||
typename std::conditional<IsDoubleDouble(), double, long double>::type;
|
||||
|
||||
// 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<MaxFloatType>::max_exponent`.
|
||||
class BinaryToDecimal {
|
||||
static constexpr size_t 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 static_cast<size_t>((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<MaxFloatType>::max_exponent);
|
||||
static_assert(
|
||||
StackArray::kMaxCapacity >=
|
||||
ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent),
|
||||
"");
|
||||
|
||||
StackArray::RunWithCapacity(
|
||||
ChunksNeeded(exp),
|
||||
[=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); });
|
||||
}
|
||||
|
||||
size_t TotalDigits() const {
|
||||
return (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.
|
||||
size_t after_chunk_index = static_cast<size_t>(exp / 32 + 1);
|
||||
decimal_start_ = decimal_end_ = ChunksNeeded(exp);
|
||||
const int offset = exp % 32;
|
||||
// Left shift v by exp bits.
|
||||
data_[after_chunk_index - 1] = static_cast<uint32_t>(v << offset);
|
||||
for (v >>= (32 - offset); v; v >>= 32)
|
||||
data_[++after_chunk_index - 1] = static_cast<uint32_t>(v);
|
||||
|
||||
while (after_chunk_index > 0) {
|
||||
// While we have more than one chunk available, go in steps of 1e9.
|
||||
// `data_[after_chunk_index - 1]` holds the highest non-zero binary chunk,
|
||||
// so keep the variable updated.
|
||||
uint32_t carry = 0;
|
||||
for (size_t i = after_chunk_index; i > 0; --i) {
|
||||
uint64_t tmp = uint64_t{data_[i - 1]} + (uint64_t{carry} << 32);
|
||||
data_[i - 1] = 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_[after_chunk_index - 1] == 0)
|
||||
--after_chunk_index;
|
||||
|
||||
--decimal_start_;
|
||||
assert(decimal_start_ != after_chunk_index - 1);
|
||||
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 size_t kDigitsPerChunk = 9;
|
||||
|
||||
size_t decimal_start_;
|
||||
size_t decimal_end_;
|
||||
|
||||
char digits_[kDigitsPerChunk];
|
||||
size_t 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<MaxFloatType>::min_exponent - limits<MaxFloatType>::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<MaxFloatType>;
|
||||
assert(-exp < 0);
|
||||
assert(-exp >= Limits::min_exponent - 128);
|
||||
static_assert(StackArray::kMaxCapacity >=
|
||||
(Limits::digits + 128 - Limits::min_exponent + 31) / 32,
|
||||
"");
|
||||
StackArray::RunWithCapacity(
|
||||
static_cast<size_t>((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 || after_chunk_index_; }
|
||||
|
||||
// Returns true if the remainder digits are greater than 5000...
|
||||
bool IsGreaterThanHalf() const {
|
||||
return next_digit_ > 5 || (next_digit_ == 5 && after_chunk_index_);
|
||||
}
|
||||
// Returns true if the remainder digits are exactly 5000...
|
||||
bool IsExactlyHalf() const { return next_digit_ == 5 && !after_chunk_index_; }
|
||||
|
||||
struct Digits {
|
||||
char digit_before_nine;
|
||||
size_t 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.
|
||||
char GetOneDigit() {
|
||||
if (!after_chunk_index_)
|
||||
return 0;
|
||||
|
||||
char carry = 0;
|
||||
for (size_t i = after_chunk_index_; i > 0; --i) {
|
||||
carry = MultiplyBy10WithCarry(&data_[i - 1], carry);
|
||||
}
|
||||
// If the lowest chunk is now empty, remove it from view.
|
||||
if (data_[after_chunk_index_ - 1] == 0)
|
||||
--after_chunk_index_;
|
||||
return carry;
|
||||
}
|
||||
|
||||
FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp)
|
||||
: after_chunk_index_(static_cast<size_t>(exp / 32 + 1)), data_(data) {
|
||||
const int offset = exp % 32;
|
||||
// Right shift `v` by `exp` bits.
|
||||
data_[after_chunk_index_ - 1] = 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 (size_t pos = after_chunk_index_ - 1; v; v >>= 32)
|
||||
data_[--pos] = static_cast<uint32_t>(v);
|
||||
|
||||
// Fill next_digit_, as GetDigits expects it to be populated always.
|
||||
next_digit_ = GetOneDigit();
|
||||
}
|
||||
|
||||
char next_digit_;
|
||||
size_t after_chunk_index_;
|
||||
absl::Span<uint32_t> data_;
|
||||
};
|
||||
|
||||
// Count the number of leading zero bits.
|
||||
int LeadingZeros(uint64_t v) { return countl_zero(v); }
|
||||
int LeadingZeros(uint128 v) {
|
||||
auto high = static_cast<uint64_t>(v >> 64);
|
||||
auto low = static_cast<uint64_t>(v);
|
||||
return high != 0 ? countl_zero(high) : 64 + countl_zero(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) {
|
||||
char 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,
|
||||
size_t precision) {
|
||||
char *p = start;
|
||||
v <<= (64 - exp);
|
||||
while (precision > 0) {
|
||||
if (!v) return p;
|
||||
*p++ = MultiplyBy10WithCarry(&v, 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);
|
||||
}
|
||||
|
||||
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,
|
||||
size_t 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) {
|
||||
char carry = MultiplyBy10WithCarry(&low, 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, 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);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
struct FormatState {
|
||||
char sign_char;
|
||||
size_t 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 {
|
||||
size_t left_spaces;
|
||||
size_t zeros;
|
||||
size_t 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};
|
||||
}
|
||||
size_t missing_chars = static_cast<size_t>(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,
|
||||
size_t padding_offset,
|
||||
size_t 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() + 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 =
|
||||
static_cast<size_t>(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,
|
||||
state.precision - static_cast<size_t>(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() ? 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() ? 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
|
||||
size_t 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 =
|
||||
static_cast<int>(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, size_t nibble_index) {
|
||||
constexpr Int mask_low_nibble = Int{0xf};
|
||||
int shift = static_cast<int>(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(size_t nibble_index, Int* n) {
|
||||
constexpr size_t kShift = sizeof(Int) * 8 - 1;
|
||||
constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4;
|
||||
Int before = *n >> kShift;
|
||||
// Here we essentially want to take the number 1 and move it into the
|
||||
// requested 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} << static_cast<int>(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(size_t nibble_index) {
|
||||
constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4;
|
||||
static const Int ones = ~Int{0};
|
||||
++nibble_index;
|
||||
return ones >> static_cast<int>(
|
||||
4 * (std::max(kNumNibbles, nibble_index) - nibble_index));
|
||||
}
|
||||
|
||||
// Return a mask with 1's below the given nibble.
|
||||
template <typename Int>
|
||||
Int MaskUpToNibbleExclusive(size_t nibble_index) {
|
||||
return nibble_index == 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
Int MoveToNibble(uint8_t nibble, size_t nibble_index) {
|
||||
return Int{nibble} << static_cast<int>(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 size_t HexFloatLeadingDigitSizeInBits() {
|
||||
return std::numeric_limits<Float>::digits % 4 > 0
|
||||
? static_cast<size_t>(std::numeric_limits<Float>::digits % 4)
|
||||
: size_t{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,
|
||||
size_t 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;
|
||||
}
|
||||
size_t rounding_nibble_idx = final_nibble_displayed - 1;
|
||||
constexpr size_t 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;
|
||||
size_t 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 size_t kTotalNibbles = sizeof(Int) * 8 / 4;
|
||||
// Index of the last nibble that we could display given precision.
|
||||
size_t final_nibble_displayed =
|
||||
precision_specified
|
||||
? (std::max(kTotalNibbles, state.precision) - 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 size_t kIntBits = sizeof(Int) * 8;
|
||||
static const Int kHighIntBit = Int{1} << (kIntBits - 1);
|
||||
const size_t 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 >> static_cast<int>(kIntBits - kLeadDigitBitsCount));
|
||||
*exp -= (*mantissa != 0) ? static_cast<int>(kLeadDigitBitsCount) : *exp;
|
||||
*mantissa <<= static_cast<int>(kLeadDigitBitsCount);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
|
||||
bool uppercase, const FormatState &state) {
|
||||
// Int properties.
|
||||
constexpr size_t kIntBits = sizeof(Int) * 8;
|
||||
constexpr size_t 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<MaxFloatType>::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 ============
|
||||
size_t digits_emitted = 0;
|
||||
while (mantissa > 0) {
|
||||
*digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)];
|
||||
mantissa <<= 4;
|
||||
++digits_emitted;
|
||||
}
|
||||
size_t trailing_zeros = 0;
|
||||
if (precision_specified) {
|
||||
assert(state.precision >= digits_emitted);
|
||||
trailing_zeros = state.precision - digits_emitted;
|
||||
}
|
||||
auto digits_result = string_view(
|
||||
digits_buffer, static_cast<size_t>(digits_iter - digits_buffer));
|
||||
|
||||
// =============== Exponent ==================
|
||||
constexpr size_t kBufSizeForExpDecRepr =
|
||||
numbers_internal::kFastToBufferSize // required 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 of any padding
|
||||
static_cast<size_t>(trailing_zeros), // remaining mantissa padding
|
||||
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(), static_cast<size_t>(n));
|
||||
break;
|
||||
}
|
||||
space.resize(static_cast<size_t>(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 size_t kMaxFixedPrecision = 39;
|
||||
|
||||
constexpr size_t 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() const {
|
||||
assert(begin < end);
|
||||
return end[-1];
|
||||
}
|
||||
|
||||
char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
|
||||
|
||||
size_t size() const { return static_cast<size_t>(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, static_cast<size_t>(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(static_cast<char>(exp / 100 + '0'));
|
||||
out->push_back(static_cast<char>(exp / 10 % 10 + '0'));
|
||||
out->push_back(static_cast<char>(exp % 10 + '0'));
|
||||
} else {
|
||||
out->push_back(static_cast<char>(exp / 10 + '0'));
|
||||
out->push_back(static_cast<char>(exp % 10 + '0'));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Float, typename Int>
|
||||
constexpr bool CanFitMantissa() {
|
||||
return
|
||||
#if defined(__clang__) && (__clang_major__ < 9) && !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>
|
||||
size_t PrintIntegralDigits(Int digits, Buffer* out) {
|
||||
size_t 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.
|
||||
void RemoveExtraPrecision(size_t extra_digits,
|
||||
bool has_leftover_value,
|
||||
Buffer* out,
|
||||
int* exp_out) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
size_t 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;
|
||||
}
|
||||
size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
|
||||
size_t digits_to_zero_pad = precision;
|
||||
if (mode == FormatStyle::Precision) {
|
||||
*exp_out = static_cast<int>(digits_printed - 1);
|
||||
if (digits_to_zero_pad < digits_printed - 1) {
|
||||
RemoveExtraPrecision(digits_printed - 1 - digits_to_zero_pad, false,
|
||||
out, exp_out);
|
||||
return true;
|
||||
}
|
||||
digits_to_zero_pad -= digits_printed - 1;
|
||||
}
|
||||
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.
|
||||
size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
|
||||
int_mantissa &= mask;
|
||||
|
||||
size_t 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 = static_cast<int>(digits_printed - 1);
|
||||
if (fractional_count < digits_printed - 1) {
|
||||
// If we had enough digits, return right away.
|
||||
// The code below will try to round again otherwise.
|
||||
RemoveExtraPrecision(digits_printed - 1 - fractional_count,
|
||||
int_mantissa != 0, out, exp_out);
|
||||
return true;
|
||||
}
|
||||
fractional_count -= digits_printed - 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto get_next_digit = [&] {
|
||||
int_mantissa *= 10;
|
||||
char digit = static_cast<char>(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');
|
||||
}
|
||||
|
||||
char 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,
|
||||
size_t 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), 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), decomposed.exponent,
|
||||
precision, out, exp);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void WriteBufferToSink(char sign_char, absl::string_view str,
|
||||
const FormatConversionSpecImpl &conv,
|
||||
FormatSinkImpl *sink) {
|
||||
size_t left_spaces = 0, zeros = 0, right_spaces = 0;
|
||||
size_t missing_chars = 0;
|
||||
if (conv.width() >= 0) {
|
||||
const size_t conv_width_size_t = static_cast<size_t>(conv.width());
|
||||
const size_t existing_chars =
|
||||
str.size() + static_cast<size_t>(sign_char != 0);
|
||||
if (conv_width_size_t > existing_chars)
|
||||
missing_chars = conv_width_size_t - existing_chars;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
size_t precision =
|
||||
conv.precision() < 0 ? 6 : static_cast<size_t>(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(precision, size_t{1}) - 1;
|
||||
if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
|
||||
&exp)) {
|
||||
return FallbackToSnprintf(v, conv, sink);
|
||||
}
|
||||
if ((exp < 0 || precision + 1 > static_cast<size_t>(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,
|
||||
static_cast<size_t>(buffer.end - buffer.begin)),
|
||||
conv, sink);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
|
||||
FormatSinkImpl *sink) {
|
||||
if (IsDoubleDouble()) {
|
||||
// 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
|
||||
37
Pods/abseil/absl/strings/internal/str_format/float_conversion.h
generated
Normal file
37
Pods/abseil/absl/strings/internal/str_format/float_conversion.h
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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_
|
||||
72
Pods/abseil/absl/strings/internal/str_format/output.cc
generated
Normal file
72
Pods/abseil/absl/strings/internal/str_format/output.cc
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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
|
||||
97
Pods/abseil/absl/strings/internal/str_format/output.h
generated
Normal file
97
Pods/abseil/absl/strings/internal/str_format/output.h
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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 <ios>
|
||||
#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(), static_cast<std::streamsize>(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_
|
||||
140
Pods/abseil/absl/strings/internal/str_format/parser.cc
generated
Normal file
140
Pods/abseil/absl/strings/internal/str_format/parser.cc
generated
Normal file
@@ -0,0 +1,140 @@
|
||||
// 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 {
|
||||
|
||||
// Define the array for non-constexpr uses.
|
||||
constexpr ConvTag ConvTagHolder::value[256];
|
||||
|
||||
ABSL_ATTRIBUTE_NOINLINE const char* ConsumeUnboundConversionNoInline(
|
||||
const char* p, const char* end, UnboundConversion* conv, int* next_arg) {
|
||||
return ConsumeUnboundConversion(p, end, conv, next_arg);
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
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
|
||||
269
Pods/abseil/absl/strings/internal/str_format/parser.h
generated
Normal file
269
Pods/abseil/absl/strings/internal/str_format/parser.h
generated
Normal file
@@ -0,0 +1,269 @@
|
||||
// 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 <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/strings/internal/str_format/checker.h"
|
||||
#include "absl/strings/internal/str_format/constexpr_parser.h"
|
||||
#include "absl/strings/internal/str_format/extension.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace str_format_internal {
|
||||
|
||||
std::string LengthModToString(LengthMod v);
|
||||
|
||||
const char* ConsumeUnboundConversionNoInline(const char* p, const char* end,
|
||||
UnboundConversion* conv,
|
||||
int* next_arg);
|
||||
|
||||
// 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, '%', static_cast<size_t>(end - p)));
|
||||
if (!percent) {
|
||||
// We found the last substring.
|
||||
return consumer.Append(string_view(p, static_cast<size_t>(end - p)));
|
||||
}
|
||||
// We found a percent, so push the text run then process the percent.
|
||||
if (ABSL_PREDICT_FALSE(!consumer.Append(
|
||||
string_view(p, static_cast<size_t>(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 = ConsumeUnboundConversionNoInline(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,
|
||||
static_cast<size_t>(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, static_cast<size_t>((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_
|
||||
Reference in New Issue
Block a user