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 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