shithub: femtolisp

ref: 60644c760ee8fc20fc19bbdde40d3ba09fa83385
dir: /llt/attic/ios.c.old/

View raw version
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>

#ifdef WIN32
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#define fileno _fileno
#else
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#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<<s->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<<s->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;
}