Skip to content

[libc] Support _IONBF buffering for read_unlocked #120677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 9, 2025
Merged
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
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
Loading