974 lines · 18.1 KB · C
RawDownload
#include "sl.h"#include "timefuncs.h" #define MOST_OF(x) ((x) - ((x)>>4)) ios *ios_stdin = nil;ios *ios_stdout = nil;ios *ios_stderr = nil; /* OS-level primitive wrappers */ void *llt_memrchr(const void *s, int c, usize n){	const u8int *src = (const u8int*)s + n;	u8int uc = c;	while(--src >= (const u8int*)s)		if(*src == uc)			return (void *)src;	return nil;} #if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)static int_enonfatal(int err){	return err == 0 || err == EAGAIN || err == EINPROGRESS || err == EINTR || err == EWOULDBLOCK;}#define SLEEP_TIME 5//ms#endif // return error code, #bytes read in *nread// these wrappers retry operations until success or a fatal errorstatic int_os_read(int fd, u8int *buf, usize n, usize *nread){	ssize r; 	while(1){#if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)		errno = 0;#endif		r = read(fd, buf, n);		if(r > -1){			*nread = (usize)r;			break;		}#if defined(__plan9__) || defined(__macos__) || defined(__dos__)		return r;#else		if(!_enonfatal(errno)){			*nread = 0;			return errno;		}		sleep_ms(SLEEP_TIME);#endif	}	return 0;} static int_os_read_all(int fd, u8int *buf, usize n, usize *nread){	usize got; 	*nread = 0; 	while(n > 0){		int err = _os_read(fd, buf, n, &got);		n -= got;		*nread += got;		buf += got;		if(err || got == 0)			return err;	}	return 0;} static int_os_write(int fd, const void *buf, usize n, usize *nwritten){	ssize r; 	while(1){#if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)		errno = 0;#endif		r = write(fd, buf, n);		if(r > -1){			*nwritten = (usize)r;			break;		}#if defined(__plan9__) || defined(__macos__) || defined(__dos__)		return r;#else		if(!_enonfatal(errno)){			*nwritten = 0;			return errno;		}		sleep_ms(SLEEP_TIME);#endif	}	return 0;} static int_os_write_all(int fd, const u8int *buf, usize n, usize *nwritten){	usize wrote; 	*nwritten = 0;	while(n > 0){		wrote = 0;		int err = _os_write(fd, buf, n, &wrote);		n -= wrote;		*nwritten += wrote;		buf += wrote;		if(err)			return err;	}	return 0;} /* internal utility functions */ static u8int *_buf_realloc(ios *s, usize sz){	u8int *temp; 	if((s->buf == nil || s->buf == &s->local[0]) && sz <= IOS_INLSIZE){		/* TODO: if we want to allow shrinking, see if the buffer shrank		   down to this size, in which case we need to copy. */		s->buf = &s->local[0];		s->maxsize = IOS_INLSIZE;		s->ownbuf = true;		return s->buf;	} 	if(sz <= s->maxsize)		return s->buf; 	if(s->ownbuf && s->buf != &s->local[0]){		// if we own the buffer we're free to resize it		// always allocate 1 bigger in case user wants to add a NUL		// terminator after taking over the buffer		temp = MEM_REALLOC(s->buf, sz);		if(temp == nil)			return nil;	}else{		temp = MEM_ALLOC(sz);		if(temp == nil)			return nil;		s->ownbuf = true;		if(s->buf != nil)			memcpy(temp, s->buf, s->size);	} 	s->buf = temp;	s->maxsize = sz;	return s->buf;} // write a block of data into the buffer at the current position, resizing// if necessary. returns # written.static usize_write_grow(ios *s, const void *data, usize n){	usize amt;	usize newsize; 	if(n == 0)		return 0; 	if(s->bpos + n > s->size){		if(s->bpos + n > s->maxsize){			/* TODO: here you might want to add a mechanism for limiting			   the growth of the io. */			newsize = s->maxsize ? s->maxsize * 2 : 8;			while(s->bpos + n > newsize)				newsize *= 2;			if(_buf_realloc(s, newsize) == nil){				/* no more space; write as much as we can */				amt = s->maxsize - s->bpos;				if(amt > 0)					memcpy(&s->buf[s->bpos], data, amt);				s->bpos += amt;				s->size = s->maxsize;				return amt;			}		}		s->size = s->bpos + n;	}	memcpy(s->buf + s->bpos, data, n);	s->bpos += n; 	return n;}  /* interface functions, low level */ static usize_ios_read(ios *s, u8int *dest, usize n, bool all){	usize tot = 0;	usize got, avail; 	if(s->state == bst_closed)		return 0; 	while(n > 0){		avail = s->size - s->bpos; 		if(avail > 0){			usize ncopy = avail >= n ? n : avail;			memcpy(dest, s->buf + s->bpos, ncopy);			s->bpos += ncopy;			if(ncopy >= n){				s->state = bst_rd;				return tot+ncopy;			}		}		if(s->bm == bm_memory || s->fd == -1){			// can't get any more data			s->state = bst_rd;			if(avail == 0)				s->_eof = true;			return avail;		} 		dest += avail;		n -= avail;		tot += avail; 		ios_flush(s);		s->bpos = s->size = 0;		s->state = bst_rd; 		s->fpos = -1;		got = 0;		if(n > MOST_OF(s->maxsize)){			// doesn't fit comfortably in buffer; go direct			if(all)				_os_read_all(s->fd, dest, n, &got);			else				_os_read(s->fd, dest, n, &got);			tot += got;			if(got == 0)				s->_eof = true;			return tot;		}else{			// refill buffer			if(_os_read(s->fd, s->buf, s->maxsize, &got)){				s->_eof = true;				return tot;			}			if(got == 0){				s->_eof = true;				return tot;			}			s->size = got;		}	} 	return tot;} usizeios_read(ios *s, void *dest, usize n){	return _ios_read(s, dest, n, false);} // ensure at least n bytes are buffered if possible. returns # available.static usizeios_readprep(ios *s, usize n){	if(s->state == bst_wr && s->bm != bm_memory){		ios_flush(s);		s->bpos = s->size = 0;	}	usize space = s->size - s->bpos;	s->state = bst_rd;	if(space >= n || s->bm == bm_memory || s->fd == -1)		return space;	if(s->maxsize < s->bpos+n){		// it won't fit. grow buffer or move data back.		if(n <= s->maxsize && space <= ((s->maxsize)>>2)){			if(space)				memmove(s->buf, s->buf+s->bpos, space);			s->size -= s->bpos;			s->bpos = 0;		}		else {			if(_buf_realloc(s, s->bpos + n) == nil)				return space;		}	}	usize got;	int result = _os_read(s->fd, s->buf+s->size, s->maxsize - s->size, &got);	if(result)		return space;	s->size += got;	return s->size - s->bpos;} static void_write_update_pos(ios *s){	if(s->bpos > s->ndirty)		s->ndirty = s->bpos;	if(s->bpos > s->size)		s->size = s->bpos;} usizeios_write(ios *s, const void *data, usize n){	if(s->readonly || s->state == bst_closed || n == 0)		return 0;	usize space;	usize wrote = 0; 	if(s->state == bst_none)		s->state = bst_wr;	if(s->state == bst_rd){		if(!s->rereadable){			s->size = 0;			s->bpos = 0;		}		space = s->size - s->bpos;	}else{		space = s->maxsize - s->bpos;	} 	if(s->bm == bm_memory){		wrote = _write_grow(s, data, n);	}else if(s->bm == bm_none){		s->fpos = -1;		_os_write_all(s->fd, data, n, &wrote);		return wrote;	}else if(n <= space){		if(s->bm == bm_line){			const char *nl;			if((nl = llt_memrchr(data, '\n', n)) != nil){				usize linesz = nl-(const char*)data+1;				s->bm = bm_block;				wrote += ios_write(s, data, linesz);				ios_flush(s);				s->bm = bm_line;				n -= linesz;				data = (const char*)data + linesz;			}		}		memcpy(s->buf + s->bpos, data, n);		s->bpos += n;		wrote += n;	}else{		s->state = bst_wr;		ios_flush(s);		if(n > MOST_OF(s->maxsize)){			_os_write_all(s->fd, data, n, &wrote);			return wrote;		}		return ios_write(s, data, n);	}	_write_update_pos(s);	return wrote;} soffsetios_seek(ios *s, soffset pos){	if(s->state == bst_closed || pos < 0)		return -1;	s->_eof = false;	if(s->bm == bm_memory){		if((usize)pos >= s->size)			pos = s->size;		s->bpos = pos;	}else{		ios_flush(s);		s->bpos = s->size = 0;		soffset fdpos = lseek(s->fd, pos, SEEK_SET);		if(fdpos < 0)			return -1;		pos = fdpos;	}	s->loc.lineno = 1;	s->loc.colno = 0;	return pos;} soffsetios_seek_end(ios *s){	if(s->state == bst_closed)		return -1;	s->_eof = true;	if(s->bm == bm_memory)		s->bpos = s->size;	else{		ios_flush(s);		soffset fdpos = lseek(s->fd, 0, SEEK_END);		if(fdpos == (soffset)-1)			return fdpos;		s->bpos = s->size = 0;	}	s->loc.lineno = 1;	s->loc.colno = 0;	return 0;} soffsetios_skip(ios *s, soffset offs, bool end){	if(s->state == bst_closed)		return -1;	if(end && ios_seek_end(s) < 0)		return -1;	s->_eof = false;	if(s->bm == bm_memory){		if(offs > 0){			if(offs <= (soffset)(s->size-s->bpos))				s->bpos += offs;			else				s->bpos = s->size;		}else if(offs < 0){			if((soffset)s->bpos+offs >= 0)				s->bpos += offs;			else				s->bpos = 0;		}		return s->bpos;	}	ios_flush(s);	s->bpos = s->size = 0;	return lseek(s->fd, offs, SEEK_CUR);} intios_trunc(ios *s, soffset size){	if(s->state == bst_closed || s->readonly || size < 0)		return -1;	if(s->bm == bm_memory){		if((usize)size == s->size)			return 0;		if((usize)size < s->size){			if(s->bpos > (usize)size)				s->bpos = size;		}else if(_buf_realloc(s, size) == nil)			return -1;		s->size = size;		return 0;	}	return ios_flush(s) == 0 ? ftruncate(s->fd, size) : -1;} boolios_eof(ios *s){	if(s->state == bst_closed)		return true;	if(s->bm == bm_memory)		return s->_eof;	return s->fd == -1 || s->_eof;} intios_flush(ios *s){	if(s->ndirty == 0 || s->bm == bm_memory || s->buf == nil)		return 0;	if(s->fd == -1)		return -1; 	if(s->state == bst_rd){		if(lseek(s->fd, -(soffset)s->size, SEEK_CUR) == (soffset)-1){			// FIXME eh?		}	} 	usize nw, ntowrite = s->ndirty;	s->fpos = -1;	int err = _os_write_all(s->fd, s->buf, ntowrite, &nw);	// todo: try recovering from some kinds of errors (e.g. retry) 	if(s->state == bst_rd){		if(lseek(s->fd, s->size - nw, SEEK_CUR) == (soffset)-1){			// FIXME eh?		}	}else if(s->state == bst_wr){		if(s->bpos != nw && lseek(s->fd, (soffset)s->bpos - (soffset)nw, SEEK_CUR) == (soffset)-1){			// FIXME eh?		}		// now preserve the invariant that data to write		// begins at the beginning of the buffer, and s->size refers		// to how much valid file data is stored in the buffer.

Lines 1–500 of 974.

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