// // POSIX spec: // http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html // #include "absl/strings/internal/str_format/arg.h" #include #include #include #include #include #include "absl/base/port.h" #include "absl/strings/internal/str_format/float_conversion.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); } // Reduce *capacity by n, clipped to a 0 minimum. void ReducePadding(size_t n, size_t *capacity) { *capacity = Excess(n, *capacity); } template struct MakeUnsigned : std::make_unsigned {}; template <> struct MakeUnsigned { using type = absl::uint128; }; template <> struct MakeUnsigned { using type = absl::uint128; }; template struct IsSigned : std::is_signed {}; template <> struct IsSigned : std::true_type {}; template <> struct IsSigned : std::false_type {}; class ConvertedIntInfo { public: template ConvertedIntInfo(T v, ConversionChar conv) { using Unsigned = typename MakeUnsigned::type; auto u = static_cast(v); if (IsNeg(v)) { is_neg_ = true; u = Unsigned{} - u; } else { is_neg_ = false; } UnsignedToStringRight(u, conv); } string_view digits() const { return {end() - size_, static_cast(size_)}; } bool is_neg() const { return is_neg_; } private: template struct IsNegImpl { static bool Eval(T v) { return v < 0; } }; template struct IsNegImpl { static bool Eval(T) { return false; } }; template bool IsNeg(T v) { return IsNegImpl::value>::Eval(v); } template void UnsignedToStringRight(T u, ConversionChar conv) { char *p = end(); switch (conv.radix()) { default: case 10: for (; u; u /= 10) *--p = static_cast('0' + static_cast(u % 10)); break; case 8: for (; u; u /= 8) *--p = static_cast('0' + static_cast(u % 8)); break; case 16: { const char *digits = kDigit[conv.upper() ? 1 : 0]; for (; u; u /= 16) *--p = digits[static_cast(u % 16)]; break; } } size_ = static_cast(end() - p); } const char *end() const { return storage_ + sizeof(storage_); } char *end() { return storage_ + sizeof(storage_); } bool is_neg_; int size_; // Max size: 128 bit value as octal -> 43 digits char storage_[128 / 3 + 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, const ConversionSpec conv) { bool alt = conv.flags().alt; int radix = conv.conv().radix(); if (conv.conv().id() == ConversionChar::p) alt = true; // always show 0x for %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 (conv.conv().upper()) return "0X"; return "0x"; } return {}; } string_view SignColumn(bool neg, const ConversionSpec conv) { if (conv.conv().is_signed()) { if (neg) return "-"; if (conv.flags().show_pos) return "+"; if (conv.flags().sign_col) return " "; } return {}; } bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, FormatSinkImpl *sink) { size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); ReducePadding(1, &fill); if (!conv.flags().left) sink->Append(fill, ' '); sink->Append(1, v); if (conv.flags().left) sink->Append(fill, ' '); return true; } bool ConvertIntImplInner(const ConvertedIntInfo &info, 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(); ReducePadding(formatted, &fill); string_view sign = SignColumn(info.is_neg(), conv); ReducePadding(sign, &fill); string_view base_indicator = BaseIndicator(info, conv); ReducePadding(base_indicator, &fill); int precision = conv.precision(); bool precision_specified = precision >= 0; if (!precision_specified) precision = 1; if (conv.flags().alt && conv.conv().id() == ConversionChar::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(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.flags().left ? fill : 0; size_t num_right_spaces = conv.flags().left ? 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.flags().zero) { 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 bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { ConvertedIntInfo info(v, conv.conv()); if (conv.flags().basic && conv.conv().id() != 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 bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { if (conv.conv().is_float()) { return FormatConvertImpl(static_cast(v), conv, sink).value; } if (conv.conv().id() == ConversionChar::c) return ConvertCharImpl(static_cast(v), conv, sink); if (!conv.conv().is_integral()) return false; if (!conv.conv().is_signed() && IsSigned::value) { using U = typename MakeUnsigned::type; return FormatConvertImpl(static_cast(v), conv, sink).value; } return ConvertIntImplInner(v, conv, sink); } template bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink); } inline bool ConvertStringArg(string_view v, const ConversionSpec conv, FormatSinkImpl *sink) { if (conv.conv().id() != ConversionChar::s) return false; if (conv.flags().basic) { sink->Append(v); return true; } return sink->PutPaddedString(v, conv.width(), conv.precision(), conv.flags().left); } } // namespace // ==================== Strings ==================== ConvertResult FormatConvertImpl(const std::string &v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } ConvertResult FormatConvertImpl(string_view v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } ConvertResult FormatConvertImpl(const char *v, const ConversionSpec conv, FormatSinkImpl *sink) { if (conv.conv().id() == ConversionChar::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 null terminator on the valid range. len = std::find(v, v + conv.precision(), '\0') - v; } return {ConvertStringArg(string_view(v, len), conv, sink)}; } // ==================== Raw pointers ==================== ConvertResult FormatConvertImpl(VoidPtr v, const ConversionSpec conv, FormatSinkImpl *sink) { if (conv.conv().id() != ConversionChar::p) return {false}; if (!v.value) { sink->Append("(nil)"); return {true}; } return {ConvertIntImplInner(v.value, conv, sink)}; } // ==================== Floats ==================== FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } FloatingConvertResult FormatConvertImpl(long double v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } // ==================== Chars ==================== IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(signed char v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned char v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } // ==================== Ints ==================== IntegralConvertResult FormatConvertImpl(short v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long long v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::int128 v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::uint128 v, const ConversionSpec conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); } // namespace str_format_internal ABSL_NAMESPACE_END } // namespace absl