30 changes: 30 additions & 0 deletions libc/src/stdio/printf_core/vfprintf_internal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===-- Internal implementation of vfprintf ---------------------*- 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/printf_core/vfprintf_internal.h"

#include "src/__support/arg_list.h"
#include "src/stdio/printf_core/file_writer.h"
#include "src/stdio/printf_core/printf_main.h"
#include "src/stdio/printf_core/writer.h"

#include <stdio.h>

namespace __llvm_libc {
namespace printf_core {

int vfprintf_internal(::FILE *__restrict stream, const char *__restrict format,
internal::ArgList &args) {
FileWriter file_writer(stream);
printf_core::Writer writer(reinterpret_cast<void *>(&file_writer),
printf_core::write_to_file);
return printf_core::printf_main(&writer, format, args);
}

} // namespace printf_core
} // namespace __llvm_libc
24 changes: 24 additions & 0 deletions libc/src/stdio/printf_core/vfprintf_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===-- Internal implementation header of vfprintf --------------*- 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_VFPRINTF_INTERNAL_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H

#include "src/__support/arg_list.h"

#include <stdio.h>

namespace __llvm_libc {
namespace printf_core {

int vfprintf_internal(::FILE *__restrict stream, const char *__restrict format,
internal::ArgList &args);
} // namespace printf_core
} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
17 changes: 8 additions & 9 deletions libc/src/stdio/printf_core/writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,23 @@
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.
int Writer::write(const char *new_string, size_t length) {
chars_written += length;
return raw_write(output, new_string, length);
}

void Writer::write_chars(char new_char, size_t length) {
int Writer::write_chars(char new_char, size_t length) {
constexpr size_t BUFF_SIZE = 8;
char buff[BUFF_SIZE];
int result;
inline_memset(buff, new_char, BUFF_SIZE);
while (length > BUFF_SIZE) {
write(buff, BUFF_SIZE);
result = write(buff, BUFF_SIZE);
if (result < 0)
return result;
length -= BUFF_SIZE;
}
write(buff, length);
return write(buff, length);
}

} // namespace printf_core
Expand Down
23 changes: 12 additions & 11 deletions libc/src/stdio/printf_core/writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace __llvm_libc {
namespace printf_core {

using WriteFunc = void (*)(void *, const char *__restrict, size_t);
using WriteFunc = int (*)(void *, const char *__restrict, size_t);

class Writer final {
// output is a pointer to the string or file that the writer is meant to write
Expand All @@ -23,26 +23,27 @@ class Writer final {

// raw_write is a function that, when called on output with a char* and
// length, will copy the number of bytes equal to the length from the char*
// onto the end of output.
// onto the end of output. It should return a positive number or zero on
// success, or a negative number on failure.
WriteFunc raw_write;

unsigned long long chars_written = 0;
int chars_written = 0;

public:
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
// written. It always increments chars_written by length.
void write(const char *new_string, size_t length);
// raw_write. It increments chars_written by length always. It returns the
// result of raw_write.
int write(const char *new_string, size_t length);

// write_chars will copy length copies of new_char into output using raw_write
// unless that would cause more bytes than max_length to be written. It always
// increments chars_written by length.
void write_chars(char new_char, size_t length);
// write_chars will copy length copies of new_char into output using the write
// function and a statically sized buffer. This is primarily used for padding.
// If write returns a negative value, this will return early with that value.
int write_chars(char new_char, size_t length);

unsigned long long get_chars_written() { return chars_written; }
int get_chars_written() { return chars_written; }
};

} // namespace printf_core
Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/stdio/fprintf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {

written =
__llvm_libc::fprintf(file, "Writing to a read only file should fail.");
EXPECT_EQ(written, -1);
EXPECT_LT(written, 0);

ASSERT_EQ(__llvm_libc::fclose(file), 0);
}
22 changes: 11 additions & 11 deletions libc/test/src/stdio/printf_core/converter_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
str_writer.terminate();

ASSERT_STREQ(str, "abc");
ASSERT_EQ(writer.get_chars_written(), 3ull);
ASSERT_EQ(writer.get_chars_written(), 3);
}

TEST_F(LlvmLibcPrintfConverterTest, PercentConversion) {
Expand All @@ -51,7 +51,7 @@ TEST_F(LlvmLibcPrintfConverterTest, PercentConversion) {
str[1] = '\0';

ASSERT_STREQ(str, "%");
ASSERT_EQ(writer.get_chars_written(), 1ull);
ASSERT_EQ(writer.get_chars_written(), 1);
}

TEST_F(LlvmLibcPrintfConverterTest, CharConversionSimple) {
Expand All @@ -69,7 +69,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionSimple) {
str_writer.terminate();

ASSERT_STREQ(str, "D");
ASSERT_EQ(writer.get_chars_written(), 1ull);
ASSERT_EQ(writer.get_chars_written(), 1);
}

TEST_F(LlvmLibcPrintfConverterTest, CharConversionRightJustified) {
Expand All @@ -84,7 +84,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionRightJustified) {
str_writer.terminate();

ASSERT_STREQ(str, " E");
ASSERT_EQ(writer.get_chars_written(), 4ull);
ASSERT_EQ(writer.get_chars_written(), 4);
}

TEST_F(LlvmLibcPrintfConverterTest, CharConversionLeftJustified) {
Expand All @@ -101,7 +101,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionLeftJustified) {
str_writer.terminate();

ASSERT_STREQ(str, "F ");
ASSERT_EQ(writer.get_chars_written(), 4ull);
ASSERT_EQ(writer.get_chars_written(), 4);
}

TEST_F(LlvmLibcPrintfConverterTest, StringConversionSimple) {
Expand All @@ -117,7 +117,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionSimple) {
str_writer.terminate();

ASSERT_STREQ(str, "DEF");
ASSERT_EQ(writer.get_chars_written(), 3ull);
ASSERT_EQ(writer.get_chars_written(), 3);
}

TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh) {
Expand All @@ -132,7 +132,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh) {
str_writer.terminate();

ASSERT_STREQ(str, "456");
ASSERT_EQ(writer.get_chars_written(), 3ull);
ASSERT_EQ(writer.get_chars_written(), 3);
}

TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow) {
Expand All @@ -147,7 +147,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow) {
str_writer.terminate();

ASSERT_STREQ(str, "xy");
ASSERT_EQ(writer.get_chars_written(), 2ull);
ASSERT_EQ(writer.get_chars_written(), 2);
}

TEST_F(LlvmLibcPrintfConverterTest, StringConversionRightJustified) {
Expand All @@ -162,7 +162,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionRightJustified) {
str_writer.terminate();

ASSERT_STREQ(str, " 789");
ASSERT_EQ(writer.get_chars_written(), 4ull);
ASSERT_EQ(writer.get_chars_written(), 4);
}

TEST_F(LlvmLibcPrintfConverterTest, StringConversionLeftJustified) {
Expand All @@ -179,7 +179,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionLeftJustified) {
str_writer.terminate();

ASSERT_STREQ(str, "ghi ");
ASSERT_EQ(writer.get_chars_written(), 4ull);
ASSERT_EQ(writer.get_chars_written(), 4);
}

TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
Expand All @@ -193,5 +193,5 @@ TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
str_writer.terminate();

ASSERT_STREQ(str, "12345");
ASSERT_EQ(writer.get_chars_written(), 5ull);
ASSERT_EQ(writer.get_chars_written(), 5);
}
22 changes: 11 additions & 11 deletions libc/test/src/stdio/printf_core/string_writer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ TEST(LlvmLibcPrintfStringWriterTest, Write) {
str_writer.terminate();

ASSERT_STREQ("abc", str);
ASSERT_EQ(writer.get_chars_written(), 3ull);
ASSERT_EQ(writer.get_chars_written(), 3);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteMultipleTimes) {
Expand All @@ -52,7 +52,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteMultipleTimes) {
str_writer.terminate();

ASSERT_STREQ("abcDEF123", str);
ASSERT_EQ(writer.get_chars_written(), 9ull);
ASSERT_EQ(writer.get_chars_written(), 9);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteChars) {
Expand All @@ -67,7 +67,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteChars) {
str_writer.terminate();

ASSERT_STREQ("aaa", str);
ASSERT_EQ(writer.get_chars_written(), 3ull);
ASSERT_EQ(writer.get_chars_written(), 3);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteCharsMultipleTimes) {
Expand All @@ -83,7 +83,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteCharsMultipleTimes) {
str_writer.terminate();

ASSERT_STREQ("aaaDDD111", str);
ASSERT_EQ(writer.get_chars_written(), 9ull);
ASSERT_EQ(writer.get_chars_written(), 9);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteManyChars) {
Expand All @@ -107,7 +107,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteManyChars) {
"ZZZZZZZZZZ"
"ZZZZZZZZZ",
str);
ASSERT_EQ(writer.get_chars_written(), 99ull);
ASSERT_EQ(writer.get_chars_written(), 99);
}

TEST(LlvmLibcPrintfStringWriterTest, MixedWrites) {
Expand All @@ -124,7 +124,7 @@ TEST(LlvmLibcPrintfStringWriterTest, MixedWrites) {
str_writer.terminate();

ASSERT_STREQ("aaaDEF111456", str);
ASSERT_EQ(writer.get_chars_written(), 12ull);
ASSERT_EQ(writer.get_chars_written(), 12);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteWithMaxLength) {
Expand All @@ -138,7 +138,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteWithMaxLength) {
str_writer.terminate();

ASSERT_STREQ("abcDEF1234", str);
ASSERT_EQ(writer.get_chars_written(), 12ull);
ASSERT_EQ(writer.get_chars_written(), 12);
}

TEST(LlvmLibcPrintfStringWriterTest, WriteCharsWithMaxLength) {
Expand All @@ -153,7 +153,7 @@ TEST(LlvmLibcPrintfStringWriterTest, WriteCharsWithMaxLength) {
str_writer.terminate();

ASSERT_STREQ("1111111111", str);
ASSERT_EQ(writer.get_chars_written(), 15ull);
ASSERT_EQ(writer.get_chars_written(), 15);
}

TEST(LlvmLibcPrintfStringWriterTest, MixedWriteWithMaxLength) {
Expand All @@ -170,7 +170,7 @@ TEST(LlvmLibcPrintfStringWriterTest, MixedWriteWithMaxLength) {
str_writer.terminate();

ASSERT_STREQ("aaaDEF1114", str);
ASSERT_EQ(writer.get_chars_written(), 12ull);
ASSERT_EQ(writer.get_chars_written(), 12);
}

TEST(LlvmLibcPrintfStringWriterTest, StringWithMaxLengthOne) {
Expand All @@ -189,7 +189,7 @@ TEST(LlvmLibcPrintfStringWriterTest, StringWithMaxLengthOne) {
str_writer.terminate();

ASSERT_STREQ("", str);
ASSERT_EQ(writer.get_chars_written(), 12ull);
ASSERT_EQ(writer.get_chars_written(), 12);
}

TEST(LlvmLibcPrintfStringWriterTest, NullStringWithZeroMaxLength) {
Expand All @@ -202,5 +202,5 @@ TEST(LlvmLibcPrintfStringWriterTest, NullStringWithZeroMaxLength) {
writer.write_chars('1', 3);
writer.write("456", 3);

ASSERT_EQ(writer.get_chars_written(), 12ull);
ASSERT_EQ(writer.get_chars_written(), 12);
}