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/Limits.h" 13 #include "src/stdio/printf_core/core_structs.h" 14 #include "src/stdio/printf_core/writer.h" 15 16 #include <inttypes.h> 17 #include <stddef.h> 18 19 namespace __llvm_libc { 20 namespace printf_core { 21 22 void inline convert_int(Writer *writer, const FormatSection &to_conv) { 23 static constexpr size_t BITS_IN_BYTE = 8; 24 static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE; 25 26 // This approximates the number of digits it takes to represent an integer of 27 // a certain number of bits. The calculation is floor((bits * 5) / 16) 28 // 32 -> 10 (actually needs 10) 29 // 64 -> 20 (actually needs 20) 30 // 128 -> 40 (actually needs 39) 31 // This estimation grows slightly faster than the actual value, but is close 32 // enough. 33 34 static constexpr size_t BUFF_LEN = 35 ((sizeof(uintmax_t) * BITS_IN_BYTE * 5) / 16); 36 uintmax_t num = to_conv.conv_val_raw; 37 char buffer[BUFF_LEN]; 38 bool is_negative = false; 39 FormatFlags flags = to_conv.flags; 40 41 if (to_conv.conv_name == 'u') { 42 // These flags are only for signed conversions, so this removes them if the 43 // conversion is unsigned. 44 flags = FormatFlags(flags & 45 ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX)); 46 } else { 47 // Check if the number is negative by checking the high bit. This works even 48 // for smaller numbers because they're sign extended by default. 49 if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) { 50 is_negative = true; 51 num = -num; 52 } 53 } 54 55 switch (to_conv.length_modifier) { 56 case LengthModifier::none: 57 num = num & cpp::NumericLimits<unsigned int>::max(); 58 break; 59 60 case LengthModifier::l: 61 num = num & cpp::NumericLimits<unsigned long>::max(); 62 break; 63 case LengthModifier::ll: 64 case LengthModifier::L: 65 num = num & cpp::NumericLimits<unsigned long long>::max(); 66 break; 67 case LengthModifier::h: 68 num = num & cpp::NumericLimits<unsigned short>::max(); 69 break; 70 case LengthModifier::hh: 71 num = num & cpp::NumericLimits<unsigned char>::max(); 72 break; 73 case LengthModifier::z: 74 num = num & cpp::NumericLimits<size_t>::max(); 75 break; 76 case LengthModifier::t: 77 // We don't have unsigned ptrdiff so uintptr_t is used, since we need an 78 // unsigned type and ptrdiff is usually the same size as a pointer. 79 static_assert(sizeof(ptrdiff_t) == sizeof(uintptr_t)); 80 num = num & cpp::NumericLimits<uintptr_t>::max(); 81 break; 82 case LengthModifier::j: 83 // j is intmax, so no mask is necessary. 84 break; 85 } 86 87 // buff_cur can never reach 0, since the buffer is sized to always be able to 88 // contain the whole integer. This means that bounds checking it should be 89 // unnecessary. 90 size_t buff_cur = BUFF_LEN; 91 for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 10) 92 buffer[buff_cur - 1] = (num % 10) + '0'; 93 94 size_t digits_written = BUFF_LEN - buff_cur; 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 int sign_char_len = (sign_char == 0 ? 0 : 1); 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 // Negative precision indicates that it was not specified. 113 if (to_conv.precision < 0) { 114 if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) == 115 FormatFlags::LEADING_ZEROES) { 116 // If this conv has flag 0 but not - and no specified precision, it's 117 // padded with 0's instead of spaces identically to if precision = 118 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001" 119 zeroes = to_conv.min_width - digits_written - sign_char_len; 120 if (zeroes < 0) 121 zeroes = 0; 122 spaces = 0; 123 } else if (digits_written < 1) { 124 // If no precision is specified, precision defaults to 1. This means that 125 // if the integer passed to the conversion is 0, a 0 will be printed. 126 // Example: ("%3d", 0) -> " 0" 127 zeroes = 1; 128 spaces = to_conv.min_width - zeroes - sign_char_len; 129 } else { 130 // If there are enough digits to pass over the precision, just write the 131 // number, padded by spaces. 132 zeroes = 0; 133 spaces = to_conv.min_width - digits_written - sign_char_len; 134 } 135 } else { 136 // If precision was specified, possibly write zeroes, and possibly write 137 // spaces. Example: ("%5.4d", 10000) -> "10000" 138 // If the check for if zeroes is negative was not there, spaces would be 139 // incorrectly evaluated as 1. 140 zeroes = to_conv.precision - digits_written; // a negative value means 0 141 if (zeroes < 0) 142 zeroes = 0; 143 spaces = to_conv.min_width - zeroes - digits_written - sign_char_len; 144 } 145 if (spaces < 0) 146 spaces = 0; 147 148 if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { 149 // If left justified it goes sign zeroes digits spaces 150 if (sign_char != 0) 151 writer->write(&sign_char, 1); 152 if (zeroes > 0) 153 writer->write_chars('0', zeroes); 154 if (digits_written > 0) 155 writer->write(buffer + buff_cur, digits_written); 156 if (spaces > 0) 157 writer->write_chars(' ', spaces); 158 } else { 159 // Else it goes spaces sign zeroes digits 160 if (spaces > 0) 161 writer->write_chars(' ', spaces); 162 if (sign_char != 0) 163 writer->write(&sign_char, 1); 164 if (zeroes > 0) 165 writer->write_chars('0', zeroes); 166 if (digits_written > 0) 167 writer->write(buffer + buff_cur, digits_written); 168 } 169 } 170 171 } // namespace printf_core 172 } // namespace __llvm_libc 173 174 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H 175