#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 . * 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_WRITE doubles as * the current-mode marker and is cleared by the read switch; F_RDWR * records the write capability of update streams so the write switch can * re-set F_WRITE. 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. * * Locking (todo 18): the process is single-threaded until the thread * todo (#45) lands, so flockfile/ftrylockfile/funlockfile only track * ownership state here. `lock` is the recursion depth (0 = unlocked), * `lock_owner` the owning thread's FS base while locked. When real * threads arrive, the same todo turns `lock` into the actual per-stream * lock word (blocking acquisition, mutex/cond state) and these two * fields become the bookkeeping that makes flockfile recursive. * * Memory streams (todo 18): fmemopen/open_memstream build a FILE whose * `mem` points at an opaque backend (defined in fmemopen.c) instead of a * descriptor. Whenever `mem` is non-NULL the buffered core below calls * back into fmemopen.c's stdio_mem_* hooks in place of every descriptor * syscall, so the position invariant above is unchanged: the backend * simply acts as an in-memory device with the same kernel-position * semantics (reads advance it past the read-ahead, writes advance it on * flush, discards rewind it to the logical position). */ #include #include "../internal/libc.h" /* Opaque memory-stream backend (fmemopen/open_memstream, todo 18). */ struct vlibc_memstream; 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 */ int lock; /* flockfile recursion depth; 0 = unlocked */ void *lock_owner; /* owning thread's FS base while locked */ struct vlibc_memstream *mem; /* memory-stream backend, or NULL */ }; /* 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 */ #define F_RDWR 0x400 /* update stream: reads and writes both enabled */ /* 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); /* * Memory-stream device hooks (fmemopen.c, todo 18). The stdio core calls * these in place of the descriptor syscalls whenever f->mem is set: * * - stdio_mem_flush: write the pending cache to the backing region * (stdio_flush's device-write half, F_APPEND included). * - stdio_mem_refill: read the next cache-full from the region * (stdio_refill's device-read half). * - stdio_mem_discard: rewind the device to the logical position, * discarding unread cached data (stdio_discard_read's seek half). * - stdio_mem_lseek: resolve a SEEK_SET/SEEK_END target against the * region (used by fseeko instead of stdio_lseek on memory streams). * - stdio_mem_close: run on fclose before the FILE is released * (open_memstream's final NUL/size sync, owned-buffer release). */ hidden int stdio_mem_flush(FILE *f); hidden void stdio_mem_refill(FILE *f); hidden int stdio_mem_discard(FILE *f); hidden off_t stdio_mem_lseek(FILE *f, off_t off, int whence); hidden void stdio_mem_close(FILE *f); #endif /* VLIBC_STDIO_STDIO_IMPL_H */