1 //===-- Hexadecimal 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_HEX_CONVERTER_H
10 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_HEX_CONVERTER_H
11 
12 #include "src/stdio/printf_core/converter_utils.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 
convert_hex(Writer * writer,const FormatSection & to_conv)22 int convert_hex(Writer *writer, const FormatSection &to_conv) {
23   // This approximates the number of digits it takes to represent a hexadecimal
24   // value of a certain number of bits. Each hex digit represents 4 bits, so the
25   // exact value is the number of bytes multiplied by 2.
26   static constexpr size_t BUFF_LEN = sizeof(uintmax_t) * 2;
27   uintmax_t num = to_conv.conv_val_raw;
28   char buffer[BUFF_LEN];
29 
30   // All of the characters will be defined relative to variable a, which will be
31   // the appropriate case based on the name of the conversion.
32   char a;
33   if (to_conv.conv_name == 'x')
34     a = 'a';
35   else
36     a = 'A';
37 
38   num = apply_length_modifier(num, to_conv.length_modifier);
39 
40   // buff_cur can never reach 0, since the buffer is sized to always be able to
41   // contain the whole integer. This means that bounds checking it should be
42   // unnecessary.
43   size_t buff_cur = BUFF_LEN;
44   for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 16)
45     buffer[buff_cur - 1] =
46         ((num % 16) > 9) ? ((num % 16) - 10 + a) : ((num % 16) + '0');
47 
48   size_t digits_written = BUFF_LEN - buff_cur;
49 
50   // these are signed to prevent underflow due to negative values. The eventual
51   // values will always be non-negative.
52   int zeroes;
53   int spaces;
54 
55   // prefix is "0x"
56   int prefix_len;
57   char prefix[2];
58   if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
59       FormatFlags::ALTERNATE_FORM) {
60     prefix_len = 2;
61     prefix[0] = '0';
62     prefix[1] = a + ('x' - 'a');
63   } else {
64     prefix_len = 0;
65     prefix[0] = 0;
66   }
67 
68   // negative precision indicates that it was not specified.
69   if (to_conv.precision < 0) {
70     if ((to_conv.flags &
71          (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
72         FormatFlags::LEADING_ZEROES) {
73       // if this conv has flag 0 but not - and no specified precision, it's
74       // padded with 0's instead of spaces identically to if precision =
75       // min_width - (2 if prefix). For example: ("%#04x", 15) -> "0x0f"
76       zeroes = to_conv.min_width - digits_written - prefix_len;
77       if (zeroes < 0)
78         zeroes = 0;
79       spaces = 0;
80     } else if (digits_written < 1) {
81       // if no precision is specified, precision defaults to 1. This means that
82       // if the integer passed to the conversion is 0, a 0 will be printed.
83       // Example: ("%3x", 0) -> "  0"
84       zeroes = 1;
85       spaces = to_conv.min_width - zeroes - prefix_len;
86     } else {
87       // If there are enough digits to pass over the precision, just write the
88       // number, padded by spaces.
89       zeroes = 0;
90       spaces = to_conv.min_width - digits_written - prefix_len;
91     }
92   } else {
93     // if precision was specified, possibly write zeroes, and possibly write
94     // spaces. Example: ("%5.4x", 0x10000) -> "10000"
95     // If the check for if zeroes is negative was not there, spaces would be
96     // incorrectly evaluated as 1.
97     zeroes = to_conv.precision - digits_written; // a negative value means 0
98     if (zeroes < 0)
99       zeroes = 0;
100     spaces = to_conv.min_width - zeroes - digits_written - prefix_len;
101   }
102   if (spaces < 0)
103     spaces = 0;
104 
105   if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
106       FormatFlags::LEFT_JUSTIFIED) {
107     // if left justified it goes prefix zeroes digits spaces
108     if (prefix[0] != 0)
109       RET_IF_RESULT_NEGATIVE(writer->write(prefix, 2));
110     if (zeroes > 0)
111       RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
112     if (digits_written > 0)
113       RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, digits_written));
114     if (spaces > 0)
115       RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
116   } else {
117     // else it goes spaces prefix zeroes digits
118     if (spaces > 0)
119       RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
120     if (prefix[0] != 0)
121       RET_IF_RESULT_NEGATIVE(writer->write(prefix, 2));
122     if (zeroes > 0)
123       RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
124     if (digits_written > 0)
125       RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, digits_written));
126   }
127   return WRITE_OK;
128 }
129 
130 } // namespace printf_core
131 } // namespace __llvm_libc
132 
133 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_HEX_CONVERTER_H
134