diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt index ab0530d6d3861..54fa40d5363b4 100644 --- a/libc/src/stdio/scanf_core/CMakeLists.txt +++ b/libc/src/stdio/scanf_core/CMakeLists.txt @@ -79,9 +79,12 @@ add_object_library( converter.cpp string_converter.cpp int_converter.cpp + float_converter.cpp HDRS converter.h int_converter.h + string_converter.h + float_converter.h DEPENDS .reader .core_structs @@ -89,6 +92,9 @@ add_object_library( libc.src.__support.CPP.bitset libc.src.__support.CPP.string_view libc.src.__support.CPP.limits + libc.src.__support.char_vector + libc.include.errno + libc.src.errno.errno ) add_object_library( diff --git a/libc/src/stdio/scanf_core/converter.cpp b/libc/src/stdio/scanf_core/converter.cpp index fbb2c1bcd5ee5..c5a2932fb18f8 100644 --- a/libc/src/stdio/scanf_core/converter.cpp +++ b/libc/src/stdio/scanf_core/converter.cpp @@ -12,6 +12,9 @@ #include "src/stdio/scanf_core/core_structs.h" #include "src/stdio/scanf_core/reader.h" +#ifndef LLVM_LIBC_SCANF_DISABLE_FLOAT +#include "src/stdio/scanf_core/float_converter.h" +#endif // LLVM_LIBC_SCANF_DISABLE_FLOAT #include "src/stdio/scanf_core/int_converter.h" #include "src/stdio/scanf_core/string_converter.h" @@ -43,24 +46,22 @@ int convert(Reader *reader, const FormatSection &to_conv) { if (ret_val != READ_OK) return ret_val; return convert_int(reader, to_conv); - // #ifndef LLVM_LIBC_SCANF_DISABLE_FLOAT - // case 'f': - // case 'F': - // case 'e': - // case 'E': - // case 'a': - // case 'A': - // case 'g': - // case 'G': - // ret_val = raw_match(reader, " "); - // if (ret_val != READ_OK) - // return ret_val; - // return convert_float(reader, to_conv); - // #endif // LLVM_LIBC_SCANF_DISABLE_FLOAT - // #ifndef LLVM_LIBC_SCANF_DISABLE_WRITE_INT +#ifndef LLVM_LIBC_SCANF_DISABLE_FLOAT + case 'f': + case 'F': + case 'e': + case 'E': + case 'a': + case 'A': + case 'g': + case 'G': + ret_val = raw_match(reader, " "); + if (ret_val != READ_OK) + return ret_val; + return convert_float(reader, to_conv); +#endif // LLVM_LIBC_SCANF_DISABLE_FLOAT // case 'n': // return convert_write_int(reader, to_conv); - // #endif // LLVM_LIBC_SCANF_DISABLE_WRITE_INT // case 'p': // ret_val = raw_match(reader, " "); // if (ret_val != READ_OK) diff --git a/libc/src/stdio/scanf_core/core_structs.h b/libc/src/stdio/scanf_core/core_structs.h index 7f331db362023..4555595ab3bff 100644 --- a/libc/src/stdio/scanf_core/core_structs.h +++ b/libc/src/stdio/scanf_core/core_structs.h @@ -84,6 +84,7 @@ enum ErrorCodes : int { FILE_READ_ERROR = -1, FILE_STATUS_ERROR = -2, MATCHING_FAILURE = -3, + ALLOCATION_FAILURE = -4, }; } // namespace scanf_core } // namespace __llvm_libc diff --git a/libc/src/stdio/scanf_core/float_converter.cpp b/libc/src/stdio/scanf_core/float_converter.cpp new file mode 100644 index 0000000000000..1a0ce42863f35 --- /dev/null +++ b/libc/src/stdio/scanf_core/float_converter.cpp @@ -0,0 +1,255 @@ +//===-- Int type specifier converters for scanf -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/scanf_core/float_converter.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/char_vector.h" +#include "src/__support/ctype_utils.h" +#include "src/__support/str_to_float.h" +#include "src/stdio/scanf_core/core_structs.h" +#include "src/stdio/scanf_core/reader.h" + +#include + +namespace __llvm_libc { +namespace scanf_core { + +constexpr char inline to_lower(char a) { return a | 32; } + +void write_with_length(char *str, const FormatSection &to_conv) { + if ((to_conv.flags & NO_WRITE) != 0) { + return; + } + + void *output_ptr = to_conv.output_ptr; + + LengthModifier lm = to_conv.length_modifier; + switch (lm) { + case (LengthModifier::l): { + auto value = internal::strtofloatingpoint(str, nullptr); + *reinterpret_cast(output_ptr) = value; + break; + } + case (LengthModifier::L): { + auto value = internal::strtofloatingpoint(str, nullptr); + *reinterpret_cast(output_ptr) = value; + break; + } + default: { + auto value = internal::strtofloatingpoint(str, nullptr); + *reinterpret_cast(output_ptr) = value; + break; + } + } +} + +// All of the floating point conversions are the same for scanf, every name will +// accept every style. +int convert_float(Reader *reader, const FormatSection &to_conv) { + // %a/A/e/E/f/F/g/G "Matches an optionally signed floating-point number, + // infinity, or NaN, whose format is the same as expected for the subject + // sequence of the strtod function. The corresponding argument shall be a + // pointer to floating." + + CharVector out_str = CharVector(); + bool is_number = false; + + size_t max_width = cpp::numeric_limits::max(); + if (to_conv.max_width > 0) { + max_width = to_conv.max_width; + } + + char cur_char = reader->getc(); + // Handle the sign. + if (cur_char == '+' || cur_char == '-') { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + if (out_str.length() == max_width) { + return MATCHING_FAILURE; + } else { + cur_char = reader->getc(); + } + } + + static constexpr char DECIMAL_POINT = '.'; + static const char inf_string[] = "infinity"; + + // Handle inf + + if (to_lower(cur_char) == inf_string[0]) { + size_t inf_index = 0; + + for (; to_lower(cur_char) == inf_string[inf_index] && + inf_index < sizeof(inf_string) && out_str.length() < max_width; + ++inf_index) { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + cur_char = reader->getc(); + } + + if (inf_index == 3 || inf_index == sizeof(inf_string) - 1) { + write_with_length(out_str.c_str(), to_conv); + return READ_OK; + } else { + return MATCHING_FAILURE; + } + } + + static const char nan_string[] = "nan"; + + // Handle nan + if (to_lower(cur_char) == nan_string[0]) { + size_t nan_index = 0; + + for (; to_lower(cur_char) == nan_string[nan_index] && + nan_index < sizeof(nan_string) && out_str.length() < max_width; + ++nan_index) { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + cur_char = reader->getc(); + } + + if (nan_index == sizeof(nan_string) - 1) { + write_with_length(out_str.c_str(), to_conv); + return READ_OK; + } else { + return MATCHING_FAILURE; + } + } + + // Assume base of 10 by default but check if it is actually base 16. + int base = 10; + + // If the string starts with 0 it might be in hex. + if (cur_char == '0') { + is_number = true; + // Read the next character to check. + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + // If we've hit the end, then this is "0", which is valid. + if (out_str.length() == max_width) { + write_with_length(out_str.c_str(), to_conv); + return READ_OK; + } else { + cur_char = reader->getc(); + } + + // If that next character is an 'x' then this is a hexadecimal number. + if (to_lower(cur_char) == 'x') { + base = 16; + + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + // If we've hit the end here, we have "0x" which is a valid prefix to a + // floating point number, and will be evaluated to 0. + if (out_str.length() == max_width) { + write_with_length(out_str.c_str(), to_conv); + return READ_OK; + } else { + cur_char = reader->getc(); + } + } + } + + const char exponent_mark = ((base == 10) ? 'e' : 'p'); + bool after_decimal = false; + + // The format for the remaining characters at this point is DD.DDe+/-DD for + // base 10 and XX.XXp+/-DD for base 16 + + // This handles the digits before and after the decimal point, but not the + // exponent. + while (out_str.length() < max_width) { + if (internal::isalnum(cur_char) && + internal::b36_char_to_int(cur_char) < base) { + is_number = true; + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + cur_char = reader->getc(); + } else if (cur_char == DECIMAL_POINT && !after_decimal) { + after_decimal = true; + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + cur_char = reader->getc(); + } else { + break; + } + } + + // Handle the exponent, which has an exponent mark, an optional sign, and + // decimal digits. + if (to_lower(cur_char) == exponent_mark) { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + if (out_str.length() == max_width) { + // This is laid out in the standard as being a matching error (100e is not + // a valid float) but may conflict with existing implementations. + return MATCHING_FAILURE; + } else { + cur_char = reader->getc(); + } + + if (cur_char == '+' || cur_char == '-') { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + if (out_str.length() == max_width) { + return MATCHING_FAILURE; + } else { + cur_char = reader->getc(); + } + } + + // It is specified by the standard that "100er" is a matching failure since + // the longest prefix of a possibly valid floating-point number (which is + // "100e") is not a valid floating-point number. If there is an exponent + // mark then there must be a digit after it else the number is not valid. + // Some implementations will roll back two characters (to just "100") and + // accept that since the prefix is not valid, and some will interpret an + // exponent mark followed by no digits as an additional exponent of 0 + // (accepting "100e" and returning 100.0). Both of these behaviors are wrong + // by the standard, but they may be used in real code, see Hyrum's law. This + // code follows the standard, but may be incompatible due to code expecting + // these bugs. + if (!internal::isdigit(cur_char)) { + return MATCHING_FAILURE; + } + + while (internal::isdigit(cur_char) && out_str.length() < max_width) { + if (!out_str.append(cur_char)) { + return ALLOCATION_FAILURE; + } + cur_char = reader->getc(); + } + } + + // We always read one more character than will be used, so we have to put the + // last one back. + reader->ungetc(cur_char); + + // If we haven't actually found any digits, this is a matching failure (this + // catches cases like "+.") + if (!is_number) { + return MATCHING_FAILURE; + } + write_with_length(out_str.c_str(), to_conv); + + return READ_OK; +} + +} // namespace scanf_core +} // namespace __llvm_libc diff --git a/libc/src/stdio/scanf_core/float_converter.h b/libc/src/stdio/scanf_core/float_converter.h new file mode 100644 index 0000000000000..e8abe8d20bcea --- /dev/null +++ b/libc/src/stdio/scanf_core/float_converter.h @@ -0,0 +1,25 @@ +//===-- Float type specifier converter for scanf ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H +#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H + +#include "src/stdio/scanf_core/core_structs.h" +#include "src/stdio/scanf_core/reader.h" + +#include + +namespace __llvm_libc { +namespace scanf_core { + +int convert_float(Reader *reader, const FormatSection &to_conv); + +} // namespace scanf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H diff --git a/libc/src/stdio/scanf_core/int_converter.cpp b/libc/src/stdio/scanf_core/int_converter.cpp index 1087166742b6c..be88a01f942d7 100644 --- a/libc/src/stdio/scanf_core/int_converter.cpp +++ b/libc/src/stdio/scanf_core/int_converter.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "src/stdio/scanf_core/string_converter.h" +#include "src/stdio/scanf_core/int_converter.h" #include "src/__support/CPP/limits.h" #include "src/__support/ctype_utils.h" diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt index f74fa46a5e77d..e94432d069f2e 100644 --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -182,6 +182,8 @@ add_libc_unittest( sscanf_test.cpp DEPENDS libc.src.stdio.sscanf + LINK_LIBRARIES + LibcFPTestHelpers ) add_libc_unittest( diff --git a/libc/test/src/stdio/sscanf_test.cpp b/libc/test/src/stdio/sscanf_test.cpp index b3e146f1d64be..fc67593f57f0b 100644 --- a/libc/test/src/stdio/sscanf_test.cpp +++ b/libc/test/src/stdio/sscanf_test.cpp @@ -7,10 +7,14 @@ //===----------------------------------------------------------------------===// #include "src/__support/CPP/limits.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/PlatformDefs.h" + #include "src/stdio/sscanf.h" #include // For EOF +#include "utils/UnitTest/FPMatcher.h" #include "utils/UnitTest/Test.h" TEST(LlvmLibcSScanfTest, SimpleStringConv) { @@ -209,6 +213,365 @@ TEST(LlvmLibcSScanfTest, IntConvNoWriteTests) { EXPECT_EQ(result, 0); } +TEST(LlvmLibcSScanfTest, FloatConvSimple) { + int ret_val; + float result = 0; + + float inf = __llvm_libc::fputil::FPBits::inf().get_val(); + float nan = __llvm_libc::fputil::FPBits::build_nan(1); + + ret_val = __llvm_libc::sscanf("123", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 123.0); + + ret_val = __llvm_libc::sscanf("456.1", "%a", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 456.1); + + ret_val = __llvm_libc::sscanf("0x789.ap0", "%e", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0x789.ap0); + + ret_val = __llvm_libc::sscanf("0x.8", "%e", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0x0.8p0); + + ret_val = __llvm_libc::sscanf("0x8.", "%e", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0x8.0p0); + + ret_val = __llvm_libc::sscanf("+12.0e1", "%g", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 12.0e1); + + ret_val = __llvm_libc::sscanf("inf", "%F", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, inf); + + ret_val = __llvm_libc::sscanf("NaN", "%A", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, nan); + + ret_val = __llvm_libc::sscanf("-InFiNiTy", "%E", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, -inf); + + ret_val = __llvm_libc::sscanf("1e10", "%G", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 1e10); + + ret_val = __llvm_libc::sscanf(".1", "%G", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.1); + + ret_val = __llvm_libc::sscanf("1.", "%G", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 1.0); + + ret_val = __llvm_libc::sscanf("0", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("Not a float", "%f", &result); + EXPECT_EQ(ret_val, 0); +} + +TEST(LlvmLibcSScanfTest, FloatConvLengthModifier) { + int ret_val; + double d_result = 0; + long double ld_result = 0; + + double d_inf = __llvm_libc::fputil::FPBits::inf().get_val(); + long double ld_nan = __llvm_libc::fputil::FPBits::build_nan(1); + + ret_val = __llvm_libc::sscanf("123", "%lf", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(d_result, 123.0); + + ret_val = __llvm_libc::sscanf("456.1", "%La", &ld_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(ld_result, 456.1L); + + ret_val = __llvm_libc::sscanf("inf", "%le", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(d_result, d_inf); + + ret_val = __llvm_libc::sscanf("nan", "%Lg", &ld_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(ld_result, ld_nan); + + ret_val = __llvm_libc::sscanf("1e-300", "%lF", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(d_result, 1e-300); + + ret_val = __llvm_libc::sscanf("1.0e600", "%LA", &ld_result); + EXPECT_EQ(ret_val, 1); +// 1e600 may be larger than the maximum long double (if long double is double). +// In that case both of these should be evaluated as inf. +#ifdef LONG_DOUBLE_IS_DOUBLE + EXPECT_FP_EQ(ld_result, d_inf); +#else + EXPECT_FP_EQ(ld_result, 1.0e600L); +#endif +} + +TEST(LlvmLibcSScanfTest, FloatConvLongNumber) { + int ret_val; + float result = 0; + double d_result = 0; + + // 32 characters + ret_val = + __llvm_libc::sscanf("123456789012345678901234567890.0", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 123456789012345678901234567890.0f); + + // 64 characters + ret_val = __llvm_libc::sscanf( + "123456789012345678901234567890123456789012345678901234567890.000", "%la", + &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ( + d_result, + 123456789012345678901234567890123456789012345678901234567890.000); + + // 128 characters + ret_val = __llvm_libc::sscanf( + "123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890.0000000", + "%le", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ( + d_result, + 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.0000000); + + // 256 characters + ret_val = __llvm_libc::sscanf("10000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000", + "%lf", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(d_result, 1e255); + + // 288 characters + ret_val = __llvm_libc::sscanf("10000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000", + "%lf", &d_result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(d_result, 1e287); +} + +TEST(LlvmLibcSScanfTest, FloatConvComplexParsing) { + int ret_val; + float result = 0; + + float inf = __llvm_libc::fputil::FPBits::inf().get_val(); + float nan = __llvm_libc::fputil::FPBits::build_nan(1); + + ret_val = __llvm_libc::sscanf("0x1.0e3", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0x1.0e3p0); + + ret_val = __llvm_libc::sscanf("", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("+", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("+.", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-.e+10", "%a", &result); + EXPECT_EQ(ret_val, 0); + + // This is a specific example from the standard. Its behavior diverges from + // other implementations that accept "100e" as being the same as "100e0" + ret_val = __llvm_libc::sscanf("100er", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("nah", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("indirection", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("infnan", "%a", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, inf); + + ret_val = __llvm_libc::sscanf("naninf", "%a", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, nan); + + ret_val = __llvm_libc::sscanf("infinityinfinity", "%a", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, inf); + + // For %f to accept a string as representing it has to be either "inf" or + // "infinity" when it stops. It only stops when it encounters a character that + // isn't the next one in the string, so it accepts "infi" as the the longest + // prefix of a possibly valid floating-point number, but determines that it is + // not valid and returns a matching failure. This is because it can only unget + // one character so when it finds that the character after the second 'i' is + // not the next character in "infinity" it can't rewind to the point where it + // had just "inf". + ret_val = __llvm_libc::sscanf("infi", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("infinite", "%a", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-.1e1", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, -.1e1); + + ret_val = __llvm_libc::sscanf("1.2.e1", "%f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 1.2); +} + +/* +TODO: + Max width tests +*/ + +TEST(LlvmLibcSScanfTest, FloatConvMaxWidth) { + int ret_val; + float result = 0; + + float inf = __llvm_libc::fputil::FPBits::inf().get_val(); + + ret_val = __llvm_libc::sscanf("123", "%3f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 123.0); + + ret_val = __llvm_libc::sscanf("123", "%5f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 123.0); + + ret_val = __llvm_libc::sscanf("456", "%1f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 4.0); + + ret_val = __llvm_libc::sscanf("-789", "%1f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-123", "%2f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, -1.0); + + ret_val = __llvm_libc::sscanf("inf", "%2f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("nan", "%1f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-inf", "%3f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("-nan", "%3f", &result); + EXPECT_EQ(ret_val, 0); + + // If the max length were not here this would fail as discussed above, but + // since the max length limits it to the 3 it succeeds. + ret_val = __llvm_libc::sscanf("infinite", "%3f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, inf); + + ret_val = __llvm_libc::sscanf("-infinite", "%4f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, -inf); + + ret_val = __llvm_libc::sscanf("01", "%1f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("0x1", "%2f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("100e", "%4f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("100e+10", "%5f", &result); + EXPECT_EQ(ret_val, 0); + + ret_val = __llvm_libc::sscanf("100e10", "%5f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 100e1); +} + +TEST(LlvmLibcSScanfTest, FloatConvNoWrite) { + int ret_val; + float result = 0; + + ret_val = __llvm_libc::sscanf("123", "%*f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("456.1", "%*a", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("0x789.ap0", "%*e", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("+12.0e1", "%*g", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("inf", "%*F", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("NaN", "%*A", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("-InFiNiTy", "%*E", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("1e10", "%*G", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf(".1", "%*G", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("123", "%*3f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("123", "%*5f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("456", "%*1f", &result); + EXPECT_EQ(ret_val, 1); + EXPECT_FP_EQ(result, 0.0); + + ret_val = __llvm_libc::sscanf("Not a float", "%*f", &result); + EXPECT_EQ(ret_val, 0); +} + TEST(LlvmLibcSScanfTest, CombinedConv) { int ret_val; int result = 0;