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/integer_to_string.h" 13 #include "src/stdio/printf_core/converter_utils.h" 14 #include "src/stdio/printf_core/core_structs.h" 15 #include "src/stdio/printf_core/writer.h" 16 17 #include <inttypes.h> 18 #include <stddef.h> 19 20 namespace __llvm_libc { 21 namespace printf_core { 22 23 int inline convert_int(Writer *writer, const FormatSection &to_conv) { 24 static constexpr size_t BITS_IN_BYTE = 8; 25 static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE; 26 27 uintmax_t num = to_conv.conv_val_raw; 28 bool is_negative = false; 29 FormatFlags flags = to_conv.flags; 30 31 if (to_conv.conv_name == 'u') { 32 // These flags are only for signed conversions, so this removes them if the 33 // conversion is unsigned. 34 flags = FormatFlags(flags & 35 ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX)); 36 } else { 37 // Check if the number is negative by checking the high bit. This works even 38 // for smaller numbers because they're sign extended by default. 39 if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) { 40 is_negative = true; 41 num = -num; 42 } 43 } 44 45 num = apply_length_modifier(num, to_conv.length_modifier); 46 47 auto const int_to_str = integer_to_string(num); 48 size_t digits_written = int_to_str.str().size(); 49 50 char sign_char = 0; 51 52 if (is_negative) 53 sign_char = '-'; 54 else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) 55 sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX 56 else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX) 57 sign_char = ' '; 58 59 int sign_char_len = (sign_char == 0 ? 0 : 1); 60 61 // These are signed to prevent underflow due to negative values. The eventual 62 // values will always be non-negative. 63 int zeroes; 64 int spaces; 65 66 // Negative precision indicates that it was not specified. 67 if (to_conv.precision < 0) { 68 if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) == 69 FormatFlags::LEADING_ZEROES) { 70 // If this conv has flag 0 but not - and no specified precision, it's 71 // padded with 0's instead of spaces identically to if precision = 72 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001" 73 zeroes = to_conv.min_width - digits_written - sign_char_len; 74 if (zeroes < 0) 75 zeroes = 0; 76 spaces = 0; 77 } else { 78 // If there are enough digits to pass over the precision, just write the 79 // number, padded by spaces. 80 zeroes = 0; 81 spaces = to_conv.min_width - digits_written - sign_char_len; 82 } 83 } else { 84 // If precision was specified, possibly write zeroes, and possibly write 85 // spaces. Example: ("%5.4d", 10000) -> "10000" 86 // If the check for if zeroes is negative was not there, spaces would be 87 // incorrectly evaluated as 1. 88 // 89 // The standard treats the case when num and precision are both zeroes as 90 // special - it requires that no characters are produced. So, we adjust for 91 // that special case first. 92 if (num == 0 && to_conv.precision == 0) 93 digits_written = 0; 94 zeroes = to_conv.precision - digits_written; // a negative value means 0 95 if (zeroes < 0) 96 zeroes = 0; 97 spaces = to_conv.min_width - zeroes - digits_written - sign_char_len; 98 } 99 if (spaces < 0) 100 spaces = 0; 101 102 if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { 103 // If left justified it goes sign zeroes digits spaces 104 if (sign_char != 0) 105 RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1)); 106 if (zeroes > 0) 107 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes)); 108 if (digits_written > 0) 109 RET_IF_RESULT_NEGATIVE( 110 writer->write(int_to_str.str().data(), digits_written)); 111 if (spaces > 0) 112 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces)); 113 } else { 114 // Else it goes spaces sign zeroes digits 115 if (spaces > 0) 116 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces)); 117 if (sign_char != 0) 118 RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1)); 119 if (zeroes > 0) 120 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes)); 121 if (digits_written > 0) 122 RET_IF_RESULT_NEGATIVE( 123 writer->write(int_to_str.str().data(), digits_written)); 124 } 125 return WRITE_OK; 126 } 127 128 } // namespace printf_core 129 } // namespace __llvm_libc 130 131 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H 132