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
convert_int(Writer * writer,const FormatSection & to_conv)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