X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Ffastbuf.c;h=ac6eaea7272b44676ec37d0e55cba1181541d2b6;hb=5de81f1debc4341fb3af4e756b0a18e196d5ba13;hp=9ba01ef4c6c0126ef24e559f3d28bf8096df0056;hpb=1cf8ac51f5495ccd5187dc220ffc69e95d6e0cfc;p=libucw.git diff --git a/ucw/fastbuf.c b/ucw/fastbuf.c index 9ba01ef4..ac6eaea7 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,29 +23,101 @@ 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"); +} + +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); } inline 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); } } @@ -49,13 +126,15 @@ 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,15 +154,20 @@ 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; } +int beof_slow(struct fastbuf *f) +{ + return f->bptr >= f->bstop && !brefill(f, 1); +} + void bputc_slow(struct fastbuf *f, uns c) { if (f->bptr >= f->bufend) - f->spout(f); + bspout(f); *f->bptr++ = c; } @@ -96,7 +180,7 @@ uns bread_slow(struct fastbuf *f, void *b, uns l, uns check) if (!k) { - f->refill(f); + brefill(f, check); k = f->bstop - f->bptr; if (!k) break; @@ -110,7 +194,7 @@ 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; } @@ -122,7 +206,7 @@ void bwrite_slow(struct fastbuf *f, const void *b, uns l) if (!k) { - f->spout(f); + bspout(f); k = f->bufend - f->bptr; } if (k > l) @@ -134,8 +218,7 @@ 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, uns l) { while (l) { @@ -147,7 +230,7 @@ bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l) { 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,21 +243,18 @@ bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l) } } -int -bconfig(struct fastbuf *f, uns item, int value) +int bconfig(struct fastbuf *f, uns 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, uns len) { while (len) { @@ -189,11 +269,12 @@ bskip_slow(struct fastbuf *f, uns len) return 1; } -ucw_off_t -bfilesize(struct fastbuf *f) +ucw_off_t bfilesize(struct fastbuf *f) { if (!f) return 0; + if (!f->seek) + return -1; ucw_off_t pos = btell(f); bflush(f); if (!f->seek(f, 0, SEEK_END)) @@ -202,3 +283,37 @@ bfilesize(struct fastbuf *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, uns 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; +}