Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
16 KiB
474 lines
16 KiB
// |
|
// POSIX spec: |
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html |
|
// |
|
#include "absl/strings/internal/str_format/arg.h" |
|
|
|
#include <cassert> |
|
#include <cerrno> |
|
#include <cstdlib> |
|
#include <string> |
|
#include <type_traits> |
|
|
|
#include "absl/base/port.h" |
|
#include "absl/strings/internal/str_format/float_conversion.h" |
|
#include "absl/strings/numbers.h" |
|
|
|
namespace absl { |
|
ABSL_NAMESPACE_BEGIN |
|
namespace str_format_internal { |
|
namespace { |
|
|
|
// Reduce *capacity by s.size(), clipped to a 0 minimum. |
|
void ReducePadding(string_view s, size_t *capacity) { |
|
*capacity = Excess(s.size(), *capacity); |
|
} |
|
|
|
// Reduce *capacity by n, clipped to a 0 minimum. |
|
void ReducePadding(size_t n, size_t *capacity) { |
|
*capacity = Excess(n, *capacity); |
|
} |
|
|
|
template <typename T> |
|
struct MakeUnsigned : std::make_unsigned<T> {}; |
|
template <> |
|
struct MakeUnsigned<absl::int128> { |
|
using type = absl::uint128; |
|
}; |
|
template <> |
|
struct MakeUnsigned<absl::uint128> { |
|
using type = absl::uint128; |
|
}; |
|
|
|
template <typename T> |
|
struct IsSigned : std::is_signed<T> {}; |
|
template <> |
|
struct IsSigned<absl::int128> : std::true_type {}; |
|
template <> |
|
struct IsSigned<absl::uint128> : std::false_type {}; |
|
|
|
// Integral digit printer. |
|
// Call one of the PrintAs* routines after construction once. |
|
// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. |
|
class IntDigits { |
|
public: |
|
// Print the unsigned integer as octal. |
|
// Supports unsigned integral types and uint128. |
|
template <typename T> |
|
void PrintAsOct(T v) { |
|
static_assert(!IsSigned<T>::value, ""); |
|
char *p = storage_ + sizeof(storage_); |
|
do { |
|
*--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); |
|
v >>= 3; |
|
} while (v); |
|
start_ = p; |
|
size_ = storage_ + sizeof(storage_) - p; |
|
} |
|
|
|
// Print the signed or unsigned integer as decimal. |
|
// Supports all integral types. |
|
template <typename T> |
|
void PrintAsDec(T v) { |
|
static_assert(std::is_integral<T>::value, ""); |
|
start_ = storage_; |
|
size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; |
|
} |
|
|
|
void PrintAsDec(int128 v) { |
|
auto u = static_cast<uint128>(v); |
|
bool add_neg = false; |
|
if (v < 0) { |
|
add_neg = true; |
|
u = uint128{} - u; |
|
} |
|
PrintAsDec(u, add_neg); |
|
} |
|
|
|
void PrintAsDec(uint128 v, bool add_neg = false) { |
|
// This function can be sped up if needed. We can call FastIntToBuffer |
|
// twice, or fix FastIntToBuffer to support uint128. |
|
char *p = storage_ + sizeof(storage_); |
|
do { |
|
p -= 2; |
|
numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p); |
|
v /= 100; |
|
} while (v); |
|
if (p[0] == '0') { |
|
// We printed one too many hexits. |
|
++p; |
|
} |
|
if (add_neg) { |
|
*--p = '-'; |
|
} |
|
size_ = storage_ + sizeof(storage_) - p; |
|
start_ = p; |
|
} |
|
|
|
// Print the unsigned integer as hex using lowercase. |
|
// Supports unsigned integral types and uint128. |
|
template <typename T> |
|
void PrintAsHexLower(T v) { |
|
static_assert(!IsSigned<T>::value, ""); |
|
char *p = storage_ + sizeof(storage_); |
|
|
|
do { |
|
p -= 2; |
|
constexpr const char* table = numbers_internal::kHexTable; |
|
std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); |
|
if (sizeof(T) == 1) break; |
|
v >>= 8; |
|
} while (v); |
|
if (p[0] == '0') { |
|
// We printed one too many digits. |
|
++p; |
|
} |
|
start_ = p; |
|
size_ = storage_ + sizeof(storage_) - p; |
|
} |
|
|
|
// Print the unsigned integer as hex using uppercase. |
|
// Supports unsigned integral types and uint128. |
|
template <typename T> |
|
void PrintAsHexUpper(T v) { |
|
static_assert(!IsSigned<T>::value, ""); |
|
char *p = storage_ + sizeof(storage_); |
|
|
|
// kHexTable is only lowercase, so do it manually for uppercase. |
|
do { |
|
*--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; |
|
v >>= 4; |
|
} while (v); |
|
start_ = p; |
|
size_ = storage_ + sizeof(storage_) - p; |
|
} |
|
|
|
// The printed value including the '-' sign if available. |
|
// For inputs of value `0`, this will return "0" |
|
string_view with_neg_and_zero() const { return {start_, size_}; } |
|
|
|
// The printed value not including the '-' sign. |
|
// For inputs of value `0`, this will return "". |
|
string_view without_neg_or_zero() const { |
|
static_assert('-' < '0', "The check below verifies both."); |
|
size_t advance = start_[0] <= '0' ? 1 : 0; |
|
return {start_ + advance, size_ - advance}; |
|
} |
|
|
|
bool is_negative() const { return start_[0] == '-'; } |
|
|
|
private: |
|
const char *start_; |
|
size_t size_; |
|
// Max size: 128 bit value as octal -> 43 digits, plus sign char |
|
char storage_[128 / 3 + 1 + 1]; |
|
}; |
|
|
|
// Note: 'o' conversions do not have a base indicator, it's just that |
|
// the '#' flag is specified to modify the precision for 'o' conversions. |
|
string_view BaseIndicator(const IntDigits &as_digits, |
|
const FormatConversionSpecImpl conv) { |
|
// always show 0x for %p. |
|
bool alt = conv.has_alt_flag() || |
|
conv.conversion_char() == FormatConversionCharInternal::p; |
|
bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || |
|
conv.conversion_char() == FormatConversionCharInternal::X || |
|
conv.conversion_char() == FormatConversionCharInternal::p); |
|
// From the POSIX description of '#' flag: |
|
// "For x or X conversion specifiers, a non-zero result shall have |
|
// 0x (or 0X) prefixed to it." |
|
if (alt && hex && !as_digits.without_neg_or_zero().empty()) { |
|
return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" |
|
: "0x"; |
|
} |
|
return {}; |
|
} |
|
|
|
string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { |
|
if (conv.conversion_char() == FormatConversionCharInternal::d || |
|
conv.conversion_char() == FormatConversionCharInternal::i) { |
|
if (neg) return "-"; |
|
if (conv.has_show_pos_flag()) return "+"; |
|
if (conv.has_sign_col_flag()) return " "; |
|
} |
|
return {}; |
|
} |
|
|
|
bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
size_t fill = 0; |
|
if (conv.width() >= 0) fill = conv.width(); |
|
ReducePadding(1, &fill); |
|
if (!conv.has_left_flag()) sink->Append(fill, ' '); |
|
sink->Append(1, v); |
|
if (conv.has_left_flag()) sink->Append(fill, ' '); |
|
return true; |
|
} |
|
|
|
bool ConvertIntImplInnerSlow(const IntDigits &as_digits, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
// Print as a sequence of Substrings: |
|
// [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] |
|
size_t fill = 0; |
|
if (conv.width() >= 0) fill = conv.width(); |
|
|
|
string_view formatted = as_digits.without_neg_or_zero(); |
|
ReducePadding(formatted, &fill); |
|
|
|
string_view sign = SignColumn(as_digits.is_negative(), conv); |
|
ReducePadding(sign, &fill); |
|
|
|
string_view base_indicator = BaseIndicator(as_digits, conv); |
|
ReducePadding(base_indicator, &fill); |
|
|
|
int precision = conv.precision(); |
|
bool precision_specified = precision >= 0; |
|
if (!precision_specified) |
|
precision = 1; |
|
|
|
if (conv.has_alt_flag() && |
|
conv.conversion_char() == FormatConversionCharInternal::o) { |
|
// From POSIX description of the '#' (alt) flag: |
|
// "For o conversion, it increases the precision (if necessary) to |
|
// force the first digit of the result to be zero." |
|
if (formatted.empty() || *formatted.begin() != '0') { |
|
int needed = static_cast<int>(formatted.size()) + 1; |
|
precision = std::max(precision, needed); |
|
} |
|
} |
|
|
|
size_t num_zeroes = Excess(formatted.size(), precision); |
|
ReducePadding(num_zeroes, &fill); |
|
|
|
size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; |
|
size_t num_right_spaces = conv.has_left_flag() ? fill : 0; |
|
|
|
// From POSIX description of the '0' (zero) flag: |
|
// "For d, i, o, u, x, and X conversion specifiers, if a precision |
|
// is specified, the '0' flag is ignored." |
|
if (!precision_specified && conv.has_zero_flag()) { |
|
num_zeroes += num_left_spaces; |
|
num_left_spaces = 0; |
|
} |
|
|
|
sink->Append(num_left_spaces, ' '); |
|
sink->Append(sign); |
|
sink->Append(base_indicator); |
|
sink->Append(num_zeroes, '0'); |
|
sink->Append(formatted); |
|
sink->Append(num_right_spaces, ' '); |
|
return true; |
|
} |
|
|
|
template <typename T> |
|
bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
using U = typename MakeUnsigned<T>::type; |
|
IntDigits as_digits; |
|
|
|
// This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes |
|
// it to complain about a switch/case type mismatch, even though both are |
|
// FormatConverionChar. Likely this is because at this point |
|
// FormatConversionChar is declared, but not defined. |
|
switch (static_cast<uint8_t>(conv.conversion_char())) { |
|
case static_cast<uint8_t>(FormatConversionCharInternal::c): |
|
return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); |
|
|
|
case static_cast<uint8_t>(FormatConversionCharInternal::o): |
|
as_digits.PrintAsOct(static_cast<U>(v)); |
|
break; |
|
|
|
case static_cast<uint8_t>(FormatConversionCharInternal::x): |
|
as_digits.PrintAsHexLower(static_cast<U>(v)); |
|
break; |
|
case static_cast<uint8_t>(FormatConversionCharInternal::X): |
|
as_digits.PrintAsHexUpper(static_cast<U>(v)); |
|
break; |
|
|
|
case static_cast<uint8_t>(FormatConversionCharInternal::u): |
|
as_digits.PrintAsDec(static_cast<U>(v)); |
|
break; |
|
|
|
case static_cast<uint8_t>(FormatConversionCharInternal::d): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::i): |
|
as_digits.PrintAsDec(v); |
|
break; |
|
|
|
case static_cast<uint8_t>(FormatConversionCharInternal::a): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::e): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::f): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::g): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::A): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::E): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::F): |
|
case static_cast<uint8_t>(FormatConversionCharInternal::G): |
|
return ConvertFloatImpl(static_cast<double>(v), conv, sink); |
|
|
|
default: |
|
ABSL_INTERNAL_ASSUME(false); |
|
} |
|
|
|
if (conv.is_basic()) { |
|
sink->Append(as_digits.with_neg_and_zero()); |
|
return true; |
|
} |
|
return ConvertIntImplInnerSlow(as_digits, conv, sink); |
|
} |
|
|
|
template <typename T> |
|
bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return FormatConversionCharIsFloat(conv.conversion_char()) && |
|
ConvertFloatImpl(v, conv, sink); |
|
} |
|
|
|
inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
if (conv.is_basic()) { |
|
sink->Append(v); |
|
return true; |
|
} |
|
return sink->PutPaddedString(v, conv.width(), conv.precision(), |
|
conv.has_left_flag()); |
|
} |
|
|
|
} // namespace |
|
|
|
// ==================== Strings ==================== |
|
StringConvertResult FormatConvertImpl(const std::string &v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertStringArg(v, conv, sink)}; |
|
} |
|
|
|
StringConvertResult FormatConvertImpl(string_view v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertStringArg(v, conv, sink)}; |
|
} |
|
|
|
ArgConvertResult<FormatConversionCharSetUnion( |
|
FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> |
|
FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
if (conv.conversion_char() == FormatConversionCharInternal::p) |
|
return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; |
|
size_t len; |
|
if (v == nullptr) { |
|
len = 0; |
|
} else if (conv.precision() < 0) { |
|
len = std::strlen(v); |
|
} else { |
|
// If precision is set, we look for the NUL-terminator on the valid range. |
|
len = std::find(v, v + conv.precision(), '\0') - v; |
|
} |
|
return {ConvertStringArg(string_view(v, len), conv, sink)}; |
|
} |
|
|
|
// ==================== Raw pointers ==================== |
|
ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( |
|
VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { |
|
if (!v.value) { |
|
sink->Append("(nil)"); |
|
return {true}; |
|
} |
|
IntDigits as_digits; |
|
as_digits.PrintAsHexLower(v.value); |
|
return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; |
|
} |
|
|
|
// ==================== Floats ==================== |
|
FloatingConvertResult FormatConvertImpl(float v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertFloatArg(v, conv, sink)}; |
|
} |
|
FloatingConvertResult FormatConvertImpl(double v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertFloatArg(v, conv, sink)}; |
|
} |
|
FloatingConvertResult FormatConvertImpl(long double v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertFloatArg(v, conv, sink)}; |
|
} |
|
|
|
// ==================== Chars ==================== |
|
IntegralConvertResult FormatConvertImpl(char v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(signed char v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(unsigned char v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
|
|
// ==================== Ints ==================== |
|
IntegralConvertResult FormatConvertImpl(short v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(int v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(unsigned v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(long v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(long long v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(absl::int128 v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
IntegralConvertResult FormatConvertImpl(absl::uint128 v, |
|
const FormatConversionSpecImpl conv, |
|
FormatSinkImpl *sink) { |
|
return {ConvertIntArg(v, conv, sink)}; |
|
} |
|
|
|
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); |
|
|
|
|
|
|
|
} // namespace str_format_internal |
|
|
|
ABSL_NAMESPACE_END |
|
} // namespace absl
|
|
|