brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · 6bbba37 Raw
234 lines · c
1//===-- Float type specifier converter for scanf ----------------*- 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_SCANF_CORE_FLOAT_CONVERTER_H10#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H11 12#include "src/__support/CPP/limits.h"13#include "src/__support/char_vector.h"14#include "src/__support/ctype_utils.h"15#include "src/__support/macros/config.h"16#include "src/stdio/scanf_core/converter_utils.h"17#include "src/stdio/scanf_core/core_structs.h"18#include "src/stdio/scanf_core/reader.h"19 20#include <stddef.h>21 22namespace LIBC_NAMESPACE_DECL {23namespace scanf_core {24 25// All of the floating point conversions are the same for scanf, every name will26// accept every style.27template <typename T>28int convert_float(Reader<T> *reader, const FormatSection &to_conv) {29  // %a/A/e/E/f/F/g/G "Matches an optionally signed floating-point number,30  // infinity, or NaN, whose format is the same as expected for the subject31  // sequence of the strtod function. The corresponding argument shall be a32  // pointer to floating."33 34  CharVector out_str = CharVector();35  bool is_number = false;36 37  size_t max_width = cpp::numeric_limits<size_t>::max();38  if (to_conv.max_width > 0) {39    max_width = to_conv.max_width;40  }41 42  char cur_char = reader->getc();43  // Handle the sign.44  if (cur_char == '+' || cur_char == '-') {45    if (!out_str.append(cur_char)) {46      return ALLOCATION_FAILURE;47    }48    if (out_str.length() == max_width) {49      return MATCHING_FAILURE;50    } else {51      cur_char = reader->getc();52    }53  }54 55  static constexpr char DECIMAL_POINT = '.';56  static const char inf_string[] = "infinity";57 58  // Handle inf59 60  if (internal::tolower(cur_char) == inf_string[0]) {61    size_t inf_index = 0;62 63    for (;64         inf_index < (sizeof(inf_string) - 1) && out_str.length() < max_width &&65         internal::tolower(cur_char) == inf_string[inf_index];66         ++inf_index) {67      if (!out_str.append(cur_char)) {68        return ALLOCATION_FAILURE;69      }70      cur_char = reader->getc();71    }72 73    if (inf_index == 3 || inf_index == sizeof(inf_string) - 1) {74      write_float_with_length(out_str.c_str(), to_conv);75      return READ_OK;76    } else {77      return MATCHING_FAILURE;78    }79  }80 81  static const char nan_string[] = "nan";82 83  // Handle nan84  if (internal::tolower(cur_char) == nan_string[0]) {85    size_t nan_index = 0;86 87    for (;88         nan_index < (sizeof(nan_string) - 1) && out_str.length() < max_width &&89         internal::tolower(cur_char) == nan_string[nan_index];90         ++nan_index) {91      if (!out_str.append(cur_char)) {92        return ALLOCATION_FAILURE;93      }94      cur_char = reader->getc();95    }96 97    if (nan_index == sizeof(nan_string) - 1) {98      write_float_with_length(out_str.c_str(), to_conv);99      return READ_OK;100    } else {101      return MATCHING_FAILURE;102    }103  }104 105  // Assume base of 10 by default but check if it is actually base 16.106  int base = 10;107 108  // If the string starts with 0 it might be in hex.109  if (cur_char == '0') {110    is_number = true;111    // Read the next character to check.112    if (!out_str.append(cur_char)) {113      return ALLOCATION_FAILURE;114    }115    // If we've hit the end, then this is "0", which is valid.116    if (out_str.length() == max_width) {117      write_float_with_length(out_str.c_str(), to_conv);118      return READ_OK;119    } else {120      cur_char = reader->getc();121    }122 123    // If that next character is an 'x' then this is a hexadecimal number.124    if (internal::tolower(cur_char) == 'x') {125      base = 16;126 127      if (!out_str.append(cur_char)) {128        return ALLOCATION_FAILURE;129      }130      // If we've hit the end here, we have "0x" which is a valid prefix to a131      // floating point number, and will be evaluated to 0.132      if (out_str.length() == max_width) {133        write_float_with_length(out_str.c_str(), to_conv);134        return READ_OK;135      } else {136        cur_char = reader->getc();137      }138    }139  }140 141  const char exponent_mark = ((base == 10) ? 'e' : 'p');142  bool after_decimal = false;143 144  // The format for the remaining characters at this point is DD.DDe+/-DD for145  // base 10 and XX.XXp+/-DD for base 16146 147  // This handles the digits before and after the decimal point, but not the148  // exponent.149  while (out_str.length() < max_width) {150    if (internal::isalnum(cur_char) &&151        internal::b36_char_to_int(cur_char) < base) {152      is_number = true;153      if (!out_str.append(cur_char)) {154        return ALLOCATION_FAILURE;155      }156      cur_char = reader->getc();157    } else if (cur_char == DECIMAL_POINT && !after_decimal) {158      after_decimal = true;159      if (!out_str.append(cur_char)) {160        return ALLOCATION_FAILURE;161      }162      cur_char = reader->getc();163    } else {164      break;165    }166  }167 168  // Handle the exponent, which has an exponent mark, an optional sign, and169  // decimal digits.170  if (internal::tolower(cur_char) == exponent_mark) {171    if (!out_str.append(cur_char)) {172      return ALLOCATION_FAILURE;173    }174    if (out_str.length() == max_width) {175      // This is laid out in the standard as being a matching error (100e is not176      // a valid float) but may conflict with existing implementations.177      return MATCHING_FAILURE;178    } else {179      cur_char = reader->getc();180    }181 182    if (cur_char == '+' || cur_char == '-') {183      if (!out_str.append(cur_char)) {184        return ALLOCATION_FAILURE;185      }186      if (out_str.length() == max_width) {187        return MATCHING_FAILURE;188      } else {189        cur_char = reader->getc();190      }191    }192 193    // It is specified by the standard that "100er" is a matching failure since194    // the longest prefix of a possibly valid floating-point number (which is195    // "100e") is not a valid floating-point number. If there is an exponent196    // mark then there must be a digit after it else the number is not valid.197    // Some implementations will roll back two characters (to just "100") and198    // accept that since the prefix is not valid, and some will interpret an199    // exponent mark followed by no digits as an additional exponent of 0200    // (accepting "100e" and returning 100.0). Both of these behaviors are wrong201    // by the standard, but they may be used in real code, see Hyrum's law. This202    // code follows the standard, but may be incompatible due to code expecting203    // these bugs.204    if (!internal::isdigit(cur_char)) {205      return MATCHING_FAILURE;206    }207 208    while (internal::isdigit(cur_char) && out_str.length() < max_width) {209      if (!out_str.append(cur_char)) {210        return ALLOCATION_FAILURE;211      }212      cur_char = reader->getc();213    }214  }215 216  // We always read one more character than will be used, so we have to put the217  // last one back.218  reader->ungetc(cur_char);219 220  // If we haven't actually found any digits, this is a matching failure (this221  // catches cases like "+.")222  if (!is_number) {223    return MATCHING_FAILURE;224  }225  write_float_with_length(out_str.c_str(), to_conv);226 227  return READ_OK;228}229 230} // namespace scanf_core231} // namespace LIBC_NAMESPACE_DECL232 233#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H234