remove more unused stuff
This commit is contained in:
parent
a5ae9c356a
commit
c81f21fdc0
|
@ -2,7 +2,7 @@ CC = gcc
|
|||
|
||||
SRCS = bitvector.c hashing.c socket.c timefuncs.c ptrhash.c utf8.c ios.c \
|
||||
dirpath.c htable.c bitvector-ops.c int2str.c dump.c random.c \
|
||||
lltinit.c arraylist.c
|
||||
lltinit.c
|
||||
OBJS = $(SRCS:%.c=%.o)
|
||||
DOBJS = $(SRCS:%.c=%.do)
|
||||
TARGET = libllt.a
|
||||
|
|
|
@ -2,7 +2,7 @@ CC = gcc
|
|||
|
||||
SRCS = bitvector.c hashing.c socket.c timefuncs.c ptrhash.c utf8.c ios.c \
|
||||
dirpath.c htable.c bitvector-ops.c int2str.c dump.c random.c \
|
||||
lltinit.c arraylist.c
|
||||
lltinit.c
|
||||
OBJS = $(SRCS:%.c=%.o)
|
||||
DOBJS = $(SRCS:%.c=%.do)
|
||||
TARGET = libllt.a
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "dtypes.h"
|
||||
#include "arraylist.h"
|
||||
|
||||
arraylist_t *arraylist_new(arraylist_t *a, size_t size)
|
||||
{
|
||||
a->len = 0;
|
||||
if (size <= AL_N_INLINE) {
|
||||
a->items = &a->_space[0];
|
||||
a->max = AL_N_INLINE;
|
||||
}
|
||||
else {
|
||||
a->items = (void**)LLT_ALLOC(size*sizeof(void*));
|
||||
a->max = size;
|
||||
}
|
||||
if (a->items == NULL) return NULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
void arraylist_free(arraylist_t *a)
|
||||
{
|
||||
if (a->items != &a->_space[0])
|
||||
LLT_FREE(a->items);
|
||||
a->len = 0;
|
||||
a->max = AL_N_INLINE;
|
||||
a->items = &a->_space[0];
|
||||
}
|
||||
|
||||
static void al_grow(arraylist_t *a, size_t n)
|
||||
{
|
||||
if (a->len+n > a->max) {
|
||||
if (a->items == &a->_space[0]) {
|
||||
void **p = LLT_ALLOC((a->len+n)*sizeof(void*));
|
||||
if (p == NULL) return;
|
||||
memcpy(p, a->items, a->len*sizeof(void*));
|
||||
a->items = p;
|
||||
a->max = a->len+n;
|
||||
}
|
||||
else {
|
||||
size_t nm = a->max*2;
|
||||
if (nm == 0) nm = 1;
|
||||
while (a->len+n > nm) nm*=2;
|
||||
void **p = LLT_REALLOC(a->items, nm*sizeof(void*));
|
||||
if (p == NULL) return;
|
||||
a->items = p;
|
||||
a->max = nm;
|
||||
}
|
||||
}
|
||||
a->len += n;
|
||||
}
|
||||
|
||||
void arraylist_push(arraylist_t *a, void *elt)
|
||||
{
|
||||
al_grow(a, 1);
|
||||
a->items[a->len-1] = elt;
|
||||
}
|
||||
|
||||
void *arraylist_pop(arraylist_t *a)
|
||||
{
|
||||
if (a->len == 0) return NULL;
|
||||
void *p = a->items[--a->len];
|
||||
a->items[a->len] = NULL;
|
||||
return p;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef __ARRAYLIST_H_
|
||||
#define __ARRAYLIST_H_
|
||||
|
||||
#define AL_N_INLINE 29
|
||||
|
||||
typedef struct {
|
||||
size_t len;
|
||||
size_t max;
|
||||
void **items;
|
||||
void *_space[AL_N_INLINE];
|
||||
} arraylist_t;
|
||||
|
||||
arraylist_t *arraylist_new(arraylist_t *a, size_t size);
|
||||
void arraylist_free(arraylist_t *a);
|
||||
|
||||
void arraylist_push(arraylist_t *a, void *elt);
|
||||
void *arraylist_pop(arraylist_t *a);
|
||||
|
||||
#endif
|
|
@ -1,396 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
#ifndef __IOS_H_
|
||||
#define __IOS_H_
|
||||
|
||||
// this flag controls when data actually moves out to the underlying I/O
|
||||
// channel. memory streams are a special case of this where the data
|
||||
// never moves out.
|
||||
typedef enum { bm_none, bm_line, bm_block, bm_mem } bufmode_t;
|
||||
|
||||
typedef enum { iost_none, iost_rd, iost_wr } iostate_t;
|
||||
|
||||
#define IOS_INLSIZE 54
|
||||
#define IOS_BUFSIZE 4095
|
||||
|
||||
typedef struct {
|
||||
bufmode_t bm;
|
||||
|
||||
// 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.
|
||||
iostate_t state;
|
||||
|
||||
char *buf; // start of buffer
|
||||
size_t maxsize; // space allocated to buffer
|
||||
size_t size; // length of valid data in buf, >=ndirty
|
||||
size_t bpos; // current position in buffer
|
||||
size_t ndirty; // # bytes at &buf[0] that need to be written
|
||||
|
||||
// this is a public field that keeps a running count of bytes
|
||||
// read or written. you can freely use and change it. this is
|
||||
// intended for keeping track of relative positions in streams
|
||||
// that don't have absolute positions (like sockets).
|
||||
size_t tally;
|
||||
|
||||
// pointer-size integer to support platforms where it might have
|
||||
// to be a pointer
|
||||
long fd;
|
||||
|
||||
byte_t bitpos;
|
||||
//unsigned char bitdirty:1; // bit buffer needs to be written
|
||||
unsigned char byteswap:1;
|
||||
//unsigned char readonly:1;
|
||||
unsigned char ownbuf:1;
|
||||
unsigned char ownfd:1;
|
||||
|
||||
// this means you can read, seek back, then read the same data
|
||||
// again any number of times. usually only true for files and strings.
|
||||
unsigned char rereadable:1;
|
||||
|
||||
// this enables "stenciled writes". you can alternately write and
|
||||
// seek without flushing in between. this performs read-before-write
|
||||
// to populate the buffer, so "rereadable" capability is required.
|
||||
// this is off by default, except for bit I/O if rereadable is true.
|
||||
unsigned char stenciled:1;
|
||||
|
||||
// request durable writes (fsync)
|
||||
// unsigned char durable:1;
|
||||
|
||||
// todo: mutex
|
||||
char local[IOS_INLSIZE];
|
||||
} ios_t;
|
||||
|
||||
/* low-level interface functions */
|
||||
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); // absolute seek
|
||||
off_t ios_seek_end(ios_t *s);
|
||||
off_t ios_skip(ios_t *s, off_t offs); // relative seek
|
||||
off_t ios_pos(ios_t *s); // get current position
|
||||
size_t ios_trunc(ios_t *s, size_t size);
|
||||
int ios_eof(ios_t *s);
|
||||
int ios_flush(ios_t *s);
|
||||
void ios_close(ios_t *s);
|
||||
char *ios_takebuf(ios_t *s, size_t *psize); // release buffer to caller
|
||||
// set buffer space to use
|
||||
int ios_setbuf(ios_t *s, char *buf, size_t size, int own);
|
||||
int ios_bufmode(ios_t *s, bufmode_t mode);
|
||||
void ios_bswap(ios_t *s, int bswap);
|
||||
int ios_copy(ios_t *to, ios_t *from, size_t nbytes, bool_t all);
|
||||
//void ios_lock(ios_t *s);
|
||||
//int ios_trylock(ios_t *s);
|
||||
//int ios_unlock(ios_t *s);
|
||||
|
||||
/* stream creation */
|
||||
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);
|
||||
// todo: ios_socket
|
||||
|
||||
/* high-level functions - output */
|
||||
int ios_putnum(ios_t *s, char *data, uint32_t type);
|
||||
int ios_putint(ios_t *s, int n);
|
||||
int ios_pututf8(ios_t *s, uint32_t wc);
|
||||
int ios_putstringz(ios_t *s, char *str, bool_t do_write_nulterm);
|
||||
/* single-bit I/O - even works for mixed reads and writes.
|
||||
mixing bit-level I/O with normal byte stream I/O has undefined effects and
|
||||
will almost certainly destroy your file. */
|
||||
int ios_putbit(ios_t *s, int bit);
|
||||
int ios_printf(ios_t *s, char *format, ...);
|
||||
|
||||
/* high-level stream functions - input */
|
||||
int ios_getnum(ios_t *s, char *data, uint32_t type);
|
||||
int ios_getutf8(ios_t *s, uint32_t *pwc);
|
||||
int ios_ungetutf8(ios_t *s, uint32_t wc);
|
||||
int ios_getstringz(ios_t *dest, ios_t *src);
|
||||
int ios_getstringn(ios_t *dest, ios_t *src, size_t nchars);
|
||||
int ios_readline(ios_t *dest, ios_t *s, char delim);
|
||||
int ios_getline(ios_t *s, char **pbuf, size_t *psz);
|
||||
int ios_getbit(ios_t *s, int *pbit); // returns # of bits read (0 or 1)
|
||||
|
||||
// seek by utf8 sequence increments
|
||||
int ios_nextutf8(ios_t *s);
|
||||
int ios_prevutf8(ios_t *s);
|
||||
|
||||
/* stdio-style functions */
|
||||
#define IOS_EOF (-1)
|
||||
int ios_putc(ios_t *s, int c);
|
||||
wint_t ios_putwc(ios_t *s, wchar_t wc);
|
||||
int ios_getc(ios_t *s);
|
||||
wint_t ios_getwc(ios_t *s);
|
||||
int ios_ungetc(ios_t *s, int c);
|
||||
wint_t ios_ungetwc(ios_t *s, wint_t wc);
|
||||
#define ios_puts(s, str) ios_write(s, str, strlen(str))
|
||||
|
||||
/*
|
||||
With memory streams, mixed reads and writes are equivalent to performing
|
||||
sequences of *p++, as either an lvalue or rvalue. File streams behave
|
||||
similarly, but other streams 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 stream).
|
||||
|
||||
Single-bit I/O is able to write partial bytes ONLY IF the stream 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 stream 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 stream 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 memory streams
|
||||
- support 64-bit and large files
|
||||
- efficient, low-latency buffering
|
||||
- unget
|
||||
- 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
|
||||
- special support for utf8
|
||||
- single-bit I/O
|
||||
- tries to allow switching between reading and writing
|
||||
- type-aware functions with byte-order swapping service
|
||||
- position counter for meaningful data offsets with sockets
|
||||
|
||||
note:
|
||||
the current code needs to be mostly rewritten. the design should be
|
||||
as follows:
|
||||
|
||||
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 stream 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.
|
||||
*/
|
||||
|
||||
#endif
|
1107
llt/attic/streams.c
1107
llt/attic/streams.c
File diff suppressed because it is too large
Load Diff
|
@ -1,213 +0,0 @@
|
|||
#ifndef __STREAMS_H_
|
||||
#define __STREAMS_H_
|
||||
|
||||
struct _stream;
|
||||
|
||||
// numeric type codes
|
||||
#define T_BOOL 0x000
|
||||
#define T_BYTE 0x001
|
||||
#define T_SHORT 0x002
|
||||
#define T_INT 0x003
|
||||
#define T_FLOAT 0x004
|
||||
#define T_DOUBLE 0x005
|
||||
#define T_INT64 0x006
|
||||
//#define T_LDOUBLE 0x007
|
||||
#define T_UNSIGNED 0x008
|
||||
#define T_CPLX 0x010
|
||||
|
||||
#define T_TYPEMASK 0x1f /* bits related to numeric type */
|
||||
#define T_TYPESIZE 0x07 /* bits related to type size */
|
||||
|
||||
#define is_type(a, t) (((a) & T_TYPESIZE) == (t))
|
||||
// type_bitseq tells whether 2 types are bit-representation compatible
|
||||
#define type_bitseq(a, b) (((a) & ~T_UNSIGNED) == ((b) & ~T_UNSIGNED))
|
||||
|
||||
extern unsigned int type_sizes[];
|
||||
|
||||
typedef struct {
|
||||
void (*free_func)(struct _stream *s);
|
||||
/* these return -1 on error, or new position if successful */
|
||||
/* set absolute position */
|
||||
off_t (*seek)(struct _stream *s, off_t where);
|
||||
/* seek to end (past last byte) */
|
||||
off_t (*seek_end)(struct _stream *s);
|
||||
/* move relative to current position */
|
||||
off_t (*skip)(struct _stream *s, off_t offs);
|
||||
|
||||
/* these return # of bytes read/written, 0 on error */
|
||||
size_t (*read)(struct _stream *s, char *dest, size_t size);
|
||||
size_t (*write)(struct _stream *s, char *data, size_t size);
|
||||
|
||||
/* truncate a stream to the given length */
|
||||
size_t (*trunc)(struct _stream *s, size_t size);
|
||||
|
||||
/* no data left? */
|
||||
bool_t (*eof)(struct _stream *s);
|
||||
|
||||
/* sync bit buffer, and sync to hardware if applicable */
|
||||
void (*flush)(struct _stream *s);
|
||||
/* could add fsync() call for durable writes */
|
||||
} stream_interface_t;
|
||||
|
||||
extern stream_interface_t fs_funcs;
|
||||
extern stream_interface_t ms_funcs;
|
||||
extern stream_interface_t ps_funcs;
|
||||
|
||||
#define is_memstream(s) (((stream_t*)s)->funcs==&ms_funcs)
|
||||
#define is_filestream(s) (((stream_t*)s)->funcs==&fs_funcs)
|
||||
#define is_pipestream(s) (((stream_t*)s)->funcs==&ps_funcs)
|
||||
|
||||
/* general i/o chunk size */
|
||||
#define CHUNK_SIZE 4096
|
||||
|
||||
/* this should be a multiple of 4; otherwise the struct gets padded to
|
||||
the next multiple of 4 anyway, wasting space */
|
||||
#define N_STREAM_LOCAL 40
|
||||
|
||||
typedef struct _stream {
|
||||
/* unfortunately, it seems that pos needs to be a signed type to be
|
||||
compatible with OS functions. */
|
||||
off_t pos;
|
||||
byte_t bitpos; /* offset within a byte, for writing individual bits */
|
||||
byte_t bitbuf; /* a copy of the byte at the current stream position */
|
||||
char ungotc;
|
||||
struct {
|
||||
/* does bit buffer need to be written? */
|
||||
unsigned char dirty:1;
|
||||
unsigned char byteswap:1;
|
||||
unsigned char readonly:1;
|
||||
unsigned char ungot:1;
|
||||
};
|
||||
|
||||
stream_interface_t *funcs;
|
||||
|
||||
/* stream-specific data */
|
||||
union {
|
||||
char *name;
|
||||
size_t maxsize;
|
||||
int rd; // pipe read descriptor
|
||||
};
|
||||
union {
|
||||
FILE *fptr;
|
||||
char *data;
|
||||
char *s;
|
||||
int wd; // pipe write descriptor
|
||||
};
|
||||
union {
|
||||
/* this is always a size in BYTES */
|
||||
size_t size;
|
||||
size_t len;
|
||||
};
|
||||
char local[N_STREAM_LOCAL];
|
||||
} stream_t;
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
stream_t *filestream_new(stream_t *s, char *fName,
|
||||
bool_t create, bool_t rewrite);
|
||||
stream_t *memstream_new(stream_t *s, size_t initsize);
|
||||
stream_t *pipestream_new(stream_t *s, int flags);
|
||||
stream_t *stream_fromfile(stream_t *s, FILE *f, char *name);
|
||||
//string_t *string_new(int len);
|
||||
//string_t *string_fromc(char *s);
|
||||
stream_t *memstream_copy(stream_t *s);
|
||||
void stream_free(stream_t *s);
|
||||
void stream_flush(stream_t *s);
|
||||
|
||||
|
||||
/* high level stream functions */
|
||||
|
||||
/* 'all' means copy to end of stream */
|
||||
int stream_copy(stream_t *to, stream_t *from, size_t nbytes, bool_t all);
|
||||
|
||||
int stream_put_num(stream_t *s, char *data, u_int32_t type);
|
||||
int stream_put_int(stream_t *s, int n);
|
||||
int stream_put_char(stream_t *s, u_int32_t wc);
|
||||
int stream_put_stringz(stream_t *s, char *str, bool_t nulterm);
|
||||
|
||||
/* single-bit I/O - even works for mixed reads and writes.
|
||||
mixing bit-level I/O with normal byte stream I/O has undefined effects and
|
||||
will almost certainly destroy your file. however, it is safe to switch
|
||||
between bit and byte I/O if you call stream_flush in between. */
|
||||
void stream_put_bit(stream_t *s, int bit);
|
||||
|
||||
/* warning: this uses a fixed-size buffer. it is intended only for printing
|
||||
normal things like "a= %d". if you might be printing a large buffer, you
|
||||
should use stream i/o functions directly. */
|
||||
int stream_printf(stream_t *s, char *format, ...);
|
||||
|
||||
|
||||
/* high level stream functions - input */
|
||||
|
||||
int stream_get_num(stream_t *s, char *data, u_int32_t type);
|
||||
int stream_get_char(stream_t *s, u_int32_t *pwc);
|
||||
int stream_get_stringz(stream_t *dest, stream_t *src);
|
||||
int stream_get_stringn(stream_t *dest, stream_t *src, size_t c);
|
||||
int stream_readline(stream_t *dest, stream_t *s, char delim);
|
||||
int stream_getline(stream_t *s, char **pbuf, size_t *psz);
|
||||
/* returns # of bits read (0 or 1) */
|
||||
int stream_get_bit(stream_t *s, int *pbit);
|
||||
|
||||
int stream_nextchar(stream_t *s);
|
||||
int stream_prevchar(stream_t *s);
|
||||
|
||||
void stream_close(stream_t *s);
|
||||
|
||||
/* TODO */
|
||||
// stream_fgetc
|
||||
// stream_ungetc
|
||||
|
||||
/* get underlying file descriptors, -1 if none */
|
||||
int stream_readfd(stream_t *s);
|
||||
int stream_writefd(stream_t *s);
|
||||
|
||||
/*
|
||||
low level stream functions
|
||||
|
||||
Streams are intended to provide a uniform function interface to various
|
||||
kinds of byte streams.
|
||||
|
||||
The eight low-level stream functions (below) are intended to be light weight.
|
||||
It must be easy to implement reasonably efficient higher-level stream
|
||||
functions, therefore any complexity required to make (for example) single
|
||||
byte reads and writes efficient must be implemented by the stream.
|
||||
|
||||
Note that you can implement file streams using fread(), fwrite(), etc.
|
||||
because buffering is already implemented in every standard C library. These
|
||||
calls do not make system calls in general, and are perfectly fine to use.
|
||||
*/
|
||||
|
||||
#define stream_seek(s, w) (s)->funcs->seek(s, w)
|
||||
#define stream_seek_end(s) (s)->funcs->seek_end(s)
|
||||
#define stream_skip(s, o) (s)->funcs->skip(s, o)
|
||||
#define stream_read(s, d, sz) (s)->funcs->read(s, (char*)d, sz)
|
||||
#define stream_write(s, d, sz) (s)->funcs->write(s, (char*)d, sz)
|
||||
#define stream_trunc(s, sz) (s)->funcs->trunc(s, sz)
|
||||
#define stream_eof(s) (s)->funcs->eof(s)
|
||||
|
||||
|
||||
STATIC_INLINE size_t stream_put_byte(stream_t *s, byte_t b)
|
||||
{
|
||||
return stream_write(s, (char*)&b, 1);
|
||||
}
|
||||
|
||||
STATIC_INLINE size_t stream_get_byte(stream_t *s, byte_t *pb)
|
||||
{
|
||||
return stream_read(s, (char*)pb, 1);
|
||||
}
|
||||
|
||||
#define stream_puts(s, str) stream_write(s, str, strlen(str))
|
||||
|
||||
/*
|
||||
stream_take_buffer
|
||||
|
||||
This lets you get the data of a stream without having to copy it. In order
|
||||
not to either lose the buffer or free it twice, this operation effectively
|
||||
empties the stream. In other words, size goes to 0, data becomes NULL.
|
||||
Whoever called the function takes full responsibility for the buffer.
|
||||
You must free it eventually.
|
||||
"size" gets set to the size of the data in the buffer.
|
||||
*/
|
||||
char *stream_take_buffer(stream_t *s, size_t *size);
|
||||
|
||||
#endif
|
|
@ -1,68 +0,0 @@
|
|||
/* moving data in small power-of-2 sized units */
|
||||
void copy_el(char *dest, char *src, size_t sz)
|
||||
{
|
||||
switch (sz) {
|
||||
case 16:
|
||||
*(int64_t*)&dest[0] = *(int64_t*)&src[0];
|
||||
*(int64_t*)&dest[8] = *(int64_t*)&src[8];
|
||||
break;
|
||||
case 8: *(int64_t*)dest = *(int64_t*)src; break;
|
||||
case 4: *(int32_t*)dest = *(int32_t*)src; break;
|
||||
case 2: *(int16_t*)dest = *(int16_t*)src; break;
|
||||
case 1: *dest = *src; break;
|
||||
}
|
||||
}
|
||||
|
||||
void swap_el(char *a, char *b, size_t sz)
|
||||
{
|
||||
int64_t i64;
|
||||
int32_t i32;
|
||||
int16_t i16;
|
||||
int8_t i8;
|
||||
switch (sz) {
|
||||
case 16:
|
||||
i64 = *(int64_t*)&a[0];
|
||||
*(int64_t*)&a[0] = *(int64_t*)&b[0];
|
||||
*(int64_t*)&b[0] = i64;
|
||||
i64 = *(int64_t*)&a[8];
|
||||
*(int64_t*)&a[8] = *(int64_t*)&b[8];
|
||||
*(int64_t*)&b[8] = i64;
|
||||
break;
|
||||
case 8:
|
||||
i64 = *(int64_t*)a;
|
||||
*(int64_t*)a = *(int64_t*)b;
|
||||
*(int64_t*)b = i64;
|
||||
break;
|
||||
case 4:
|
||||
i32 = *(int32_t*)a;
|
||||
*(int32_t*)a = *(int32_t*)b;
|
||||
*(int32_t*)b = i32;
|
||||
break;
|
||||
case 2:
|
||||
i16 = *(int16_t*)a;
|
||||
*(int16_t*)a = *(int16_t*)b;
|
||||
*(int16_t*)b = i16;
|
||||
break;
|
||||
case 1:
|
||||
i8 = *a;
|
||||
*a = *b;
|
||||
*b = i8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void neg_any(void *dest, void *a, numerictype_t tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case T_INT8: *(int8_t *)dest = -*(int8_t *)a; break;
|
||||
case T_UINT8: *(uint8_t *)dest = -*(uint8_t *)a; break;
|
||||
case T_INT16: *(int16_t *)dest = -*(int16_t *)a; break;
|
||||
case T_UINT16: *(uint16_t*)dest = -*(uint16_t*)a; break;
|
||||
case T_INT32: *(int32_t *)dest = -*(int32_t *)a; break;
|
||||
case T_UINT32: *(uint32_t*)dest = -*(uint32_t*)a; break;
|
||||
case T_INT64: *(int64_t *)dest = -*(int64_t *)a; break;
|
||||
case T_UINT64: *(uint64_t*)dest = -*(uint64_t*)a; break;
|
||||
case T_FLOAT: *(float *)dest = -*(float *)a; break;
|
||||
case T_DOUBLE: *(double *)dest = -*(double *)a; break;
|
||||
}
|
||||
}
|
176
llt/scrap
176
llt/scrap
|
@ -1,176 +0,0 @@
|
|||
/* null stream */
|
||||
off_t null_seek(struct _stream *s, off_t where)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t null_skip(struct _stream *s, off_t offs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
off_t null_seek_end(struct _stream *s)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t null_read(struct _stream *s, char *dest, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t null_write(struct _stream *s, char *data, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t null_trunc(struct _stream *s, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void null_flush(struct _stream *s)
|
||||
{
|
||||
}
|
||||
|
||||
bool_t null_eof(struct _stream *s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_null_stream(stream_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
DLLEXPORT stream_interface_t null_funcs =
|
||||
{free_null_stream, null_seek, null_seek_end, null_skip,
|
||||
null_read, null_write, null_trunc, null_eof, null_flush};
|
||||
|
||||
stream_t *nullstream_new()
|
||||
{
|
||||
stream_t *s;
|
||||
|
||||
s = (stream_t*)obj_alloc(stream_pool);
|
||||
s->funcs = &null_funcs;
|
||||
s->byteswap = false;
|
||||
s->bitpos = s->bitbuf = 0;
|
||||
s->pos = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void free_roms_stream(stream_t *s)
|
||||
{
|
||||
(void)s;
|
||||
}
|
||||
|
||||
size_t roms_write(struct _stream *s, char *data, size_t size)
|
||||
{
|
||||
(void)s;
|
||||
(void)data;
|
||||
(void)size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t roms_trunc(struct _stream *s, size_t size)
|
||||
{
|
||||
(void)size;
|
||||
return s->size;
|
||||
}
|
||||
|
||||
void roms_flush(struct _stream *s)
|
||||
{
|
||||
s->bitpos = 0;
|
||||
}
|
||||
|
||||
stream_interface_t roms_funcs =
|
||||
{free_roms_stream, ms_seek, ms_seek_end, ms_skip,
|
||||
ms_read, roms_write, roms_trunc, ms_eof, roms_flush};
|
||||
|
||||
/* read-only memory stream */
|
||||
stream_t *romemstream_new(stream_t *s, char *data, size_t len)
|
||||
{
|
||||
memstream_new(s, 0);
|
||||
|
||||
s->funcs = &roms_funcs;
|
||||
|
||||
s->data = data;
|
||||
s->size = len;
|
||||
s->maxsize = len+1;
|
||||
s->pos = 0;
|
||||
s->bitpos = s->bitbuf = 0;
|
||||
s->byteswap = false;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int stream_vput_int(stream_t *s, int nargs, ...)
|
||||
{
|
||||
u_int32_t val, i, c=0;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, nargs);
|
||||
for(i=0; i < (unsigned)nargs; i++) {
|
||||
val = va_arg(ap, int);
|
||||
c += stream_put_int(s, val);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
// after this function you are guaranteed to be able to perform
|
||||
// operations of the kind requested.
|
||||
static int _ios_setstate(ios_t *s, iostate_t newstate)
|
||||
{
|
||||
if (s->state != newstate) {
|
||||
if (s->state == iost_none || s->bm == bm_mem || s->bm == bm_none) {
|
||||
}
|
||||
else if (s->state == iost_rd) {
|
||||
// reading -> writing; try to put back unused buffer data
|
||||
// todo: another possibility here is to seek back s->size bytes,
|
||||
// not move any data, and retain the ability to seek backwards
|
||||
// within the buffer. the downside to that would be redundant
|
||||
// writes of stuff that was already in the file.
|
||||
ios_skip(s, -(off_t)(s->size - s->bpos));
|
||||
// todo: if the seek fails...?
|
||||
_discard_partial_buffer(s);
|
||||
// note: bitpos is left alone, so if all goes well we pick up
|
||||
// writing exactly where we stopped reading.
|
||||
}
|
||||
else if (s->state == iost_wr) {
|
||||
ios_flush(s);
|
||||
}
|
||||
s->state = newstate;
|
||||
}
|
||||
|
||||
// now make sure buffer is set up for the state we're in
|
||||
if (s->state == iost_wr) {
|
||||
// TODO: fill buffer if stenciling is requested
|
||||
}
|
||||
else if (s->state == iost_rd) {
|
||||
// TODO: fill buffer if needed
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* convert double to int64 in software */
|
||||
int64_t double_to_int64(double d)
|
||||
{
|
||||
int64_t i;
|
||||
int ex;
|
||||
union ieee754_double dl;
|
||||
|
||||
if (fabs(d) < 1) return 0;
|
||||
|
||||
dl.d = d;
|
||||
ex = dl.ieee.exponent - IEEE754_DOUBLE_BIAS;
|
||||
// fill mantissa into bits 0 to 51
|
||||
i = ((((int64_t)dl.mantissa0)<<32) | ((int64_t)dl.mantissa1));
|
||||
if (ex < 52)
|
||||
i >>= (52-ex);
|
||||
else if (ex > 52)
|
||||
i <<= (ex-52);
|
||||
}
|
Loading…
Reference in New Issue