[libc-commits] [libc] [libc] Implement strfromd() and strfroml() (PR #86113)

Vinayak Dev via libc-commits libc-commits at lists.llvm.org
Fri Mar 22 09:27:31 PDT 2024


https://github.com/vinayakdsci updated https://github.com/llvm/llvm-project/pull/86113

>From d4781891122be7d67ac36c27fa304909a9451b15 Mon Sep 17 00:00:00 2001
From: Vinayak Dev <vinayakdev.sci at gmail.com>
Date: Thu, 21 Mar 2024 16:46:46 +0530
Subject: [PATCH] [libc] Implement strfromd() and strfroml()

---
 libc/config/linux/x86_64/entrypoints.txt |   2 +
 libc/spec/stdc.td                        |   2 +
 libc/src/stdlib/CMakeLists.txt           |  20 ++
 libc/src/stdlib/strfromd.cpp             |  42 +++
 libc/src/stdlib/strfromd.h               |  21 ++
 libc/src/stdlib/strfromf.h               |   2 +-
 libc/src/stdlib/strfroml.cpp             |  44 +++
 libc/src/stdlib/strfroml.h               |  22 ++
 libc/test/src/stdlib/CMakeLists.txt      |  31 ++
 libc/test/src/stdlib/StrfromTest.h       | 434 +++++++++++++++++++++++
 libc/test/src/stdlib/strfromd_test.cpp   |  13 +
 libc/test/src/stdlib/strfromf_test.cpp   |  98 +----
 libc/test/src/stdlib/strfroml_test.cpp   |  13 +
 13 files changed, 647 insertions(+), 97 deletions(-)
 create mode 100644 libc/src/stdlib/strfromd.cpp
 create mode 100644 libc/src/stdlib/strfromd.h
 create mode 100644 libc/src/stdlib/strfroml.cpp
 create mode 100644 libc/src/stdlib/strfroml.h
 create mode 100644 libc/test/src/stdlib/StrfromTest.h
 create mode 100644 libc/test/src/stdlib/strfromd_test.cpp
 create mode 100644 libc/test/src/stdlib/strfroml_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 8e1ab5cd65f005..8b1cd3fb105271 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -180,7 +180,9 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.qsort_r
     libc.src.stdlib.rand
     libc.src.stdlib.srand
+    libc.src.stdlib.strfromd
     libc.src.stdlib.strfromf
+    libc.src.stdlib.strfroml
     libc.src.stdlib.strtod
     libc.src.stdlib.strtof
     libc.src.stdlib.strtol
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 76010a4b4533a7..43a687d392936c 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -961,7 +961,9 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"rand", RetValSpec<IntType>, [ArgSpec<VoidType>]>,
           FunctionSpec<"srand", RetValSpec<VoidType>, [ArgSpec<UnsignedIntType>]>,
 
+          FunctionSpec<"strfromd", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<DoubleType>]>,
           FunctionSpec<"strfromf", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<FloatType>]>,
+          FunctionSpec<"strfroml", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<LongDoubleType>]>,
 
           FunctionSpec<"strtof", RetValSpec<FloatType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
           FunctionSpec<"strtod", RetValSpec<DoubleType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 22f7f990fb08a8..a2b16e73f35321 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -62,6 +62,26 @@ add_entrypoint_object(
     .str_from_util
 )
 
+add_entrypoint_object(
+  strfromd
+  SRCS
+    strfromd.cpp
+  HDRS
+    strfromd.h
+  DEPENDS
+    .str_from_util
+)
+
+add_entrypoint_object(
+  strfroml
+  SRCS
+    strfroml.cpp
+  HDRS
+    strfroml.h
+  DEPENDS
+    .str_from_util
+)
+
 add_header_library(
   str_from_util
   HDRS
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
new file mode 100644
index 00000000000000..1d02a7ad112442
--- /dev/null
+++ b/libc/src/stdlib/strfromd.cpp
@@ -0,0 +1,42 @@
+//===-- Implementation of strfromd ------------------------------*- 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/stdlib/strfromd.h"
+#include "src/stdlib/str_from_util.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, strfromd,
+                   (char *__restrict s, size_t n, const char *__restrict format,
+                    double fp)) {
+  LIBC_ASSERT(s != nullptr);
+
+  printf_core::FormatSection section =
+      internal::parse_format_string(format, fp);
+  printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
+  printf_core::Writer writer(&wb);
+
+  int result = 0;
+  if (section.has_conv)
+    result = internal::strfromfloat_convert<double>(&writer, section);
+  else
+    result = writer.write(section.raw_string);
+
+  if (result < 0)
+    return result;
+
+  if (n > 0)
+    wb.buff[wb.buff_cur] = '\0';
+
+  return writer.get_chars_written();
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/stdlib/strfromd.h b/libc/src/stdlib/strfromd.h
new file mode 100644
index 00000000000000..d2c3fefb6300dc
--- /dev/null
+++ b/libc/src/stdlib/strfromd.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for strfromd ------------------------*- 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_STDLIB_STRFROMD_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROMD_H
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+int strfromd(char *__restrict s, size_t n, const char *__restrict format,
+             double fp);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROMD_H
diff --git a/libc/src/stdlib/strfromf.h b/libc/src/stdlib/strfromf.h
index b551a58af05a3e..492c2c33cf080b 100644
--- a/libc/src/stdlib/strfromf.h
+++ b/libc/src/stdlib/strfromf.h
@@ -18,4 +18,4 @@ int strfromf(char *__restrict s, size_t n, const char *__restrict format,
 
 } // namespace LIBC_NAMESPACE
 
-#endif // LLVM_LIBC_SRC_STDLIB_STRTOF_H
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROMF_H
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
new file mode 100644
index 00000000000000..40459651353077
--- /dev/null
+++ b/libc/src/stdlib/strfroml.cpp
@@ -0,0 +1,44 @@
+//===-- Implementation of strfroml ------------------------------*- 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/stdlib/strfroml.h"
+#include "src/stdlib/str_from_util.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, strfroml,
+                   (char *__restrict s, size_t n, const char *__restrict format,
+                    long double fp)) {
+  LIBC_ASSERT(s != nullptr);
+
+  printf_core::FormatSection section =
+      internal::parse_format_string(format, fp);
+  section.length_modifier = printf_core::LengthModifier::L;
+
+  printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
+  printf_core::Writer writer(&wb);
+
+  int result = 0;
+  if (section.has_conv)
+    result = internal::strfromfloat_convert<long double>(&writer, section);
+  else
+    result = writer.write(section.raw_string);
+
+  if (result < 0)
+    return result;
+
+  if (n > 0)
+    wb.buff[wb.buff_cur] = '\0';
+
+  return writer.get_chars_written();
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/stdlib/strfroml.h b/libc/src/stdlib/strfroml.h
new file mode 100644
index 00000000000000..05f939e187b885
--- /dev/null
+++ b/libc/src/stdlib/strfroml.h
@@ -0,0 +1,22 @@
+
+//===-- Implementation header for strfroml ------------------------*- 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_STDLIB_STRFROML_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROML_H
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+int strfroml(char *__restrict s, size_t n, const char *__restrict format,
+             long double fp);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROML_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index cb42bc56f51c5f..3ccc1cde91934c 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -168,6 +168,14 @@ add_libc_test(
     .strtol_test_support
 )
 
+add_header_library(
+  strfrom_test_support
+  HDRS
+  StrfromTest.h
+  DEPENDS
+  libc.src.__support.CPP.type_traits
+)
+
 add_libc_test(
   strfromf_test
   SUITE
@@ -175,9 +183,32 @@ add_libc_test(
   SRCS
     strfromf_test.cpp
   DEPENDS
+    .strfrom_test_support
     libc.src.stdlib.strfromf
 )
 
+add_libc_test(
+  strfromd_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    strfromd_test.cpp
+  DEPENDS
+    .strfrom_test_support
+    libc.src.stdlib.strfromd
+)
+
+add_libc_test(
+  strfroml_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    strfroml_test.cpp
+  DEPENDS
+    .strfrom_test_support
+    libc.src.stdlib.strfroml
+)
+
 add_libc_test(
   abs_test
   SUITE
diff --git a/libc/test/src/stdlib/StrfromTest.h b/libc/test/src/stdlib/StrfromTest.h
new file mode 100644
index 00000000000000..57d1b089553302
--- /dev/null
+++ b/libc/test/src/stdlib/StrfromTest.h
@@ -0,0 +1,434 @@
+//===-- A template class for testing strfrom functions ----------*- 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/__support/CPP/type_traits.h"
+#include "test/UnitTest/Test.h"
+
+#define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str)             \
+  EXPECT_EQ(actual_written, static_cast<int>(sizeof(expected_str) - 1));       \
+  EXPECT_STREQ(actual_str, expected_str);
+
+template <typename InputT>
+class StrfromTest : public LIBC_NAMESPACE::testing::Test {
+
+  static const bool is_single_prec =
+      LIBC_NAMESPACE::cpp::is_same<InputT, float>::value;
+  static const bool is_double_prec =
+      LIBC_NAMESPACE::cpp::is_same<InputT, double>::value;
+
+  using FunctionT = int (*)(char *, size_t, const char *, InputT fp);
+
+public:
+  void floatDecimalFormat(FunctionT func) {
+    if (is_single_prec)
+      floatDecimalSinglePrec(func);
+    else if (is_double_prec)
+      floatDecimalDoublePrec(func);
+    else
+      floatDecimalLongDoublePrec(func);
+  }
+
+  void floatHexExpFormat(FunctionT func) {
+    if (is_single_prec)
+      floatHexExpSinglePrec(func);
+    else if (is_double_prec)
+      floatHexExpDoublePrec(func);
+    else
+      floatHexExpLongDoublePrec(func);
+  }
+
+  void floatDecimalExpFormat(FunctionT func) {
+    if (is_single_prec)
+      floatDecimalExpSinglePrec(func);
+    else if (is_double_prec)
+      floatDecimalExpDoublePrec(func);
+    else
+      floatDecimalExpLongDoublePrec(func);
+  }
+
+  void floatDecimalAutoFormat(FunctionT func) {
+    if (is_single_prec)
+      floatDecimalAutoSinglePrec(func);
+    else if (is_double_prec)
+      floatDecimalAutoDoublePrec(func);
+    else
+      floatDecimalAutoLongDoublePrec(func);
+  }
+
+  void improperFormatString(FunctionT func) {
+    char buff[100];
+    int written;
+    const bool is_long_double = !is_single_prec && !is_double_prec;
+
+    written = func(buff, 37, "A simple string with no conversions.", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "A simple string with no conversions.");
+
+    written =
+        func(buff, 37,
+             "%A simple string with one conversion, should overwrite.", 1.0);
+    ASSERT_STREQ_LEN(written, buff, is_long_double ? "0X8P-3" : "0X1P+0");
+
+    written = func(buff, 74,
+                   "A simple string with one conversion in %A "
+                   "between, writes string as it is",
+                   1.0);
+    ASSERT_STREQ_LEN(written, buff,
+                     "A simple string with one conversion in %A between, "
+                     "writes string as it is");
+
+    written = func(buff, 36, "A simple string with one conversion", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "A simple string with one conversion");
+
+    written = func(buff, 20, "%1f", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "%1f");
+  }
+
+  void insufficentBufsize(FunctionT func) {
+    char buff[20];
+    int written;
+
+    written = func(buff, 5, "%f", 1234567890.0);
+    EXPECT_EQ(written, 17);
+    ASSERT_STREQ(buff, "1234");
+
+    written = func(buff, 5, "%.5f", 1.05);
+    EXPECT_EQ(written, 7);
+    ASSERT_STREQ(buff, "1.05");
+
+    written = func(buff, 0, "%g", 1.0);
+    EXPECT_EQ(written, 1);
+  }
+
+  void floatDecimalSinglePrec(FunctionT func) {
+    char buff[70];
+    int written;
+
+    written = func(buff, 16, "%f", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+    written = func(buff, 20, "%f", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "1234567936.000000");
+
+    written = func(buff, 67, "%.3f", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "1.000");
+  }
+
+  void floatDecimalDoublePrec(FunctionT func) {
+    char buff[500];
+    int written;
+
+    written = func(buff, 99, "%f", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+    written = func(buff, 99, "%F", -1.0);
+    ASSERT_STREQ_LEN(written, buff, "-1.000000");
+
+    written = func(buff, 99, "%f", -1.234567);
+    ASSERT_STREQ_LEN(written, buff, "-1.234567");
+
+    written = func(buff, 99, "%f", 0.0);
+    ASSERT_STREQ_LEN(written, buff, "0.000000");
+
+    written = func(buff, 99, "%f", 1.5);
+    ASSERT_STREQ_LEN(written, buff, "1.500000");
+
+    written = func(buff, 499, "%f", 1e300);
+    ASSERT_STREQ_LEN(written, buff,
+                     "100000000000000005250476025520442024870446858110815915491"
+                     "585411551180245"
+                     "798890819578637137508044786404370444383288387817694252323"
+                     "536043057564479"
+                     "218478670698284838720092657580373783023379478809005936895"
+                     "323497079994508"
+                     "111903896764088007465274278014249457925878882005684283811"
+                     "566947219638686"
+                     "5459400540160.000000");
+
+    written = func(buff, 99, "%f", 0.1);
+    ASSERT_STREQ_LEN(written, buff, "0.100000");
+
+    written = func(buff, 99, "%f", 1234567890123456789.0);
+    ASSERT_STREQ_LEN(written, buff, "1234567890123456768.000000");
+
+    written = func(buff, 99, "%f", 9999999999999.99);
+    ASSERT_STREQ_LEN(written, buff, "9999999999999.990234");
+
+    written = func(buff, 99, "%f", 0.1);
+    ASSERT_STREQ_LEN(written, buff, "0.100000");
+
+    written = func(buff, 99, "%f", 1234567890123456789.0);
+    ASSERT_STREQ_LEN(written, buff, "1234567890123456768.000000");
+
+    written = func(buff, 99, "%f", 9999999999999.99);
+    ASSERT_STREQ_LEN(written, buff, "9999999999999.990234");
+
+    // Precision Tests
+    written = func(buff, 100, "%.2f", 9999999999999.99);
+    ASSERT_STREQ_LEN(written, buff, "9999999999999.99");
+
+    written = func(buff, 100, "%.1f", 9999999999999.99);
+    ASSERT_STREQ_LEN(written, buff, "10000000000000.0");
+
+    written = func(buff, 100, "%.5f", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.25000");
+
+    written = func(buff, 100, "%.0f", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1");
+
+    written = func(buff, 100, "%.20f", 1.234e-10);
+    ASSERT_STREQ_LEN(written, buff, "0.00000000012340000000");
+  }
+
+  void floatDecimalLongDoublePrec(FunctionT func) {
+    char buff[45];
+    int written;
+
+    written = func(buff, 40, "%f", 1.0L);
+    ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+    written = func(buff, 10, "%.f", -2.5L);
+    ASSERT_STREQ_LEN(written, buff, "-2");
+  }
+
+  void floatHexExpSinglePrec(FunctionT func) {
+    char buff[25];
+    int written;
+
+    written = func(buff, 0, "%a", 1234567890.0);
+    EXPECT_EQ(written, 14);
+
+    written = func(buff, 20, "%a", 1234567890.0);
+    EXPECT_EQ(written, 14);
+    ASSERT_STREQ(buff, "0x1.26580cp+30");
+
+    written = func(buff, 20, "%A", 1234567890.0);
+    EXPECT_EQ(written, 14);
+    ASSERT_STREQ(buff, "0X1.26580CP+30");
+  }
+
+  void floatHexExpDoublePrec(FunctionT func) {
+    char buff[60];
+    int written;
+
+    written = func(buff, 10, "%a", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "0x1p+0");
+
+    written = func(buff, 10, "%A", -1.0);
+    ASSERT_STREQ_LEN(written, buff, "-0X1P+0");
+
+    written = func(buff, 30, "%a", -0x1.abcdef12345p0);
+    ASSERT_STREQ_LEN(written, buff, "-0x1.abcdef12345p+0");
+
+    written = func(buff, 50, "%A", 0x1.abcdef12345p0);
+    ASSERT_STREQ_LEN(written, buff, "0X1.ABCDEF12345P+0");
+
+    written = func(buff, 10, "%a", 0.0);
+    ASSERT_STREQ_LEN(written, buff, "0x0p+0");
+
+    written = func(buff, 40, "%a", 1.0e100);
+    ASSERT_STREQ_LEN(written, buff, "0x1.249ad2594c37dp+332");
+
+    written = func(buff, 30, "%a", 0.1);
+    ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
+  }
+
+  void floatHexExpLongDoublePrec(FunctionT func) {
+    char buff[55];
+    int written;
+
+    written = func(buff, 50, "%a", 0.1L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    ASSERT_STREQ_LEN(written, buff, "0xc.ccccccccccccccdp-7");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+    ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+    ASSERT_STREQ_LEN(written, buff, "0x1.999999999999999999999999999ap-4");
+#endif
+
+    written = func(buff, 20, "%.1a", 0.1L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    ASSERT_STREQ_LEN(written, buff, "0xc.dp-7");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+    ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+    ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
+#endif
+
+    written = func(buff, 50, "%a", 1.0e1000L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    ASSERT_STREQ_LEN(written, buff, "0xf.38db1f9dd3dac05p+3318");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+    ASSERT_STREQ_LEN(written, buff, "inf");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+    ASSERT_STREQ_LEN(written, buff, "0x1.e71b63f3ba7b580af1a52d2a7379p+3321");
+#endif
+
+    written = func(buff, 50, "%a", 1.0e-1000L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    ASSERT_STREQ_LEN(written, buff, "0x8.68a9188a89e1467p-3325");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+    ASSERT_STREQ_LEN(written, buff, "0x0p+0");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+    ASSERT_STREQ_LEN(written, buff, "0x1.0d152311513c28ce202627c06ec2p-3322");
+#endif
+
+    written = func(buff, 50, "%.1a", 0xf.fffffffffffffffp16380L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    ASSERT_STREQ_LEN(written, buff, "0x1.0p+16384");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+    ASSERT_STREQ_LEN(written, buff, "inf");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+    ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383");
+#endif
+  }
+
+  void floatDecimalExpSinglePrec(FunctionT func) {
+    char buff[25];
+    int written;
+
+    written = func(buff, 20, "%.9e", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "1.234567936e+09");
+
+    written = func(buff, 20, "%.9E", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "1.234567936E+09");
+  }
+
+  void floatDecimalExpDoublePrec(FunctionT func) {
+    char buff[101];
+    int written;
+
+    written = func(buff, 100, "%e", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "1.000000e+00");
+
+    written = func(buff, 100, "%E", -1.0);
+    ASSERT_STREQ_LEN(written, buff, "-1.000000E+00");
+
+    written = func(buff, 100, "%e", -1.234567);
+    ASSERT_STREQ_LEN(written, buff, "-1.234567e+00");
+
+    written = func(buff, 100, "%e", 0.0);
+    ASSERT_STREQ_LEN(written, buff, "0.000000e+00");
+
+    written = func(buff, 100, "%e", 1.5);
+    ASSERT_STREQ_LEN(written, buff, "1.500000e+00");
+
+    written = func(buff, 100, "%e", 1e300);
+    ASSERT_STREQ_LEN(written, buff, "1.000000e+300");
+
+    written = func(buff, 100, "%e", 1234567890123456789.0);
+    ASSERT_STREQ_LEN(written, buff, "1.234568e+18");
+
+    // Precision Tests
+    written = func(buff, 100, "%.1e", 1.0);
+    ASSERT_STREQ_LEN(written, buff, "1.0e+00");
+
+    written = func(buff, 100, "%.1e", 1.99);
+    ASSERT_STREQ_LEN(written, buff, "2.0e+00");
+
+    written = func(buff, 100, "%.1e", 9.99);
+    ASSERT_STREQ_LEN(written, buff, "1.0e+01");
+  }
+
+  void floatDecimalExpLongDoublePrec(FunctionT func) {
+    char buff[100];
+    int written;
+
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    written = func(buff, 90, "%.9e", 1000000000500000000.1L);
+    ASSERT_STREQ_LEN(written, buff, "1.000000001e+18");
+
+    written = func(buff, 90, "%.9e", 1000000000500000000.0L);
+    ASSERT_STREQ_LEN(written, buff, "1.000000000e+18");
+
+    written = func(buff, 90, "%e", 0xf.fffffffffffffffp+16380L);
+    ASSERT_STREQ_LEN(written, buff, "1.189731e+4932");
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+  }
+
+  void floatDecimalAutoSinglePrec(FunctionT func) {
+    char buff[25];
+    int written;
+
+    written = func(buff, 20, "%.9g", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "1.23456794e+09");
+
+    written = func(buff, 20, "%.9G", 1234567890.0);
+    ASSERT_STREQ_LEN(written, buff, "1.23456794E+09");
+  }
+
+  void floatDecimalAutoDoublePrec(FunctionT func) {
+    char buff[120];
+    int written;
+
+    written = func(buff, 100, "%g", 1234567890123456789.0);
+    ASSERT_STREQ_LEN(written, buff, "1.23457e+18");
+
+    written = func(buff, 100, "%g", 9999990000000.00);
+    ASSERT_STREQ_LEN(written, buff, "9.99999e+12");
+
+    written = func(buff, 100, "%g", 9999999000000.00);
+    ASSERT_STREQ_LEN(written, buff, "1e+13");
+
+    written = func(buff, 100, "%g", 0xa.aaaaaaaaaaaaaabp-7);
+    ASSERT_STREQ_LEN(written, buff, "0.0833333");
+
+    written = func(buff, 100, "%g", 0.00001);
+    ASSERT_STREQ_LEN(written, buff, "1e-05");
+
+    // Precision Tests
+    written = func(buff, 100, "%.0g", 0.0);
+    ASSERT_STREQ_LEN(written, buff, "0");
+
+    written = func(buff, 100, "%.2g", 0.1);
+    ASSERT_STREQ_LEN(written, buff, "0.1");
+
+    written = func(buff, 100, "%.2g", 1.09);
+    ASSERT_STREQ_LEN(written, buff, "1.1");
+
+    written = func(buff, 100, "%.15g", 22.25);
+    ASSERT_STREQ_LEN(written, buff, "22.25");
+
+    written = func(buff, 100, "%.20g", 1.234e-10);
+    ASSERT_STREQ_LEN(written, buff, "1.2340000000000000814e-10");
+  }
+
+  void floatDecimalAutoLongDoublePrec(FunctionT func) {
+    char buff[100];
+    int written;
+
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+    written = func(buff, 99, "%g", 0xf.fffffffffffffffp+16380L);
+    ASSERT_STREQ_LEN(written, buff, "1.18973e+4932");
+
+    written = func(buff, 99, "%g", 0xa.aaaaaaaaaaaaaabp-7L);
+    ASSERT_STREQ_LEN(written, buff, "0.0833333");
+
+    written = func(buff, 99, "%g", 9.99999999999e-100L);
+    ASSERT_STREQ_LEN(written, buff, "1e-99");
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+  }
+};
+
+#define STRFROM_TEST(InputType, name, func)                                    \
+  using LlvmLibc##name##Test = StrfromTest<InputType>;                         \
+  TEST_F(LlvmLibc##name##Test, FloatDecimalFormat) {                           \
+    floatDecimalFormat(func);                                                  \
+  }                                                                            \
+  TEST_F(LlvmLibc##name##Test, FloatHexExpFormat) { floatHexExpFormat(func); } \
+  TEST_F(LlvmLibc##name##Test, FloatDecimalAutoFormat) {                       \
+    floatDecimalAutoFormat(func);                                              \
+  }                                                                            \
+  TEST_F(LlvmLibc##name##Test, FloatDecimalExpFormat) {                        \
+    floatDecimalExpFormat(func);                                               \
+  }                                                                            \
+  TEST_F(LlvmLibc##name##Test, ImproperFormatString) {                         \
+    improperFormatString(func);                                                \
+  }                                                                            \
+  TEST_F(LlvmLibc##name##Test, InsufficientBufferSize) {                       \
+    insufficentBufsize(func);                                                  \
+  }
diff --git a/libc/test/src/stdlib/strfromd_test.cpp b/libc/test/src/stdlib/strfromd_test.cpp
new file mode 100644
index 00000000000000..55724d7e902b5c
--- /dev/null
+++ b/libc/test/src/stdlib/strfromd_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for strfromd --------------------------------------------===//
+//
+// 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 "StrfromTest.h"
+#include "src/stdlib/strfromd.h"
+#include "test/UnitTest/Test.h"
+
+STRFROM_TEST(double, Strfromd, LIBC_NAMESPACE::strfromd)
diff --git a/libc/test/src/stdlib/strfromf_test.cpp b/libc/test/src/stdlib/strfromf_test.cpp
index c5489f5f3af2ac..8b987fd434ac54 100644
--- a/libc/test/src/stdlib/strfromf_test.cpp
+++ b/libc/test/src/stdlib/strfromf_test.cpp
@@ -6,102 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "StrfromTest.h"
 #include "src/stdlib/strfromf.h"
 #include "test/UnitTest/Test.h"
 
-TEST(LlvmLibcStrfromfTest, DecimalFloatFormat) {
-  char buff[100];
-  int written;
-
-  written = LIBC_NAMESPACE::strfromf(buff, 16, "%f", 1.0);
-  EXPECT_EQ(written, 8);
-  ASSERT_STREQ(buff, "1.000000");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%f", 1234567890.0);
-  EXPECT_EQ(written, 17);
-  ASSERT_STREQ(buff, "1234567936.000000");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 5, "%f", 1234567890.0);
-  EXPECT_EQ(written, 17);
-  ASSERT_STREQ(buff, "1234");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 67, "%.3f", 1.0);
-  EXPECT_EQ(written, 5);
-  ASSERT_STREQ(buff, "1.000");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%1f", 1234567890.0);
-  EXPECT_EQ(written, 3);
-  ASSERT_STREQ(buff, "%1f");
-}
-
-TEST(LlvmLibcStrfromfTest, HexExpFloatFormat) {
-  char buff[100];
-  int written;
-
-  written = LIBC_NAMESPACE::strfromf(buff, 0, "%a", 1234567890.0);
-  EXPECT_EQ(written, 14);
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%a", 1234567890.0);
-  EXPECT_EQ(written, 14);
-  ASSERT_STREQ(buff, "0x1.26580cp+30");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%A", 1234567890.0);
-  EXPECT_EQ(written, 14);
-  ASSERT_STREQ(buff, "0X1.26580CP+30");
-}
-
-TEST(LlvmLibcStrfromfTest, DecimalExpFloatFormat) {
-  char buff[100];
-  int written;
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9e", 1234567890.0);
-  EXPECT_EQ(written, 15);
-  ASSERT_STREQ(buff, "1.234567936e+09");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9E", 1234567890.0);
-  EXPECT_EQ(written, 15);
-  ASSERT_STREQ(buff, "1.234567936E+09");
-}
-
-TEST(LlvmLibcStrfromfTest, AutoDecimalFloatFormat) {
-  char buff[100];
-  int written;
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9g", 1234567890.0);
-  EXPECT_EQ(written, 14);
-  ASSERT_STREQ(buff, "1.23456794e+09");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9G", 1234567890.0);
-  EXPECT_EQ(written, 14);
-  ASSERT_STREQ(buff, "1.23456794E+09");
-
-  written = LIBC_NAMESPACE::strfromf(buff, 0, "%G", 1.0);
-  EXPECT_EQ(written, 1);
-}
-
-TEST(LlvmLibcStrfromfTest, ImproperFormatString) {
-
-  char buff[100];
-  int retval;
-  retval = LIBC_NAMESPACE::strfromf(
-      buff, 37, "A simple string with no conversions.", 1.0);
-  EXPECT_EQ(retval, 36);
-  ASSERT_STREQ(buff, "A simple string with no conversions.");
-
-  retval = LIBC_NAMESPACE::strfromf(
-      buff, 37, "%A simple string with one conversion, should overwrite.", 1.0);
-  EXPECT_EQ(retval, 6);
-  ASSERT_STREQ(buff, "0X1P+0");
-
-  retval = LIBC_NAMESPACE::strfromf(buff, 74,
-                                    "A simple string with one conversion in %A "
-                                    "between, writes string as it is",
-                                    1.0);
-  EXPECT_EQ(retval, 73);
-  ASSERT_STREQ(buff, "A simple string with one conversion in %A between, "
-                     "writes string as it is");
-
-  retval = LIBC_NAMESPACE::strfromf(buff, 36,
-                                    "A simple string with one conversion", 1.0);
-  EXPECT_EQ(retval, 35);
-  ASSERT_STREQ(buff, "A simple string with one conversion");
-}
+STRFROM_TEST(float, StrFromf, LIBC_NAMESPACE::strfromf)
diff --git a/libc/test/src/stdlib/strfroml_test.cpp b/libc/test/src/stdlib/strfroml_test.cpp
new file mode 100644
index 00000000000000..cf472a39a5bf88
--- /dev/null
+++ b/libc/test/src/stdlib/strfroml_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for strfroml --------------------------------------------===//
+//
+// 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 "StrfromTest.h"
+#include "src/stdlib/strfroml.h"
+#include "test/UnitTest/Test.h"
+
+STRFROM_TEST(long double, Strfroml, LIBC_NAMESPACE::strfroml)



More information about the libc-commits mailing list