| // |
| // 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" |
| |
| namespace absl { |
| 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 <typename T> |
| struct MakeUnsigned : std::make_unsigned<T> {}; |
| template <> |
| struct MakeUnsigned<absl::uint128> { |
| using type = absl::uint128; |
| }; |
| |
| template <typename T> |
| struct IsSigned : std::is_signed<T> {}; |
| template <> |
| struct IsSigned<absl::uint128> : std::false_type {}; |
| |
| class ConvertedIntInfo { |
| public: |
| 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; |
| } |
| UnsignedToStringRight(u, conv); |
| } |
| |
| string_view digits() const { |
| return {end() - size_, static_cast<size_t>(size_)}; |
| } |
| bool is_neg() const { return is_neg_; } |
| |
| private: |
| template <typename T, bool IsSigned> |
| struct IsNegImpl { |
| static bool Eval(T v) { return v < 0; } |
| }; |
| template <typename T> |
| struct IsNegImpl<T, false> { |
| static bool Eval(T) { |
| return false; |
| } |
| }; |
| |
| template <typename T> |
| bool IsNeg(T v) { |
| return IsNegImpl<T, IsSigned<T>::value>::Eval(v); |
| } |
| |
| template <typename T> |
| void UnsignedToStringRight(T u, ConversionChar conv) { |
| char *p = end(); |
| switch (conv.radix()) { |
| 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[conv.upper() ? 1 : 0]; |
| for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)]; |
| break; |
| } |
| } |
| size_ = static_cast<int>(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<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.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 <typename T> |
| 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 <typename T> |
| bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { |
| if (conv.conv().is_float()) { |
| return FormatConvertImpl(static_cast<double>(v), conv, sink).value; |
| } |
| if (conv.conv().id() == ConversionChar::c) |
| return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); |
| if (!conv.conv().is_integral()) |
| return false; |
| if (!conv.conv().is_signed() && IsSigned<T>::value) { |
| using U = typename MakeUnsigned<T>::type; |
| return FormatConvertImpl(static_cast<U>(v), conv, sink).value; |
| } |
| return ConvertIntImplInner(v, conv, sink); |
| } |
| |
| template <typename T> |
| 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<Conv::s> FormatConvertImpl(const std::string &v, |
| const ConversionSpec conv, |
| FormatSinkImpl *sink) { |
| return {ConvertStringArg(v, conv, sink)}; |
| } |
| |
| ConvertResult<Conv::s> FormatConvertImpl(string_view v, |
| const ConversionSpec conv, |
| FormatSinkImpl *sink) { |
| return {ConvertStringArg(v, conv, sink)}; |
| } |
| |
| ConvertResult<Conv::s | Conv::p> 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<Conv::p> 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::uint128 v, |
| const ConversionSpec conv, |
| FormatSinkImpl *sink) { |
| return {ConvertIntArg(v, conv, sink)}; |
| } |
| |
| ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); |
| |
| |
| } // namespace str_format_internal |
| |
| } // namespace absl |