summaryrefslogtreecommitdiff
path: root/absl/strings/internal/str_format/arg.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal/str_format/arg.cc')
-rw-r--r--absl/strings/internal/str_format/arg.cc254
1 files changed, 162 insertions, 92 deletions
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 3aa0296b..12842276 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -12,14 +12,13 @@
#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 {
-const char kDigit[2][32] = { "0123456789abcdef", "0123456789ABCDEF" };
-
// Reduce *capacity by s.size(), clipped to a 0 minimum.
void ReducePadding(string_view s, size_t *capacity) {
*capacity = Excess(s.size(), *capacity);
@@ -48,94 +47,144 @@ struct IsSigned<absl::int128> : std::true_type {};
template <>
struct IsSigned<absl::uint128> : std::false_type {};
-class ConvertedIntInfo {
+// 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>
- ConvertedIntInfo(T v, ConversionChar conv) {
- using Unsigned = typename MakeUnsigned<T>::type;
- auto u = static_cast<Unsigned>(v);
- if (IsNeg(v)) {
- is_neg_ = true;
- u = Unsigned{} - u;
- } else {
- is_neg_ = false;
+ 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;
}
- UnsignedToStringRight(u, conv);
+ PrintAsDec(u, add_neg);
}
- string_view digits() const {
- return {end() - size_, static_cast<size_t>(size_)};
+ 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;
}
- bool is_neg() const { return is_neg_; }
- private:
- template <typename T, bool IsSigned>
- struct IsNegImpl {
- static bool Eval(T v) { return v < 0; }
- };
+ // Print the unsigned integer as hex using lowercase.
+ // Supports unsigned integral types and uint128.
template <typename T>
- struct IsNegImpl<T, false> {
- static bool Eval(T) {
- return false;
+ 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>
- bool IsNeg(T v) {
- return IsNegImpl<T, IsSigned<T>::value>::Eval(v);
+ 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;
}
- template <typename T>
- void UnsignedToStringRight(T u, ConversionChar conv) {
- char *p = end();
- switch (FormatConversionCharRadix(conv)) {
- default:
- case 10:
- for (; u; u /= 10)
- *--p = static_cast<char>('0' + static_cast<size_t>(u % 10));
- break;
- case 8:
- for (; u; u /= 8)
- *--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
- break;
- case 16: {
- const char *digits = kDigit[FormatConversionCharIsUpper(conv) ? 1 : 0];
- for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
- break;
- }
- }
- size_ = static_cast<int>(end() - 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};
}
- const char *end() const { return storage_ + sizeof(storage_); }
- char *end() { return storage_ + sizeof(storage_); }
+ bool is_negative() const { return start_[0] == '-'; }
- bool is_neg_;
- int size_;
- // Max size: 128 bit value as octal -> 43 digits
- char storage_[128 / 3 + 1];
+ 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 ConvertedIntInfo &info,
+string_view BaseIndicator(const IntDigits &as_digits,
const ConversionSpec conv) {
- bool alt = conv.has_alt_flag();
- int radix = FormatConversionCharRadix(conv.conversion_char());
- if (conv.conversion_char() == ConversionChar::p)
- alt = true; // always show 0x for %p.
+ // always show 0x for %p.
+ bool alt = conv.has_alt_flag() || conv.conversion_char() == ConversionChar::p;
+ bool hex = (conv.conversion_char() == FormatConversionChar::x ||
+ conv.conversion_char() == FormatConversionChar::X ||
+ conv.conversion_char() == FormatConversionChar::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 && radix == 16 && !info.digits().empty()) {
- if (FormatConversionCharIsUpper(conv.conversion_char())) return "0X";
- return "0x";
+ if (alt && hex && !as_digits.without_neg_or_zero().empty()) {
+ return conv.conversion_char() == FormatConversionChar::X ? "0X" : "0x";
}
return {};
}
string_view SignColumn(bool neg, const ConversionSpec conv) {
- if (FormatConversionCharIsSigned(conv.conversion_char())) {
+ if (conv.conversion_char() == FormatConversionChar::d ||
+ conv.conversion_char() == FormatConversionChar::i) {
if (neg) return "-";
if (conv.has_show_pos_flag()) return "+";
if (conv.has_sign_col_flag()) return " ";
@@ -154,20 +203,20 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
return true;
}
-bool ConvertIntImplInner(const ConvertedIntInfo &info,
- const ConversionSpec conv, FormatSinkImpl *sink) {
+bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
+ const ConversionSpec 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 = info.digits();
+ string_view formatted = as_digits.without_neg_or_zero();
ReducePadding(formatted, &fill);
- string_view sign = SignColumn(info.is_neg(), conv);
+ string_view sign = SignColumn(as_digits.is_negative(), conv);
ReducePadding(sign, &fill);
- string_view base_indicator = BaseIndicator(info, conv);
+ string_view base_indicator = BaseIndicator(as_digits, conv);
ReducePadding(base_indicator, &fill);
int precision = conv.precision();
@@ -209,34 +258,53 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
}
template <typename T>
-bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- ConvertedIntInfo info(v, conv.conversion_char());
- if (conv.is_basic() && (conv.conversion_char() != ConversionChar::p)) {
- if (info.is_neg()) sink->Append(1, '-');
- if (info.digits().empty()) {
- sink->Append(1, '0');
- } else {
- sink->Append(info.digits());
- }
- return true;
- }
- return ConvertIntImplInner(info, conv, sink);
-}
-
-template <typename T>
bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- if (FormatConversionCharIsFloat(conv.conversion_char())) {
- return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
+ using U = typename MakeUnsigned<T>::type;
+ IntDigits as_digits;
+
+ switch (conv.conversion_char()) {
+ case FormatConversionChar::c:
+ return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
+
+ case FormatConversionChar::o:
+ as_digits.PrintAsOct(static_cast<U>(v));
+ break;
+
+ case FormatConversionChar::x:
+ as_digits.PrintAsHexLower(static_cast<U>(v));
+ break;
+ case FormatConversionChar::X:
+ as_digits.PrintAsHexUpper(static_cast<U>(v));
+ break;
+
+ case FormatConversionChar::u:
+ as_digits.PrintAsDec(static_cast<U>(v));
+ break;
+
+ case FormatConversionChar::d:
+ case FormatConversionChar::i:
+ as_digits.PrintAsDec(v);
+ break;
+
+ case FormatConversionChar::a:
+ case FormatConversionChar::e:
+ case FormatConversionChar::f:
+ case FormatConversionChar::g:
+ case FormatConversionChar::A:
+ case FormatConversionChar::E:
+ case FormatConversionChar::F:
+ case FormatConversionChar::G:
+ return ConvertFloatImpl(static_cast<double>(v), conv, sink);
+
+ default:
+ return false;
}
- if (conv.conversion_char() == ConversionChar::c)
- return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
- if (!FormatConversionCharIsIntegral(conv.conversion_char())) return false;
- if (!FormatConversionCharIsSigned(conv.conversion_char()) &&
- IsSigned<T>::value) {
- using U = typename MakeUnsigned<T>::type;
- return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
+
+ if (conv.is_basic()) {
+ sink->Append(as_digits.with_neg_and_zero());
+ return true;
}
- return ConvertIntImplInner(v, conv, sink);
+ return ConvertIntImplInnerSlow(as_digits, conv, sink);
}
template <typename T>
@@ -296,7 +364,9 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
sink->Append("(nil)");
return {true};
}
- return {ConvertIntImplInner(v.value, conv, sink)};
+ IntDigits as_digits;
+ as_digits.PrintAsHexLower(v.value);
+ return {ConvertIntImplInnerSlow(as_digits, conv, sink)};
}
// ==================== Floats ====================