[libc-commits] [libc] e072a12 - [libc] add printf writer

Michael Jones via libc-commits libc-commits at lists.llvm.org
Tue May 3 10:15:10 PDT 2022


Author: Michael Jones
Date: 2022-05-03T10:15:04-07:00
New Revision: e072a123d3b24995b0921802d69c93c08e3667bf

URL: https://github.com/llvm/llvm-project/commit/e072a123d3b24995b0921802d69c93c08e3667bf
DIFF: https://github.com/llvm/llvm-project/commit/e072a123d3b24995b0921802d69c93c08e3667bf.diff

LOG: [libc] add printf writer

The printf implmentation is made up of three main pieces, the parser,
the converter, and the writer. This patch adds the implementation for
the writer, as well as the function for writing to a string, along with
tests.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D124421

Added: 
    libc/src/stdio/printf_core/string_writer.h
    libc/src/stdio/printf_core/writer.cpp
    libc/test/src/stdio/printf_core/string_writer_test.cpp

Modified: 
    libc/src/stdio/printf_core/CMakeLists.txt
    libc/src/stdio/printf_core/writer.h
    libc/test/src/stdio/printf_core/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index f93022527b605..d167a0191c840 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -19,3 +19,21 @@ add_object_library(
     libc.src.__support.CPP.bit
 
 )
+
+add_header_library(
+  string_writer
+  HDRS
+    string_writer.h
+  DEPENDS
+    libc.src.string.memory_utils.memcpy_implementation
+)
+
+add_object_library(
+  writer
+  SRCS
+    writer.cpp
+  HDRS
+    writer.h
+  DEPENDS
+    libc.src.string.memory_utils.memset_implementation
+)

diff  --git a/libc/src/stdio/printf_core/string_writer.h b/libc/src/stdio/printf_core/string_writer.h
new file mode 100644
index 0000000000000..d8d6dbf670b78
--- /dev/null
+++ b/libc/src/stdio/printf_core/string_writer.h
@@ -0,0 +1,56 @@
+//===-- String Writer class for printf -----------------------*- 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_PRINTF_CORE_STRING_WRITER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_STRING_WRITER_H
+
+#include "src/string/memory_utils/memcpy_implementations.h"
+#include <stddef.h>
+
+namespace __llvm_libc {
+namespace printf_core {
+
+class StringWriter {
+  char *__restrict cur_buffer;
+  size_t available_capacity;
+
+public:
+  // StringWriter is intended to take a copy of the cur_buffer pointer, as well
+  // as the maximum length of the string. This maximum length should not include
+  // the null terminator, since that's written separately.
+  StringWriter(char *__restrict buffer, size_t max_len = ~size_t(0))
+      : cur_buffer(buffer), available_capacity(max_len) {}
+
+  void write(const char *__restrict to_write, size_t len) {
+    if (len > available_capacity)
+      len = available_capacity;
+
+    if (len > 0) {
+      inline_memcpy(cur_buffer, to_write, len);
+      cur_buffer += len;
+      available_capacity -= len;
+    }
+  }
+  // Terminate should only be called if the original max length passed to
+  // snprintf was greater than 0. It writes a null byte to the end of the
+  // cur_buffer, regardless of available_capacity.
+  void terminate() { *cur_buffer = '\0'; }
+};
+
+// write_to_string treats raw_pointer as a StringWriter and calls its write
+// function.
+void write_to_string(void *raw_pointer, const char *__restrict to_write,
+                     size_t len) {
+  StringWriter *string_writer = reinterpret_cast<StringWriter *>(raw_pointer);
+  string_writer->write(to_write, len);
+}
+
+} // namespace printf_core
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_STRING_WRITER_H

diff  --git a/libc/src/stdio/printf_core/writer.cpp b/libc/src/stdio/printf_core/writer.cpp
new file mode 100644
index 0000000000000..8f5e5167142e3
--- /dev/null
+++ b/libc/src/stdio/printf_core/writer.cpp
@@ -0,0 +1,37 @@
+//===-- Writer definition for printf ----------------------------*- 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 "writer.h"
+#include "src/string/memory_utils/memset_implementations.h"
+#include <stddef.h>
+
+namespace __llvm_libc {
+namespace printf_core {
+
+void Writer::write(const char *new_string, size_t length) {
+
+  raw_write(output, new_string, length);
+
+  // chars_written tracks the number of chars that would have been written
+  // regardless of what the raw_write call does.
+  chars_written += length;
+}
+
+void Writer::write_chars(char new_char, size_t length) {
+  constexpr size_t BUFF_SIZE = 8;
+  char buff[BUFF_SIZE];
+  inline_memset(buff, new_char, BUFF_SIZE);
+  while (length > BUFF_SIZE) {
+    write(buff, BUFF_SIZE);
+    length -= BUFF_SIZE;
+  }
+  write(buff, length);
+}
+
+} // namespace printf_core
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h
index 47aa05a203eba..0ad37e492eb28 100644
--- a/libc/src/stdio/printf_core/writer.h
+++ b/libc/src/stdio/printf_core/writer.h
@@ -1,4 +1,4 @@
-//===-- String writer for printf --------------------------------*- C++ -*-===//
+//===-- Writer definition for printf ----------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -26,11 +26,11 @@ class Writer final {
   // onto the end of output.
   WriteFunc raw_write;
 
-  size_t max_length;
-  size_t chars_written;
+  unsigned long long chars_written = 0;
 
 public:
-  Writer(void *output, WriteFunc raw_write, size_t max_length);
+  Writer(void *init_output, WriteFunc init_raw_write)
+      : output(init_output), raw_write(init_raw_write) {}
 
   // write will copy length bytes from new_string into output using
   // raw_write, unless that would cause more bytes than max_length to be
@@ -42,7 +42,7 @@ class Writer final {
   // increments chars_written by length.
   void write_chars(char new_char, size_t length);
 
-  size_t get_chars_written();
+  unsigned long long get_chars_written() { return chars_written; }
 };
 
 } // namespace printf_core

diff  --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
index 04544ff9d0a27..23e3133522c36 100644
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -10,3 +10,14 @@ add_libc_unittest(
 )
 
 target_link_libraries(libc.test.src.stdio.printf_core.parser_test PRIVATE LibcPrintfHelpers)
+
+add_libc_unittest(
+  string_writer_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    string_writer_test.cpp
+  DEPENDS
+    libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.string_writer
+)

diff  --git a/libc/test/src/stdio/printf_core/string_writer_test.cpp b/libc/test/src/stdio/printf_core/string_writer_test.cpp
new file mode 100644
index 0000000000000..6701a23b3f374
--- /dev/null
+++ b/libc/test/src/stdio/printf_core/string_writer_test.cpp
@@ -0,0 +1,206 @@
+//===-- Unittests for the printf String Writer ----------------------------===//
+//
+// 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/printf_core/string_writer.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include "utils/UnitTest/Test.h"
+
+TEST(LlvmLibcPrintfStringWriterTest, Constructor) {
+  char str[10];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, Write) {
+  char str[4] = {'D', 'E', 'F', 'G'};
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write("abc", 3);
+
+  EXPECT_EQ(str[3], 'G');
+  // This null terminates the string. The writer has no indication when the
+  // string is done, so it relies on the user to tell it when to null terminate
+  // the string. Importantly, it can't tell the 
diff erence between an intended
+  // max length of 0 (write nothing) or 1 (write just a null byte), and so it
+  // relies on the caller to do that bounds check.
+  str_writer.terminate();
+
+  ASSERT_STREQ("abc", str);
+  ASSERT_EQ(writer.get_chars_written(), 3ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteMultipleTimes) {
+  char str[10];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write("abc", 3);
+  writer.write("DEF", 3);
+  writer.write("1234", 3);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("abcDEF123", str);
+  ASSERT_EQ(writer.get_chars_written(), 9ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteChars) {
+  char str[4] = {'D', 'E', 'F', 'G'};
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('a', 3);
+
+  EXPECT_EQ(str[3], 'G');
+  str_writer.terminate();
+
+  ASSERT_STREQ("aaa", str);
+  ASSERT_EQ(writer.get_chars_written(), 3ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteCharsMultipleTimes) {
+  char str[10];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('a', 3);
+  writer.write_chars('D', 3);
+  writer.write_chars('1', 3);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("aaaDDD111", str);
+  ASSERT_EQ(writer.get_chars_written(), 9ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteManyChars) {
+  char str[100];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('Z', 99);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZZ"
+               "ZZZZZZZZZ",
+               str);
+  ASSERT_EQ(writer.get_chars_written(), 99ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, MixedWrites) {
+  char str[13];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('a', 3);
+  writer.write("DEF", 3);
+  writer.write_chars('1', 3);
+  writer.write("456", 3);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("aaaDEF111456", str);
+  ASSERT_EQ(writer.get_chars_written(), 12ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteWithMaxLength) {
+  char str[11];
+  __llvm_libc::printf_core::StringWriter str_writer(str, 10);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write("abcDEF123456", 12);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("abcDEF1234", str);
+  ASSERT_EQ(writer.get_chars_written(), 12ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, WriteCharsWithMaxLength) {
+  char str[11];
+  __llvm_libc::printf_core::StringWriter str_writer(str, 10);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+
+  writer.write_chars('1', 15);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("1111111111", str);
+  ASSERT_EQ(writer.get_chars_written(), 15ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, MixedWriteWithMaxLength) {
+  char str[11];
+  __llvm_libc::printf_core::StringWriter str_writer(str, 10);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('a', 3);
+  writer.write("DEF", 3);
+  writer.write_chars('1', 3);
+  writer.write("456", 3);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("aaaDEF1114", str);
+  ASSERT_EQ(writer.get_chars_written(), 12ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, StringWithMaxLengthOne) {
+  char str[1];
+  __llvm_libc::printf_core::StringWriter str_writer(str, 0);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  // This is because the max length should be at most 1 less than the size of
+  // the buffer it's writing to.
+  writer.write_chars('a', 3);
+  writer.write("DEF", 3);
+  writer.write_chars('1', 3);
+  writer.write("456", 3);
+
+  str_writer.terminate();
+
+  ASSERT_STREQ("", str);
+  ASSERT_EQ(writer.get_chars_written(), 12ull);
+}
+
+TEST(LlvmLibcPrintfStringWriterTest, NullStringWithZeroMaxLength) {
+  __llvm_libc::printf_core::StringWriter str_writer(nullptr, 0);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+  writer.write_chars('a', 3);
+  writer.write("DEF", 3);
+  writer.write_chars('1', 3);
+  writer.write("456", 3);
+
+  ASSERT_EQ(writer.get_chars_written(), 12ull);
+}


        


More information about the libc-commits mailing list