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