| 1 | // |
| 2 | // POSIX spec: |
| 3 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html |
| 4 | // |
| 5 | #include "absl/strings/internal/str_format/arg.h" |
| 6 | |
| 7 | #include <cassert> |
| 8 | #include <cerrno> |
| 9 | #include <cstdlib> |
| 10 | #include <string> |
| 11 | #include <type_traits> |
| 12 | |
| 13 | #include "absl/base/port.h" |
| 14 | #include "absl/strings/internal/str_format/float_conversion.h" |
| 15 | |
| 16 | namespace absl { |
| 17 | namespace str_format_internal { |
| 18 | namespace { |
| 19 | |
| 20 | const char kDigit[2][32] = { "0123456789abcdef" , "0123456789ABCDEF" }; |
| 21 | |
| 22 | // Reduce *capacity by s.size(), clipped to a 0 minimum. |
| 23 | void ReducePadding(string_view s, size_t *capacity) { |
| 24 | *capacity = Excess(s.size(), *capacity); |
| 25 | } |
| 26 | |
| 27 | // Reduce *capacity by n, clipped to a 0 minimum. |
| 28 | void ReducePadding(size_t n, size_t *capacity) { |
| 29 | *capacity = Excess(n, *capacity); |
| 30 | } |
| 31 | |
| 32 | template <typename T> |
| 33 | struct MakeUnsigned : std::make_unsigned<T> {}; |
| 34 | template <> |
| 35 | struct MakeUnsigned<absl::uint128> { |
| 36 | using type = absl::uint128; |
| 37 | }; |
| 38 | |
| 39 | template <typename T> |
| 40 | struct IsSigned : std::is_signed<T> {}; |
| 41 | template <> |
| 42 | struct IsSigned<absl::uint128> : std::false_type {}; |
| 43 | |
| 44 | class ConvertedIntInfo { |
| 45 | public: |
| 46 | template <typename T> |
| 47 | ConvertedIntInfo(T v, ConversionChar conv) { |
| 48 | using Unsigned = typename MakeUnsigned<T>::type; |
| 49 | auto u = static_cast<Unsigned>(v); |
| 50 | if (IsNeg(v)) { |
| 51 | is_neg_ = true; |
| 52 | u = Unsigned{} - u; |
| 53 | } else { |
| 54 | is_neg_ = false; |
| 55 | } |
| 56 | UnsignedToStringRight(u, conv); |
| 57 | } |
| 58 | |
| 59 | string_view digits() const { |
| 60 | return {end() - size_, static_cast<size_t>(size_)}; |
| 61 | } |
| 62 | bool is_neg() const { return is_neg_; } |
| 63 | |
| 64 | private: |
| 65 | template <typename T, bool IsSigned> |
| 66 | struct IsNegImpl { |
| 67 | static bool Eval(T v) { return v < 0; } |
| 68 | }; |
| 69 | template <typename T> |
| 70 | struct IsNegImpl<T, false> { |
| 71 | static bool Eval(T) { |
| 72 | return false; |
| 73 | } |
| 74 | }; |
| 75 | |
| 76 | template <typename T> |
| 77 | bool IsNeg(T v) { |
| 78 | return IsNegImpl<T, IsSigned<T>::value>::Eval(v); |
| 79 | } |
| 80 | |
| 81 | template <typename T> |
| 82 | void UnsignedToStringRight(T u, ConversionChar conv) { |
| 83 | char *p = end(); |
| 84 | switch (conv.radix()) { |
| 85 | default: |
| 86 | case 10: |
| 87 | for (; u; u /= 10) |
| 88 | *--p = static_cast<char>('0' + static_cast<size_t>(u % 10)); |
| 89 | break; |
| 90 | case 8: |
| 91 | for (; u; u /= 8) |
| 92 | *--p = static_cast<char>('0' + static_cast<size_t>(u % 8)); |
| 93 | break; |
| 94 | case 16: { |
| 95 | const char *digits = kDigit[conv.upper() ? 1 : 0]; |
| 96 | for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)]; |
| 97 | break; |
| 98 | } |
| 99 | } |
| 100 | size_ = static_cast<int>(end() - p); |
| 101 | } |
| 102 | |
| 103 | const char *end() const { return storage_ + sizeof(storage_); } |
| 104 | char *end() { return storage_ + sizeof(storage_); } |
| 105 | |
| 106 | bool is_neg_; |
| 107 | int size_; |
| 108 | // Max size: 128 bit value as octal -> 43 digits |
| 109 | char storage_[128 / 3 + 1]; |
| 110 | }; |
| 111 | |
| 112 | // Note: 'o' conversions do not have a base indicator, it's just that |
| 113 | // the '#' flag is specified to modify the precision for 'o' conversions. |
| 114 | string_view BaseIndicator(const ConvertedIntInfo &info, |
| 115 | const ConversionSpec conv) { |
| 116 | bool alt = conv.flags().alt; |
| 117 | int radix = conv.conv().radix(); |
| 118 | if (conv.conv().id() == ConversionChar::p) |
| 119 | alt = true; // always show 0x for %p. |
| 120 | // From the POSIX description of '#' flag: |
| 121 | // "For x or X conversion specifiers, a non-zero result shall have |
| 122 | // 0x (or 0X) prefixed to it." |
| 123 | if (alt && radix == 16 && !info.digits().empty()) { |
| 124 | if (conv.conv().upper()) return "0X" ; |
| 125 | return "0x" ; |
| 126 | } |
| 127 | return {}; |
| 128 | } |
| 129 | |
| 130 | string_view SignColumn(bool neg, const ConversionSpec conv) { |
| 131 | if (conv.conv().is_signed()) { |
| 132 | if (neg) return "-" ; |
| 133 | if (conv.flags().show_pos) return "+" ; |
| 134 | if (conv.flags().sign_col) return " " ; |
| 135 | } |
| 136 | return {}; |
| 137 | } |
| 138 | |
| 139 | bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, |
| 140 | FormatSinkImpl *sink) { |
| 141 | size_t fill = 0; |
| 142 | if (conv.width() >= 0) fill = conv.width(); |
| 143 | ReducePadding(1, &fill); |
| 144 | if (!conv.flags().left) sink->Append(fill, ' '); |
| 145 | sink->Append(1, v); |
| 146 | if (conv.flags().left) sink->Append(fill, ' '); |
| 147 | return true; |
| 148 | } |
| 149 | |
| 150 | bool ConvertIntImplInner(const ConvertedIntInfo &info, |
| 151 | const ConversionSpec conv, FormatSinkImpl *sink) { |
| 152 | // Print as a sequence of Substrings: |
| 153 | // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] |
| 154 | size_t fill = 0; |
| 155 | if (conv.width() >= 0) fill = conv.width(); |
| 156 | |
| 157 | string_view formatted = info.digits(); |
| 158 | ReducePadding(formatted, &fill); |
| 159 | |
| 160 | string_view sign = SignColumn(info.is_neg(), conv); |
| 161 | ReducePadding(sign, &fill); |
| 162 | |
| 163 | string_view base_indicator = BaseIndicator(info, conv); |
| 164 | ReducePadding(base_indicator, &fill); |
| 165 | |
| 166 | int precision = conv.precision(); |
| 167 | bool precision_specified = precision >= 0; |
| 168 | if (!precision_specified) |
| 169 | precision = 1; |
| 170 | |
| 171 | if (conv.flags().alt && conv.conv().id() == ConversionChar::o) { |
| 172 | // From POSIX description of the '#' (alt) flag: |
| 173 | // "For o conversion, it increases the precision (if necessary) to |
| 174 | // force the first digit of the result to be zero." |
| 175 | if (formatted.empty() || *formatted.begin() != '0') { |
| 176 | int needed = static_cast<int>(formatted.size()) + 1; |
| 177 | precision = std::max(precision, needed); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | size_t num_zeroes = Excess(formatted.size(), precision); |
| 182 | ReducePadding(num_zeroes, &fill); |
| 183 | |
| 184 | size_t num_left_spaces = !conv.flags().left ? fill : 0; |
| 185 | size_t num_right_spaces = conv.flags().left ? fill : 0; |
| 186 | |
| 187 | // From POSIX description of the '0' (zero) flag: |
| 188 | // "For d, i, o, u, x, and X conversion specifiers, if a precision |
| 189 | // is specified, the '0' flag is ignored." |
| 190 | if (!precision_specified && conv.flags().zero) { |
| 191 | num_zeroes += num_left_spaces; |
| 192 | num_left_spaces = 0; |
| 193 | } |
| 194 | |
| 195 | sink->Append(num_left_spaces, ' '); |
| 196 | sink->Append(sign); |
| 197 | sink->Append(base_indicator); |
| 198 | sink->Append(num_zeroes, '0'); |
| 199 | sink->Append(formatted); |
| 200 | sink->Append(num_right_spaces, ' '); |
| 201 | return true; |
| 202 | } |
| 203 | |
| 204 | template <typename T> |
| 205 | bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { |
| 206 | ConvertedIntInfo info(v, conv.conv()); |
| 207 | if (conv.flags().basic && conv.conv().id() != ConversionChar::p) { |
| 208 | if (info.is_neg()) sink->Append(1, '-'); |
| 209 | if (info.digits().empty()) { |
| 210 | sink->Append(1, '0'); |
| 211 | } else { |
| 212 | sink->Append(info.digits()); |
| 213 | } |
| 214 | return true; |
| 215 | } |
| 216 | return ConvertIntImplInner(info, conv, sink); |
| 217 | } |
| 218 | |
| 219 | template <typename T> |
| 220 | bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { |
| 221 | if (conv.conv().is_float()) { |
| 222 | return FormatConvertImpl(static_cast<double>(v), conv, sink).value; |
| 223 | } |
| 224 | if (conv.conv().id() == ConversionChar::c) |
| 225 | return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); |
| 226 | if (!conv.conv().is_integral()) |
| 227 | return false; |
| 228 | if (!conv.conv().is_signed() && IsSigned<T>::value) { |
| 229 | using U = typename MakeUnsigned<T>::type; |
| 230 | return FormatConvertImpl(static_cast<U>(v), conv, sink).value; |
| 231 | } |
| 232 | return ConvertIntImplInner(v, conv, sink); |
| 233 | } |
| 234 | |
| 235 | template <typename T> |
| 236 | bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { |
| 237 | return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink); |
| 238 | } |
| 239 | |
| 240 | inline bool ConvertStringArg(string_view v, const ConversionSpec conv, |
| 241 | FormatSinkImpl *sink) { |
| 242 | if (conv.conv().id() != ConversionChar::s) |
| 243 | return false; |
| 244 | if (conv.flags().basic) { |
| 245 | sink->Append(v); |
| 246 | return true; |
| 247 | } |
| 248 | return sink->PutPaddedString(v, conv.width(), conv.precision(), |
| 249 | conv.flags().left); |
| 250 | } |
| 251 | |
| 252 | } // namespace |
| 253 | |
| 254 | // ==================== Strings ==================== |
| 255 | ConvertResult<Conv::s> FormatConvertImpl(const std::string &v, |
| 256 | const ConversionSpec conv, |
| 257 | FormatSinkImpl *sink) { |
| 258 | return {ConvertStringArg(v, conv, sink)}; |
| 259 | } |
| 260 | |
| 261 | ConvertResult<Conv::s> FormatConvertImpl(string_view v, |
| 262 | const ConversionSpec conv, |
| 263 | FormatSinkImpl *sink) { |
| 264 | return {ConvertStringArg(v, conv, sink)}; |
| 265 | } |
| 266 | |
| 267 | ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, |
| 268 | const ConversionSpec conv, |
| 269 | FormatSinkImpl *sink) { |
| 270 | if (conv.conv().id() == ConversionChar::p) |
| 271 | return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; |
| 272 | size_t len; |
| 273 | if (v == nullptr) { |
| 274 | len = 0; |
| 275 | } else if (conv.precision() < 0) { |
| 276 | len = std::strlen(v); |
| 277 | } else { |
| 278 | // If precision is set, we look for the null terminator on the valid range. |
| 279 | len = std::find(v, v + conv.precision(), '\0') - v; |
| 280 | } |
| 281 | return {ConvertStringArg(string_view(v, len), conv, sink)}; |
| 282 | } |
| 283 | |
| 284 | // ==================== Raw pointers ==================== |
| 285 | ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, |
| 286 | FormatSinkImpl *sink) { |
| 287 | if (conv.conv().id() != ConversionChar::p) |
| 288 | return {false}; |
| 289 | if (!v.value) { |
| 290 | sink->Append("(nil)" ); |
| 291 | return {true}; |
| 292 | } |
| 293 | return {ConvertIntImplInner(v.value, conv, sink)}; |
| 294 | } |
| 295 | |
| 296 | // ==================== Floats ==================== |
| 297 | FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv, |
| 298 | FormatSinkImpl *sink) { |
| 299 | return {ConvertFloatArg(v, conv, sink)}; |
| 300 | } |
| 301 | FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv, |
| 302 | FormatSinkImpl *sink) { |
| 303 | return {ConvertFloatArg(v, conv, sink)}; |
| 304 | } |
| 305 | FloatingConvertResult FormatConvertImpl(long double v, |
| 306 | const ConversionSpec conv, |
| 307 | FormatSinkImpl *sink) { |
| 308 | return {ConvertFloatArg(v, conv, sink)}; |
| 309 | } |
| 310 | |
| 311 | // ==================== Chars ==================== |
| 312 | IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv, |
| 313 | FormatSinkImpl *sink) { |
| 314 | return {ConvertIntArg(v, conv, sink)}; |
| 315 | } |
| 316 | IntegralConvertResult FormatConvertImpl(signed char v, |
| 317 | const ConversionSpec conv, |
| 318 | FormatSinkImpl *sink) { |
| 319 | return {ConvertIntArg(v, conv, sink)}; |
| 320 | } |
| 321 | IntegralConvertResult FormatConvertImpl(unsigned char v, |
| 322 | const ConversionSpec conv, |
| 323 | FormatSinkImpl *sink) { |
| 324 | return {ConvertIntArg(v, conv, sink)}; |
| 325 | } |
| 326 | |
| 327 | // ==================== Ints ==================== |
| 328 | IntegralConvertResult FormatConvertImpl(short v, // NOLINT |
| 329 | const ConversionSpec conv, |
| 330 | FormatSinkImpl *sink) { |
| 331 | return {ConvertIntArg(v, conv, sink)}; |
| 332 | } |
| 333 | IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT |
| 334 | const ConversionSpec conv, |
| 335 | FormatSinkImpl *sink) { |
| 336 | return {ConvertIntArg(v, conv, sink)}; |
| 337 | } |
| 338 | IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv, |
| 339 | FormatSinkImpl *sink) { |
| 340 | return {ConvertIntArg(v, conv, sink)}; |
| 341 | } |
| 342 | IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv, |
| 343 | FormatSinkImpl *sink) { |
| 344 | return {ConvertIntArg(v, conv, sink)}; |
| 345 | } |
| 346 | IntegralConvertResult FormatConvertImpl(long v, // NOLINT |
| 347 | const ConversionSpec conv, |
| 348 | FormatSinkImpl *sink) { |
| 349 | return {ConvertIntArg(v, conv, sink)}; |
| 350 | } |
| 351 | IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT |
| 352 | const ConversionSpec conv, |
| 353 | FormatSinkImpl *sink) { |
| 354 | return {ConvertIntArg(v, conv, sink)}; |
| 355 | } |
| 356 | IntegralConvertResult FormatConvertImpl(long long v, // NOLINT |
| 357 | const ConversionSpec conv, |
| 358 | FormatSinkImpl *sink) { |
| 359 | return {ConvertIntArg(v, conv, sink)}; |
| 360 | } |
| 361 | IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT |
| 362 | const ConversionSpec conv, |
| 363 | FormatSinkImpl *sink) { |
| 364 | return {ConvertIntArg(v, conv, sink)}; |
| 365 | } |
| 366 | IntegralConvertResult FormatConvertImpl(absl::uint128 v, |
| 367 | const ConversionSpec conv, |
| 368 | FormatSinkImpl *sink) { |
| 369 | return {ConvertIntArg(v, conv, sink)}; |
| 370 | } |
| 371 | |
| 372 | ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); |
| 373 | |
| 374 | |
| 375 | } // namespace str_format_internal |
| 376 | |
| 377 | } // namespace absl |
| 378 | |