Implement stdio buffering #36

Merged
apio merged 7 commits from stdio-buffers into main 2023-07-22 09:39:02 +00:00
2 changed files with 77 additions and 5 deletions
Showing only changes of commit 420270ebd4 - Show all commits

View File

@ -4,7 +4,7 @@
int main()
{
fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_mode);
fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_buf.mode);
fputs("hi!", stdout);
sleep(3);
putchar('\n');
@ -14,7 +14,7 @@ int main()
assert(f);
assert(setvbuf(f, NULL, _IOFBF, 0) == 0);
fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_mode);
fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_buf.mode);
fputs("Hello world!\nHow are you doing!\nThis is a test for many lines of buffering.\n", f);

View File

@ -69,9 +69,27 @@ static int flush_write_buffer(FILE* stream)
stream->_buf.index = 0;
stream->_buf.size = 0;
stream->_buf.status &= ~FileStatusFlags::LastWrite;
return result < 0 ? EOF : 0;
}
static int flush_read_buffer(FILE* stream)
{
if (stream->_buf.mode == _IONBF) return 0;
// Reset the stream to its expected position.
ssize_t unread_bytes = stream->_buf.size - stream->_buf.index;
lseek(stream->_fd, -unread_bytes, SEEK_CUR);
stream->_buf.index = 0;
stream->_buf.size = 0;
stream->_buf.status &= ~FileStatusFlags::LastRead;
return 0;
}
static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
{
ssize_t total_written = 0;
@ -81,6 +99,8 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
ssize_t nwritten;
if (stream->_buf.mode != _IONBF)
{
if (stream->_buf.status & FileStatusFlags::LastRead) flush_read_buffer(stream);
if ((stream->_buf.size + size) > stream->_buf.capacity)
{
if (flush_write_buffer(stream) < 0) return -1;
@ -90,6 +110,8 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
nwritten = size > size_remaining ? size_remaining : size;
memcpy(stream->_buf.buffer + stream->_buf.size, data, nwritten);
stream->_buf.status |= FileStatusFlags::LastWrite;
stream->_buf.size += nwritten;
if (stream->_buf.mode == _IOLBF && memchr(data, '\n', nwritten))
@ -108,6 +130,51 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
return total_written;
}
static ssize_t read_data_into_buffer(FILE* stream)
{
stream->_buf.index = 0;
ssize_t nread = read(stream->_fd, stream->_buf.buffer, stream->_buf.capacity);
if (nread >= 0) stream->_buf.size = nread;
stream->_buf.status |= FileStatusFlags::LastRead;
return nread;
}
static ssize_t read_from_buffer(FILE* stream, u8* data, ssize_t size)
{
ssize_t total_read = 0;
while (size > 0)
{
ssize_t nread;
if (stream->_buf.mode != _IONBF)
{
if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream);
if (stream->_buf.size == stream->_buf.index)
{
ssize_t rc;
if ((rc = read_data_into_buffer(stream)) < 0) return -1;
if (rc == 0) return total_read;
}
ssize_t size_remaining = stream->_buf.size - stream->_buf.index;
nread = size > size_remaining ? size_remaining : size;
memcpy(data, stream->_buf.buffer + stream->_buf.index, nread);
stream->_buf.index += nread;
}
else
nread = read(stream->_fd, data, size > BUFSIZ ? BUFSIZ : size);
if (nread < 0) return nread;
if (nread == 0) return total_read;
size -= nread;
data += nread;
total_read += nread;
}
return total_read;
}
extern "C"
{
void _init_stdio()
@ -117,7 +184,12 @@ extern "C"
int fflush(FILE* stream)
{
if (stream && stream->_buf.mode != _IONBF) flush_write_buffer(stream);
if (stream && stream->_buf.mode != _IONBF)
{
if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream);
else if (stream->_buf.status & FileStatusFlags::LastRead)
flush_read_buffer(stream);
}
else if (!stream)
{
for (int i = 0; i < FOPEN_MAX; i++)
@ -246,7 +318,7 @@ extern "C"
{
if (size * nmemb == 0) return 0;
ssize_t nread = read(stream->_fd, buf, size * nmemb);
ssize_t nread = read_from_buffer(stream, (u8*)buf, size * nmemb);
if (nread < 0)
{
@ -351,7 +423,7 @@ extern "C"
int fgetc(FILE* stream)
{
u8 value;
ssize_t rc = read(stream->_fd, &value, 1);
ssize_t rc = read_from_buffer(stream, &value, 1);
if (rc <= 0) return EOF;
return value;
}