Compare commits

..

No commits in common. "77022abafdc60fc965de0b9667f7272e9aaffb0a" and "a3ed950be8da30d4d7ad9ce85242ad5495a3a8aa" have entirely different histories.

3 changed files with 54 additions and 151 deletions

View File

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

View File

@ -14,19 +14,16 @@
typedef struct typedef struct
{ {
int _fd; // The underlying file descriptor. int _fd;
int _err; // The error status flag. int _err;
int _eof; // The end-of-file status flag. int _eof;
struct size_t _bufcap;
{ size_t _bufsize;
size_t capacity; // The buffer's total capacity. size_t _bufindex;
size_t size; // The buffer's used size. char* _buffer;
size_t index; // The read index into the buffer. int _flags;
char* buffer; // The memory used for the buffer. int _pending;
int status; // The buffer status flags. int _mode;
int mode; // The buffering mode.
} _buf;
int _flags; // The file access mode with which the file was opened.
} FILE; } FILE;
#define EOF -1 #define EOF -1

View File

@ -13,12 +13,7 @@ FILE* stdout = nullptr;
FILE* s_open_files[FOPEN_MAX]; FILE* s_open_files[FOPEN_MAX];
enum FileStatusFlags #define FFLAG_FREE_AFTER_BUFFER_USE (1 << 0)
{
BufferIsMalloced = (1 << 0),
LastRead = (1 << 1),
LastWrite = (1 << 2),
};
static const char* read_tmpdir() static const char* read_tmpdir()
{ {
@ -62,34 +57,16 @@ static int fdopen_check_compatible_mode(int fd, int new_flags)
static int flush_write_buffer(FILE* stream) static int flush_write_buffer(FILE* stream)
{ {
if (stream->_buf.mode == _IONBF) return 0; if (stream->_mode == _IONBF) return 0;
ssize_t result = write(stream->_fd, stream->_buf.buffer, stream->_buf.size); ssize_t result = write(stream->_fd, stream->_buffer, stream->_bufsize);
stream->_buf.index = 0; stream->_bufindex = 0;
stream->_buf.size = 0; stream->_bufsize = 0;
stream->_buf.status &= ~FileStatusFlags::LastWrite;
return result < 0 ? EOF : 0; 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) static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
{ {
ssize_t total_written = 0; ssize_t total_written = 0;
@ -97,24 +74,20 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
while (size > 0) while (size > 0)
{ {
ssize_t nwritten; ssize_t nwritten;
if (stream->_buf.mode != _IONBF) if (stream->_mode != _IONBF)
{ {
if (stream->_buf.status & FileStatusFlags::LastRead) flush_read_buffer(stream); if ((stream->_bufsize + size) > stream->_bufcap)
if ((stream->_buf.size + size) > stream->_buf.capacity)
{ {
if (flush_write_buffer(stream) < 0) return -1; if (flush_write_buffer(stream) < 0) return -1;
} }
ssize_t size_remaining = stream->_buf.capacity - stream->_buf.size; ssize_t size_remaining = stream->_bufcap - stream->_bufsize;
nwritten = size > size_remaining ? size_remaining : size; nwritten = size > size_remaining ? size_remaining : size;
memcpy(stream->_buf.buffer + stream->_buf.size, data, nwritten); memcpy(stream->_buffer + stream->_bufsize, data, nwritten);
stream->_buf.status |= FileStatusFlags::LastWrite; stream->_bufsize += nwritten;
stream->_buf.size += nwritten; if (stream->_mode == _IOLBF && memchr(data, '\n', nwritten))
if (stream->_buf.mode == _IOLBF && memchr(data, '\n', nwritten))
{ {
if (flush_write_buffer(stream) < 0) return -1; if (flush_write_buffer(stream) < 0) return -1;
} }
@ -130,51 +103,6 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
return total_written; 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" extern "C"
{ {
void _init_stdio() void _init_stdio()
@ -184,12 +112,7 @@ extern "C"
int fflush(FILE* stream) int fflush(FILE* stream)
{ {
if (stream && stream->_buf.mode != _IONBF) if (stream && stream->_mode != _IONBF) flush_write_buffer(stream);
{
if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream);
else if (stream->_buf.status & FileStatusFlags::LastRead)
flush_read_buffer(stream);
}
else if (!stream) else if (!stream)
{ {
for (int i = 0; i < FOPEN_MAX; i++) for (int i = 0; i < FOPEN_MAX; i++)
@ -219,12 +142,12 @@ extern "C"
f->_fd = fd; f->_fd = fd;
clearerr(f); clearerr(f);
f->_pending = 0;
f->_flags = flags; f->_flags = flags;
f->_buf.status = 0; f->_mode = isatty(fd) ? _IOLBF : _IOFBF;
f->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF; f->_bufsize = f->_bufindex = 0;
f->_buf.size = f->_buf.index = 0; f->_buffer = nullptr;
f->_buf.buffer = nullptr; setvbuf(f, NULL, f->_mode, 0);
setvbuf(f, NULL, f->_buf.mode, 0);
s_open_files[fd] = f; s_open_files[fd] = f;
@ -245,12 +168,12 @@ extern "C"
f->_fd = fd; f->_fd = fd;
clearerr(f); clearerr(f);
f->_pending = 0;
f->_flags = flags; f->_flags = flags;
f->_buf.status = 0; f->_mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode;
f->_buf.mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode; f->_bufsize = f->_bufindex = 0;
f->_buf.size = f->_buf.index = 0; f->_buffer = nullptr;
f->_buf.buffer = nullptr; setvbuf(f, NULL, f->_mode, 0);
setvbuf(f, NULL, f->_buf.mode, 0);
s_open_files[fd] = f; s_open_files[fd] = f;
@ -272,7 +195,7 @@ extern "C"
s_open_files[stream->_fd] = nullptr; s_open_files[stream->_fd] = nullptr;
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
if (!path) { fail("FIXME: freopen() called with path=nullptr"); } if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
@ -282,12 +205,12 @@ extern "C"
stream->_fd = fd; stream->_fd = fd;
clearerr(stream); clearerr(stream);
stream->_pending = 0;
stream->_flags = flags; stream->_flags = flags;
stream->_buf.status = 0; stream->_mode = isatty(fd) ? _IOLBF : _IOFBF;
stream->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF; stream->_bufsize = stream->_bufindex = 0;
stream->_buf.size = stream->_buf.index = 0; stream->_buffer = nullptr;
stream->_buf.buffer = nullptr; setvbuf(stream, NULL, stream->_mode, 0);
setvbuf(stream, NULL, stream->_buf.mode, 0);
s_open_files[fd] = stream; s_open_files[fd] = stream;
@ -300,7 +223,7 @@ extern "C"
if (close(stream->_fd) < 0) return EOF; if (close(stream->_fd) < 0) return EOF;
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
s_open_files[stream->_fd] = nullptr; s_open_files[stream->_fd] = nullptr;
@ -318,7 +241,7 @@ extern "C"
{ {
if (size * nmemb == 0) return 0; if (size * nmemb == 0) return 0;
ssize_t nread = read_from_buffer(stream, (u8*)buf, size * nmemb); ssize_t nread = read(stream->_fd, buf, size * nmemb);
if (nread < 0) if (nread < 0)
{ {
@ -350,8 +273,6 @@ extern "C"
int fseek(FILE* stream, long offset, int whence) int fseek(FILE* stream, long offset, int whence)
{ {
fflush(stream);
long result = lseek(stream->_fd, offset, whence); long result = lseek(stream->_fd, offset, whence);
if (result < 0) return -1; if (result < 0) return -1;
@ -363,15 +284,11 @@ extern "C"
long ftell(FILE* stream) long ftell(FILE* stream)
{ {
fflush(stream);
return lseek(stream->_fd, 0, SEEK_CUR); return lseek(stream->_fd, 0, SEEK_CUR);
} }
void rewind(FILE* stream) void rewind(FILE* stream)
{ {
fflush(stream);
lseek(stream->_fd, 0, SEEK_SET); lseek(stream->_fd, 0, SEEK_SET);
clearerr(stream); clearerr(stream);
@ -389,8 +306,6 @@ extern "C"
int fsetpos(FILE* stream, const fpos_t* pos) int fsetpos(FILE* stream, const fpos_t* pos)
{ {
fflush(stream);
return fseek(stream, *pos, SEEK_SET); return fseek(stream, *pos, SEEK_SET);
} }
@ -431,7 +346,7 @@ extern "C"
int fgetc(FILE* stream) int fgetc(FILE* stream)
{ {
u8 value; u8 value;
ssize_t rc = read_from_buffer(stream, &value, 1); ssize_t rc = read(stream->_fd, &value, 1);
if (rc <= 0) return EOF; if (rc <= 0) return EOF;
return value; return value;
} }
@ -688,44 +603,35 @@ extern "C"
return f; return f;
} }
int ungetc(int c, FILE* stream) int ungetc(int, FILE*)
{ {
if (stream->_buf.index == 0) fail("FIXME: ungetc: not implemented");
return EOF; // No data currently in the read buffer, or no data has been read from it.
if (stream->_buf.mode == _IONBF)
return EOF; // FIXME: C doesn't state that ungetc() should only work on buffered streams.
stream->_buf.index--;
stream->_buf.buffer[stream->_buf.index] = (char)c;
return 0;
} }
int setvbuf(FILE* stream, char* buf, int mode, size_t size) int setvbuf(FILE* stream, char* buf, int mode, size_t size)
{ {
int status = 0; int pending = 0;
if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1; if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1;
if (stream->_buf.size != 0 || stream->_buf.index != 0) return -1; // Buffer is already in use. if (stream->_bufsize != 0 || stream->_bufindex != 0) return -1; // Buffer is already in use.
if (mode != _IONBF && buf == NULL) if (mode != _IONBF && buf == NULL)
{ {
size = BUFSIZ; size = BUFSIZ;
buf = (char*)calloc(size, 1); buf = (char*)calloc(size, 1);
if (!buf) return -1; if (!buf) return -1;
status = FileStatusFlags::BufferIsMalloced; pending = FFLAG_FREE_AFTER_BUFFER_USE;
} }
else if (mode == _IONBF) else
{ {
buf = NULL; buf = NULL;
size = 0; size = 0;
} }
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
stream->_buf.buffer = buf; stream->_buffer = buf;
stream->_buf.capacity = size; stream->_bufcap = size;
stream->_buf.mode = mode; stream->_mode = mode;
stream->_buf.status = status; stream->_pending = pending;
return 0; return 0;
} }