Skip to content

[libc]: Implement strfromf() and shared utilities #85438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

vinayakdsci
Copy link
Contributor

@vinayakdsci vinayakdsci commented Mar 15, 2024

Fixes #84244.

Implements the function strfromf() introduced in C23, and adds shared utilities for implementation of other strfrom*() functions, including strfromd() and strfroml().

Copy link

github-actions bot commented Mar 15, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@vinayakdsci vinayakdsci force-pushed the libc-strfromf-float-single-prec branch from d4046e8 to 82fa77d Compare March 15, 2024 17:48
Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a good patch with some things that need to be fixed.

(char *__restrict s, size_t n, const char *__restrict format,
float fp)) {

if (!s)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change this to LIBC_ASSERT(s != nullptr)

Copy link
Contributor Author

@vinayakdsci vinayakdsci Mar 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK!


namespace LIBC_NAMESPACE::internal {

template <typename T> struct type_of {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since strfromf is only used for floating point types, we don't need this extra machinery. You can just put using storage_type = typename fputil::FPBits<T>::StorageType; inside the function.

Copy link
Contributor Author

@vinayakdsci vinayakdsci Mar 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


section.conv_name = format[cur_pos];
switch (format[cur_pos]) {
case '%':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't provide the %% conversion here. It's not defined for this function, and the result isn't intuitive. Here's the doc describing how to deal with this sort of undefined behavior: https://libc.llvm.org/dev/undefined_behavior.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think the best solution would be to remove this case and simply treat it as another instance of invalid input.

return writer->write("%");
case 'f':
case 'F':
return convert_float_decimal(writer, section);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what the problem you're running into is: convert_float_decimal selects the type based on the FormatSection it's passed (see: https://github.com/llvm/llvm-project/blob/main/libc/src/stdio/printf_core/float_dec_converter.h#L1112)

The easiest way to solve this problem would be to copy the code to check for inf/nan and then call convert_float_decimal_typed<T> (and similar below)

At some point those functions should be refactored to have one write_if_inf_or_nan function that they all call, but you don't have to do that if you don't want to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no convert_float_hex_exp_typed() counterpart of hex conversion. Thus this is working perfectly for the decimal conversions, but not for hex conversions. So as a temporary measure(in the hope that we will be able to arrive at a better solution), I have added a check in the parsing function that converts the float if it is single precision, specifically for hex conversions.

return section;
}

int convert(const printf_core::FormatSection &section,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to not have two functions named the same thing. The function in converter.h is already named convert, so I'd recommend naming this strfromfloat_convert. Also the order of the arguments should match the existing convert function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, understood. I will rename this too.

@vinayakdsci vinayakdsci force-pushed the libc-strfromf-float-single-prec branch from 82fa77d to 4081088 Compare March 16, 2024 11:12

bool t_is_single_prec_type = cpp::is_same<T, float>::value;
if (t_is_single_prec_type)
new_fp = (double)fp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment explaining why this is necessary

HDRS
str_from_util.h
DEPENDS
libc.src.stdio.printf_core.converter_atlas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be libc.src.stdio.printf_core.converter, converter_atlas doesn't have its own target.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, understood.

EXPECT_EQ(written, 17);
ASSERT_STREQ(buff, "1234567936.000000");

written = LIBC_NAMESPACE::strfromf(buff, 5, "%f", "1234567890.0");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to remove the quotes around this number.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am really sorry, I don't know how I didn't notice this. Thanks for catching this!

@vinayakdsci vinayakdsci force-pushed the libc-strfromf-float-single-prec branch from 4081088 to 453695d Compare March 19, 2024 10:09
if (t_is_single_prec_type)
section.conv_val_raw = cpp::bit_cast<storage_type<double>>(new_fp);
else
section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're just passing the format section to convert_float_hex_exp, then you'll need to set the length modifier to L for long doubles.

Copy link
Contributor Author

@vinayakdsci vinayakdsci Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but wouldn't I want to do that in strfroml.cpp? I can parse the format string, get the section, and then set the length modifier to L in strfroml.cpp itself. Otherwise we would require a check for all types in the utils file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, that would also work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok then, should I change this PR from draft to ready for review?
Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@vinayakdsci
Copy link
Contributor Author

@michaelrj-google Should I implement stfromd()and strfroml() within this PR only, or would it be better to do so in subsequent PRs?

@michaelrj-google
Copy link
Contributor

Given how simple they should be from this base I would say implementing them in this PR is fine, but if you want to leave them for a followup patch that's also fine.

@vinayakdsci
Copy link
Contributor Author

Given how simple they should be from this base I would say implementing them in this PR is fine, but if you want to leave them for a followup patch that's also fine.

OK then, I will cover them up in a follow-up patch if you don't mind, it would be easier for me to implement them cleanly that way. Thanks a lot!

@vinayakdsci vinayakdsci marked this pull request as ready for review March 20, 2024 17:23
@llvmbot llvmbot added the libc label Mar 20, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 20, 2024

@llvm/pr-subscribers-libc

Author: Vinayak Dev (vinayakdsci)

Changes

Fixes #84244.

Still a Work-In-Progress


Full diff: https://github.com/llvm/llvm-project/pull/85438.diff

8 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/spec/stdc.td (+2)
  • (modified) libc/src/stdlib/CMakeLists.txt (+22)
  • (added) libc/src/stdlib/str_from_util.h (+138)
  • (added) libc/src/stdlib/strfromf.cpp (+42)
  • (added) libc/src/stdlib/strfromf.h (+21)
  • (modified) libc/test/src/stdlib/CMakeLists.txt (+10-1)
  • (added) libc/test/src/stdlib/strfromf_test.cpp (+107)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index f81d334e9e788d..73f2b2ea780887 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -180,6 +180,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdlib.qsort_r
     libc.src.stdlib.rand
     libc.src.stdlib.srand
+    libc.src.stdlib.strfromf
     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 84d28cc3350304..920036adfed5c1 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -956,6 +956,8 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"rand", RetValSpec<IntType>, [ArgSpec<VoidType>]>,
           FunctionSpec<"srand", RetValSpec<VoidType>, [ArgSpec<UnsignedIntType>]>,
 
+          FunctionSpec<"strfromf", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<FloatType>]>,
+
           FunctionSpec<"strtof", RetValSpec<FloatType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
           FunctionSpec<"strtod", RetValSpec<DoubleType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
           FunctionSpec<"strtold", RetValSpec<LongDoubleType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index bd0bcffe0045d1..22f7f990fb08a8 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -52,6 +52,28 @@ add_entrypoint_object(
     libc.config.linux.app_h
 )
 
+add_entrypoint_object(
+  strfromf
+  SRCS
+    strfromf.cpp
+  HDRS
+    strfromf.h
+  DEPENDS
+    .str_from_util
+)
+
+add_header_library(
+  str_from_util
+  HDRS
+    str_from_util.h
+  DEPENDS
+    libc.src.stdio.printf_core.converter
+    libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.writer
+    libc.src.__support.str_to_integer
+    libc.src.__support.CPP.type_traits
+)
+
 add_entrypoint_object(
   strtof
   SRCS
diff --git a/libc/src/stdlib/str_from_util.h b/libc/src/stdlib/str_from_util.h
new file mode 100644
index 00000000000000..c4c1c0a0ba4ef0
--- /dev/null
+++ b/libc/src/stdlib/str_from_util.h
@@ -0,0 +1,138 @@
+//===-- Implementation header for strfromx() utilitites -------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// According to the C23 standard, any input character sequences except a
+// precision specifier and the usual floating point formats, namely
+// %{a,A,e,E,f,F,g,G}, are not allowed and any code that does otherwise results
+// in undefined behaviour(including use of a '%%' conversion specifier); which
+// in this case is that the buffer string is simply populated with the format
+// string. The case of the input being NULL should be handled in the calling
+// function (strfromf, strfromd, strfroml) itself.
+
+#ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
+
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/str_to_integer.h"
+#include "src/stdio/printf_core/converter_atlas.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE::internal {
+
+template <typename T>
+using storage_type = typename fputil::FPBits<T>::StorageType;
+
+template <typename T>
+printf_core::FormatSection parse_format_string(const char *__restrict format,
+                                               T fp) {
+  printf_core::FormatSection section;
+  size_t cur_pos = 0;
+
+  // There is no typed conversion function to convert single precision float
+  // to hex exponential format, and the function convert_float_hex_exp()
+  // requires a double or long double value to work correctly.
+  // To work around this, we convert fp to double if it is single precision, and
+  // then use that double precision value in the %{A, a} conversion specifiers.
+  [[maybe_unused]] double new_fp;
+  bool t_is_single_prec_type = cpp::is_same<T, float>::value;
+  if (t_is_single_prec_type)
+    new_fp = (double)fp;
+
+  if (format[cur_pos] == '%') {
+    section.has_conv = true;
+    ++cur_pos;
+
+    // handle precision
+    section.precision = -1;
+    if (format[cur_pos] == '.') {
+      ++cur_pos;
+      section.precision = 0;
+
+      // The standard does not allow the '*' (asterisk) operator for strfromx()
+      // functions
+      if (internal::isdigit(format[cur_pos])) {
+        auto result = internal::strtointeger<int>(format + cur_pos, 10);
+        section.precision += result.value;
+        cur_pos += result.parsed_len;
+      }
+    }
+
+    section.conv_name = format[cur_pos];
+    switch (format[cur_pos]) {
+    case 'a':
+    case 'A':
+      if (t_is_single_prec_type)
+        section.conv_val_raw = cpp::bit_cast<storage_type<double>>(new_fp);
+      else
+        section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
+      break;
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+      section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
+      break;
+    default:
+      section.has_conv = false;
+      while (format[cur_pos] != '\0')
+        ++cur_pos;
+      break;
+    }
+
+    if (format[cur_pos] != '\0')
+      ++cur_pos;
+  } else {
+    section.has_conv = false;
+    // We are looking for exactly one section, so no more '%'
+    while (format[cur_pos] != '\0')
+      ++cur_pos;
+  }
+
+  section.raw_string = {format, cur_pos};
+  return section;
+}
+
+template <typename T>
+int strfromfloat_convert(printf_core::Writer *writer,
+                         const printf_core::FormatSection &section) {
+  if (!section.has_conv)
+    return writer->write(section.raw_string);
+
+  auto res = static_cast<storage_type<T>>(section.conv_val_raw);
+
+  fputil::FPBits<T> strfromfloat_bits(res);
+  if (strfromfloat_bits.is_inf_or_nan())
+    return convert_inf_nan(writer, section);
+
+  switch (section.conv_name) {
+  case 'f':
+  case 'F':
+    return convert_float_decimal_typed(writer, section, strfromfloat_bits);
+  case 'e':
+  case 'E':
+    return convert_float_dec_exp_typed(writer, section, strfromfloat_bits);
+  case 'a':
+  case 'A':
+    return convert_float_hex_exp(writer, section);
+  case 'g':
+  case 'G':
+    return convert_float_dec_auto_typed(writer, section, strfromfloat_bits);
+  default:
+    return writer->write(section.raw_string);
+  }
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE::internal
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
new file mode 100644
index 00000000000000..40eff87eb454c5
--- /dev/null
+++ b/libc/src/stdlib/strfromf.cpp
@@ -0,0 +1,42 @@
+//===-- Implementation of strfromf ------------------------------*- 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/strfromf.h"
+#include "src/stdlib/str_from_util.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, strfromf,
+                   (char *__restrict s, size_t n, const char *__restrict format,
+                    float 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<float>(&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/strfromf.h b/libc/src/stdlib/strfromf.h
new file mode 100644
index 00000000000000..b551a58af05a3e
--- /dev/null
+++ b/libc/src/stdlib/strfromf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for strfromf ------------------------*- 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_STRFROMF_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROMF_H
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+int strfromf(char *__restrict s, size_t n, const char *__restrict format,
+             float fp);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRTOF_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 5488a61c4ef187..cb42bc56f51c5f 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -168,6 +168,16 @@ add_libc_test(
     .strtol_test_support
 )
 
+add_libc_test(
+  strfromf_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    strfromf_test.cpp
+  DEPENDS
+    libc.src.stdlib.strfromf
+)
+
 add_libc_test(
   abs_test
   SUITE
@@ -259,7 +269,6 @@ add_libc_test(
     libc.src.stdlib.qsort
 )
 
-
 add_libc_test(
   qsort_r_test
   SUITE
diff --git a/libc/test/src/stdlib/strfromf_test.cpp b/libc/test/src/stdlib/strfromf_test.cpp
new file mode 100644
index 00000000000000..c5489f5f3af2ac
--- /dev/null
+++ b/libc/test/src/stdlib/strfromf_test.cpp
@@ -0,0 +1,107 @@
+//===-- Unittests for strfromf --------------------------------------------===//
+//
+// 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/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");
+}

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case this patch LGTM, but update the name to match what's actually implemented.

@vinayakdsci vinayakdsci changed the title [libc]: Implement strfrom* functions and utils [libc]: Implement strfromf() and shared utilities Mar 20, 2024
@vinayakdsci
Copy link
Contributor Author

vinayakdsci commented Mar 20, 2024

Done. If everything looks alright, could you land this for me? Thanks a lot!

@michaelrj-google michaelrj-google merged commit 5ea1520 into llvm:main Mar 20, 2024
michaelrj-google pushed a commit that referenced this pull request Mar 22, 2024
Follow up to #85438.

Implements the functions `strfromd()` and `strfroml()` introduced in
C23, and unifies the testing framework for `strfrom*()` functions.
chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
Fixes llvm#84244.

Implements the function `strfromf()` introduced in C23, and adds shared
utilities for implementation of other `strfrom*()` functions, including
`strfromd()` and `strfroml()`.
chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
Follow up to llvm#85438.

Implements the functions `strfromd()` and `strfroml()` introduced in
C23, and unifies the testing framework for `strfrom*()` functions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add strfrom<float> functions to LLVM-libc
3 participants