1 //===-- Octal 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_OCT_CONVERTER_H
10 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_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_oct(Writer * writer,const FormatSection & to_conv)22 int inline convert_oct(Writer *writer, const FormatSection &to_conv) {
23 // This is the number of digits it takes to represent a octal value of a
24 // certain number of bits. Each oct digit represents 3 bits, so the value is
25 // ceil(number of bits / 3).
26 constexpr size_t BUFF_LEN = ((sizeof(uintmax_t) * 8) + 2) / 3;
27 uintmax_t num = to_conv.conv_val_raw;
28 char buffer[BUFF_LEN];
29
30 num = apply_length_modifier(num, to_conv.length_modifier);
31
32 // Since the buffer is size to sized to be able fit the entire number, buf_cur
33 // can never reach 0. So, we do not need bounds checking on buf_cur.
34 size_t buff_cur = BUFF_LEN;
35 for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 8)
36 buffer[buff_cur - 1] = (num % 8) + '0';
37
38 size_t num_digits = BUFF_LEN - buff_cur;
39
40 // These are signed to prevent underflow due to negative values. Negative
41 // values are treated the same as 0.
42 int zeroes;
43 int spaces;
44
45 // Negative precision indicates that it was not specified.
46 if (to_conv.precision < 0) {
47 if ((to_conv.flags &
48 (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
49 FormatFlags::LEADING_ZEROES) {
50 // If this conv has flag 0 but not - and no specified precision, it's
51 // padded with 0's instead of spaces identically to if precision =
52 // min_width. For example: ("%04o", 15) -> "0017"
53 zeroes = to_conv.min_width - num_digits;
54 spaces = 0;
55 } else if (num_digits < 1) {
56 // If no precision is specified, precision defaults to 1. This means that
57 // if the integer passed to the conversion is 0, a 0 will be printed.
58 // Example: ("%3o", 0) -> " 0"
59 zeroes = 1;
60 spaces = to_conv.min_width - zeroes;
61 } else {
62 // If there are enough digits to pass over the precision, just write the
63 // number, padded by spaces.
64 zeroes = 0;
65 spaces = to_conv.min_width - num_digits;
66 }
67 } else {
68 // If precision was specified, possibly write zeroes, and possibly write
69 // spaces. Example: ("%5.4o", 010000) -> "10000"
70 // If the check for if zeroes is negative was not there, spaces would be
71 // incorrectly evaluated as 1.
72 zeroes = to_conv.precision - num_digits; // a negative value means 0
73 if (zeroes < 0)
74 zeroes = 0;
75 spaces = to_conv.min_width - zeroes - num_digits;
76 }
77
78 // The alternate form prefix is "0", so it's handled by increasing the number
79 // of zeroes if necessary.
80 if (((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
81 FormatFlags::ALTERNATE_FORM) &&
82 zeroes < 1) {
83 zeroes = 1;
84 --spaces;
85 }
86
87 if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
88 FormatFlags::LEFT_JUSTIFIED) {
89 // If left justified the pattern is zeroes digits spaces
90 if (zeroes > 0)
91 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
92 if (num_digits > 0)
93 RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
94 if (spaces > 0)
95 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
96 } else {
97 // Else the pattern is spaces zeroes digits
98 if (spaces > 0)
99 RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
100 if (zeroes > 0)
101 RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
102 if (num_digits > 0)
103 RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
104 }
105 return WRITE_OK;
106 }
107
108 } // namespace printf_core
109 } // namespace __llvm_libc
110
111 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H
112