diff --git a/apps/buffer-test.cpp b/apps/buffer-test.cpp index ed64bfa3..09319c73 100644 --- a/apps/buffer-test.cpp +++ b/apps/buffer-test.cpp @@ -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); diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index de32c539..4f09faf1 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -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; }