parrotcode: IO buffer layer | |
Contents | C |
src/io/io_buf.c - IO buffer layer
The "buf" layer of Parrot IO. Buffering and all the fun stuff.
*/
#include "parrot/parrot.h" #include "io_private.h"
/* HEADERIZER HFILE: none */ /* HEADERIZER BEGIN: static */
static INTVAL PIO_buf_close( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
PARROT_CAN_RETURN_NULL PARROT_WARN_UNUSED_RESULT static ParrotIO * PIO_buf_fdopen( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), PIOHANDLE fd, INTVAL flags ) __attribute__nonnull__(1) __attribute__nonnull__(2);
static size_t PIO_buf_fill_readbuf( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(ParrotIOBuf *b) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) __attribute__nonnull__(4);
static INTVAL PIO_buf_flush( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
static INTVAL PIO_buf_init( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer) ) __attribute__nonnull__(1) __attribute__nonnull__(2);
PARROT_CAN_RETURN_NULL static ParrotIO * PIO_buf_open( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(const char *path), INTVAL flags ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
static size_t PIO_buf_peek( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) __attribute__nonnull__(4);
static size_t PIO_buf_read( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) __attribute__nonnull__(4);
static size_t PIO_buf_readline( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) __attribute__nonnull__(4);
static PIOOFF_T PIO_buf_seek( PARROT_INTERP, NOTNULL(ParrotIOLayer *l), NOTNULL(ParrotIO *io), PIOOFF_T offset, INTVAL whence ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
static INTVAL PIO_buf_setbuf( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), size_t bufsize ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
static INTVAL PIO_buf_setlinebuf( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);
static PIOOFF_T PIO_buf_tell( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(3);
static size_t PIO_buf_write( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s) ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) __attribute__nonnull__(4);
/* HEADERIZER END: static */
/* Defined at bottom */ extern const ParrotIOLayerAPI pio_buf_layer_api;
ParrotIOLayer pio_buf_layer = { NULL, "buf", PIO_L_TERMINAL, &pio_buf_layer_api, 0, 0 };
/* XXX: This is not portable */ #define DEFAULT_RECSEP '\n' #define IS_EOL(io,c) (io->recsep == (*c)) /* #define IS_EOL(io,c) ((*c) == '\n') */
/*
FUNCODC: PIO_buf_init
The buffer layer's Init
function.
Initializes buffering.
*/
static INTVAL PIO_buf_init(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer)) { if (PIO_STDOUT(interp)) PIO_buf_setlinebuf(interp, layer, PMC_data_typed(PIO_STDOUT(interp), ParrotIO *));
if (PIO_STDIN(interp))
PIO_buf_setbuf(interp, layer,
PMC_data_typed(PIO_STDIN(interp), ParrotIO *), PIO_UNBOUND);
return 0;
}
/*
FUNCDOC: PIO_buf_open
The buffer layer's Open
function.
*/
PARROT_CAN_RETURN_NULL static ParrotIO * PIO_buf_open(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(const char *path), INTVAL flags) { ParrotIOLayer * const l = PIO_DOWNLAYER(layer); ParrotIO * const io = PIO_open_down(interp, l, path, flags); if (!io) { /* error creating IO stream */ return NULL; } /* * We have an IO stream. Now setup stuff * for our layer before returning it. * XXX: Make default behaviour linebuffered? */ /*PIO_buf_setlinebuf(interp, l, io);*/ PIO_buf_setbuf(interp, l, io, PIO_UNBOUND); return io; }
/*
FUNCDOC: PIO_buf_setbuf
The buffer layer's SetBuf
function.
Don't pass SetBuf
calls down the stack, top layer wins. This doesn't mean other layers can't buffer, I just need to think about the mechanism for buffer control or if it even makes sense this way. Most layers will not implement SetBuf
.
*/
static INTVAL PIO_buf_setbuf(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), size_t bufsize) { ParrotIOLayer * const l = layer ? layer : io->stack; ParrotIOBuf * const b = &io->b;
/* If there is a buffer, make sure we flush before
* dinking around with the buffer.
*/
if (b->startb)
PIO_buf_flush(interp, l, io);
/* Choose an appropriate buffer size for caller */
switch (bufsize) {
case 0:
b->size = 0;
break;
case PIO_UNBOUND:
b->size = PIO_getblksize(io->fd);
break;
default:
b->size = (bufsize >= PIO_GRAIN ? bufsize : PIO_GRAIN);
break;
}
if (b->startb && (b->flags & PIO_BF_MALLOC)) {
mem_sys_free(b->startb);
b->startb = b->next = NULL;
}
if (b->size > 0) {
b->startb = b->next = (unsigned char *)mem_sys_allocate(b->size);
b->flags |= PIO_BF_MALLOC;
}
else
b->flags &= ~PIO_BF_MALLOC;
if (b->size != 0) {
io->flags &= ~PIO_F_LINEBUF;
io->flags |= PIO_F_BLKBUF;
}
else
io->flags &= ~(PIO_F_BLKBUF | PIO_F_LINEBUF);
return 0;
}
/*
FUNCDOC: PIO_buf_setlinebuf
The buffer layer's SetLineBuf
function.
*/
static INTVAL PIO_buf_setlinebuf(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { int err; ParrotIOLayer * const l = layer ? layer : io->stack;
/* already linebuffering */
if (io->flags & PIO_F_LINEBUF)
return 0;
/* Reuse setbuf call */
if ((err = PIO_buf_setbuf(interp, l, io, PIO_LINEBUFSIZE)) >= 0) {
/* Then switch to linebuf */
io->flags &= ~PIO_F_BLKBUF;
io->flags |= PIO_F_LINEBUF;
io->recsep = DEFAULT_RECSEP;
return 0;
}
return err;
}
/*
FUNCDOC: PIO_buf_fdopen
The buffer layer's FDOpen
function.
*/
PARROT_CAN_RETURN_NULL PARROT_WARN_UNUSED_RESULT static ParrotIO * PIO_buf_fdopen(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), PIOHANDLE fd, INTVAL flags) { ParrotIOLayer * const l = PIO_DOWNLAYER(layer); ParrotIO * const io = PIO_fdopen_down(interp, l, fd, flags);
if (io) {
if (io->flags & PIO_F_CONSOLE)
PIO_buf_setlinebuf(interp, l, io);
else
PIO_buf_setbuf(interp, l, io, PIO_UNBOUND);
}
return io;
}
/*
FUNCDOC: PIO_buf_close
The buffer layer's Close
function.
*/
static INTVAL PIO_buf_close(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { ParrotIOLayer * const l = PIO_DOWNLAYER(layer); PIO_buf_flush(interp, layer, io);
return PIO_close_down(interp, l, io);
}
/*
FUNCDOC: PIO_buf_flush
The buffer layer's Flush
function.
*/
static INTVAL PIO_buf_flush(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { long wrote; size_t to_write; STRING fake; /* * Either buffering is null, disabled, or empty. */ if (!io->b.startb || (io->flags & (PIO_F_BLKBUF | PIO_F_LINEBUF)) == 0 || (io->b.flags & (PIO_BF_WRITEBUF | PIO_BF_READBUF)) == 0) return 0; /* * Write flush */ if (io->b.flags & PIO_BF_WRITEBUF) { ParrotIOLayer * const l = layer; to_write = io->b.next - io->b.startb;
/* Flush to next layer */
fake.strstart = (char *)io->b.startb;
fake.bufused = to_write;
wrote = PIO_write_down(interp, PIO_DOWNLAYER(l), io, &fake);
if (wrote == (long)to_write) {
io->b.next = io->b.startb;
/* Release buffer */
io->b.flags &= ~PIO_BF_WRITEBUF;
return 0;
}
else {
/* FIXME: I/O Error */
}
}
/*
* Read flush
*/
else if (io->b.flags & PIO_BF_READBUF) {
io->b.flags &= ~PIO_BF_READBUF;
io->b.next = io->b.startb;
}
return -1;
}
/*
FUNCDOC: PIO_buf_fill_readbuf
The buffer layer's Fill
function.
*/
static size_t PIO_buf_fill_readbuf(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(ParrotIOBuf *b)) { size_t got; PIOOFF_T pos = io->fpos; STRING fake, *s; fake.strstart = (char *)b->startb; fake.bufused = b->size; s = &fake;
got = PIO_read_down(interp, PIO_DOWNLAYER(layer),
io, &s);
/* buffer-filling does not change fileposition */
io->fpos = pos;
/* nothing to get */
if (got == 0)
return 0;
b->endb = b->startb + got;
b->next = b->startb;
b->flags |= PIO_BF_READBUF;
return got;
}
/*
FUNCDOC: PIO_buf_read
The buffer layer's Read
function.
*/
static size_t PIO_buf_read(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf)) { ParrotIOLayer *l = layer; ParrotIOBuf *b; unsigned char *out_buf; STRING *s; size_t len; size_t current = 0;
/* write buffer flush */
if (io->b.flags & PIO_BF_WRITEBUF) {
PIO_buf_flush(interp, layer, io);
}
b = &io->b;
/* line buffered read */
if (io->flags & PIO_F_LINEBUF) {
return PIO_buf_readline(interp, layer, io, buf);
}
if (*buf == NULL) {
*buf = new_string_header(interp, 0);
(*buf)->bufused = len = 2048;
}
s = *buf;
len = s->bufused;
if (!s->strstart) {
Parrot_allocate_string(interp, s, len);
}
out_buf = (unsigned char *)s->strstart;
/* read Data from buffer */
if (b->flags & PIO_BF_READBUF) {
const size_t avail = b->endb - b->next;
current = avail < len ? avail : len;
memcpy(out_buf, b->next, current);
b->next += current;
io->fpos += current;
/* buffer completed */
if (current == avail) {
io->b.flags &= ~PIO_BF_READBUF;
/* XXX: Is the reset of next and endb really necessary ? */
io->b.endb = NULL;
io->b.next = io->b.startb;
}
if (len == current) {
s->strlen = s->bufused = len;
return current;
}
else {
/* more data needed from downlayer */
out_buf += current;
len -= current;
}
}
/* (re)fill the readbuffer */
if (!(b->flags & PIO_BF_READBUF)) {
size_t got;
if (len >= io->b.size) {
STRING fake;
STRING *sf = &fake;
fake.strstart = (char *)out_buf;
fake.bufused = len;
got = PIO_read_down(interp, PIO_DOWNLAYER(l), io, &sf);
s->strlen = s->bufused = current + got;
io->fpos += got;
return current + got;
}
got = PIO_buf_fill_readbuf(interp, l, io, b);
len = len < got ? len : got;
}
/* read from the read_buffer */
memcpy(out_buf, io->b.next, len);
s->strlen = s->bufused = current + len;
io->b.next += len;
io->fpos += len;
/* is the buffer is completely empty ? */
if (io->b.next == io->b.endb) {
io->b.flags &= ~PIO_BF_READBUF;
/* XXX: Is the reset of next and encb really necessary ? */
io->b.endb = NULL;
io->b.next = io->b.startb;
}
return current + len;
}
static size_t PIO_buf_peek(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf)) { ParrotIOLayer *l = layer; ParrotIOBuf *b; size_t len = 1; size_t avail = 0;
STRING * const s = PIO_make_io_string(interp, buf, 1);
/* write buffer flush */
if (io->b.flags & PIO_BF_WRITEBUF) {
PIO_buf_flush(interp, layer, io);
}
b = &io->b;
/* read Data from buffer */
if (b->flags & PIO_BF_READBUF) {
avail = b->endb - b->next;
/* if we have data available, copy out the next byte */
if (avail) {
ret_string:
memcpy(s->strstart, b->next, len);
s->bufused = s->strlen = len;
return len;
}
}
/* (re)fill the buffer */
if (! (b->flags & PIO_BF_READBUF)) {
size_t got;
/* exception if we're unbuffered */
if (io->b.size == 0)
real_exception(interp, NULL, PIO_ERROR, "Can't peek at unbuffered PIO");
got = PIO_buf_fill_readbuf(interp, l, io, b);
len = (len < got) ? len : got;
}
/* if we got any data, then copy out the next byte */
goto ret_string;
}
/*
FUNCDOC: PIO_buf_readline
This is called from PIO_buf_read()
to do line buffered reading if that is what is required.
*/
static size_t PIO_buf_readline(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf)) { size_t l; unsigned char *out_buf; unsigned char *buf_start; ParrotIOBuf * const b = &io->b; size_t len; STRING *s;
if (*buf == NULL) {
*buf = new_string_header(interp, 0);
}
s = *buf;
s->strlen = 0;
/* fill empty buffer */
if (!(b->flags & PIO_BF_READBUF)) {
if (PIO_buf_fill_readbuf(interp, layer, io, b) == 0)
return 0;
}
buf_start = b->next;
for (l = 0; b->next < b->endb;) {
l++;
if (IS_EOL(io, b->next++)) {
break;
}
/* if there is a buffer, readline is called by the read opcode
* - return just that part
*/
if (s->bufused && l == s->bufused)
break;
/* buffer completed; copy out and refill */
if (b->next == b->endb) {
len = b->endb - buf_start;
if (s->bufused < l) {
if (s->strstart) {
Parrot_reallocate_string(interp, s, l);
}
else {
Parrot_allocate_string(interp, s, l);
}
}
out_buf = (unsigned char*)s->strstart + s->strlen;
memcpy(out_buf, buf_start, len);
s->strlen = s->bufused = l;
if (PIO_buf_fill_readbuf(interp, layer, io, b) == 0)
return l;
buf_start = b->startb;
}
}
if (s->bufused < l) {
if (s->strstart) {
Parrot_reallocate_string(interp, s, l);
}
else {
Parrot_allocate_string(interp, s, l);
}
}
out_buf = (unsigned char*)s->strstart + s->strlen;
len = b->next - buf_start;
memcpy(out_buf, buf_start, len);
s->strlen = s->bufused = l;
/* check if buffer is finished */
if (b->next == b->endb) {
b->next = b->startb;
b->endb = NULL;
b->flags &= ~PIO_BF_READBUF;
}
return l;
}
/*
FUNCDOC: PIO_buf_write
The buffer layer's Write
function.
*/
static size_t PIO_buf_write(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s)) { size_t avail; void * const buffer = s->strstart; size_t len = s->bufused; int need_flush;
if (len <= 0)
return 0;
if (io->b.flags & PIO_BF_WRITEBUF) {
avail = io->b.size - (io->b.next - io->b.startb);
}
else if (io->b.flags & PIO_BF_READBUF) {
io->b.flags &= ~PIO_BF_READBUF;
io->b.next = io->b.startb;
avail = io->b.size;
}
else {
avail = io->b.size;
}
/* If we are line buffered, check for newlines.
* If any, we should flush
*/
need_flush = 0;
if (io->flags & PIO_F_LINEBUF) {
/* scan from end, it's likely that EOL is at end of string */
const char *p = (char*)buffer + len - 1;
size_t i;
for (i = 0; i < len; ++i, --p)
if (IS_EOL(io, p)) {
need_flush = 1;
break;
}
}
/*
* Large writes (multiples of blocksize) should write
* through generally for best performance, else you are
* just doing extra memcpys.
* FIXME: This is badly optimized, will fixup later.
*/
if (need_flush || len >= io->b.size) {
long wrote;
/* Write through, skip buffer. */
PIO_buf_flush(interp, layer, io);
wrote = PIO_write_down(interp, PIO_DOWNLAYER(layer), io, s);
if (wrote == (long)len) {
io->fpos += wrote;
return wrote;
}
else {
return (size_t)-1; /* Write error */
}
}
else if (avail > len) {
io->b.flags |= PIO_BF_WRITEBUF;
memcpy(io->b.next, buffer, len);
io->b.next += len;
io->fpos += len;
return len;
}
else {
const unsigned int diff = (int)(len - avail);
io->b.flags |= PIO_BF_WRITEBUF;
/* Fill remainder, flush, then try to buffer more */
memcpy(io->b.next, buffer, avail);
io->b.next += avail;
io->fpos += avail;
PIO_buf_flush(interp, layer, io);
memcpy(io->b.startb, ((const char *)buffer + avail), diff);
io->b.next += diff;
io->fpos += diff;
return len;
}
return (size_t)-1;
}
/*
FUNCDOC: PIO_buf_seek
The buffer layer's Seek
function.
*/
static PIOOFF_T PIO_buf_seek(PARROT_INTERP, NOTNULL(ParrotIOLayer *l), NOTNULL(ParrotIO *io), PIOOFF_T offset, INTVAL whence) { PIOOFF_T newpos;
switch (whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = io->fpos + offset;
break;
case SEEK_END:
newpos = PIO_seek_down(interp, PIO_DOWNLAYER(l), io, offset,
whence);
if (newpos == -1)
return -1;
break;
default:
/* XXX: somehow report the illegal whence value */
return -1;
}
if ((newpos < io->fpos - (io->b.next - io->b.startb))
|| (newpos >= io->fpos + (io->b.endb - io->b.next))) {
PIO_buf_flush(interp, l, io);
newpos = PIO_seek_down(interp, PIO_DOWNLAYER(l), io, newpos, SEEK_SET);
}
else {
io->b.next += newpos - io->fpos;
}
io->lpos = io->fpos;
io->fpos = newpos;
return io->fpos;
}
/*
FUNCDOC: PIO_buf_tell
The buffer layer's Tell
function.
*/
static PIOOFF_T PIO_buf_tell(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { return io->fpos; }
const ParrotIOLayerAPI pio_buf_layer_api = { PIO_buf_init, PIO_base_new_layer, PIO_base_delete_layer, PIO_null_push_layer, PIO_null_pop_layer, PIO_buf_open, PIO_null_open2, PIO_null_open3, PIO_null_open_async, PIO_buf_fdopen, PIO_buf_close, PIO_buf_write, PIO_null_write_async, PIO_buf_read, PIO_null_read_async, PIO_buf_flush, PIO_buf_peek, PIO_buf_seek, PIO_buf_tell, PIO_buf_setbuf, PIO_buf_setlinebuf, PIO_null_getcount, PIO_null_fill, PIO_null_eof, 0, /* no poll */ 0, /* no socket */ 0, /* no connect */ 0, /* no send */ 0, /* no recv */ 0, /* no bind */ 0, /* no listen */ 0 /* no accept */ };
/*
src/io/io_passdown.c, src/io/io_stdio.c, src/io/io_unix.c, src/io/io_win32.c, src/io/io.c, src/io/io_private.h.
Initially written by Melvin Smith.
Some ideas from AT&T SFIO.
*/
/* * Local variables: * c-file-style: "parrot" * End: * vim: expandtab shiftwidth=4: */
|