Make fread() more consistent with glibc

This commit is contained in:
Justine Tunney 2024-08-17 02:57:22 -07:00
parent 1d532ba3f8
commit 8e14b27749
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
3 changed files with 57 additions and 24 deletions

View file

@ -28,21 +28,27 @@
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.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; ssize_t rc;
size_t got, toto; size_t got, toto;
toto = 0; for (toto = 0;;) {
do {
if ((rc = readv(fd, iov, iovlen)) == -1) { // perform i/o
if (toto) { if ((rc = readv(f->fd, iov, iovlen)) == -1) {
if (errno == EINTR) f->state = errno;
continue; if (toto)
return toto; return toto;
}
return -1; return -1;
} }
got = rc; got = rc;
toto += got; toto += got;
if (!got) {
f->state = EOF;
return toto;
}
// roll forward iov
// skip over empty elements
for (;;) { for (;;) {
if (!iov->iov_len) { if (!iov->iov_len) {
--iovlen; --iovlen;
@ -56,9 +62,14 @@ static ssize_t readvall(int fd, struct iovec *iov, int iovlen) {
iov->iov_len -= got; iov->iov_len -= got;
break; 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_base = NULL;
iov[1].iov_len = 0; iov[1].iov_len = 0;
} }
if (f->bufmode == _IONBF) { rc = readvall(f, iov, 2, need);
rc = readv(f->fd, iov, 2); if (rc == -1)
} else {
rc = readvall(f->fd, iov, 2);
}
if (rc == -1) {
f->state = errno;
return 0; return 0;
}
got = rc; got = rc;
// handle partial fulfillment // handle partial fulfillment
if (got < need) { if (got < need) {
got += m; got += m;
if (got % stride) {
f->state = eio();
return 0;
}
f->beg = 0; f->beg = 0;
f->end = 0; f->end = 0;
f->state = EOF;
return got / stride; return got / stride;
} }

View file

@ -33,9 +33,13 @@ void SetUpOnce(void) {
TEST(fputc, test) { TEST(fputc, test) {
ASSERT_NE(NULL, (f = fopen("hog", "w+"))); ASSERT_NE(NULL, (f = fopen("hog", "w+")));
EXPECT_EQ('h', fputc('h', f)); EXPECT_EQ('h', fputc('h', f));
EXPECT_FALSE(feof(f));
EXPECT_EQ(0xFF, fputc(-1, f)); EXPECT_EQ(0xFF, fputc(-1, f));
EXPECT_FALSE(feof(f));
EXPECT_NE(-1, fseek(f, 0, SEEK_SET)); EXPECT_NE(-1, fseek(f, 0, SEEK_SET));
EXPECT_FALSE(feof(f));
EXPECT_EQ('h', fgetc(f)); EXPECT_EQ('h', fgetc(f));
EXPECT_FALSE(feof(f));
EXPECT_EQ(0, fread(NULL, 0, 0, f)); EXPECT_EQ(0, fread(NULL, 0, 0, f));
EXPECT_FALSE(feof(f)); EXPECT_FALSE(feof(f));
EXPECT_EQ(0xFF, fgetc(f)); EXPECT_EQ(0xFF, fgetc(f));

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -45,7 +46,7 @@ TEST(fread, eofIsSticky) {
} }
TEST(fread, seekWithBuffer) { TEST(fread, seekWithBuffer) {
FILE *f; FILE* f;
char b[8] = "hellosup"; char b[8] = "hellosup";
char c[8] = {0}; char c[8] = {0};
char d[8] = {0}; char d[8] = {0};
@ -60,3 +61,31 @@ TEST(fread, seekWithBuffer) {
ASSERT_STREQ("ellos", d); ASSERT_STREQ("ellos", d);
ASSERT_EQ(0, fclose(f)); 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);
}