brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.8 KiB · 11234c3 Raw
213 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H10#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H11 12#include "src/__support/CPP/span.h"13#include "src/__support/CPP/string_view.h"14#include "src/__support/ctype_utils.h"15#include "src/__support/integer_to_string.h"16#include "src/__support/macros/config.h"17#include "src/stdio/printf_core/converter_utils.h"18#include "src/stdio/printf_core/core_structs.h"19#include "src/stdio/printf_core/writer.h"20 21#include <inttypes.h>22#include <stddef.h>23 24namespace LIBC_NAMESPACE_DECL {25namespace printf_core {26 27namespace details {28 29using HexFmt = IntegerToString<uintmax_t, radix::Hex>;30using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>;31using OctFmt = IntegerToString<uintmax_t, radix::Oct>;32using DecFmt = IntegerToString<uintmax_t>;33using BinFmt = IntegerToString<uintmax_t, radix::Bin>;34 35LIBC_INLINE constexpr size_t num_buf_size() {36  cpp::array<size_t, 5> sizes{37      HexFmt::buffer_size(), HexFmtUppercase::buffer_size(),38      OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()};39 40  auto result = sizes[0];41  for (size_t i = 1; i < sizes.size(); i++)42    result = cpp::max(result, sizes[i]);43  return result;44}45 46LIBC_INLINE cpp::optional<cpp::string_view>47num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {48  if (internal::tolower(conv_name) == 'x') {49    if (internal::islower(conv_name))50      return HexFmt::format_to(bufref, num);51    else52      return HexFmtUppercase::format_to(bufref, num);53  } else if (conv_name == 'o') {54    return OctFmt::format_to(bufref, num);55  } else if (internal::tolower(conv_name) == 'b') {56    return BinFmt::format_to(bufref, num);57  } else {58    return DecFmt::format_to(bufref, num);59  }60}61 62} // namespace details63 64template <WriteMode write_mode>65LIBC_INLINE int convert_int(Writer<write_mode> *writer,66                            const FormatSection &to_conv) {67  static constexpr size_t BITS_IN_BYTE = 8;68  static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;69 70  uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw);71  bool is_negative = false;72  FormatFlags flags = to_conv.flags;73 74  // If the conversion is signed, then handle negative values.75  if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') {76    // Check if the number is negative by checking the high bit. This works even77    // for smaller numbers because they're sign extended by default.78    if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) {79      is_negative = true;80      num = -num;81    }82  } else {83    // These flags are only for signed conversions, so this removes them if the84    // conversion is unsigned.85    flags = FormatFlags(flags &86                        ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX));87  }88 89  num =90      apply_length_modifier(num, {to_conv.length_modifier, to_conv.bit_width});91  cpp::array<char, details::num_buf_size()> buf;92  auto str = details::num_to_strview(num, buf, to_conv.conv_name);93  if (!str)94    return INT_CONVERSION_ERROR;95 96  size_t digits_written = str->size();97 98  char sign_char = 0;99 100  if (is_negative)101    sign_char = '-';102  else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)103    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX104  else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)105    sign_char = ' ';106 107  // These are signed to prevent underflow due to negative values. The eventual108  // values will always be non-negative.109  int zeroes;110  int spaces;111 112  // prefix is "0x" for hexadecimal, or the sign character for signed113  // conversions. Since hexadecimal is unsigned these will never conflict.114  size_t prefix_len;115  char prefix[2];116  if ((internal::tolower(to_conv.conv_name) == 'x') &&117      ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {118    prefix_len = 2;119    prefix[0] = '0';120    prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';121  } else if ((internal::tolower(to_conv.conv_name) == 'b') &&122             ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {123    prefix_len = 2;124    prefix[0] = '0';125    prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B';126  } else {127    prefix_len = (sign_char == 0 ? 0 : 1);128    prefix[0] = sign_char;129  }130 131  // Negative precision indicates that it was not specified.132  if (to_conv.precision < 0) {133    if ((flags & (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==134        FormatFlags::LEADING_ZEROES) {135      // If this conv has flag 0 but not - and no specified precision, it's136      // padded with 0's instead of spaces identically to if precision =137      // min_width - (1 if sign_char). For example: ("%+04d", 1) -> "+001"138      zeroes =139          static_cast<int>(to_conv.min_width - digits_written - prefix_len);140      spaces = 0;141    } else {142      // If there are enough digits to pass over the precision, just write the143      // number, padded by spaces.144      zeroes = 0;145      spaces =146          static_cast<int>(to_conv.min_width - digits_written - prefix_len);147    }148  } else {149    // If precision was specified, possibly write zeroes, and possibly write150    // spaces. Example: ("%5.4d", 10000) -> "10000"151    // If the check for if zeroes is negative was not there, spaces would be152    // incorrectly evaluated as 1.153    //154    // The standard treats the case when num and precision are both zeroes as155    // special - it requires that no characters are produced. So, we adjust for156    // that special case first.157    if (num == 0 && to_conv.precision == 0)158      digits_written = 0;159    zeroes = static_cast<int>(to_conv.precision -160                              digits_written); // a negative value means 0161    if (zeroes < 0)162      zeroes = 0;163    spaces = static_cast<int>(to_conv.min_width - zeroes - digits_written -164                              prefix_len);165  }166 167  // The standard says that alternate form for the o conversion "increases168  // the precision, if and only if necessary, to force the first digit of the169  // result to be a zero (if the value and precision are both 0, a single 0 is170  // printed)"171  // This if checks the following conditions:172  // 1) is this an o conversion in alternate form?173  // 2) does this number has a leading zero?174  //    2a) ... because there are additional leading zeroes?175  //    2b) ... because it is just "0", unless it will not write any digits.176  const bool has_leading_zero =177      (zeroes > 0) || ((num == 0) && (digits_written != 0));178  if ((to_conv.conv_name == 'o') &&179      ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) &&180      !has_leading_zero) {181    zeroes = 1;182    --spaces;183  }184 185  if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) {186    // If left justified it goes prefix zeroes digits spaces187    if (prefix_len != 0)188      RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));189    if (zeroes > 0)190      RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));191    if (digits_written > 0)192      RET_IF_RESULT_NEGATIVE(writer->write(*str));193    if (spaces > 0)194      RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));195  } else {196    // Else it goes spaces prefix zeroes digits197    if (spaces > 0)198      RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));199    if (prefix_len != 0)200      RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));201    if (zeroes > 0)202      RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));203    if (digits_written > 0)204      RET_IF_RESULT_NEGATIVE(writer->write(*str));205  }206  return WRITE_OK;207}208 209} // namespace printf_core210} // namespace LIBC_NAMESPACE_DECL211 212#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H213