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