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 LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) { 65 static constexpr size_t BITS_IN_BYTE = 8; 66 static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE; 67 68 uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw); 69 bool is_negative = false; 70 FormatFlags flags = to_conv.flags; 71 72 // If the conversion is signed, then handle negative values. 73 if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') { 74 // Check if the number is negative by checking the high bit. This works even 75 // for smaller numbers because they're sign extended by default. 76 if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) { 77 is_negative = true; 78 num = -num; 79 } 80 } else { 81 // These flags are only for signed conversions, so this removes them if the 82 // conversion is unsigned. 83 flags = FormatFlags(flags & 84 ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX)); 85 } 86 87 num = 88 apply_length_modifier(num, {to_conv.length_modifier, to_conv.bit_width}); 89 cpp::array<char, details::num_buf_size()> buf; 90 auto str = details::num_to_strview(num, buf, to_conv.conv_name); 91 if (!str) 92 return INT_CONVERSION_ERROR; 93 94 size_t digits_written = str->size(); 95 96 char sign_char = 0; 97 98 if (is_negative) 99 sign_char = '-'; 100 else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) 101 sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX 102 else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX) 103 sign_char = ' '; 104 105 // These are signed to prevent underflow due to negative values. The eventual 106 // values will always be non-negative. 107 int zeroes; 108 int spaces; 109 110 // prefix is "0x" for hexadecimal, or the sign character for signed 111 // conversions. Since hexadecimal is unsigned these will never conflict. 112 size_t prefix_len; 113 char prefix[2]; 114 if ((internal::tolower(to_conv.conv_name) == 'x') && 115 ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) { 116 prefix_len = 2; 117 prefix[0] = '0'; 118 prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X'; 119 } else if ((internal::tolower(to_conv.conv_name) == 'b') && 120 ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) { 121 prefix_len = 2; 122 prefix[0] = '0'; 123 prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B'; 124 } else { 125 prefix_len = (sign_char == 0 ? 0 : 1); 126 prefix[0] = sign_char; 127 } 128 129 // Negative precision indicates that it was not specified. 130 if (to_conv.precision < 0) { 131 if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) == 132 FormatFlags::LEADING_ZEROES) { 133 // If this conv has flag 0 but not - and no specified precision, it's 134 // padded with 0's instead of spaces identically to if precision = 135 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001" 136 zeroes = 137 static_cast<int>(to_conv.min_width - digits_written - prefix_len); 138 spaces = 0; 139 } else { 140 // If there are enough digits to pass over the precision, just write the 141 // number, padded by spaces. 142 zeroes = 0; 143 spaces = 144 static_cast<int>(to_conv.min_width - digits_written - prefix_len); 145 } 146 } else { 147 // If precision was specified, possibly write zeroes, and possibly write 148 // spaces. Example: ("%5.4d", 10000) -> "10000" 149 // If the check for if zeroes is negative was not there, spaces would be 150 // incorrectly evaluated as 1. 151 // 152 // The standard treats the case when num and precision are both zeroes as 153 // special - it requires that no characters are produced. So, we adjust for 154 // that special case first. 155 if (num == 0 && to_conv.precision == 0) 156 digits_written = 0; 157 zeroes = static_cast<int>(to_conv.precision - 158 digits_written); // a negative value means 0 159 if (zeroes < 0) 160 zeroes = 0; 161 spaces = static_cast<int>(to_conv.min_width - zeroes - digits_written - 162 prefix_len); 163 } 164 165 // The standard says that alternate form for the o conversion "increases 166 // the precision, if and only if necessary, to force the first digit of the 167 // result to be a zero (if the value and precision are both 0, a single 0 is 168 // printed)" 169 // This if checks the following conditions: 170 // 1) is this an o conversion in alternate form? 171 // 2) does this number has a leading zero? 172 // 2a) ... because there are additional leading zeroes? 173 // 2b) ... because it is just "0", unless it will not write any digits. 174 const bool has_leading_zero = 175 (zeroes > 0) || ((num == 0) && (digits_written != 0)); 176 if ((to_conv.conv_name == 'o') && 177 ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) && 178 !has_leading_zero) { 179 zeroes = 1; 180 --spaces; 181 } 182 183 if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { 184 // If left justified it goes prefix zeroes digits spaces 185 if (prefix_len != 0) 186 RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len})); 187 if (zeroes > 0) 188 RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes)); 189 if (digits_written > 0) 190 RET_IF_RESULT_NEGATIVE(writer->write(*str)); 191 if (spaces > 0) 192 RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces)); 193 } else { 194 // Else it goes spaces prefix zeroes digits 195 if (spaces > 0) 196 RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces)); 197 if (prefix_len != 0) 198 RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len})); 199 if (zeroes > 0) 200 RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes)); 201 if (digits_written > 0) 202 RET_IF_RESULT_NEGATIVE(writer->write(*str)); 203 } 204 return WRITE_OK; 205 } 206 207 } // namespace printf_core 208 } // namespace LIBC_NAMESPACE_DECL 209 210 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H 211