--- /dev/null
+//===-- 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
--- /dev/null
+//===-- 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 difference 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);
+}