Warning: This file is not a C or C++ file. It does not have highlighting.
| 1 | //===-- Integer Converter for printf ----------------------------*- C++ -*-===// |
|---|---|
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H |
| 10 | #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H |
| 11 | |
| 12 | #include "src/__support/CPP/span.h" |
| 13 | #include "src/__support/CPP/string_view.h" |
| 14 | #include "src/__support/ctype_utils.h" |
| 15 | #include "src/__support/integer_to_string.h" |
| 16 | #include "src/__support/macros/config.h" |
| 17 | #include "src/stdio/printf_core/converter_utils.h" |
| 18 | #include "src/stdio/printf_core/core_structs.h" |
| 19 | #include "src/stdio/printf_core/writer.h" |
| 20 | |
| 21 | #include <inttypes.h> |
| 22 | #include <stddef.h> |
| 23 | |
| 24 | namespace LIBC_NAMESPACE_DECL { |
| 25 | namespace printf_core { |
| 26 | |
| 27 | namespace details { |
| 28 | |
| 29 | using HexFmt = IntegerToString<uintmax_t, radix::Hex>; |
| 30 | using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>; |
| 31 | using OctFmt = IntegerToString<uintmax_t, radix::Oct>; |
| 32 | using DecFmt = IntegerToString<uintmax_t>; |
| 33 | using BinFmt = IntegerToString<uintmax_t, radix::Bin>; |
| 34 | |
| 35 | LIBC_INLINE constexpr size_t num_buf_size() { |
| 36 | cpp::array<size_t, 5> sizes{ |
| 37 | HexFmt::buffer_size(), HexFmtUppercase::buffer_size(), |
| 38 | OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()}; |
| 39 | |
| 40 | auto result = sizes[0]; |
| 41 | for (size_t i = 1; i < sizes.size(); i++) |
| 42 | result = cpp::max(result, sizes[i]); |
| 43 | return result; |
| 44 | } |
| 45 | |
| 46 | LIBC_INLINE cpp::optional<cpp::string_view> |
| 47 | num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) { |
| 48 | if (internal::tolower(conv_name) == 'x') { |
| 49 | if (internal::islower(conv_name)) |
| 50 | return HexFmt::format_to(bufref, num); |
| 51 | else |
| 52 | return HexFmtUppercase::format_to(bufref, num); |
| 53 | } else if (conv_name == 'o') { |
| 54 | return OctFmt::format_to(bufref, num); |
| 55 | } else if (internal::tolower(conv_name) == 'b') { |
| 56 | return BinFmt::format_to(bufref, num); |
| 57 | } else { |
| 58 | return DecFmt::format_to(bufref, num); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | } // namespace details |
| 63 | |
| 64 | template <WriteMode write_mode> |
| 65 | LIBC_INLINE int convert_int(Writer<write_mode> *writer, |
| 66 | const FormatSection &to_conv) { |
| 67 | static constexpr size_t BITS_IN_BYTE = 8; |
| 68 | static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE; |
| 69 | |
| 70 | uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw); |
| 71 | bool is_negative = false; |
| 72 | FormatFlags flags = to_conv.flags; |
| 73 | |
| 74 | // If the conversion is signed, then handle negative values. |
| 75 | if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') { |
| 76 | // Check if the number is negative by checking the high bit. This works even |
| 77 | // for smaller numbers because they're sign extended by default. |
| 78 | if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) { |
| 79 | is_negative = true; |
| 80 | num = -num; |
| 81 | } |
| 82 | } else { |
| 83 | // These flags are only for signed conversions, so this removes them if the |
| 84 | // conversion is unsigned. |
| 85 | flags = FormatFlags(flags & |
| 86 | ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX)); |
| 87 | } |
| 88 | |
| 89 | num = |
| 90 | apply_length_modifier(num, {to_conv.length_modifier, to_conv.bit_width}); |
| 91 | cpp::array<char, details::num_buf_size()> buf; |
| 92 | auto str = details::num_to_strview(num, buf, to_conv.conv_name); |
| 93 | if (!str) |
| 94 | return INT_CONVERSION_ERROR; |
| 95 | |
| 96 | size_t digits_written = str->size(); |
| 97 | |
| 98 | char sign_char = 0; |
| 99 | |
| 100 | if (is_negative) |
| 101 | sign_char = '-'; |
| 102 | else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) |
| 103 | sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX |
| 104 | else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX) |
| 105 | sign_char = ' '; |
| 106 | |
| 107 | // These are signed to prevent underflow due to negative values. The eventual |
| 108 | // values will always be non-negative. |
| 109 | int zeroes; |
| 110 | int spaces; |
| 111 | |
| 112 | // prefix is "0x" for hexadecimal, or the sign character for signed |
| 113 | // conversions. Since hexadecimal is unsigned these will never conflict. |
| 114 | size_t prefix_len; |
| 115 | char prefix[2]; |
| 116 | if ((internal::tolower(to_conv.conv_name) == 'x') && |
| 117 | ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) { |
| 118 | prefix_len = 2; |
| 119 | prefix[0] = '0'; |
| 120 | prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X'; |
| 121 | } else if ((internal::tolower(to_conv.conv_name) == 'b') && |
| 122 | ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) { |
| 123 | prefix_len = 2; |
| 124 | prefix[0] = '0'; |
| 125 | prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B'; |
| 126 | } else { |
| 127 | prefix_len = (sign_char == 0 ? 0 : 1); |
| 128 | prefix[0] = sign_char; |
| 129 | } |
| 130 | |
| 131 | // Negative precision indicates that it was not specified. |
| 132 | if (to_conv.precision < 0) { |
| 133 | if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) == |
| 134 | FormatFlags::LEADING_ZEROES) { |
| 135 | // If this conv has flag 0 but not - and no specified precision, it's |
| 136 | // padded with 0's instead of spaces identically to if precision = |
| 137 | // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001" |
| 138 | zeroes = |
| 139 | static_cast<int>(to_conv.min_width - digits_written - prefix_len); |
| 140 | spaces = 0; |
| 141 | } else { |
| 142 | // If there are enough digits to pass over the precision, just write the |
| 143 | // number, padded by spaces. |
| 144 | zeroes = 0; |
| 145 | spaces = |
| 146 | static_cast<int>(to_conv.min_width - digits_written - prefix_len); |
| 147 | } |
| 148 | } else { |
| 149 | // If precision was specified, possibly write zeroes, and possibly write |
| 150 | // spaces. Example: ("%5.4d", 10000) -> "10000" |
| 151 | // If the check for if zeroes is negative was not there, spaces would be |
| 152 | // incorrectly evaluated as 1. |
| 153 | // |
| 154 | // The standard treats the case when num and precision are both zeroes as |
| 155 | // special - it requires that no characters are produced. So, we adjust for |
| 156 | // that special case first. |
| 157 | if (num == 0 && to_conv.precision == 0) |
| 158 | digits_written = 0; |
| 159 | zeroes = static_cast<int>(to_conv.precision - |
| 160 | digits_written); // a negative value means 0 |
| 161 | if (zeroes < 0) |
| 162 | zeroes = 0; |
| 163 | spaces = static_cast<int>(to_conv.min_width - zeroes - digits_written - |
| 164 | prefix_len); |
| 165 | } |
| 166 | |
| 167 | // The standard says that alternate form for the o conversion "increases |
| 168 | // the precision, if and only if necessary, to force the first digit of the |
| 169 | // result to be a zero (if the value and precision are both 0, a single 0 is |
| 170 | // printed)" |
| 171 | // This if checks the following conditions: |
| 172 | // 1) is this an o conversion in alternate form? |
| 173 | // 2) does this number has a leading zero? |
| 174 | // 2a) ... because there are additional leading zeroes? |
| 175 | // 2b) ... because it is just "0", unless it will not write any digits. |
| 176 | const bool has_leading_zero = |
| 177 | (zeroes > 0) || ((num == 0) && (digits_written != 0)); |
| 178 | if ((to_conv.conv_name == 'o') && |
| 179 | ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) && |
| 180 | !has_leading_zero) { |
| 181 | zeroes = 1; |
| 182 | --spaces; |
| 183 | } |
| 184 | |
| 185 | if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { |
| 186 | // If left justified it goes prefix zeroes digits spaces |
| 187 | if (prefix_len != 0) |
| 188 | RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len})); |
| 189 | if (zeroes > 0) |
| 190 | RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes)); |
| 191 | if (digits_written > 0) |
| 192 | RET_IF_RESULT_NEGATIVE(writer->write(*str)); |
| 193 | if (spaces > 0) |
| 194 | RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces)); |
| 195 | } else { |
| 196 | // Else it goes spaces prefix zeroes digits |
| 197 | if (spaces > 0) |
| 198 | RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces)); |
| 199 | if (prefix_len != 0) |
| 200 | RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len})); |
| 201 | if (zeroes > 0) |
| 202 | RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes)); |
| 203 | if (digits_written > 0) |
| 204 | RET_IF_RESULT_NEGATIVE(writer->write(*str)); |
| 205 | } |
| 206 | return WRITE_OK; |
| 207 | } |
| 208 | |
| 209 | } // namespace printf_core |
| 210 | } // namespace LIBC_NAMESPACE_DECL |
| 211 | |
| 212 | #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H |
| 213 |
Warning: This file is not a C or C++ file. It does not have highlighting.
