Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions libc/src/stdio/baremetal/printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ namespace LIBC_NAMESPACE_DECL {

namespace {

LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
write_to_stdout(new_str);
LIBC_INLINE int stdout_write_hook(const char *new_str, size_t new_str_len,
void *) {
write_to_stdout({new_str, new_str_len});
return printf_core::WRITE_OK;
}

Expand Down
5 changes: 3 additions & 2 deletions libc/src/stdio/baremetal/vprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ namespace LIBC_NAMESPACE_DECL {

namespace {

LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
write_to_stdout(new_str);
LIBC_INLINE int stdout_write_hook(const char *new_str, size_t new_str_len,
void *) {
write_to_stdout({new_str, new_str_len});
return printf_core::WRITE_OK;
}

Expand Down
6 changes: 3 additions & 3 deletions libc/src/stdio/printf_core/vasprintf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {

LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str,
LIBC_INLINE int resize_overflow_hook(const char *new_str, size_t new_str_len,
ResizingBuffer *wb) {
size_t new_size = new_str.size() + wb->buff_cur;
size_t new_size = new_str_len + wb->buff_cur;
const bool isBuffOnStack = (wb->buff == wb->init_buff);
char *new_buff = static_cast<char *>(
isBuffOnStack ? malloc(new_size + 1)
Expand All @@ -33,7 +33,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str,
if (isBuffOnStack)
inline_memcpy(new_buff, wb->buff, wb->buff_cur);
wb->buff = new_buff;
inline_memcpy(wb->buff + wb->buff_cur, new_str.data(), new_str.size());
inline_memcpy(wb->buff + wb->buff_cur, new_str, new_str_len);
wb->buff_cur = new_size;
wb->buff_len = new_size;
return printf_core::WRITE_OK;
Expand Down
9 changes: 5 additions & 4 deletions libc/src/stdio/printf_core/vfprintf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,20 @@ LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,

namespace printf_core {

LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
LIBC_INLINE int file_write_hook(const char *new_str, size_t new_str_len,
void *fp) {
::FILE *target_file = reinterpret_cast<::FILE *>(fp);
// Write new_str to the target file. The logic preventing a zero-length write
// is in the writer, so we don't check here.
auto write_result = internal::fwrite_unlocked(new_str.data(), sizeof(char),
new_str.size(), target_file);
auto write_result = internal::fwrite_unlocked(new_str, sizeof(char),
new_str_len, target_file);
// Propagate actual system error in FileIOResult.
if (write_result.has_error())
return -write_result.error;

// In case short write occured or error was not set on FileIOResult for some
// reason.
if (write_result.value != new_str.size() ||
if (write_result.value != new_str_len ||
internal::ferror_unlocked(target_file))
return FILE_WRITE_ERROR;

Expand Down
105 changes: 56 additions & 49 deletions libc/src/stdio/printf_core/writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,40 +41,43 @@ template <WriteMode write_mode> struct Mode {
template <WriteMode write_mode> class Writer;

template <WriteMode write_mode> struct WriteBuffer {
char *buff;
using CharType = char;
CharType *buff;
size_t buff_len;
size_t buff_cur = 0;
// The current writing mode in case the user wants runtime dispatch of the
// stream writer with function pointers.
[[maybe_unused]] WriteMode write_mode_;

protected:
LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, WriteMode mode)
LIBC_INLINE WriteBuffer(CharType *buff, size_t buff_len, WriteMode mode)
: buff(buff), buff_len(buff_len), write_mode_(mode) {}

private:
friend class Writer<write_mode>;
// The overflow_write method will handle the case when adding new_str to
// the buffer would overflow it. Specific actions will depend on the buffer
// type / write_mode.
LIBC_INLINE int overflow_write(cpp::string_view new_str);
LIBC_INLINE int overflow_write(const CharType *new_str, size_t new_str_len);
};

// Buffer variant that discards characters that don't fit into the buffer.
struct DropOverflowBuffer
: public WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> {
LIBC_INLINE DropOverflowBuffer(char *buff, size_t buff_len)
LIBC_INLINE DropOverflowBuffer(CharType *buff, size_t buff_len)
: WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>(
buff, buff_len, WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}

LIBC_INLINE int fill_remaining_to_buff(cpp::string_view new_str) {
LIBC_INLINE int fill_remaining_to_buff(const CharType *new_str,
size_t new_str_len) {
if (buff_cur < buff_len) {
size_t bytes_to_write = buff_len - buff_cur;
if (bytes_to_write > new_str.size()) {
bytes_to_write = new_str.size();
size_t chars_to_write = buff_len - buff_cur;
if (chars_to_write > new_str_len) {
chars_to_write = new_str_len;
}
inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write);
buff_cur += bytes_to_write;
inline_memcpy(buff + buff_cur, new_str,
chars_to_write * sizeof(CharType));
buff_cur += chars_to_write;
}
return WRITE_OK;
}
Expand All @@ -84,94 +87,101 @@ struct DropOverflowBuffer
struct FlushingBuffer
: public WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> {
// The stream writer will be called when the buffer is full. It will be passed
// string_views to write to the stream.
using StreamWriter = int (*)(cpp::string_view, void *);
// buffers to write, and the target provided at construction time.
using StreamWriter = int (*)(const CharType *, size_t, void *);
const StreamWriter stream_writer;
void *output_target;

LIBC_INLINE FlushingBuffer(char *buff, size_t buff_len, StreamWriter hook,
LIBC_INLINE FlushingBuffer(CharType *buff, size_t buff_len, StreamWriter hook,
void *target)
: WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value>(
buff, buff_len, WriteMode::FLUSH_TO_STREAM),
stream_writer(hook), output_target(target) {}

// Flushes the entire current buffer to stream, followed by the new_str (if
// non-empty).
LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
// Flushes the entire current buffer to stream, followed by the new_str
// buffer.
LIBC_INLINE int flush_to_stream(const CharType *new_str, size_t new_str_len) {
if (buff_cur > 0) {
int retval = stream_writer({buff, buff_cur}, output_target);
int retval = stream_writer(buff, buff_cur, output_target);
if (retval < 0)
return retval;
}
if (new_str.size() > 0) {
int retval = stream_writer(new_str, output_target);
if (new_str_len > 0) {
int retval = stream_writer(new_str, new_str_len, output_target);
if (retval < 0)
return retval;
}
buff_cur = 0;
return WRITE_OK;
}

LIBC_INLINE int flush_to_stream() { return flush_to_stream({}); }
LIBC_INLINE int flush_to_stream() { return flush_to_stream(nullptr, 0); }
};

// Buffer variant that calls a resizing callback when it gets full.
struct ResizingBuffer
: public WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> {
using ResizeWriter = int (*)(cpp::string_view, ResizingBuffer *);
using ResizeWriter = int (*)(const CharType *, size_t, ResizingBuffer *);
const ResizeWriter resize_writer;
const char *init_buff; // for checking when resize.
const CharType *init_buff; // for checking when resize.

LIBC_INLINE ResizingBuffer(char *buff, size_t buff_len, ResizeWriter hook)
LIBC_INLINE ResizingBuffer(CharType *buff, size_t buff_len, ResizeWriter hook)
: WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value>(
buff, buff_len, WriteMode::RESIZE_AND_FILL_BUFF),
resize_writer(hook), init_buff(buff) {}

// Invokes the callback that is supposed to resize the buffer and make
// it large enough to fit the new_str addition.
LIBC_INLINE int resize_and_write(cpp::string_view new_str) {
return resize_writer(new_str, this);
LIBC_INLINE int resize_and_write(const CharType *new_str,
size_t new_str_len) {
return resize_writer(new_str, new_str_len, this);
}
};

template <>
LIBC_INLINE int WriteBuffer<WriteMode::RUNTIME_DISPATCH>::overflow_write(
cpp::string_view new_str) {
const CharType *new_str, size_t new_str_len) {
if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff(
new_str);
new_str, new_str_len);
else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str);
return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(
new_str, new_str_len);
else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(new_str);
return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(
new_str, new_str_len);
__builtin_unreachable();
}

template <>
LIBC_INLINE int
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::overflow_write(
cpp::string_view new_str) {
const CharType *new_str, size_t new_str_len) {
return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff(
new_str);
new_str, new_str_len);
}

template <>
LIBC_INLINE int WriteBuffer<WriteMode::FLUSH_TO_STREAM>::overflow_write(
cpp::string_view new_str) {
return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str);
LIBC_INLINE int
WriteBuffer<WriteMode::FLUSH_TO_STREAM>::overflow_write(const CharType *new_str,
size_t new_str_len) {
return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str,
new_str_len);
}

template <>
LIBC_INLINE int WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>::overflow_write(
cpp::string_view new_str) {
return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(new_str);
const CharType *new_str, size_t new_str_len) {
return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(
new_str, new_str_len);
}

template <WriteMode write_mode> class Writer final {
using CharType = char;
WriteBuffer<write_mode> &wb;
size_t chars_written = 0;

LIBC_INLINE int pad(char new_char, size_t length) {
LIBC_INLINE int pad(CharType new_char, size_t length) {
// First, fill as much of the buffer as possible with the padding char.
size_t written = 0;
const size_t buff_space = wb.buff_len - wb.buff_cur;
Expand All @@ -184,24 +194,23 @@ template <WriteMode write_mode> class Writer final {

// Next, overflow write the rest of length using the mini_buff.
constexpr size_t MINI_BUFF_SIZE = 64;
char mini_buff[MINI_BUFF_SIZE];
CharType mini_buff[MINI_BUFF_SIZE];
inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
while (written + MINI_BUFF_SIZE < length) {
int result = wb.overflow_write(mb_string_view);
int result = wb.overflow_write(mini_buff, MINI_BUFF_SIZE);
if (result != WRITE_OK)
return result;
written += MINI_BUFF_SIZE;
}
cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
return wb.overflow_write(mb_substr);
return wb.overflow_write(mini_buff, length - written);
}

public:
LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}

// Takes a string, copies it into the buffer if there is space, else passes it
// to the overflow mechanism to be handled separately.
// TODO: Migrate all callers away from string_view to CharType*/size_t pair.
LIBC_INLINE int write(cpp::string_view new_string) {
chars_written += new_string.size();
if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {
Expand All @@ -210,18 +219,17 @@ template <WriteMode write_mode> class Writer final {
wb.buff_cur += new_string.size();
return WRITE_OK;
}
return wb.overflow_write(new_string);
return wb.overflow_write(new_string.data(), new_string.size());
}

// Takes a char and a length, memsets the next length characters of the buffer
// if there is space, else calls pad which will loop and call the overflow
// mechanism on a secondary buffer.
LIBC_INLINE int write(char new_char, size_t length) {
LIBC_INLINE int write(CharType new_char, size_t length) {
chars_written += length;

if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {
inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char),
length);
inline_memset(wb.buff + wb.buff_cur, new_char, length);
wb.buff_cur += length;
return WRITE_OK;
}
Expand All @@ -230,15 +238,14 @@ template <WriteMode write_mode> class Writer final {

// Takes a char, copies it into the buffer if there is space, else passes it
// to the overflow mechanism to be handled separately.
LIBC_INLINE int write(char new_char) {
LIBC_INLINE int write(CharType new_char) {
chars_written += 1;
if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) {
wb.buff[wb.buff_cur] = new_char;
wb.buff_cur += 1;
return WRITE_OK;
}
cpp::string_view char_string_view(&new_char, 1);
return wb.overflow_write(char_string_view);
return wb.overflow_write(&new_char, 1);
}

LIBC_INLINE size_t get_chars_written() { return chars_written; }
Expand Down
12 changes: 5 additions & 7 deletions libc/test/src/stdio/printf_core/writer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@

#include "src/stdio/printf_core/writer.h"

#include "src/__support/CPP/string_view.h"
#include "src/string/memory_utils/inline_memcpy.h"
#include "test/UnitTest/Test.h"

namespace {

using LIBC_NAMESPACE::cpp::string_view;
using LIBC_NAMESPACE::printf_core::DropOverflowBuffer;
using LIBC_NAMESPACE::printf_core::FlushingBuffer;
using LIBC_NAMESPACE::printf_core::WriteMode;
Expand Down Expand Up @@ -196,17 +194,17 @@ struct OutBuff {
size_t cur_pos = 0;
};

int copy_to_out(string_view new_str, void *raw_out_buff) {
if (new_str.size() == 0) {
int copy_to_out(const char *new_str, size_t new_str_len, void *raw_out_buff) {
if (new_str_len == 0) {
return 0;
}

OutBuff *out_buff = reinterpret_cast<OutBuff *>(raw_out_buff);

LIBC_NAMESPACE::inline_memcpy(out_buff->out_str + out_buff->cur_pos,
new_str.data(), new_str.size());
LIBC_NAMESPACE::inline_memcpy(out_buff->out_str + out_buff->cur_pos, new_str,
new_str_len);

out_buff->cur_pos += new_str.size();
out_buff->cur_pos += new_str_len;
return 0;
}

Expand Down
Loading