Warning: This file is not a C or C++ file. It does not have highlighting.

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/span.h"
13#include "src/__support/CPP/string_view.h"
14#include "src/__support/ctype_utils.h"
15#include "src/__support/integer_to_string.h"
16#include "src/__support/macros/config.h"
17#include "src/stdio/printf_core/converter_utils.h"
18#include "src/stdio/printf_core/core_structs.h"
19#include "src/stdio/printf_core/writer.h"
20
21#include <inttypes.h>
22#include <stddef.h>
23
24namespace LIBC_NAMESPACE_DECL {
25namespace printf_core {
26
27namespace details {
28
29using HexFmt = IntegerToString<uintmax_t, radix::Hex>;
30using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>;
31using OctFmt = IntegerToString<uintmax_t, radix::Oct>;
32using DecFmt = IntegerToString<uintmax_t>;
33using BinFmt = IntegerToString<uintmax_t, radix::Bin>;
34
35LIBC_INLINE constexpr size_t num_buf_size() {
36 cpp::array<size_t, 5> sizes{
37 HexFmt::buffer_size(), HexFmtUppercase::buffer_size(),
38 OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()};
39
40 auto result = sizes[0];
41 for (size_t i = 1; i < sizes.size(); i++)
42 result = cpp::max(result, sizes[i]);
43 return result;
44}
45
46LIBC_INLINE cpp::optional<cpp::string_view>
47num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
48 if (internal::tolower(conv_name) == 'x') {
49 if (internal::islower(conv_name))
50 return HexFmt::format_to(bufref, num);
51 else
52 return HexFmtUppercase::format_to(bufref, num);
53 } else if (conv_name == 'o') {
54 return OctFmt::format_to(bufref, num);
55 } else if (internal::tolower(conv_name) == 'b') {
56 return BinFmt::format_to(bufref, num);
57 } else {
58 return DecFmt::format_to(bufref, num);
59 }
60}
61
62} // namespace details
63
64template <WriteMode write_mode>
65LIBC_INLINE int convert_int(Writer<write_mode> *writer,
66 const FormatSection &to_conv) {
67 static constexpr size_t BITS_IN_BYTE = 8;
68 static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
69
70 uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw);
71 bool is_negative = false;
72 FormatFlags flags = to_conv.flags;
73
74 // If the conversion is signed, then handle negative values.
75 if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') {
76 // Check if the number is negative by checking the high bit. This works even
77 // for smaller numbers because they're sign extended by default.
78 if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) {
79 is_negative = true;
80 num = -num;
81 }
82 } else {
83 // These flags are only for signed conversions, so this removes them if the
84 // conversion is unsigned.
85 flags = FormatFlags(flags &
86 ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX));
87 }
88
89 num =
90 apply_length_modifier(num, {to_conv.length_modifier, to_conv.bit_width});
91 cpp::array<char, details::num_buf_size()> buf;
92 auto str = details::num_to_strview(num, buf, to_conv.conv_name);
93 if (!str)
94 return INT_CONVERSION_ERROR;
95
96 size_t digits_written = str->size();
97
98 char sign_char = 0;
99
100 if (is_negative)
101 sign_char = '-';
102 else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
103 sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
104 else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)
105 sign_char = ' ';
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 // prefix is "0x" for hexadecimal, or the sign character for signed
113 // conversions. Since hexadecimal is unsigned these will never conflict.
114 size_t prefix_len;
115 char prefix[2];
116 if ((internal::tolower(to_conv.conv_name) == 'x') &&
117 ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
118 prefix_len = 2;
119 prefix[0] = '0';
120 prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
121 } else if ((internal::tolower(to_conv.conv_name) == 'b') &&
122 ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
123 prefix_len = 2;
124 prefix[0] = '0';
125 prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B';
126 } else {
127 prefix_len = (sign_char == 0 ? 0 : 1);
128 prefix[0] = sign_char;
129 }
130
131 // Negative precision indicates that it was not specified.
132 if (to_conv.precision < 0) {
133 if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
134 FormatFlags::LEADING_ZEROES) {
135 // If this conv has flag 0 but not - and no specified precision, it's
136 // padded with 0's instead of spaces identically to if precision =
137 // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001"
138 zeroes =
139 static_cast<int>(to_conv.min_width - digits_written - prefix_len);
140 spaces = 0;
141 } else {
142 // If there are enough digits to pass over the precision, just write the
143 // number, padded by spaces.
144 zeroes = 0;
145 spaces =
146 static_cast<int>(to_conv.min_width - digits_written - prefix_len);
147 }
148 } else {
149 // If precision was specified, possibly write zeroes, and possibly write
150 // spaces. Example: ("%5.4d", 10000) -> "10000"
151 // If the check for if zeroes is negative was not there, spaces would be
152 // incorrectly evaluated as 1.
153 //
154 // The standard treats the case when num and precision are both zeroes as
155 // special - it requires that no characters are produced. So, we adjust for
156 // that special case first.
157 if (num == 0 && to_conv.precision == 0)
158 digits_written = 0;
159 zeroes = static_cast<int>(to_conv.precision -
160 digits_written); // a negative value means 0
161 if (zeroes < 0)
162 zeroes = 0;
163 spaces = static_cast<int>(to_conv.min_width - zeroes - digits_written -
164 prefix_len);
165 }
166
167 // The standard says that alternate form for the o conversion "increases
168 // the precision, if and only if necessary, to force the first digit of the
169 // result to be a zero (if the value and precision are both 0, a single 0 is
170 // printed)"
171 // This if checks the following conditions:
172 // 1) is this an o conversion in alternate form?
173 // 2) does this number has a leading zero?
174 // 2a) ... because there are additional leading zeroes?
175 // 2b) ... because it is just "0", unless it will not write any digits.
176 const bool has_leading_zero =
177 (zeroes > 0) || ((num == 0) && (digits_written != 0));
178 if ((to_conv.conv_name == 'o') &&
179 ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) &&
180 !has_leading_zero) {
181 zeroes = 1;
182 --spaces;
183 }
184
185 if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) {
186 // If left justified it goes prefix zeroes digits spaces
187 if (prefix_len != 0)
188 RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));
189 if (zeroes > 0)
190 RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));
191 if (digits_written > 0)
192 RET_IF_RESULT_NEGATIVE(writer->write(*str));
193 if (spaces > 0)
194 RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));
195 } else {
196 // Else it goes spaces prefix zeroes digits
197 if (spaces > 0)
198 RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));
199 if (prefix_len != 0)
200 RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));
201 if (zeroes > 0)
202 RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));
203 if (digits_written > 0)
204 RET_IF_RESULT_NEGATIVE(writer->write(*str));
205 }
206 return WRITE_OK;
207}
208
209} // namespace printf_core
210} // namespace LIBC_NAMESPACE_DECL
211
212#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
213

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of libc/src/stdio/printf_core/int_converter.h