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/stdio/printf_core/converter_utils.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 int 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 num = apply_length_modifier(num, to_conv.length_modifier); 56 57 // buff_cur can never reach 0, since the buffer is sized to always be able to 58 // contain the whole integer. This means that bounds checking it should be 59 // unnecessary. 60 size_t buff_cur = BUFF_LEN; 61 for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 10) 62 buffer[buff_cur - 1] = (num % 10) + '0'; 63 64 size_t digits_written = BUFF_LEN - buff_cur; 65 66 char sign_char = 0; 67 68 if (is_negative) 69 sign_char = '-'; 70 else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) 71 sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX 72 else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX) 73 sign_char = ' '; 74 75 int sign_char_len = (sign_char == 0 ? 0 : 1); 76 77 // These are signed to prevent underflow due to negative values. The eventual 78 // values will always be non-negative. 79 int zeroes; 80 int spaces; 81 82 // Negative precision indicates that it was not specified. 83 if (to_conv.precision < 0) { 84 if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) == 85 FormatFlags::LEADING_ZEROES) { 86 // If this conv has flag 0 but not - and no specified precision, it's 87 // padded with 0's instead of spaces identically to if precision = 88 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001" 89 zeroes = to_conv.min_width - digits_written - sign_char_len; 90 if (zeroes < 0) 91 zeroes = 0; 92 spaces = 0; 93 } else if (digits_written < 1) { 94 // If no precision is specified, precision defaults to 1. This means that 95 // if the integer passed to the conversion is 0, a 0 will be printed. 96 // Example: ("%3d", 0) -> " 0" 97 zeroes = 1; 98 spaces = to_conv.min_width - zeroes - sign_char_len; 99 } else { 100 // If there are enough digits to pass over the precision, just write the 101 // number, padded by spaces. 102 zeroes = 0; 103 spaces = to_conv.min_width - digits_written - sign_char_len; 104 } 105 } else { 106 // If precision was specified, possibly write zeroes, and possibly write 107 // spaces. Example: ("%5.4d", 10000) -> "10000" 108 // If the check for if zeroes is negative was not there, spaces would be 109 // incorrectly evaluated as 1. 110 zeroes = to_conv.precision - digits_written; // a negative value means 0 111 if (zeroes < 0) 112 zeroes = 0; 113 spaces = to_conv.min_width - zeroes - digits_written - sign_char_len; 114 } 115 if (spaces < 0) 116 spaces = 0; 117 118 if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { 119 // If left justified it goes sign zeroes digits spaces 120 if (sign_char != 0) 121 RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1)); 122 if (zeroes > 0) 123 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes)); 124 if (digits_written > 0) 125 RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, digits_written)); 126 if (spaces > 0) 127 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces)); 128 } else { 129 // Else it goes spaces sign zeroes digits 130 if (spaces > 0) 131 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces)); 132 if (sign_char != 0) 133 RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1)); 134 if (zeroes > 0) 135 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes)); 136 if (digits_written > 0) 137 RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, digits_written)); 138 } 139 return 0; 140 } 141 142 } // namespace printf_core 143 } // namespace __llvm_libc 144 145 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H 146