diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -19,3 +19,21 @@
     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
--- /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.h b/libc/src/stdio/printf_core/writer.h
--- 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 @@
   // 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 @@
   // 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/src/stdio/printf_core/writer.cpp b/libc/src/stdio/printf_core/writer.cpp
new file mode 100644
--- /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/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -10,3 +10,14 @@
 )
 
 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
--- /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 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);
+}