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/Limits.h"
13 #include "src/stdio/printf_core/core_structs.h"
14 #include "src/stdio/printf_core/writer.h"
15 
16 #include <inttypes.h>
17 #include <stddef.h>
18 
19 namespace __llvm_libc {
20 namespace printf_core {
21 
22 void inline convert_int(Writer *writer, const FormatSection &to_conv) {
23   static constexpr size_t BITS_IN_BYTE = 8;
24   static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
25 
26   // This approximates the number of digits it takes to represent an integer of
27   // a certain number of bits. The calculation is floor((bits * 5) / 16)
28   // 32 -> 10 (actually needs 10)
29   // 64 -> 20 (actually needs 20)
30   // 128 -> 40 (actually needs 39)
31   // This estimation grows slightly faster than the actual value, but is close
32   // enough.
33 
34   static constexpr size_t BUFF_LEN =
35       ((sizeof(uintmax_t) * BITS_IN_BYTE * 5) / 16);
36   uintmax_t num = to_conv.conv_val_raw;
37   char buffer[BUFF_LEN];
38   bool is_negative = false;
39   FormatFlags flags = to_conv.flags;
40 
41   if (to_conv.conv_name == 'u') {
42     // These flags are only for signed conversions, so this removes them if the
43     // conversion is unsigned.
44     flags = FormatFlags(flags &
45                         ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX));
46   } else {
47     // Check if the number is negative by checking the high bit. This works even
48     // for smaller numbers because they're sign extended by default.
49     if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) {
50       is_negative = true;
51       num = -num;
52     }
53   }
54 
55   switch (to_conv.length_modifier) {
56   case LengthModifier::none:
57     num = num & cpp::NumericLimits<unsigned int>::max();
58     break;
59 
60   case LengthModifier::l:
61     num = num & cpp::NumericLimits<unsigned long>::max();
62     break;
63   case LengthModifier::ll:
64   case LengthModifier::L:
65     num = num & cpp::NumericLimits<unsigned long long>::max();
66     break;
67   case LengthModifier::h:
68     num = num & cpp::NumericLimits<unsigned short>::max();
69     break;
70   case LengthModifier::hh:
71     num = num & cpp::NumericLimits<unsigned char>::max();
72     break;
73   case LengthModifier::z:
74     num = num & cpp::NumericLimits<size_t>::max();
75     break;
76   case LengthModifier::t:
77     // We don't have unsigned ptrdiff so uintptr_t is used, since we need an
78     // unsigned type and ptrdiff is usually the same size as a pointer.
79     static_assert(sizeof(ptrdiff_t) == sizeof(uintptr_t));
80     num = num & cpp::NumericLimits<uintptr_t>::max();
81     break;
82   case LengthModifier::j:
83     // j is intmax, so no mask is necessary.
84     break;
85   }
86 
87   // buff_cur can never reach 0, since the buffer is sized to always be able to
88   // contain the whole integer. This means that bounds checking it should be
89   // unnecessary.
90   size_t buff_cur = BUFF_LEN;
91   for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 10)
92     buffer[buff_cur - 1] = (num % 10) + '0';
93 
94   size_t digits_written = BUFF_LEN - buff_cur;
95 
96   char sign_char = 0;
97 
98   if (is_negative)
99     sign_char = '-';
100   else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
101     sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
102   else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)
103     sign_char = ' ';
104 
105   int sign_char_len = (sign_char == 0 ? 0 : 1);
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   // Negative precision indicates that it was not specified.
113   if (to_conv.precision < 0) {
114     if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
115         FormatFlags::LEADING_ZEROES) {
116       // If this conv has flag 0 but not - and no specified precision, it's
117       // padded with 0's instead of spaces identically to if precision =
118       // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001"
119       zeroes = to_conv.min_width - digits_written - sign_char_len;
120       if (zeroes < 0)
121         zeroes = 0;
122       spaces = 0;
123     } else if (digits_written < 1) {
124       // If no precision is specified, precision defaults to 1. This means that
125       // if the integer passed to the conversion is 0, a 0 will be printed.
126       // Example: ("%3d", 0) -> "  0"
127       zeroes = 1;
128       spaces = to_conv.min_width - zeroes - sign_char_len;
129     } else {
130       // If there are enough digits to pass over the precision, just write the
131       // number, padded by spaces.
132       zeroes = 0;
133       spaces = to_conv.min_width - digits_written - sign_char_len;
134     }
135   } else {
136     // If precision was specified, possibly write zeroes, and possibly write
137     // spaces. Example: ("%5.4d", 10000) -> "10000"
138     // If the check for if zeroes is negative was not there, spaces would be
139     // incorrectly evaluated as 1.
140     zeroes = to_conv.precision - digits_written; // a negative value means 0
141     if (zeroes < 0)
142       zeroes = 0;
143     spaces = to_conv.min_width - zeroes - digits_written - sign_char_len;
144   }
145   if (spaces < 0)
146     spaces = 0;
147 
148   if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) {
149     // If left justified it goes sign zeroes digits spaces
150     if (sign_char != 0)
151       writer->write(&sign_char, 1);
152     if (zeroes > 0)
153       writer->write_chars('0', zeroes);
154     if (digits_written > 0)
155       writer->write(buffer + buff_cur, digits_written);
156     if (spaces > 0)
157       writer->write_chars(' ', spaces);
158   } else {
159     // Else it goes spaces sign zeroes digits
160     if (spaces > 0)
161       writer->write_chars(' ', spaces);
162     if (sign_char != 0)
163       writer->write(&sign_char, 1);
164     if (zeroes > 0)
165       writer->write_chars('0', zeroes);
166     if (digits_written > 0)
167       writer->write(buffer + buff_cur, digits_written);
168   }
169 }
170 
171 } // namespace printf_core
172 } // namespace __llvm_libc
173 
174 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
175