d85783fd0b
-- f34cd235a12ad0ee1fea3a1ee5a427272dc2b285 by Abseil Team <absl-team@google.com>: Migrates uses of deprecated map types to recommended types. PiperOrigin-RevId: 309945156 -- e3410a47ad32c0775b6911610bc47b22938decad by Matthew Brown <matthewbr@google.com>: Internal Change PiperOrigin-RevId: 309856021 -- a58cfa25e0bb59e7fa9647ac1aae65eaccff0086 by Greg Falcon <gfalcon@google.com>: Internal change. PiperOrigin-RevId: 309804612 -- cdc5ec310035fbe25f496bda283fe655d94d7769 by Mark Barolak <mbar@google.com>: Standardize the header comments for friend functions in cord.h PiperOrigin-RevId: 309779073 -- fe61602701be795e54477b0fdbf5ffc1df12a6b7 by Samuel Benzaquen <sbenza@google.com>: Implement %f natively for any input. It evaluates the input at runtime and allocates stack space accordingly. This removes a potential fallback into snprintf, improves performance, and removes all memory allocations in this formatting path. PiperOrigin-RevId: 309752501 -- 79e2a24f3f959e8b06ddf1d440bbabbd5f89b5b7 by Greg Falcon <gfalcon@google.com>: Add a Cord::swap() method. Many other Abseil types already provide this, but it was missing here. We already provided a two-argument free function form of `swap()`, but that API is better suited for generic code. The swap member function is a better API when the types are known. PiperOrigin-RevId: 309751740 -- 85cdf60024f153fb4fcb7fe68ed2b14b9faf119d by Derek Mauro <dmauro@google.com>: Cleanup uses of "linker initialized" SpinLocks PiperOrigin-RevId: 309581867 -- 9e5443bfcec4b94056b13c75326576e987ab88fb by Matt Kulukundis <kfm@google.com>: Clarify intended mixing properties of `absl::Hash` PiperOrigin-RevId: 309520174 -- a0630f0827b67f217aaeae68a448fe4c1101e17d by Greg Falcon <gfalcon@google.com>: Comment out a test in Emscripten to sidestep `long double` issues. PiperOrigin-RevId: 309482953 GitOrigin-RevId: f34cd235a12ad0ee1fea3a1ee5a427272dc2b285 Change-Id: Icce0c9d547117374d596b9d684e4054ddd118669
472 lines
16 KiB
C++
472 lines
16 KiB
C++
//
|
|
// 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;
|
|
|
|
switch (conv.conversion_char()) {
|
|
case FormatConversionCharInternal::c:
|
|
return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
|
|
|
|
case FormatConversionCharInternal::o:
|
|
as_digits.PrintAsOct(static_cast<U>(v));
|
|
break;
|
|
|
|
case FormatConversionCharInternal::x:
|
|
as_digits.PrintAsHexLower(static_cast<U>(v));
|
|
break;
|
|
case FormatConversionCharInternal::X:
|
|
as_digits.PrintAsHexUpper(static_cast<U>(v));
|
|
break;
|
|
|
|
case FormatConversionCharInternal::u:
|
|
as_digits.PrintAsDec(static_cast<U>(v));
|
|
break;
|
|
|
|
case FormatConversionCharInternal::d:
|
|
case FormatConversionCharInternal::i:
|
|
as_digits.PrintAsDec(v);
|
|
break;
|
|
|
|
case FormatConversionCharInternal::a:
|
|
case FormatConversionCharInternal::e:
|
|
case FormatConversionCharInternal::f:
|
|
case FormatConversionCharInternal::g:
|
|
case FormatConversionCharInternal::A:
|
|
case FormatConversionCharInternal::E:
|
|
case FormatConversionCharInternal::F:
|
|
case FormatConversionCharInternal::G:
|
|
return ConvertFloatImpl(static_cast<double>(v), conv, sink);
|
|
|
|
default:
|
|
return 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.conversion_char() != FormatConversionCharInternal::s) return false;
|
|
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 (conv.conversion_char() != FormatConversionCharInternal::p) return {false};
|
|
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
|