From 8e14b27749e425f8f4496bdeec767070ec7ed2b0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 17 Aug 2024 02:57:22 -0700 Subject: [PATCH] Make fread() more consistent with glibc --- libc/stdio/fread_unlocked.c | 46 ++++++++++++++++++------------------ test/libc/stdio/fputc_test.c | 4 ++++ test/libc/stdio/fread_test.c | 31 +++++++++++++++++++++++- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/libc/stdio/fread_unlocked.c b/libc/stdio/fread_unlocked.c index 2ebc08713..ef341d8ec 100644 --- a/libc/stdio/fread_unlocked.c +++ b/libc/stdio/fread_unlocked.c @@ -28,21 +28,27 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" -static ssize_t readvall(int fd, struct iovec *iov, int iovlen) { +static ssize_t readvall(FILE *f, struct iovec *iov, int iovlen, size_t need) { ssize_t rc; size_t got, toto; - toto = 0; - do { - if ((rc = readv(fd, iov, iovlen)) == -1) { - if (toto) { - if (errno == EINTR) - continue; + for (toto = 0;;) { + + // perform i/o + if ((rc = readv(f->fd, iov, iovlen)) == -1) { + f->state = errno; + if (toto) return toto; - } return -1; } got = rc; toto += got; + if (!got) { + f->state = EOF; + return toto; + } + + // roll forward iov + // skip over empty elements for (;;) { if (!iov->iov_len) { --iovlen; @@ -56,9 +62,14 @@ static ssize_t readvall(int fd, struct iovec *iov, int iovlen) { iov->iov_len -= got; break; } + if (!iovlen) + return toto; } - } while (got && iovlen); - return toto; + + // don't trigger eof condition if we're rolling greed to fill buffer + if (toto >= need) + return toto; + } } /** @@ -134,27 +145,16 @@ size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) { iov[1].iov_base = NULL; iov[1].iov_len = 0; } - if (f->bufmode == _IONBF) { - rc = readv(f->fd, iov, 2); - } else { - rc = readvall(f->fd, iov, 2); - } - if (rc == -1) { - f->state = errno; + rc = readvall(f, iov, 2, need); + if (rc == -1) return 0; - } got = rc; // handle partial fulfillment if (got < need) { got += m; - if (got % stride) { - f->state = eio(); - return 0; - } f->beg = 0; f->end = 0; - f->state = EOF; return got / stride; } diff --git a/test/libc/stdio/fputc_test.c b/test/libc/stdio/fputc_test.c index 6520e77bb..b63ecc1c3 100644 --- a/test/libc/stdio/fputc_test.c +++ b/test/libc/stdio/fputc_test.c @@ -33,9 +33,13 @@ void SetUpOnce(void) { TEST(fputc, test) { ASSERT_NE(NULL, (f = fopen("hog", "w+"))); EXPECT_EQ('h', fputc('h', f)); + EXPECT_FALSE(feof(f)); EXPECT_EQ(0xFF, fputc(-1, f)); + EXPECT_FALSE(feof(f)); EXPECT_NE(-1, fseek(f, 0, SEEK_SET)); + EXPECT_FALSE(feof(f)); EXPECT_EQ('h', fgetc(f)); + EXPECT_FALSE(feof(f)); EXPECT_EQ(0, fread(NULL, 0, 0, f)); EXPECT_FALSE(feof(f)); EXPECT_EQ(0xFF, fgetc(f)); diff --git a/test/libc/stdio/fread_test.c b/test/libc/stdio/fread_test.c index 8e5e690e5..2fc4f00a5 100644 --- a/test/libc/stdio/fread_test.c +++ b/test/libc/stdio/fread_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" @@ -45,7 +46,7 @@ TEST(fread, eofIsSticky) { } TEST(fread, seekWithBuffer) { - FILE *f; + FILE* f; char b[8] = "hellosup"; char c[8] = {0}; char d[8] = {0}; @@ -60,3 +61,31 @@ TEST(fread, seekWithBuffer) { ASSERT_STREQ("ellos", d); ASSERT_EQ(0, fclose(f)); } + +TEST(fread, zero) { + FILE* f; + char buf[8] = {0}; + ASSERT_NE(NULL, (f = fopen("foo", "w"))); + ASSERT_EQ(2, fwrite("hi", 1, 2, f)); + ASSERT_EQ(0, fclose(f)); + ASSERT_NE(NULL, (f = fopen("foo", "r"))); + ASSERT_EQ(0, fread(buf, 0, 0, f)); + ASSERT_EQ(0, ferror(stdin)); + ASSERT_EQ(0, feof(stdin)); + ASSERT_STREQ("", buf); + ASSERT_EQ(0, fclose(f)); +} + +TEST(fread, partial) { + FILE* f; + char buf[8] = {0}; + ASSERT_NE(NULL, (f = fopen("foo", "w"))); + ASSERT_EQ(2, fwrite("hi", 1, 2, f)); + ASSERT_EQ(0, fclose(f)); + ASSERT_NE(NULL, (f = fopen("foo", "r"))); + ASSERT_EQ(0, fread(buf, 8, 1, f)); + ASSERT_EQ(0, ferror(stdin)); + ASSERT_EQ(0, feof(stdin)); + ASSERT_EQ(0, fclose(f)); + ASSERT_STREQ("hi", buf); +}