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.cc379
1 files changed, 379 insertions, 0 deletions
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
new file mode 100644
index 00000000..e5e1eee5
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.cc
@@ -0,0 +1,379 @@
+//
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+
+} // inline namespace lts_2018_12_18
+} // namespace absl