Sigrid Solveig Haflínudóttirfile: add :exclusive and :remove-when-closed flags3c79e5e2 months agoHistory
210 lines · 7.6 KB · C
RawDownload
#pragma once typedef struct ios_loc ios_loc;typedef struct ios ios; // this flag controls when data actually moves out to the underlying I/O// channel. in-memory io is a special case of this where the data// never moves out.typedef enum {	IOS_BUF_NONE,	IOS_BUF_LINE,	IOS_BUF_BLOCK,	IOS_BUF_MEM,}sl_packed ios_buf_mode; typedef enum {	IOS_STATE_NONE,	IOS_STATE_RD,	IOS_STATE_WR,	IOS_STATE_CLOSED,}sl_packed ios_state; typedef enum {	IOS_CREATE = 1<<0,	IOS_READ = 1<<1,	IOS_WRITE = 1<<2,	IOS_TRUNC = 1<<3,	IOS_APPEND = 1<<4,	IOS_EXCL = 1<<5,	IOS_DIR = 1<<6,	IOS_RCLOSE = 1<<7, 	IOS_RDWR = IOS_READ|IOS_WRITE,}sl_enum_flags sl_packed ios_flags; typedef enum {	IOS_FL_RDONLY = 1<<0,	IOS_FL_WRONLY = 1<<1,	IOS_FL_OWNBUF = 1<<2,	IOS_FL_OWNFD = 1<<3,	IOS_FL_EOF = 1<<4,	// this means you can read, seek back, then read the same data	// again any number of times. usually only true for files and in-memory buffers.	IOS_FL_REREADABLE = 1<<5,}sl_enum_flags sl_packed ios_state_flags; enum {	IOS_INLSIZE = 128+2,	// on Windows XP read with 32K buffer might return ERROR_NOT_ENOUGH_MEMORY	IOS_BUFSIZE = 16384,}; struct ios_loc {	u32int lineno;	u32int colno;}; struct ios {	u8int *buf; // start of buffer	usize maxsize; // space allocated to buffer	usize size; // length of valid data in buf, >=ndirty	usize bpos; // current position in buffer	usize ndirty; // # bytes at &buf[0] that need to be written	char *filename;	sl_v data;	int fd;	ios_loc loc; 	// the state only indicates where the underlying file position is relative	// to the buffer. reading: at the end. writing: at the beginning.	// in general, you can do any operation in any state.	ios_state state;	ios_state_flags flags;	ios_buf_mode bm;	u8int colnowait; 	union {		u8int local[IOS_INLSIZE];		struct {			u64int tmp;			usize start;			usize end;		};	};}; void *sl_memrchr(const void *s, int c, usize n) sl_rd(1, 3) sl_purefn; /* low-level interface functions */usize ios_read(ios *s, void *dest, usize n) sl_nonnull(1, 2) sl_wr(2, 3);usize ios_peek(ios *s, void *dest, usize n) sl_nonnull(1, 2) sl_wr(2, 3);usize ios_write(ios *s, const void *data, usize n) sl_nonnull(1, 2) sl_rd(2, 3);soffset ios_seek(ios *s, soffset pos) sl_nonnull(1);   // absolute seeksoffset ios_seek_end(ios *s) sl_nonnull(1);soffset ios_skip(ios *s, soffset offs, bool end) sl_nonnull(1);  // relative seekint ios_trunc(ios *s, soffset size) sl_nonnull(1);int ios_flush(ios *s) sl_nonnull(1);void ios_close(ios *s) sl_nonnull(1);void ios_free(ios *s) sl_nonnull(1);usize ios_readprep(ios *s, usize n) sl_nonnull(1);u8int *ios_takebuf(ios *s, usize *psize) sl_nonnull(1, 2);  // null-terminate and release buffer to caller// set buffer space to useint ios_setbuf(ios *s, u8int *buf, usize size, bool own) sl_nonnull(1, 2);int ios_bufmode(ios *s, ios_buf_mode mode) sl_nonnull(1);void ios_set_readonly(ios *s) sl_nonnull(1);usize ios_copy(ios *to, ios *from, usize nbytes) sl_nonnull(1, 2);usize ios_copyall(ios *to, ios *from) sl_nonnull(1, 2);usize ios_copyuntil(ios *to, ios *from, Rune delim) sl_nonnull(1, 2); /* io creation */ios *ios_file(ios *s, const char *fname, ios_flags flags, usize mode) sl_nonnull(1, 2) sl_cstr(2);ios *ios_mem(ios *s, usize initsize) sl_nonnull(1) sl_returns_nonnull;ios *ios_pipe(ios *s, ios_flags flags, int *otherside) sl_nonnull(1, 3) sl_returns_nonnull;ios *ios_static_buffer(ios *s, const u8int *buf, usize sz) sl_nonnull(1, 2) sl_returns_nonnull sl_rd(2, 3);ios *ios_fd(ios *s, int fd, const char *name, bool isfile, bool own) sl_nonnull(1, 3) sl_returns_nonnull sl_cstr(3) sl_fd(2); extern ios *ios_stdin;extern ios *ios_stdout;extern ios *ios_stderr; void ios_init_std(void); /* high-level functions - output */int ios_putrune(ios *s, Rune r) sl_nonnull(1);int ios_printf(ios *s, const char *format, ...) sl_printfmt(2, 3) sl_nonnull(1, 2);int ios_vprintf(ios *s, const char *format, va_list args) sl_printfmt(2, 0) sl_nonnull(1, 2); /* high-level io functions - input */int ios_peekrune(ios *s, Rune *r) sl_nonnull(1, 2);int ios_getrune(ios *s, Rune *r) sl_nonnull(1, 2); // discard data buffered for readingvoid ios_purge(ios *s) sl_nonnull(1); /* stdio-style functions */#define IOS_EOF (-1)int ios_putc(ios *s, int c) sl_nonnull(1);int ios_getc(ios *s) sl_nonnull(1);int ios_peekc(ios *s) sl_nonnull(1);#define ios_puts(s, str) ios_write(s, str, strlen(str)) /*  With in-memory io, mixed reads and writes are equivalent to performing  sequences of *p++, as either an lvalue or rvalue. File ios behave  similarly, but other ios might not support this. Using unbuffered  mode makes this more predictable.   Note on "unget" functions:  There are two kinds of functions here: those that operate on sized  blocks of bytes and those that operate on logical units like "character"  or "integer". The "unget" functions only work on logical units. There  is no "unget n bytes". You can only do an unget after a matching get.  However, data pushed back by an unget is available to all read operations.  The reason for this is that unget is defined in terms of its effect on  the underlying buffer (namely, it rebuffers data as if it had been  buffered but not read yet). IOS reserves the right to perform large block  operations directly, bypassing the buffer. In such a case data was  never buffered, so "rebuffering" has no meaning (i.e. there is no  correspondence between the buffer and the physical io).   Single-bit I/O is able to write partial bytes ONLY IF the io supports  seeking. Also, line buffering is not well-defined in the context of  single-bit I/O, so it might not do what you expect.   implementation notes:  in order to know where we are in a file, we must ensure the buffer  is only populated from the underlying io starting with p==buf.   to switch from writing to reading: flush, set p=buf, cnt=0  to switch from reading to writing: seek backwards cnt bytes, p=buf, cnt=0   when writing: buf starts at curr. physical io pos, p - buf is how  many bytes we've written logically. cnt==0   dirty == (bitpos>0 && state==iost_wr), EXCEPT right after switching from  reading to writing, where we might be in the middle of a byte without  having changed it.   to write a bit: if !dirty, read up to maxsize-(p-buf) into buffer, then  seek back by the same amount (undo it). write onto those bits. now set  the dirty bit. in this state, we can bit-read up to the end of the byte,  then formally switch to the read state using flush.   design points:  - data-source independence, including in-memory io  - expose buffer to user, allow user-owned buffers  - allow direct I/O, don't always go through buffer  - buffer-internal seeking. makes seeking back 1-2 bytes very fast,	and makes it possible for sockets where it otherwise wouldn't be  - tries to allow switching between reading and writing  - support 64-bit and large files  - efficient, low-latency buffering  - special support for utf8  - type-aware functions with byte-order swapping service  - position counter for meaningful data offsets with sockets   theory of operation:   the buffer is a view of part of a file/stream. you can seek, read, and  write around in it as much as you like, as if it were just a string.   we keep track of the part of the buffer that's invalid (written to).  we remember whether the position of the underlying io is aligned  with the end of the buffer (reading mode) or the beginning (writing mode).   based on this info, we might have to seek back before doing a flush.   as optimizations, we do no writing if the buffer isn't "dirty", and we  do no reading if the data will only be overwritten.*/

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t