#include #include #include #include #include #include #ifdef WIN32 #include #include #include #define fileno _fileno #else #include #include #include #endif #include "dtypes.h" #include "utils.h" #include "utf8.h" #include "ios.h" #include "socket.h" /* OS-level primitive wrappers */ // return error code, #bytes read in *nread static int _os_read(long fd, void *buf, size_t n, size_t *nread) { ssize_t r = read((int)fd, buf, n); if (r == -1) { *nread = 0; return errno; } *nread = (size_t)r; return 0; } static int _os_write(long fd, void *buf, size_t n, size_t *nwritten) { ssize_t r = write((int)fd, buf, n); if (r == -1) { *nwritten = 0; return errno; } *nread = (size_t)r; return 0; } static int _fd_available(long fd) { #ifndef WIN32 fd_set set; struct timeval tv = {0, 0}; FD_ZERO(&set); FD_SET(fd, &set); return (select(fd+1, &set, NULL, NULL, &tv)!=0); #else return 0; #endif } /* internal utility functions */ static char *_buf_realloc(ios_t *s, size_t sz) { char *temp; if (sz <= s->maxsize) return s->buf; if ((s->buf==NULL || 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 = 1; return s->buf; } else 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 = realloc(s->buf, sz+1); if (temp == NULL) return NULL; } else { temp = malloc(sz+1); s->ownbuf = 1; if (temp == NULL) return NULL; } if (s->buf != temp && s->size > 0) 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 size_t _writebuf_force(ios_t *s, char *data, size_t n) { size_t amt; size_t newsize; if (n == 0) return 0; if (s->bpos + n > s->size) { if (s->bpos + n > s->maxsize) { /* TO DO: here you might want to add a mechanism for limiting the growth of the stream. */ newsize = s->maxsize * 2; while (s->bpos + n > newsize) newsize *= 2; if (_buf_realloc(s, newsize) == NULL) { /* 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 */ size_t ios_read(ios_t *s, char *dest, size_t n) { } size_t ios_write(ios_t *s, char *data, size_t n) { } off_t ios_seek(ios_t *s, off_t pos) { } off_t ios_seek_end(ios_t *s) { } off_t ios_skip(ios_t *s, off_t offs) { } off_t ios_pos(ios_t *s) { if (s->bm == bm_mem) return (off_t)s->bpos; off_t fdpos = lseek(s->fd, 0, SEEK_CUR); if (fdpos == (off_t)-1) return fdpos; if (s->state == iost_wr) fdpos += s->bpos; else if (s->state == iost_rd) fdpos -= (s->size - s->bpos); return fdpos; } size_t ios_trunc(ios_t *s, size_t size) { } int ios_eof(ios_t *s) { if (s->bm == bm_mem) return (s->bpos >= s->size); if (s->fd == -1) return 1; // todo } static void _discard_partial_buffer(ios_t *s) { // this function preserves 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. // this needs to be called when normal operation is interrupted in // the middle of the buffer. "normal operation" is reading or // writing to the end of the buffer. this happens e.g. when flushing. if (s->bpos && s->size > s->bpos) { memmove(s->buf, s->buf + s->bpos, s->size - s->bpos); } s->size -= s->bpos; s->bpos = 0; } int ios_flush(ios_t *s) { if (ndirty == 0 || s->bm == bm_mem || s->buf == NULL) return 0; if (s->fd == -1) return -1; int partialb=0; if (s->bitpos > 0 && s->ndirty==s->bpos+1) { // flushing partial byte partialb=1; } size_t nw, ntowrite=s->ndirty; int err = _os_write(s->fd, s->buf, ntowrite, &nw); // todo: try recovering from some kinds of errors (e.g. retry) if (partialb) { // skip back 1, since we might have to write this byte again // if more bits in it are changed if (lseek(s->fd, -1, SEEK_CUR) == (off_t)-1) { // uhoh. can't write the "rest" of this byte. move to next // byte instead. s->bpos++; s->bitpos = 0; } } _discard_partial_buffer(s); s->ndirty = 0; if (err) return err; if (nw < ntowrite) return -1; return 0; } void ios_close(ios_t *s) { ios_flush(s); if (s->fd != -1 && s->ownfd) close(s->fd); s->fd = -1; } char *ios_takebuf(ios_t *s, size_t *psize) { char *buf; ios_flush(s); if (s->buf == &s->local[0]) { buf = malloc(s->size+1); if (buf == NULL) return NULL; if (s->size) memcpy(buf, s->buf, s->size); buf[s->size] = '\0'; } else { buf = s->buf; } *psize = s->size+1; // buffer is actually 1 bigger for terminating NUL /* empty stream and reinitialize */ if (s->bm == bm_mem || s->bm == bm_none) { s->buf = &s->local[0]; s->maxsize = IOS_INLSIZE; } else { s->buf = NULL; _buf_realloc(s, IOS_BUFSIZE); } s->size = s->bpos = 0; s->bitpos = 0; return buf; } int ios_setbuf(ios_t *s, char *buf, size_t size, int own) { ios_flush(s); size_t nvalid=0; nvalid = s->size; if (s->size == s->bpos && s->bitpos>0) nvalid++; if (size < nvalid) nvalid = size; memcpy(buf, s->buf, nvalid); if (s->bpos > nvalid) { // truncated s->bpos = nvalid; s->bitpos = 0; } s->size = nvalid; if (s->buf!=NULL && s->ownbuf && s->buf!=&s->local[0]) free(s->buf); s->buf = buf; s->maxsize = size; s->ownbuf = own; return 0; } int ios_bufmode(ios_t *s, bufmode_t mode) { // no fd; can only do mem-only buffering if (s->fd == -1 && mode != bm_mem) return -1; s->bm = mode; return 0; } void ios_bswap(ios_t *s, int bswap) { s->byteswap = !!bswap; } int ios_copy(ios_t *to, ios_t *from, size_t nbytes, bool_t all) { } /* stream object initializers. we do no allocation. */ ios_t *ios_file(ios_t *s, char *fname, int create, int rewrite) { } ios_t *ios_mem(ios_t *s, size_t initsize) { } ios_t *ios_fd(ios_t *s, long fd) { } /* higher level interface */ int ios_putbit(ios_t *s, int bit) { byte_t mask = 1<bitpos; if (_ios_setstate(s, iost_wr)) return 0; if (!s->dirty) { // haven't written any bits yet // if stenciling is turned on, the buffer is already full and // we don't need to do anything if (s->rereadable && !s->stenciled) { // fill buffer if we can } } if (bit) s->buf[s->bpos] |= mask; else s->buf[s->bpos] &= ~mask; s->dirty = 1; s->bitpos++; if (s->bitpos > 7) { s->bitpos = 0; s->bpos++; s->tally++; } return 1; } int ios_getbit(ios_t *s, int *pbit) { byte_t mask = 1<bitpos; if (_ios_setstate(s, iost_rd)) return 0; *pbit = (s->buf[s->bpos] & mask) ? 1 : 0; s->bitpos++; if (s->bitpos > 7) { s->bitpos = 0; s->bpos++; s->tally++; } return 1; }