1 //===-- Integer Converter for printf ----------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
10 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
12 #include "src/__support/CPP/span.h"
13 #include "src/__support/CPP/string_view.h"
14 #include "src/__support/common.h"
15 #include "src/__support/integer_to_string.h"
16 #include "src/stdio/printf_core/converter_utils.h"
17 #include "src/stdio/printf_core/core_structs.h"
18 #include "src/stdio/printf_core/writer.h"
23 namespace LIBC_NAMESPACE
{
24 namespace printf_core
{
26 // These functions only work on characters that are already known to be in the
27 // alphabet. Their behavior is undefined otherwise.
28 LIBC_INLINE
constexpr char to_lower(char a
) { return a
| 32; }
29 LIBC_INLINE
constexpr bool is_lower(char a
) { return (a
& 32) > 0; }
33 using HexFmt
= IntegerToString
<uintmax_t, radix::Hex
>;
34 using HexFmtUppercase
= IntegerToString
<uintmax_t, radix::Hex::Uppercase
>;
35 using OctFmt
= IntegerToString
<uintmax_t, radix::Oct
>;
36 using DecFmt
= IntegerToString
<uintmax_t>;
38 LIBC_INLINE
constexpr size_t num_buf_size() {
39 constexpr auto max
= [](size_t a
, size_t b
) -> size_t {
40 return (a
< b
) ? b
: a
;
42 return max(HexFmt::buffer_size(),
43 max(HexFmtUppercase::buffer_size(),
44 max(OctFmt::buffer_size(), DecFmt::buffer_size())));
47 LIBC_INLINE
cpp::optional
<cpp::string_view
>
48 num_to_strview(uintmax_t num
, cpp::span
<char> bufref
, char conv_name
) {
49 if (to_lower(conv_name
) == 'x') {
50 if (is_lower(conv_name
))
51 return HexFmt::format_to(bufref
, num
);
53 return HexFmtUppercase::format_to(bufref
, num
);
54 } else if (conv_name
== 'o') {
55 return OctFmt::format_to(bufref
, num
);
57 return DecFmt::format_to(bufref
, num
);
61 } // namespace details
63 LIBC_INLINE
int convert_int(Writer
*writer
, const FormatSection
&to_conv
) {
64 static constexpr size_t BITS_IN_BYTE
= 8;
65 static constexpr size_t BITS_IN_NUM
= sizeof(uintmax_t) * BITS_IN_BYTE
;
67 uintmax_t num
= static_cast<uintmax_t>(to_conv
.conv_val_raw
);
68 bool is_negative
= false;
69 FormatFlags flags
= to_conv
.flags
;
71 const char a
= is_lower(to_conv
.conv_name
) ? 'a' : 'A';
73 // If the conversion is signed, then handle negative values.
74 if (to_conv
.conv_name
== 'd' || to_conv
.conv_name
== 'i') {
75 // Check if the number is negative by checking the high bit. This works even
76 // for smaller numbers because they're sign extended by default.
77 if ((num
& (uintmax_t(1) << (BITS_IN_NUM
- 1))) > 0) {
82 // These flags are only for signed conversions, so this removes them if the
83 // conversion is unsigned.
84 flags
= FormatFlags(flags
&
85 ~(FormatFlags::FORCE_SIGN
| FormatFlags::SPACE_PREFIX
));
88 num
= apply_length_modifier(num
, to_conv
.length_modifier
);
90 cpp::array
<char, details::num_buf_size()> buf
;
91 auto str
= details::num_to_strview(num
, buf
, to_conv
.conv_name
);
93 return INT_CONVERSION_ERROR
;
95 size_t digits_written
= str
->size();
101 else if ((flags
& FormatFlags::FORCE_SIGN
) == FormatFlags::FORCE_SIGN
)
102 sign_char
= '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
103 else if ((flags
& FormatFlags::SPACE_PREFIX
) == FormatFlags::SPACE_PREFIX
)
106 // These are signed to prevent underflow due to negative values. The eventual
107 // values will always be non-negative.
111 // prefix is "0x" for hexadecimal, or the sign character for signed
112 // conversions. Since hexadecimal is unsigned these will never conflict.
115 if ((to_lower(to_conv
.conv_name
) == 'x') &&
116 ((flags
& FormatFlags::ALTERNATE_FORM
) != 0) && num
!= 0) {
119 prefix
[1] = a
+ ('x' - 'a');
121 prefix_len
= (sign_char
== 0 ? 0 : 1);
122 prefix
[0] = sign_char
;
125 // Negative precision indicates that it was not specified.
126 if (to_conv
.precision
< 0) {
127 if ((flags
& (FormatFlags::LEADING_ZEROES
| FormatFlags::LEFT_JUSTIFIED
)) ==
128 FormatFlags::LEADING_ZEROES
) {
129 // If this conv has flag 0 but not - and no specified precision, it's
130 // padded with 0's instead of spaces identically to if precision =
131 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001"
133 static_cast<int>(to_conv
.min_width
- digits_written
- prefix_len
);
136 // If there are enough digits to pass over the precision, just write the
137 // number, padded by spaces.
140 static_cast<int>(to_conv
.min_width
- digits_written
- prefix_len
);
143 // If precision was specified, possibly write zeroes, and possibly write
144 // spaces. Example: ("%5.4d", 10000) -> "10000"
145 // If the check for if zeroes is negative was not there, spaces would be
146 // incorrectly evaluated as 1.
148 // The standard treats the case when num and precision are both zeroes as
149 // special - it requires that no characters are produced. So, we adjust for
150 // that special case first.
151 if (num
== 0 && to_conv
.precision
== 0)
153 zeroes
= static_cast<int>(to_conv
.precision
-
154 digits_written
); // a negative value means 0
157 spaces
= static_cast<int>(to_conv
.min_width
- zeroes
- digits_written
-
161 // The standard says that alternate form for the o conversion "increases
162 // the precision, if and only if necessary, to force the first digit of the
163 // result to be a zero (if the value and precision are both 0, a single 0 is
165 // This if checks the following conditions:
166 // 1) is this an o conversion in alternate form?
167 // 2) does this number has a leading zero?
168 // 2a) ... because there are additional leading zeroes?
169 // 2b) ... because it is just "0", unless it will not write any digits.
170 const bool has_leading_zero
=
171 (zeroes
> 0) || ((num
== 0) && (digits_written
!= 0));
172 if ((to_conv
.conv_name
== 'o') &&
173 ((to_conv
.flags
& FormatFlags::ALTERNATE_FORM
) != 0) &&
179 if ((flags
& FormatFlags::LEFT_JUSTIFIED
) == FormatFlags::LEFT_JUSTIFIED
) {
180 // If left justified it goes prefix zeroes digits spaces
182 RET_IF_RESULT_NEGATIVE(writer
->write({prefix
, prefix_len
}));
184 RET_IF_RESULT_NEGATIVE(writer
->write('0', zeroes
));
185 if (digits_written
> 0)
186 RET_IF_RESULT_NEGATIVE(writer
->write(*str
));
188 RET_IF_RESULT_NEGATIVE(writer
->write(' ', spaces
));
190 // Else it goes spaces prefix zeroes digits
192 RET_IF_RESULT_NEGATIVE(writer
->write(' ', spaces
));
194 RET_IF_RESULT_NEGATIVE(writer
->write({prefix
, prefix_len
}));
196 RET_IF_RESULT_NEGATIVE(writer
->write('0', zeroes
));
197 if (digits_written
> 0)
198 RET_IF_RESULT_NEGATIVE(writer
->write(*str
));
203 } // namespace printf_core
204 } // namespace LIBC_NAMESPACE
206 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H