Skip to content

Commit

Permalink
[libc] Support _IONBF buffering for read_unlocked (llvm#120677)
Browse files Browse the repository at this point in the history
Support _IONBF buffering for read_unlocked. Add the functions
read_unlocked_nbf() and read_unlocked_fbf().

Fixes: llvm#120155
  • Loading branch information
jackhong12 authored Jan 9, 2025
1 parent 783dc59 commit c6b7bd4
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 13 deletions.
68 changes: 55 additions & 13 deletions libc/src/__support/File/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
if (pos > 0) { // If the buffer is not empty
// Flush the buffer
const size_t write_size = pos;
auto write_result = platform_write(this, buf, write_size);
FileIOResult write_result = platform_write(this, buf, write_size);
pos = 0; // Buffer is now empty so reset pos to the beginning.
// If less bytes were written than expected, then an error occurred.
if (write_result < write_size) {
Expand All @@ -52,7 +52,7 @@ FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
}
}

auto write_result = platform_write(this, data, len);
FileIOResult write_result = platform_write(this, data, len);
if (write_result < len)
err = true;
return write_result;
Expand Down Expand Up @@ -99,7 +99,7 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
// is full.
const size_t write_size = pos;

auto buf_result = platform_write(this, buf, write_size);
FileIOResult buf_result = platform_write(this, buf, write_size);
size_t bytes_written = buf_result.value;

pos = 0; // Buffer is now empty so reset pos to the beginning.
Expand All @@ -121,7 +121,8 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
pos = remainder.size();
} else {

auto result = platform_write(this, remainder.data(), remainder.size());
FileIOResult result =
platform_write(this, remainder.data(), remainder.size());
size_t bytes_written = buf_result.value;

// If less bytes were written than expected, then an error occurred. Return
Expand Down Expand Up @@ -190,6 +191,17 @@ FileIOResult File::read_unlocked(void *data, size_t len) {

prev_op = FileOp::READ;

if (bufmode == _IONBF) { // unbuffered.
return read_unlocked_nbf(static_cast<uint8_t *>(data), len);
} else if (bufmode == _IOFBF) { // fully buffered
return read_unlocked_fbf(static_cast<uint8_t *>(data), len);
} else /*if (bufmode == _IOLBF) */ { // line buffered
// There is no line buffered mode for read. Use fully buffered instead.
return read_unlocked_fbf(static_cast<uint8_t *>(data), len);
}
}

size_t File::copy_data_from_buf(uint8_t *data, size_t len) {
cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len);

Expand All @@ -209,32 +221,42 @@ FileIOResult File::read_unlocked(void *data, size_t len) {
for (size_t i = 0; i < available_data; ++i)
dataref[i] = bufref[i + pos];
read_limit = pos = 0; // Reset the pointers.

return available_data;
}

FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
// Read data from the buffer first.
size_t available_data = copy_data_from_buf(data, len);
if (available_data == len)
return available_data;

// Update the dataref to reflect that fact that we have already
// copied |available_data| into |data|.
dataref = cpp::span<uint8_t>(dataref.data() + available_data,
dataref.size() - available_data);

size_t to_fetch = len - available_data;
cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data,
to_fetch);

if (to_fetch > bufsize) {
auto result = platform_read(this, dataref.data(), to_fetch);
FileIOResult result = platform_read(this, dataref.data(), to_fetch);
size_t fetched_size = result.value;
if (result.has_error() || fetched_size < to_fetch) {
if (!result.has_error())
eof = true;
else
err = true;
return {available_data + fetched_size, result.has_error()};
return {available_data + fetched_size, result.error};
}
return len;
}

// Fetch and buffer another buffer worth of data.
auto result = platform_read(this, buf, bufsize);
FileIOResult result = platform_read(this, buf, bufsize);
size_t fetched_size = result.value;
read_limit += fetched_size;
size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
for (size_t i = 0; i < transfer_size; ++i)
dataref[i] = bufref[i];
dataref[i] = buf[i];
pos += transfer_size;
if (result.has_error() || fetched_size < to_fetch) {
if (!result.has_error())
Expand All @@ -245,6 +267,26 @@ FileIOResult File::read_unlocked(void *data, size_t len) {
return {transfer_size + available_data, result.error};
}

FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) {
// Check whether there is a character in the ungetc buffer.
size_t available_data = copy_data_from_buf(data, len);
if (available_data == len)
return available_data;

// Directly copy the data into |data|.
cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data,
len - available_data);
FileIOResult result = platform_read(this, dataref.data(), dataref.size());

if (result.has_error() || result < dataref.size()) {
if (!result.has_error())
eof = true;
else
err = true;
}
return {result + available_data, result.error};
}

int File::ungetc_unlocked(int c) {
// There is no meaning to unget if:
// 1. You are trying to push back EOF.
Expand Down Expand Up @@ -287,7 +329,7 @@ ErrorOr<int> File::seek(off_t offset, int whence) {
FileLock lock(this);
if (prev_op == FileOp::WRITE && pos > 0) {

auto buf_result = platform_write(this, buf, pos);
FileIOResult buf_result = platform_write(this, buf, pos);
if (buf_result.has_error() || buf_result.value < pos) {
err = true;
return Error(buf_result.error);
Expand Down Expand Up @@ -325,7 +367,7 @@ ErrorOr<off_t> File::tell() {

int File::flush_unlocked() {
if (prev_op == FileOp::WRITE && pos > 0) {
auto buf_result = platform_write(this, buf, pos);
FileIOResult buf_result = platform_write(this, buf, pos);
if (buf_result.has_error() || buf_result.value < pos) {
err = true;
return buf_result.error;
Expand Down
4 changes: 4 additions & 0 deletions libc/src/__support/File/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ class File {
FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);

FileIOResult read_unlocked_fbf(uint8_t *data, size_t len);
FileIOResult read_unlocked_nbf(uint8_t *data, size_t len);
size_t copy_data_from_buf(uint8_t *data, size_t len);

constexpr void adjust_buf() {
if (read_allowed() && (buf == nullptr || bufsize == 0)) {
// We should allow atleast one ungetc operation.
Expand Down

0 comments on commit c6b7bd4

Please sign in to comment.