From 548387c43affdebfbb371665a0dc478b86cb9f51 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Wed, 1 Apr 2026 18:51:13 +0100 Subject: [PATCH] libc: io: implement file io buffering and internal locking for concurrency --- lib/libc/io/stdio/fclose.c | 9 +- lib/libc/io/stdio/{ferr.c => ferror.c} | 2 +- lib/libc/io/stdio/fgetc.c | 28 +- lib/libc/io/stdio/fgets.c | 7 +- lib/libc/io/stdio/file.c | 345 +++++++++++++++++++++---- lib/libc/io/stdio/file.h | 25 +- lib/libc/io/stdio/fopen.c | 6 + lib/libc/io/stdio/fprintf.c | 6 + lib/libc/io/stdio/fputc.c | 27 ++ lib/libc/io/stdio/fputs.c | 31 +++ lib/libc/io/stdio/fread.c | 26 ++ lib/libc/io/stdio/fseek.c | 8 + lib/libc/io/stdio/ftell.c | 8 + lib/libc/io/stdio/fwrite.c | 29 +++ lib/libc/io/stdio/printf.c | 12 + lib/libc/io/stdio/ringbuf.c | 270 +++++++++++++++++++ lib/libc/io/stdio/ringbuf.h | 53 ++++ lib/libc/io/stdio/setvbuf.c | 39 +++ lib/libc/io/stdio/stderr.c | 14 + lib/libc/io/stdio/stdin.c | 14 + lib/libc/io/stdio/stdout.c | 14 + lib/libc/io/stdio/vprintf.c | 27 ++ lib/libc/io/unistd/write.c | 21 ++ 23 files changed, 948 insertions(+), 73 deletions(-) rename lib/libc/io/stdio/{ferr.c => ferror.c} (61%) create mode 100644 lib/libc/io/stdio/fprintf.c create mode 100644 lib/libc/io/stdio/fseek.c create mode 100644 lib/libc/io/stdio/ftell.c create mode 100644 lib/libc/io/stdio/printf.c create mode 100644 lib/libc/io/stdio/ringbuf.c create mode 100644 lib/libc/io/stdio/ringbuf.h create mode 100644 lib/libc/io/stdio/setvbuf.c create mode 100644 lib/libc/io/stdio/stderr.c create mode 100644 lib/libc/io/stdio/stdin.c create mode 100644 lib/libc/io/stdio/stdout.c create mode 100644 lib/libc/io/stdio/vprintf.c diff --git a/lib/libc/io/stdio/fclose.c b/lib/libc/io/stdio/fclose.c index 0a1f3a9..15041a8 100644 --- a/lib/libc/io/stdio/fclose.c +++ b/lib/libc/io/stdio/fclose.c @@ -15,10 +15,13 @@ int fclose(struct __opaque_file *stream) close(stream->f_fd); - if (stream->f_buf) { - free(stream->f_buf); + if (stream->f_buf.buf_ptr) { + free(stream->f_buf.buf_ptr); + } + + if (!(stream->f_flags & FILE_STATIC)) { + free(stream); } - free(stream); return SUCCESS; } diff --git a/lib/libc/io/stdio/ferr.c b/lib/libc/io/stdio/ferror.c similarity index 61% rename from lib/libc/io/stdio/ferr.c rename to lib/libc/io/stdio/ferror.c index c69aa41..ce15ebb 100644 --- a/lib/libc/io/stdio/ferr.c +++ b/lib/libc/io/stdio/ferror.c @@ -1,6 +1,6 @@ #include "file.h" -int ferr(struct __opaque_file *stream) +int ferror(struct __opaque_file *stream) { return (stream->f_flags & FILE_ERR) != 0; } diff --git a/lib/libc/io/stdio/fgetc.c b/lib/libc/io/stdio/fgetc.c index d36c518..88b9104 100644 --- a/lib/libc/io/stdio/fgetc.c +++ b/lib/libc/io/stdio/fgetc.c @@ -3,29 +3,25 @@ #include #include -int fgetc(struct __opaque_file *stream) +int __fgetc(struct __opaque_file *stream) { if (stream->f_flags & (FILE_EOF | FILE_ERR)) { return EOF; } - size_t available = __libc_file_available(stream); - if (available == 0) { - int err = __libc_file_refill(stream); - if (err != SUCCESS) { - __set_errno(err); - stream->f_flags |= FILE_ERR; - return EOF; - } - - available = __libc_file_available(stream); - } - - if (available == 0) { - stream->f_flags |= FILE_EOF; + char c = 0; + int ret = __libc_file_read(stream, &c, 1); + if (ret < 1) { return EOF; } - char c = stream->f_buf[stream->f_buf_readptr++]; return c; } + +int fgetc(struct __opaque_file *stream) +{ + __libc_file_lock(stream); + int ret = __fgetc(stream); + __libc_file_unlock(stream); + return ret; +} diff --git a/lib/libc/io/stdio/fgets.c b/lib/libc/io/stdio/fgets.c index a484ed6..8da8c93 100644 --- a/lib/libc/io/stdio/fgets.c +++ b/lib/libc/io/stdio/fgets.c @@ -7,7 +7,9 @@ char *fgets( int count, struct __opaque_file *restrict stream) { + __libc_file_lock(stream); if (stream->f_flags & (FILE_EOF | FILE_ERR)) { + __libc_file_unlock(stream); return NULL; } @@ -28,13 +30,16 @@ char *fgets( } } - if (ferr(stream)) { + if (ferror(stream)) { + __libc_file_unlock(stream); return NULL; } if (feof(stream) && i == 0) { + __libc_file_unlock(stream); return NULL; } + __libc_file_unlock(stream); return str; } diff --git a/lib/libc/io/stdio/file.c b/lib/libc/io/stdio/file.c index ed7774c..dd2cb0f 100644 --- a/lib/libc/io/stdio/file.c +++ b/lib/libc/io/stdio/file.c @@ -1,76 +1,329 @@ #include "file.h" #include +#include +#include +#include #include #include #include -#define DEFAULT_BUFFER_SIZE 4096 +#define OP_READ 1 +#define OP_WRITE 2 -#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0) +#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0) -int __libc_file_refill(struct __opaque_file *f) +static long clear_buf(struct __opaque_file *f) { - f->f_buf_datalen = 0; - f->f_buf_readptr = 0; + __libc_ringbuf_clear(&f->f_buf); + lseek(f->f_fd, f->f_seek, SEEK_SET); + return 0; +} - if (f->f_fd < 0) { - return EBADF; +static long refill_buf(struct __opaque_file *f) +{ + kern_tracef("refill"); + char *buf = NULL; + size_t available = 0; + long r = 0; + size_t nr_read = 0; + + while (1) { + __libc_ringbuf_get_write_buffer(&f->f_buf, &buf, &available); + if (available == 0) { + __libc_ringbuf_put_write_buffer( + &f->f_buf, + &buf, + &available); + break; + } + + kern_tracef("read(%d, %p, %zu)", f->f_fd, buf, available); + r = read(f->f_fd, buf, available); + if (r <= 0) { + break; + } + + available = r; + nr_read += r; + __libc_ringbuf_put_write_buffer(&f->f_buf, &buf, &available); } - if (f->f_flags & FILE_ERR) { - return EIO; + if (nr_read > 0) { + return nr_read; } - if (f->f_flags & FILE_EOF) { - return SUCCESS; + if (r < 0) { + return -errno; } - if (!f->f_buf) { - f->f_buf_max = DEFAULT_BUFFER_SIZE; - f->f_buf = malloc(f->f_buf_max); - if (!f->f_buf) { - f->f_buf_max = 0; - return ENOMEM; + return nr_read; +} + +static long flush_buf(struct __opaque_file *f) +{ + const char *buf = NULL; + size_t available = 0; + long r = 0; + size_t nr_written = 0; + + while (1) { + __libc_ringbuf_get_read_buffer(&f->f_buf, &buf, &available); + if (available == 0) { + __libc_ringbuf_put_read_buffer( + &f->f_buf, + &buf, + &available); + break; + } + + r = write(f->f_fd, buf, available); + kern_tracef( + "write(%d, %p, %zu) = %ld", + f->f_fd, + buf, + available, + r); + if (r < 0) { + break; + } + + available = r; + nr_written += r; + __libc_ringbuf_put_read_buffer(&f->f_buf, &buf, &available); + } + + if (nr_written > 0) { + return nr_written; + } + + if (r < 0) { + return -errno; + } + + return 0; +} + +void __libc_file_lock(struct __opaque_file *f) +{ + kern_futex_t expected = 0; + while (1) { + kern_tracef("lock=%u (%p)", f->f_lock, f); + expected = 0; + if (__atomic_compare_exchange_n( + &f->f_lock, + &expected, + 1, + 0, + __ATOMIC_ACQUIRE, + __ATOMIC_ACQUIRE)) { + kern_tracef("locked=%u (%p)", f->f_lock, f); + return; + } + + kern_tracef("wait=%u (%p)", expected, f); + futex_wait(&f->f_lock, 1, FUTEX_PRIVATE); + } +} + +void __libc_file_unlock(struct __opaque_file *f) +{ + f->f_lock = 0; + futex_wake(&f->f_lock, 1, FUTEX_PRIVATE); + kern_tracef("unlocked=%u (%p)", f->f_lock, f); +} + +static long read_buf(struct __opaque_file *f, void *out, size_t count) +{ + kern_tracef("read_buf(%p, %zu)", out, count); + char *dest = out; + size_t nr_read = 0; + long r = 0; + + while (nr_read < count) { + r = __libc_ringbuf_read( + &f->f_buf, + dest + nr_read, + count - nr_read); + if (r < 0) { + break; + } + + nr_read += r; + + if (r == 0) { + r = refill_buf(f); + if (r <= 0) { + break; + } } } - long r = read(f->f_fd, f->f_buf, f->f_buf_max); if (r < 0) { - return -r; + return r; } - f->f_buf_datalen = r; - return SUCCESS; + return nr_read; } -int __libc_file_read(struct __opaque_file *f, void *out, size_t count) +static long read_nobuf(struct __opaque_file *f, void *out, size_t count) { - if (f->f_fd < 0) { - return EBADF; - } - - if (f->f_flags & FILE_ERR) { - return EIO; - } - - if (f->f_flags & FILE_EOF) { - return SUCCESS; - } - - size_t available = f->f_buf_datalen - f->f_buf_readptr; - if (count > available) { - count = available; - } - - memcpy(out, f->f_buf + f->f_buf_readptr, count); - - f->f_buf_readptr += count; - - return count; + kern_tracef("read_nobuf"); + return read(f->f_fd, out, count); } -size_t __libc_file_available(struct __opaque_file *f) +long __libc_file_read(struct __opaque_file *f, void *out, size_t count) { - return f->f_buf_datalen - f->f_buf_readptr; + long ret = 0; + if (f->f_prev != OP_READ) { + ret = flush_buf(f); + clear_buf(f); + } + + if (ret != 0) { + return __set_errno(ret); + } + + switch (f->f_buffer_mode) { + case _IOFBF: + case _IOLBF: + ret = read_buf(f, out, count); + break; + case _IONBF: + ret = read_nobuf(f, out, count); + break; + default: + ret = -EINVAL; + break; + } + + f->f_prev = OP_READ; + + if (ret < 0) { + f->f_flags |= FILE_ERR; + return __set_errno(-ret); + } + + if (ret == 0) { + f->f_flags |= FILE_EOF; + } + + f->f_seek += ret; + + return ret; +} + +static long write_fullbuf(struct __opaque_file *f, const void *p, size_t count) +{ + const char *src = p; + size_t nr_written = 0; + long r = 0; + + while (nr_written < count) { + r = __libc_ringbuf_write( + &f->f_buf, + src + nr_written, + count - nr_written); + if (r < 0) { + break; + } + + nr_written += r; + + if (r == 0) { + r = flush_buf(f); + if (r <= 0) { + break; + } + } + } + + if (r < 0) { + return r; + } + + return nr_written; +} + +static long write_linebuf( + struct __opaque_file *f, + const void *out, + size_t count) +{ + const char *s = out; + size_t nr_written = 0; + long r = 0; + + while (nr_written < count) { + char c = s[nr_written]; + + r = __libc_ringbuf_write(&f->f_buf, &c, 1); + if (r < 0) { + break; + } + + nr_written += r; + + if (r == 0 || c == '\n') { + r = flush_buf(f); + if (r < 0) { + break; + } + } + } + + if (r < 0) { + return r; + } + + return nr_written; +} + +static long write_nobuf(struct __opaque_file *f, const void *out, size_t count) +{ + return write(f->f_fd, out, count); +} + +long __libc_file_write(struct __opaque_file *f, const void *buf, size_t count) +{ + if (f->f_lock != 1) { + kern_tracef("file is not locked!!!"); + } + long ret = 0; + if (f->f_prev != OP_WRITE) { + ret = clear_buf(f); + } + + if (ret != 0) { + return __set_errno(ret); + } + + switch (f->f_buffer_mode) { + case _IOFBF: + ret = write_fullbuf(f, buf, count); + break; + case _IOLBF: + ret = write_linebuf(f, buf, count); + break; + case _IONBF: + ret = write_nobuf(f, buf, count); + break; + default: + ret = -EINVAL; + break; + } + + f->f_prev = OP_WRITE; + + if (ret < 0) { + f->f_flags |= FILE_ERR; + return __set_errno(-ret); + } + + if (ret == 0) { + f->f_flags |= FILE_EOF; + } + + f->f_seek += ret; + return ret; } diff --git a/lib/libc/io/stdio/file.h b/lib/libc/io/stdio/file.h index 2d1c90d..30d1c36 100644 --- a/lib/libc/io/stdio/file.h +++ b/lib/libc/io/stdio/file.h @@ -1,25 +1,38 @@ #ifndef _LIBC_IO_FILE_H_ #define _LIBC_IO_FILE_H_ +#include "ringbuf.h" + +#include #include enum file_flags { FILE_EOF = 0x01u, FILE_ERR = 0x02u, FILE_BIN = 0x04u, + FILE_STATIC = 0x08u, }; struct __opaque_file { enum file_flags f_flags; + kern_futex_t f_lock; + int f_prev; + int f_buffer_mode; int f_fd; char f_unget; - char *f_buf; - size_t f_buf_datalen, f_buf_max; - size_t f_buf_readptr; + /* the current seek position of the buffered data (i.e. the offset of + * the data that would be returned by reading f_buf). */ + size_t f_seek; + struct ringbuf f_buf; }; -extern int __libc_file_refill(struct __opaque_file *f); -extern int __libc_file_read(struct __opaque_file *f, void *out, size_t count); -extern size_t __libc_file_available(struct __opaque_file *f); +extern void __libc_file_lock(struct __opaque_file *f); +extern void __libc_file_unlock(struct __opaque_file *f); + +extern long __libc_file_read(struct __opaque_file *f, void *p, size_t count); +extern long __libc_file_write( + struct __opaque_file *f, + const void *p, + size_t count); #endif diff --git a/lib/libc/io/stdio/fopen.c b/lib/libc/io/stdio/fopen.c index 9b059a1..358cb4b 100644 --- a/lib/libc/io/stdio/fopen.c +++ b/lib/libc/io/stdio/fopen.c @@ -143,6 +143,12 @@ struct __opaque_file *fopen(const char *path, const char *mode) return NULL; } + if (fflags & FILE_BIN) { + out->f_buffer_mode = _IOFBF; + } else { + out->f_buffer_mode = _IOLBF; + } + out->f_fd = fd; out->f_flags = fflags; __set_errno(SUCCESS); diff --git a/lib/libc/io/stdio/fprintf.c b/lib/libc/io/stdio/fprintf.c new file mode 100644 index 0000000..35e7d36 --- /dev/null +++ b/lib/libc/io/stdio/fprintf.c @@ -0,0 +1,6 @@ +#include "file.h" + +int fprintf(struct __opaque_file *stream, const char *format, ...) +{ + return 0; +} diff --git a/lib/libc/io/stdio/fputc.c b/lib/libc/io/stdio/fputc.c index e69de29..df41978 100644 --- a/lib/libc/io/stdio/fputc.c +++ b/lib/libc/io/stdio/fputc.c @@ -0,0 +1,27 @@ +#include "file.h" + +#include +#include + +int __fputc(int c, struct __opaque_file *stream) +{ + if (stream->f_flags & (FILE_EOF | FILE_ERR)) { + return EOF; + } + + char cv = c; + int ret = __libc_file_write(stream, &cv, 1); + if (ret < 1) { + return EOF; + } + + return c; +} + +int fputc(int c, struct __opaque_file *stream) +{ + __libc_file_lock(stream); + int ret = __fputc(c, stream); + __libc_file_unlock(stream); + return ret; +} diff --git a/lib/libc/io/stdio/fputs.c b/lib/libc/io/stdio/fputs.c index e69de29..d8c005c 100644 --- a/lib/libc/io/stdio/fputs.c +++ b/lib/libc/io/stdio/fputs.c @@ -0,0 +1,31 @@ +#include "file.h" + +#include + +extern int __fputc(int c, struct __opaque_file *stream); + +int fputs(const char *restrict str, struct __opaque_file *restrict stream) +{ + __libc_file_lock(stream); + if (stream->f_flags & (FILE_EOF | FILE_ERR)) { + __libc_file_unlock(stream); + return EOF; + } + + size_t i = 0; + int err = 0; + for (i = 0; str[i]; i++) { + err = __fputc(str[i], stream); + if (err < 0) { + break; + } + } + + __libc_file_unlock(stream); + + if (i > 0) { + return i; + } + + return err; +} diff --git a/lib/libc/io/stdio/fread.c b/lib/libc/io/stdio/fread.c index e69de29..17342ab 100644 --- a/lib/libc/io/stdio/fread.c +++ b/lib/libc/io/stdio/fread.c @@ -0,0 +1,26 @@ +#include "file.h" + +#include + +size_t fread(void *buf, size_t size, size_t count, struct __opaque_file *stream) +{ + __libc_file_lock(stream); + size_t total = size * count; + if (total == 0) { + __libc_file_unlock(stream); + return 0; + } + + long ret = __libc_file_read(stream, buf, total); + if (ret == 0) { + stream->f_flags |= FILE_EOF; + } + + if (ret < 0) { + stream->f_flags |= FILE_ERR; + ret = 0; + } + + __libc_file_unlock(stream); + return ret; +} diff --git a/lib/libc/io/stdio/fseek.c b/lib/libc/io/stdio/fseek.c new file mode 100644 index 0000000..16d498f --- /dev/null +++ b/lib/libc/io/stdio/fseek.c @@ -0,0 +1,8 @@ +#include "file.h" + +#include + +int fseek(struct __opaque_file *stream, long offset, int origin) +{ + return lseek(stream->f_fd, offset, origin); +} diff --git a/lib/libc/io/stdio/ftell.c b/lib/libc/io/stdio/ftell.c new file mode 100644 index 0000000..7d50be0 --- /dev/null +++ b/lib/libc/io/stdio/ftell.c @@ -0,0 +1,8 @@ +#include "file.h" + +#include + +long ftell(struct __opaque_file *stream) +{ + return lseek(stream->f_fd, 0, SEEK_CUR); +} diff --git a/lib/libc/io/stdio/fwrite.c b/lib/libc/io/stdio/fwrite.c index e69de29..aad26b1 100644 --- a/lib/libc/io/stdio/fwrite.c +++ b/lib/libc/io/stdio/fwrite.c @@ -0,0 +1,29 @@ +#include "file.h" + +#include + +size_t fwrite( + const void *buf, + size_t size, + size_t count, + struct __opaque_file *stream) +{ + __libc_file_lock(stream); + size_t total = size * count; + if (total == 0) { + return 0; + } + + long ret = __libc_file_write(stream, buf, total); + if (ret == 0) { + stream->f_flags |= FILE_EOF; + } + + if (ret < 0) { + stream->f_flags |= FILE_ERR; + ret = 0; + } + + __libc_file_unlock(stream); + return ret; +} diff --git a/lib/libc/io/stdio/printf.c b/lib/libc/io/stdio/printf.c new file mode 100644 index 0000000..67e4a1c --- /dev/null +++ b/lib/libc/io/stdio/printf.c @@ -0,0 +1,12 @@ +#include +#include + +int printf(const char *format, ...) +{ + va_list arg; + va_start(arg, format); + int ret = vprintf(format, arg); + va_end(arg); + + return ret; +} diff --git a/lib/libc/io/stdio/ringbuf.c b/lib/libc/io/stdio/ringbuf.c new file mode 100644 index 0000000..d336f8c --- /dev/null +++ b/lib/libc/io/stdio/ringbuf.c @@ -0,0 +1,270 @@ +#include "file.h" + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_BUFFER_SIZE 4096 + +static int ringbuf_init(struct ringbuf *buf) +{ + if (buf->buf_ptr) { + return SUCCESS; + } + + if (!buf->buf_max) { + buf->buf_max = DEFAULT_BUFFER_SIZE; + } + + buf->buf_ptr = malloc(buf->buf_max); + if (!buf->buf_ptr) { + return ENOMEM; + } + + return SUCCESS; +} + +int __libc_ringbuf_get_read_buffer( + struct ringbuf *buf, + const char **out_bufp, + size_t *out_len) +{ + int r = ringbuf_init(buf); + if (r != SUCCESS) { + return r; + } + + size_t contiguous_capacity = 0; + if (buf->buf_readp > buf->buf_writep) { + contiguous_capacity = buf->buf_max - buf->buf_readp; + } else { + contiguous_capacity = buf->buf_writep - buf->buf_readp; + } + + kern_tracef( + "OPEN READ: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu", + buf->buf_readp, + buf->buf_writep, + buf->buf_max, + buf->buf_ptr + buf->buf_readp, + contiguous_capacity); + *out_len = contiguous_capacity; + + if (contiguous_capacity == 0) { + *out_bufp = NULL; + } else { + *out_bufp = buf->buf_ptr + buf->buf_readp; + } + + return SUCCESS; +} + +void __libc_ringbuf_put_read_buffer( + struct ringbuf *buf, + const char **bufp, + size_t *len) +{ + buf->buf_readp += *len; + + if (buf->buf_readp == buf->buf_writep) { + buf->buf_readp = buf->buf_writep = 0; + } + + kern_tracef("CLOSE READ: %p r=%zu", *bufp, *len); + + *bufp = NULL; + *len = 0; +} + +int __libc_ringbuf_get_write_buffer( + struct ringbuf *buf, + char **out_bufp, + size_t *out_len) +{ + int r = ringbuf_init(buf); + if (r != SUCCESS) { + return r; + } + + size_t contiguous_capacity = 0; + if (buf->buf_writep >= buf->buf_readp) { + contiguous_capacity = buf->buf_max - buf->buf_writep - 1; + + if (buf->buf_readp > 0) { + contiguous_capacity++; + } + } else { + contiguous_capacity = buf->buf_readp - buf->buf_writep - 1; + } + + kern_tracef( + "OPEN WRITE: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu", + buf->buf_readp, + buf->buf_writep, + buf->buf_max, + buf->buf_ptr + buf->buf_readp, + contiguous_capacity); + + *out_len = contiguous_capacity; + + if (contiguous_capacity == 0) { + *out_bufp = NULL; + } else { + *out_bufp = buf->buf_ptr + buf->buf_writep; + } + + return SUCCESS; +} + +void __libc_ringbuf_put_write_buffer( + struct ringbuf *buf, + char **bufp, + size_t *len) +{ + buf->buf_writep += *len; + + if (buf->buf_readp == buf->buf_writep) { + buf->buf_readp = buf->buf_writep = 0; + } + + kern_tracef( + "CLOSE WRITE: %p r=%zu (%c)", + *bufp, + *len, + *(char *)(*bufp)); + + *bufp = NULL; + *len = 0; +} + +long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count) +{ + size_t nr_read = 0; + long r = 0; + + while (nr_read < count) { + const char *src = NULL; + size_t available = 0; + r = __libc_ringbuf_get_read_buffer(buf, &src, &available); + if (r < 0) { + break; + } + + char *dest = p + nr_read; + size_t to_copy = count - nr_read; + + if (available == 0) { + __libc_ringbuf_put_read_buffer(buf, &src, &available); + break; + } + + if (to_copy > available) { + to_copy = available; + } + + memcpy(dest, src, to_copy); + + nr_read += available; + __libc_ringbuf_put_read_buffer(buf, &src, &to_copy); + } + + if (r < 0) { + return r; + } + + return nr_read; +} + +long __libc_ringbuf_write(struct ringbuf *buf, const void *p, size_t count) +{ + size_t nr_written = 0; + long r = 0; + + while (nr_written < count) { + char *dest = NULL; + size_t available = 0; + r = __libc_ringbuf_get_write_buffer(buf, &dest, &available); + if (r < 0) { + break; + } + + const char *src = p + nr_written; + size_t to_copy = count - nr_written; + + if (available == 0) { + __libc_ringbuf_put_write_buffer(buf, &dest, &available); + break; + } + + if (to_copy > available) { + to_copy = available; + } + + memcpy(dest, src, to_copy); + + nr_written += available; + __libc_ringbuf_put_write_buffer(buf, &dest, &to_copy); + } + + if (r < 0) { + return r; + } + + return nr_written; +} + +int __libc_ringbuf_clear(struct ringbuf *buf) +{ + buf->buf_readp = buf->buf_writep = 0; + return 0; +} + +int __libc_ringbuf_resize(struct ringbuf *buf, size_t max) +{ + if (!(buf->buf_flags & RINGBUF_STATIC)) { + free(buf->buf_ptr); + buf->buf_ptr = NULL; + } + + buf->buf_readp = buf->buf_writep = 0; + buf->buf_flags &= ~RINGBUF_STATIC; + buf->buf_max = max; + return 0; +} + +int __libc_ringbuf_resize_static(struct ringbuf *buf, void *p, size_t max) +{ + if (!(buf->buf_flags & RINGBUF_STATIC)) { + free(buf->buf_ptr); + buf->buf_ptr = NULL; + } + + buf->buf_readp = buf->buf_writep = 0; + buf->buf_flags |= RINGBUF_STATIC; + buf->buf_ptr = p; + buf->buf_max = max; + return 0; +} + +size_t __libc_ringbuf_available(const struct ringbuf *buf) +{ + if (buf->buf_readp < buf->buf_writep) { + return buf->buf_writep - buf->buf_readp; + } else if (buf->buf_readp > buf->buf_writep) { + return buf->buf_max - buf->buf_readp + buf->buf_writep; + } else { + return 0; + } +} + +size_t __libc_ringbuf_capacity(const struct ringbuf *buf) +{ + if (buf->buf_readp > buf->buf_writep) { + return buf->buf_readp - buf->buf_writep - 1; + } else { + return buf->buf_max - buf->buf_writep + buf->buf_readp - 1; + } +} diff --git a/lib/libc/io/stdio/ringbuf.h b/lib/libc/io/stdio/ringbuf.h new file mode 100644 index 0000000..1184f21 --- /dev/null +++ b/lib/libc/io/stdio/ringbuf.h @@ -0,0 +1,53 @@ +#ifndef _LIBC_IO_RINGBUF_H_ +#define _LIBC_IO_RINGBUF_H_ + +#include + +enum ringbuf_flags { + RINGBUF_NONE = 0, + RINGBUF_STATIC, +}; + +struct ringbuf { + enum ringbuf_flags buf_flags; + char *buf_ptr; + size_t buf_max; + + size_t buf_readp, buf_writep; +}; + +extern int __libc_ringbuf_get_read_buffer( + struct ringbuf *buf, + const char **out_bufp, + size_t *out_len); +extern void __libc_ringbuf_put_read_buffer( + struct ringbuf *buf, + const char **bufp, + size_t *len); + +extern int __libc_ringbuf_get_write_buffer( + struct ringbuf *buf, + char **out_bufp, + size_t *out_len); +extern void __libc_ringbuf_put_write_buffer( + struct ringbuf *buf, + char **bufp, + size_t *len); + +extern long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count); +extern long __libc_ringbuf_write( + struct ringbuf *buf, + const void *p, + size_t count); + +extern int __libc_ringbuf_clear(struct ringbuf *buf); +extern int __libc_ringbuf_resize(struct ringbuf *buf, size_t max); +extern int __libc_ringbuf_resize_static( + struct ringbuf *buf, + void *p, + size_t max); + +extern size_t __libc_ringbuf_available(const struct ringbuf *buf); +extern size_t __libc_ringbuf_capacity(const struct ringbuf *buf); + +#endif diff --git a/lib/libc/io/stdio/setvbuf.c b/lib/libc/io/stdio/setvbuf.c new file mode 100644 index 0000000..6861a11 --- /dev/null +++ b/lib/libc/io/stdio/setvbuf.c @@ -0,0 +1,39 @@ +#include "file.h" + +#include +#include + +extern int setvbuf( + struct __opaque_file *restrict stream, + char *restrict buf, + int mode, + size_t size) +{ + switch (mode) { + case _IOFBF: + case _IOLBF: + case _IONBF: + break; + default: + return __set_errno(EINVAL); + } + + if (stream->f_prev != 0) { + return __set_errno(EINVAL); + } + + int err = SUCCESS; + if (buf) { + err = __libc_ringbuf_resize_static(&stream->f_buf, buf, size); + } else { + err = __libc_ringbuf_resize(&stream->f_buf, size); + } + + if (err != SUCCESS) { + return __set_errno(err); + } + + stream->f_buffer_mode = mode; + + return 0; +} diff --git a/lib/libc/io/stdio/stderr.c b/lib/libc/io/stdio/stderr.c new file mode 100644 index 0000000..2fce06c --- /dev/null +++ b/lib/libc/io/stdio/stderr.c @@ -0,0 +1,14 @@ +#include "file.h" + +#include + +static struct __opaque_file __stderr = { + .f_fd = 2, + .f_flags = FILE_STATIC, + .f_buffer_mode = _IONBF, +}; + +extern struct __opaque_file *__libc_file_stderr(void) +{ + return &__stderr; +} diff --git a/lib/libc/io/stdio/stdin.c b/lib/libc/io/stdio/stdin.c new file mode 100644 index 0000000..21e4df8 --- /dev/null +++ b/lib/libc/io/stdio/stdin.c @@ -0,0 +1,14 @@ +#include "file.h" + +#include + +static struct __opaque_file __stdin = { + .f_fd = 0, + .f_flags = FILE_STATIC, + .f_buffer_mode = _IOLBF, +}; + +extern struct __opaque_file *__libc_file_stdin(void) +{ + return &__stdin; +} diff --git a/lib/libc/io/stdio/stdout.c b/lib/libc/io/stdio/stdout.c new file mode 100644 index 0000000..cfaaec2 --- /dev/null +++ b/lib/libc/io/stdio/stdout.c @@ -0,0 +1,14 @@ +#include "file.h" + +#include + +static struct __opaque_file __stdout = { + .f_fd = 1, + .f_flags = FILE_STATIC, + .f_buffer_mode = _IOLBF, +}; + +extern struct __opaque_file *__libc_file_stdout(void) +{ + return &__stdout; +} diff --git a/lib/libc/io/stdio/vprintf.c b/lib/libc/io/stdio/vprintf.c new file mode 100644 index 0000000..7d8300a --- /dev/null +++ b/lib/libc/io/stdio/vprintf.c @@ -0,0 +1,27 @@ +#include "file.h" + +#include +#include + +extern int __libc_fctprintf( + void (*out)(char character, void *arg), + void *arg, + const char *format, + va_list va); +extern int __fputc(int c, struct __opaque_file *stream); + +static inline void _out_file(char character, void *arg) +{ + struct __opaque_file *fp = arg; + __fputc(character, fp); +} + +int vprintf(const char *format, va_list arg) +{ + __libc_file_lock(stdout); + + int ret = __libc_fctprintf(_out_file, stdout, format, arg); + + __libc_file_unlock(stdout); + return ret; +} diff --git a/lib/libc/io/unistd/write.c b/lib/libc/io/unistd/write.c index e69de29..6e95f0b 100644 --- a/lib/libc/io/unistd/write.c +++ b/lib/libc/io/unistd/write.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + +int write(int fd, const void *buf, size_t count) +{ + int err; + size_t nr_written; + kern_status_t status = fs_write(fd, buf, count, &err, &nr_written); + if (status != KERN_OK) { + return __set_errno(__errno_from_kern_status(status)); + } + + if (err != SUCCESS) { + return __set_errno(err); + } + + return nr_written; +}