X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Ffastbuf.c;h=22501f43ed87cba8ff3e1529da0ebe2b57038b8c;hb=bc2bbfcbe76e78db9cde27455ddbcfe1ddcc61d6;hp=3ab8b826898fd3d68465482925576876252856d1;hpb=031256ad2e123eec58521f8e3eb9496c197641d2;p=libucw.git diff --git a/ucw/fastbuf.c b/ucw/fastbuf.c index 3ab8b826..22501f43 100644 --- a/ucw/fastbuf.c +++ b/ucw/fastbuf.c @@ -1,14 +1,19 @@ /* * UCW Library -- Fast Buffered I/O * - * (c) 1997--2007 Martin Mares + * (c) 1997--2011 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. */ -#include "ucw/lib.h" -#include "ucw/fastbuf.h" +#undef LOCAL_DEBUG + +#include +#include +#include +#include +#include #include #include @@ -18,44 +23,118 @@ void bclose(struct fastbuf *f) if (f) { bflush(f); + res_detach(f->res); + DBG("FB: closing", f); if (f->close) - f->close(f); + f->close(f); /* Should always free all internal resources, even if it throws an exception */ + } +} + +void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...) +{ + DBG("FB: throwing %s", full_id); + char full_id[16]; + snprintf(full_id, sizeof(full_id), "ucw.fb.%s", id); + ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */ + va_list args; + va_start(args, fmt); + if (!f->res) + die("Fastbuf %s error: %s", f->name ? : "", stk_vprintf(fmt, args)); + f->flags |= FB_DEAD; + f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */ + trans_vthrow(full_id, f, fmt, args); +} + +int brefill(struct fastbuf *f, int allow_eof) +{ + DBG("FB: refill"); + ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); + if (!f->refill) + bthrow(f, "read", "Stream not readable"); + if (f->refill(f)) + { + ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend); + return 1; + } + else + { + ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend); + if (!allow_eof && (f->flags & FB_DIE_ON_EOF)) + bthrow(f, "eof", "Unexpected EOF"); + return 0; + } +} + +static void do_spout(struct fastbuf *f) +{ + DBG("FB: spout"); + ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */ + if (!f->spout) + bthrow(f, "write", "Stream not writeable"); + f->spout(f); + ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); +} + +void bspout(struct fastbuf *f) +{ + do_spout(f); + if (f->bstop == f->bufend) + { + do_spout(f); + ASSERT(f->bstop < f->bufend); } } void bflush(struct fastbuf *f) { if (f->bptr > f->bstop) - f->spout(f); - else if (f->bstop > f->buffer) - f->bptr = f->bstop = f->buffer; + do_spout(f); + else + f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */ + DBG("FB: flushed"); } -inline void bsetpos(struct fastbuf *f, sh_off_t pos) +static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence) +{ + bflush(f); + DBG("FB: seeking to pos=%lld whence=%d %p %p %p %p", (long long)pos, whence, f->buffer, f->bstop, f->bptr, f->bufend); + if (!f->seek || !f->seek(f, pos, whence)) + bthrow(f, "seek", "Stream not seekable"); + DBG("FB: seeked %p %p %p %p", f->buffer, f->bstop, f->bptr, f->bufend); + ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); + if (whence == SEEK_SET) + ASSERT(pos == btell(f)); + else + ASSERT(btell(f) >= 0); +} + +void bsetpos(struct fastbuf *f, ucw_off_t pos) { /* We can optimize seeks only when reading */ - if (pos >= f->pos - (f->bstop - f->buffer) && pos <= f->pos) + if (f->bptr < f->bstop && pos <= f->pos && pos >= f->pos - (f->bstop - f->buffer)) /* If bptr == bstop, then [buffer, bstop] may be undefined */ f->bptr = f->bstop + (pos - f->pos); - else + else if (pos != btell(f)) { - bflush(f); - if (!f->seek || !f->seek(f, pos, SEEK_SET)) - die("bsetpos: stream not seekable"); + if (pos < 0) + bthrow(f, "seek", "Seek out of range"); + do_seek(f, pos, SEEK_SET); } } -void bseek(struct fastbuf *f, sh_off_t pos, int whence) +void bseek(struct fastbuf *f, ucw_off_t pos, int whence) { switch (whence) { case SEEK_SET: - return bsetpos(f, pos); + bsetpos(f, pos); + break; case SEEK_CUR: - return bsetpos(f, btell(f) + pos); + bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */ + break; case SEEK_END: - bflush(f); - if (!f->seek || !f->seek(f, pos, SEEK_END)) - die("bseek: stream not seekable"); + if (pos > 0) + bthrow(f, "seek", "Seek out of range"); + do_seek(f, pos, SEEK_END); break; default: die("bseek: invalid whence=%d", whence); @@ -66,7 +145,7 @@ int bgetc_slow(struct fastbuf *f) { if (f->bptr < f->bstop) return *f->bptr++; - if (!f->refill(f)) + if (!brefill(f, 0)) return -1; return *f->bptr++; } @@ -75,28 +154,33 @@ int bpeekc_slow(struct fastbuf *f) { if (f->bptr < f->bstop) return *f->bptr; - if (!f->refill(f)) + if (!brefill(f, 0)) return -1; return *f->bptr; } -void bputc_slow(struct fastbuf *f, uns c) +int beof_slow(struct fastbuf *f) +{ + return f->bptr >= f->bstop && !brefill(f, 1); +} + +void bputc_slow(struct fastbuf *f, uint c) { if (f->bptr >= f->bufend) - f->spout(f); + bspout(f); *f->bptr++ = c; } -uns bread_slow(struct fastbuf *f, void *b, uns l, uns check) +uint bread_slow(struct fastbuf *f, void *b, uint l, uint check) { - uns total = 0; + uint total = 0; while (l) { - uns k = f->bstop - f->bptr; + uint k = f->bstop - f->bptr; if (!k) { - f->refill(f); + brefill(f, check); k = f->bstop - f->bptr; if (!k) break; @@ -110,19 +194,19 @@ uns bread_slow(struct fastbuf *f, void *b, uns l, uns check) total += k; } if (check && total && l) - die("breadb: short read"); + bthrow(f, "eof", "breadb: short read"); return total; } -void bwrite_slow(struct fastbuf *f, const void *b, uns l) +void bwrite_slow(struct fastbuf *f, const void *b, uint l) { while (l) { - uns k = f->bufend - f->bptr; + uint k = f->bufend - f->bptr; if (!k) { - f->spout(f); + bspout(f); k = f->bufend - f->bptr; } if (k > l) @@ -134,20 +218,19 @@ void bwrite_slow(struct fastbuf *f, const void *b, uns l) } } -void -bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l) +void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uint l) { while (l) { byte *fptr, *tptr; - uns favail, tavail, n; + uint favail, tavail, n; favail = bdirect_read_prepare(f, &fptr); if (!favail) { if (l == ~0U) return; - die("bbcopy: source exhausted"); + bthrow(f, "eof", "bbcopy: source exhausted"); } tavail = bdirect_write_prepare(t, &tptr); n = MIN(l, favail); @@ -160,26 +243,23 @@ bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l) } } -int -bconfig(struct fastbuf *f, uns item, int value) +int bconfig(struct fastbuf *f, uint item, int value) { - return f->config ? f->config(f, item, value) : -1; + return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1; } -void -brewind(struct fastbuf *f) +void brewind(struct fastbuf *f) { bflush(f); bsetpos(f, 0); } -int -bskip_slow(struct fastbuf *f, uns len) +int bskip_slow(struct fastbuf *f, uint len) { while (len) { byte *buf; - uns l = bdirect_read_prepare(f, &buf); + uint l = bdirect_read_prepare(f, &buf); if (!l) return 0; l = MIN(l, len); @@ -189,16 +269,51 @@ bskip_slow(struct fastbuf *f, uns len) return 1; } -sh_off_t -bfilesize(struct fastbuf *f) +ucw_off_t bfilesize(struct fastbuf *f) { if (!f) return 0; - sh_off_t pos = btell(f); + if (!f->seek) + return -1; + ucw_off_t pos = btell(f); bflush(f); if (!f->seek(f, 0, SEEK_END)) return -1; - sh_off_t len = btell(f); + ucw_off_t len = btell(f); bsetpos(f, pos); return len; } + +/* Resources */ + +static void fb_res_detach(struct resource *r) +{ + struct fastbuf *f = r->priv; + f->res = NULL; +} + +static void fb_res_free(struct resource *r) +{ + struct fastbuf *f = r->priv; + f->res = NULL; + bclose(f); +} + +static void fb_res_dump(struct resource *r, uint indent UNUSED) +{ + struct fastbuf *f = r->priv; + printf(" name=%s\n", f->name); +} + +static const struct res_class fb_res_class = { + .name = "fastbuf", + .detach = fb_res_detach, + .dump = fb_res_dump, + .free = fb_res_free, +}; + +struct fastbuf *fb_tie(struct fastbuf *f) +{ + f->res = res_new(&fb_res_class, f); + return f; +}