forked from OSchip/llvm-project
[libc] add sprintf
This adds the sprintf entrypoint, as well as unit tests. Currently sprintf only supports %%, %s, and %c, but the other conversions are on the way. Reviewed By: sivachandra, lntue Differential Revision: https://reviews.llvm.org/D125573
This commit is contained in:
parent
dbf3b5f114
commit
ff6fe39eca
|
@ -268,6 +268,7 @@ if(LLVM_LIBC_FULL_BUILD)
|
||||||
libc.src.stdio.funlockfile
|
libc.src.stdio.funlockfile
|
||||||
libc.src.stdio.fwrite
|
libc.src.stdio.fwrite
|
||||||
libc.src.stdio.fwrite_unlocked
|
libc.src.stdio.fwrite_unlocked
|
||||||
|
libc.src.stdio.sprintf
|
||||||
|
|
||||||
# signal.h entrypoints
|
# signal.h entrypoints
|
||||||
# TODO: Enable signal.h entrypoints after fixing signal.h
|
# TODO: Enable signal.h entrypoints after fixing signal.h
|
||||||
|
|
|
@ -532,6 +532,13 @@ def StdC : StandardSpec<"stdc"> {
|
||||||
ArgSpec<SizeTType>,
|
ArgSpec<SizeTType>,
|
||||||
ArgSpec<FILERestrictedPtr>]
|
ArgSpec<FILERestrictedPtr>]
|
||||||
>,
|
>,
|
||||||
|
FunctionSpec<
|
||||||
|
"sprintf",
|
||||||
|
RetValSpec<IntType>,
|
||||||
|
[ArgSpec<CharRestrictedPtr>,
|
||||||
|
ArgSpec<ConstCharRestrictedPtr>,
|
||||||
|
ArgSpec<VarArgType>]
|
||||||
|
>,
|
||||||
]
|
]
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|
|
@ -202,3 +202,17 @@ add_entrypoint_object(
|
||||||
libc.include.stdio
|
libc.include.stdio
|
||||||
libc.src.__support.File.file
|
libc.src.__support.File.file
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_entrypoint_object(
|
||||||
|
sprintf
|
||||||
|
SRCS
|
||||||
|
sprintf.cpp
|
||||||
|
HDRS
|
||||||
|
sprintf.h
|
||||||
|
DEPENDS
|
||||||
|
libc.include.stdio
|
||||||
|
libc.src.stdio.printf_core.printf_main
|
||||||
|
libc.src.stdio.printf_core.string_writer
|
||||||
|
libc.src.stdio.printf_core.writer
|
||||||
|
)
|
||||||
|
|
|
@ -51,3 +51,16 @@ add_object_library(
|
||||||
.writer
|
.writer
|
||||||
.core_structs
|
.core_structs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_header_library(
|
||||||
|
printf_main
|
||||||
|
HDRS
|
||||||
|
printf_main.h
|
||||||
|
DEPENDS
|
||||||
|
.parser
|
||||||
|
.converter
|
||||||
|
.writer
|
||||||
|
.core_structs
|
||||||
|
libc.src.__support.arg_list
|
||||||
|
)
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
namespace __llvm_libc {
|
namespace __llvm_libc {
|
||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
void convert_char(Writer *writer, FormatSection to_conv) {
|
void convert_char(Writer *writer, const FormatSection &to_conv) {
|
||||||
char c = to_conv.conv_val_raw;
|
char c = to_conv.conv_val_raw;
|
||||||
|
|
||||||
if (to_conv.min_width > 1) {
|
if (to_conv.min_width > 1) {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
namespace __llvm_libc {
|
namespace __llvm_libc {
|
||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
void convert(Writer *writer, FormatSection to_conv) {
|
void convert(Writer *writer, const FormatSection &to_conv) {
|
||||||
if (!to_conv.has_conv) {
|
if (!to_conv.has_conv) {
|
||||||
writer->write(to_conv.raw_string, to_conv.raw_len);
|
writer->write(to_conv.raw_string, to_conv.raw_len);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace printf_core {
|
||||||
// convert will call a conversion function to convert the FormatSection into
|
// convert will call a conversion function to convert the FormatSection into
|
||||||
// its string representation, and then that will write the result to the
|
// its string representation, and then that will write the result to the
|
||||||
// writer.
|
// writer.
|
||||||
void convert(Writer *writer, FormatSection to_conv);
|
void convert(Writer *writer, const FormatSection &to_conv);
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace __llvm_libc
|
} // namespace __llvm_libc
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace __llvm_libc {
|
||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
int printf_main(Writer *writer, const char *__restrict str,
|
int printf_main(Writer *writer, const char *__restrict str,
|
||||||
internal::ArgList args) {
|
internal::ArgList &args) {
|
||||||
Parser parser(str, args);
|
Parser parser(str, args);
|
||||||
|
|
||||||
for (FormatSection cur_section = parser.get_next_section();
|
for (FormatSection cur_section = parser.get_next_section();
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
namespace __llvm_libc {
|
namespace __llvm_libc {
|
||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
void convert_string(Writer *writer, FormatSection to_conv) {
|
void convert_string(Writer *writer, const FormatSection &to_conv) {
|
||||||
int string_len = 0;
|
int string_len = 0;
|
||||||
|
|
||||||
for (char *cur_str = reinterpret_cast<char *>(to_conv.conv_val_ptr);
|
for (char *cur_str = reinterpret_cast<char *>(to_conv.conv_val_ptr);
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
//===-- Implementation of sprintf -------------------------------*- 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/sprintf.h"
|
||||||
|
|
||||||
|
#include "src/__support/arg_list.h"
|
||||||
|
#include "src/stdio/printf_core/printf_main.h"
|
||||||
|
#include "src/stdio/printf_core/string_writer.h"
|
||||||
|
#include "src/stdio/printf_core/writer.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
namespace __llvm_libc {
|
||||||
|
|
||||||
|
LLVM_LIBC_FUNCTION(int, sprintf,
|
||||||
|
(char *__restrict buffer, const char *__restrict format,
|
||||||
|
...)) {
|
||||||
|
va_list vlist;
|
||||||
|
va_start(vlist, format);
|
||||||
|
internal::ArgList args(vlist); // This holder class allows for easier copying
|
||||||
|
// and pointer semantics, as well as handing
|
||||||
|
// destruction automatically.
|
||||||
|
va_end(vlist);
|
||||||
|
printf_core::StringWriter str_writer(buffer);
|
||||||
|
printf_core::Writer writer(reinterpret_cast<void *>(&str_writer),
|
||||||
|
printf_core::write_to_string);
|
||||||
|
|
||||||
|
int ret_val = printf_core::printf_main(&writer, format, args);
|
||||||
|
str_writer.terminate();
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __llvm_libc
|
|
@ -0,0 +1,18 @@
|
||||||
|
//===-- Implementation header of sprintf ------------------------*- 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_SPRINTF_H
|
||||||
|
#define LLVM_LIBC_SRC_STDIO_SPRINTF_H
|
||||||
|
|
||||||
|
namespace __llvm_libc {
|
||||||
|
|
||||||
|
int sprintf(char *__restrict buffer, const char *__restrict format, ...);
|
||||||
|
|
||||||
|
} // namespace __llvm_libc
|
||||||
|
|
||||||
|
#endif // LLVM_LIBC_SRC_STDIO_SPRINTF_H
|
|
@ -63,6 +63,16 @@ add_libc_unittest(
|
||||||
LibcMemoryHelpers
|
LibcMemoryHelpers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_libc_unittest(
|
||||||
|
sprintf_test
|
||||||
|
SUITE
|
||||||
|
libc_stdio_unittests
|
||||||
|
SRCS
|
||||||
|
sprintf_test.cpp
|
||||||
|
DEPENDS
|
||||||
|
libc.src.stdio.sprintf
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(printf_core)
|
add_subdirectory(printf_core)
|
||||||
|
|
||||||
add_subdirectory(testdata)
|
add_subdirectory(testdata)
|
||||||
|
|
|
@ -53,6 +53,7 @@ TEST(LlvmLibcPrintfParserTest, EvalRaw) {
|
||||||
expected.raw_string = str;
|
expected.raw_string = str;
|
||||||
|
|
||||||
ASSERT_FORMAT_EQ(expected, format_arr[0]);
|
ASSERT_FORMAT_EQ(expected, format_arr[0]);
|
||||||
|
// TODO: add checks that the format_arr after the last one has length 0
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LlvmLibcPrintfParserTest, EvalSimple) {
|
TEST(LlvmLibcPrintfParserTest, EvalSimple) {
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
//===-- Unittests for sprintf ---------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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/sprintf.h"
|
||||||
|
|
||||||
|
#include "utils/UnitTest/Test.h"
|
||||||
|
|
||||||
|
TEST(LlvmLibcSPrintfTest, SimpleNoConv) {
|
||||||
|
char buff[64];
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "A simple string with no conversions.");
|
||||||
|
EXPECT_EQ(written, 36);
|
||||||
|
ASSERT_STREQ(buff, "A simple string with no conversions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LlvmLibcSPrintfTest, PercentConv) {
|
||||||
|
char buff[64];
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%%");
|
||||||
|
EXPECT_EQ(written, 1);
|
||||||
|
ASSERT_STREQ(buff, "%");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "abc %% def");
|
||||||
|
EXPECT_EQ(written, 9);
|
||||||
|
ASSERT_STREQ(buff, "abc % def");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%%%%%%");
|
||||||
|
EXPECT_EQ(written, 3);
|
||||||
|
ASSERT_STREQ(buff, "%%%");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LlvmLibcSPrintfTest, CharConv) {
|
||||||
|
char buff[64];
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%c", 'a');
|
||||||
|
EXPECT_EQ(written, 1);
|
||||||
|
ASSERT_STREQ(buff, "a");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%3c %-3c", '1', '2');
|
||||||
|
EXPECT_EQ(written, 7);
|
||||||
|
ASSERT_STREQ(buff, " 1 2 ");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%*c", 2, '3');
|
||||||
|
EXPECT_EQ(written, 2);
|
||||||
|
ASSERT_STREQ(buff, " 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LlvmLibcSPrintfTest, StringConv) {
|
||||||
|
char buff[64];
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%s", "abcDEF123");
|
||||||
|
EXPECT_EQ(written, 9);
|
||||||
|
ASSERT_STREQ(buff, "abcDEF123");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%10s %-10s", "centered", "title");
|
||||||
|
EXPECT_EQ(written, 21);
|
||||||
|
ASSERT_STREQ(buff, " centered title ");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%-5.4s%-4.4s", "words can describe",
|
||||||
|
"soups most delicious");
|
||||||
|
EXPECT_EQ(written, 9);
|
||||||
|
ASSERT_STREQ(buff, "word soup");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%*s %.*s %*.*s", 10, "beginning", 2,
|
||||||
|
"isn't", 12, 10, "important. Ever.");
|
||||||
|
EXPECT_EQ(written, 26);
|
||||||
|
ASSERT_STREQ(buff, " beginning is important.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE
|
||||||
|
TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
|
||||||
|
char buff[64];
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%1$s", "abcDEF123");
|
||||||
|
EXPECT_EQ(written, 9);
|
||||||
|
ASSERT_STREQ(buff, "abcDEF123");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(buff, "%1$s %%", "abcDEF123");
|
||||||
|
EXPECT_EQ(written, 11);
|
||||||
|
ASSERT_STREQ(buff, "abcDEF123 %");
|
||||||
|
|
||||||
|
written =
|
||||||
|
__llvm_libc::sprintf(buff, "%3$s %1$s %2$s", "is", "hard", "ordering");
|
||||||
|
EXPECT_EQ(written, 16);
|
||||||
|
ASSERT_STREQ(buff, "ordering is hard");
|
||||||
|
|
||||||
|
written = __llvm_libc::sprintf(
|
||||||
|
buff, "%10$s %9$s %8$c %7$s %6$s, %6$s %5$s %4$-*1$s %3$.*11$s %2$s. %%",
|
||||||
|
6, "pain", "alphabetical", "such", "is", "this", "do", 'u', "would",
|
||||||
|
"why", 1);
|
||||||
|
EXPECT_EQ(written, 45);
|
||||||
|
ASSERT_STREQ(buff, "why would u do this, this is such a pain. %");
|
||||||
|
}
|
||||||
|
#endif // LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE
|
Loading…
Reference in New Issue