feat(stdio): FILE buffering core and stream I/O

This commit is contained in:
2026-09-04 23:27:27 -04:00
parent 5355574121
commit 22587bd545
4 changed files with 2276 additions and 0 deletions
+1168
View File
File diff suppressed because it is too large Load Diff
+94
View File
@@ -0,0 +1,94 @@
#ifndef VLIBC_STDIO_STDIO_IMPL_H
#define VLIBC_STDIO_STDIO_IMPL_H
/*
* vlibc — internal FILE layout and stream helpers (todo 15).
*
* struct vlibc_FILE is the object behind the opaque FILE of <stdio.h>.
* Position invariant (authoritative):
*
* - Read mode: `pos` is the file offset of buf[0]. The logical position
* (offset of the byte at rpos) is pos + (rpos - buf), and the kernel
* position is pos + (rstop - buf); with an empty buffer
* (rpos == rstop) the kernel sits exactly at the logical position.
* - Write mode: `pos` is the file offset of buf[0]; the next written byte
* lands at pos + (wpos - buf). With an empty buffer the kernel sits at
* pos. A flush advances pos by the number of bytes written.
*
* Mode switching: reading while write data is pending flushes it first;
* writing while unread buffered data is pending seeks the kernel back to
* the logical position (discarding the unread data). F_PUSHED marks the
* single guaranteed ungetc pushback byte in `ungot`; a successful seek
* discards it.
*
* The helper prototypes below are shared with the formatted-I/O todos
* (16: vfprintf, 17: vfscanf) so they operate on the same buffer state.
*/
#include <stdio.h>
#include "../internal/libc.h"
struct vlibc_FILE
{
unsigned char *buf; /* buffer base: library- or user-supplied */
unsigned char *rpos; /* read mode: next byte to deliver */
unsigned char *rstop; /* read mode: end of valid buffered data */
unsigned char *wpos; /* write mode: next byte to store */
unsigned char *wstop; /* write mode: end of the buffer */
int fd; /* underlying descriptor; -1 when closed */
int flags; /* F_* bits below */
size_t buf_size; /* usable buffer size; 0 = not yet sized */
off_t pos; /* position per the invariant above */
struct vlibc_FILE *next; /* registry link for fflush(NULL) */
unsigned char ungot; /* pushed-back byte when F_PUSHED */
};
/* Stream flags. */
#define F_READ 0x001 /* reads are enabled by the open mode */
#define F_WRITE 0x002 /* writes are enabled by the open mode */
#define F_EOF 0x004 /* end of file has been seen */
#define F_ERR 0x008 /* an I/O error occurred */
#define F_LINEBUF 0x010 /* line buffered: flush on newline */
#define F_UNBUF 0x020 /* unbuffered: 1-byte buffer */
#define F_APPEND 0x040 /* append mode: writes land at end of file */
#define F_OWNBUF 0x080 /* buf was allocated by the library */
#define F_HEAP 0x100 /* the FILE struct itself is heap-allocated */
#define F_PUSHED 0x200 /* ungot holds a pushed-back byte */
/* Flush pending write data; returns 0, or -1 with F_ERR set. */
hidden int
stdio_flush(FILE *f);
/* Refill the read buffer from the descriptor (read mode only). */
hidden void
stdio_refill(FILE *f);
/* Discard unread buffered data, seeking the kernel back to pos. */
hidden int
stdio_discard_read(FILE *f);
/* Allocate the buffer lazily (std streams, setvbuf with NULL). */
hidden void
stdio_init_if_needed(FILE *f);
/* Allocate and register a FILE over an open descriptor. */
hidden FILE *
stdio_alloc_file(int fd, int m);
/*
* Parse a fopen-style mode string. On success stores the F_READ/F_WRITE
* capability bits in *m, the openat flags in *oflags, and returns 1;
* returns 0 for an invalid mode.
*/
hidden int
stdio_parse_mode(const char *mode, int *m, int *oflags);
/*
* Raw lseek with errno translation. Separate from syscall_ret() because
* syscall_ret narrows results to int and would truncate large offsets.
*/
hidden off_t
stdio_lseek(int fd, off_t off, int whence);
#endif /* VLIBC_STDIO_STDIO_IMPL_H */