From: Pavel Charvat Date: Thu, 13 Nov 2008 14:21:44 +0000 (+0100) Subject: * Fixed several bugs in fastbufs. * Implemented vdie(). * fb-grow can be allocated... X-Git-Tag: v5.0~74^2~32 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=f20ecabf44f24743a1383f2ae2148adb660aa8f5;p=libucw.git * Fixed several bugs in fastbufs. * Implemented vdie(). * fb-grow can be allocated from mempool... also added support for mixed R/W access --- diff --git a/ucw/fastbuf.c b/ucw/fastbuf.c index fb2cc49f..4cd8bdfd 100644 --- a/ucw/fastbuf.c +++ b/ucw/fastbuf.c @@ -7,10 +7,13 @@ * of the GNU Lesser General Public License. */ +#undef LOCAL_DEBUG + #include "ucw/lib.h" #include "ucw/fastbuf.h" #include "ucw/respool.h" #include "ucw/trans.h" +#include "ucw/stkstring.h" #include #include @@ -19,70 +22,104 @@ void bclose(struct fastbuf *f) { if (f) { - if (!(f->flags & FB_DEAD)) - bflush(f); - if (f->close) - f->close(f); + bflush(f); if (f->res) - res_drop(f->res); + { + res_drop(f->res); + f->res = NULL; + } + DBG("FB: closing", f); + if (f->close) + 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, ...) { - ASSERT(!(f->flags & FB_DEAD)); - f->flags |= FB_DEAD; + ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */ + DBG("FB: throwing %s", id); 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(id, f, fmt, args); } int brefill(struct fastbuf *f, int allow_eof) { - ASSERT(f->bptr >= f->bstop); + 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, "fb.read", "Stream not readable"); if (f->refill(f)) { - ASSERT(f->bptr < f->bstop); + 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, "fb.eof", "Unexpected EOF"); - ASSERT(f->bptr == f->bstop); return 0; } } -void bspout(struct fastbuf *f) +static void do_spout(struct fastbuf *f) { - ASSERT(f->bptr > f->bstop || f->bptr >= f->bufend); + 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, "fb.write", "Stream not writeable"); f->spout(f); - ASSERT(f->bptr < f->bufend); + 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) - bspout(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, "fb.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)) - bthrow(f, "fb.seek", "Stream not seekable"); + if (pos < 0) + bthrow(f, "fb.seek", "Seek out of range"); + do_seek(f, pos, SEEK_SET); } } @@ -91,13 +128,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)) - bthrow(f, "fb.seek", "Stream not seekable"); + if (pos > 0) + bthrow(f, "fb.seek", "Seek out of range"); + do_seek(f, pos, SEEK_END); break; default: die("bseek: invalid whence=%d", whence); @@ -181,8 +220,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) { @@ -194,7 +232,7 @@ bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l) { if (l == ~0U) return; - die("bbcopy: source exhausted"); + bthrow(f, "fb.read", "bbcopy: source exhausted"); } tavail = bdirect_write_prepare(t, &tptr); n = MIN(l, favail); @@ -207,21 +245,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) { @@ -236,11 +271,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)) @@ -252,23 +288,20 @@ bfilesize(struct fastbuf *f) /* Resources */ -static void -fb_res_detach(struct resource *r) +static void fb_res_detach(struct resource *r) { struct fastbuf *f = r->priv; f->res = NULL; } -static void -fb_res_free(struct resource *r) +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) +static void fb_res_dump(struct resource *r) { struct fastbuf *f = r->priv; printf(" name=%s", f->name); @@ -281,8 +314,7 @@ static const struct res_class fb_res_class = { .free = fb_res_free, }; -void -fb_tie(struct fastbuf *f) +void fb_tie(struct fastbuf *f) { f->res = res_new(&fb_res_class, f); } diff --git a/ucw/fastbuf.h b/ucw/fastbuf.h index 90c7648e..41f339ee 100644 --- a/ucw/fastbuf.h +++ b/ucw/fastbuf.h @@ -380,7 +380,10 @@ static inline uns fbbuf_count_written(struct fastbuf *f) /** Calculates, how man * All fastbufs of this type are tied to resources automatically. ***/ +struct mempool; + struct fastbuf *fbgrow_create(unsigned basic_size); /** Create the growing buffer pre-allocated to @basic_size bytes. **/ +struct fastbuf *fbgrow_create_mp(struct mempool *mp, unsigned basic_size); /** Create the growing buffer pre-allocated to @basic_size bytes. **/ void fbgrow_reset(struct fastbuf *b); /** Reset stream and prepare for writing. **/ void fbgrow_rewind(struct fastbuf *b); /** Prepare for reading (of already written data). **/ @@ -391,7 +394,6 @@ void fbgrow_rewind(struct fastbuf *b); /** Prepare for reading (of already wri * buffer, but this time the buffer is allocated from within a memory pool. ***/ -struct mempool; struct fbpool { /** Structure for fastbufs & mempools. **/ struct fastbuf fb; struct mempool *mp; diff --git a/ucw/fb-buffer.c b/ucw/fb-buffer.c index 7fff1a1c..e0350f37 100644 --- a/ucw/fb-buffer.c +++ b/ucw/fb-buffer.c @@ -14,9 +14,11 @@ #include static int -fbbuf_refill(struct fastbuf *f UNUSED) +fbbuf_refill(struct fastbuf *f) { - return 0; + f->bstop = f->bufend; + f->pos = f->bstop - f->buffer; + return f->bptr < f->bstop; } static int @@ -26,10 +28,11 @@ fbbuf_seek(struct fastbuf *f, ucw_off_t pos, int whence) ucw_off_t len = f->bufend - f->buffer; if (whence == SEEK_END) pos += len; - ASSERT(pos >= 0 && pos <= len); + if (pos < 0 || pos > len) + bthrow(f, "fb.seek", "Seek out of range"); f->bptr = f->buffer + pos; - f->bstop = f->bufend; - f->pos = len; + f->bstop = f->buffer; + f->pos = 0; return 1; } diff --git a/ucw/fb-file.c b/ucw/fb-file.c index 46b7e71b..35adbae4 100644 --- a/ucw/fb-file.c +++ b/ucw/fb-file.c @@ -91,7 +91,7 @@ long_seek: goto long_seek; } /* Seek into previous window (do nothing... for example brewind) */ - else if ((uns)diff <= F->wlen) + else if ((uns)diff <= F->wlen) { f->bstop = f->buffer + F->wlen; f->bptr = f->bstop - diff; @@ -165,29 +165,23 @@ bfd_spout(struct fastbuf *f) l -= z; c += z; } - f->bptr = f->buffer = FB_BUFFER(f); + f->bptr = f->bstop = f->buffer = FB_BUFFER(f); } static int bfd_seek(struct fastbuf *f, ucw_off_t pos, int whence) { + ASSERT(f->bptr == f->bstop); /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */ - ucw_off_t l; switch (whence) { case SEEK_SET: f->pos = pos; return 1; - case SEEK_CUR: - l = f->pos + pos; - if ((pos > 0) ^ (l > f->pos)) - return 0; - f->pos = l; - return 1; - case SEEK_END: - l = ucw_seek(FB_FILE(f)->fd, pos, SEEK_END); + case SEEK_END: ; + ucw_off_t l = ucw_seek(FB_FILE(f)->fd, pos, SEEK_END); if (l < 0) - return 0; + bthrow(f, "fb.seek", "Error seeking %s: %m", f->name); FB_FILE(f)->wpos = f->pos = l; FB_FILE(f)->wlen = 0; return 1; diff --git a/ucw/fb-grow.c b/ucw/fb-grow.c index 7d4a8f84..27b5bf03 100644 --- a/ucw/fb-grow.c +++ b/ucw/fb-grow.c @@ -9,100 +9,107 @@ #include "ucw/lib.h" #include "ucw/fastbuf.h" +#include "ucw/mempool.h" #include #include struct fb_gbuf { struct fastbuf fb; - byte *last_written; + struct mempool *mp; + byte *end; }; #define FB_GBUF(f) ((struct fb_gbuf *)(f)->is_fastbuf) -static int -fbgrow_refill(struct fastbuf *b) +static int fbgrow_refill(struct fastbuf *b) { - if (b->bstop != FB_GBUF(b)->last_written) - { - /* There was an intervening flush */ - b->bstop = FB_GBUF(b)->last_written; - b->pos = b->bstop - b->buffer; - return 1; - } - /* We are at the end */ - return 0; + b->bstop = FB_GBUF(b)->end; + b->pos = b->bstop - b->buffer; + return b->bstop > b->bptr; } -static void -fbgrow_spout(struct fastbuf *b) +static void fbgrow_spout(struct fastbuf *b) { - if (b->bptr >= b->bufend) + if (b->bptr == b->bufend) { uns len = b->bufend - b->buffer; - b->buffer = xrealloc(b->buffer, 2*len); - b->bufend = b->buffer + 2*len; - b->bstop = b->buffer; - b->bptr = b->buffer + len; + if (FB_GBUF(b)->mp) + { + byte *old = b->buffer; + b->buffer = mp_alloc(FB_GBUF(b)->mp, 2 * len); + memcpy(b->buffer, old, len); + } + else + b->buffer = xrealloc(b->buffer, 2 * len); + b->bufend = b->buffer + 2 * len; + FB_GBUF(b)->end = b->bptr = b->buffer + len; } + else if (FB_GBUF(b)->end < b->bptr) + FB_GBUF(b)->end = b->bptr; + b->bstop = b->buffer; + b->pos = 0; } -static int -fbgrow_seek(struct fastbuf *b, ucw_off_t pos, int whence) +static int fbgrow_seek(struct fastbuf *b, ucw_off_t pos, int whence) { - ASSERT(FB_GBUF(b)->last_written); /* Seeks allowed only in read mode */ - ucw_off_t len = FB_GBUF(b)->last_written - b->buffer; + ucw_off_t len = FB_GBUF(b)->end - b->buffer; if (whence == SEEK_END) pos += len; - ASSERT(pos >= 0 && pos <= len); + if (pos < 0 || pos > len) + bthrow(b, "fb.seek", "Seek out of range"); b->bptr = b->buffer + pos; - b->bstop = FB_GBUF(b)->last_written; - b->pos = len; + b->bstop = b->buffer; + b->pos = 0; return 1; } -static void -fbgrow_close(struct fastbuf *b) +static void fbgrow_close(struct fastbuf *b) { xfree(b->buffer); xfree(b); } -struct fastbuf * -fbgrow_create(unsigned basic_size) +struct fastbuf *fbgrow_create_mp(struct mempool *mp, unsigned basic_size) { - struct fastbuf *b = xmalloc_zero(sizeof(struct fb_gbuf)); - b->buffer = xmalloc(basic_size); + ASSERT(basic_size); + struct fastbuf *b; + if (mp) + { + b = mp_alloc_zero(mp, sizeof(struct fb_gbuf)); + b->buffer = mp_alloc(mp, basic_size); + FB_GBUF(b)->mp = mp; + } + else + { + b = xmalloc_zero(sizeof(struct fb_gbuf)); + b->buffer = xmalloc(basic_size); + b->close = fbgrow_close; + } b->bufend = b->buffer + basic_size; b->bptr = b->bstop = b->buffer; b->name = ""; b->refill = fbgrow_refill; b->spout = fbgrow_spout; b->seek = fbgrow_seek; - b->close = fbgrow_close; b->can_overwrite_buffer = 1; fb_tie(b); return b; } -void -fbgrow_reset(struct fastbuf *b) +struct fastbuf *fbgrow_create(unsigned basic_size) { - b->bptr = b->bstop = b->buffer; + return fbgrow_create_mp(NULL, basic_size); +} + +void fbgrow_reset(struct fastbuf *b) +{ + FB_GBUF(b)->end = b->bptr = b->bstop = b->buffer; b->pos = 0; - FB_GBUF(b)->last_written = NULL; } -void -fbgrow_rewind(struct fastbuf *b) +void fbgrow_rewind(struct fastbuf *b) { - if (!FB_GBUF(b)->last_written) - { - /* Last operation was a write, so remember the end position */ - FB_GBUF(b)->last_written = b->bptr; - } - b->bptr = b->buffer; - b->bstop = FB_GBUF(b)->last_written; - b->pos = b->bstop - b->buffer; + brewind(b); } #ifdef TEST diff --git a/ucw/fb-mem.c b/ucw/fb-mem.c index 2356e86b..38fa9001 100644 --- a/ucw/fb-mem.c +++ b/ucw/fb-mem.c @@ -112,10 +112,10 @@ fbmem_seek(struct fastbuf *f, ucw_off_t pos, int whence) { if (pos <= b->pos + (ucw_off_t)b->size) /* <=, because we need to be able to seek just after file end */ { - f->buffer = b->data; + f->buffer = f->bstop = b->data; f->bptr = b->data + (pos - b->pos); - f->bufend = f->bstop = b->data + b->size; - f->pos = b->pos + b->size; + f->bufend = b->data + b->size; + f->pos = b->pos; FB_MEM(f)->block = b; return 1; } @@ -123,7 +123,7 @@ fbmem_seek(struct fastbuf *f, ucw_off_t pos, int whence) if (!m->first && !pos) { /* Seeking to offset 0 in an empty file needs an exception */ - f->buffer = f->bptr = f->bufend = NULL; + f->buffer = f->bptr = f->bstop = f->bufend = NULL; f->pos = 0; FB_MEM(f)->block = NULL; return 1; diff --git a/ucw/fb-pool.c b/ucw/fb-pool.c index e889c8e9..17a9c6dc 100644 --- a/ucw/fb-pool.c +++ b/ucw/fb-pool.c @@ -19,12 +19,11 @@ static void fbpool_spout(struct fastbuf *b) { - if (b->bptr >= b->bufend) + if (b->bptr == b->bufend) { uns len = b->bufend - b->buffer; - b->buffer = mp_expand(FB_POOL(b)->mp); + b->bstop = b->buffer = mp_expand(FB_POOL(b)->mp); b->bufend = b->buffer + mp_avail(FB_POOL(b)->mp); - b->bstop = b->buffer; b->bptr = b->buffer + len; } } diff --git a/ucw/lib.h b/ucw/lib.h index 65ee86c0..f114dbe9 100644 --- a/ucw/lib.h +++ b/ucw/lib.h @@ -118,6 +118,7 @@ enum log_levels { /** The available log levels to pass to msg() and friends. * void msg(uns flags, const char *fmt, ...) FORMAT_CHECK(printf,2,3); void vmsg(uns flags, const char *fmt, va_list args); /** A vararg version of msg(). **/ void die(const char *, ...) NONRET FORMAT_CHECK(printf,1,2); /** Log a fatal error message and exit the program. **/ +void vdie(const char *fmt, va_list args) NONRET; /** va_list version of die() **/ extern char *log_title; /** An optional log message title. Set to program name by log_init(). **/ extern int log_pid; /** An optional PID printed in each log message. Set to 0 if it shouldn't be logged. **/ diff --git a/ucw/log.c b/ucw/log.c index 7a870261..31252c9e 100644 --- a/ucw/log.c +++ b/ucw/log.c @@ -342,18 +342,22 @@ do_die(void) } void -die(const char *fmt, ...) +vdie(const char *fmt, va_list args) { - va_list args; - - va_start(args, fmt); vmsg(L_FATAL, fmt, args); - va_end(args); if (log_die_hook) log_die_hook(); do_die(); } +void +die(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vdie(fmt, args); +} + void assert_failed(const char *assertion, const char *file, int line) { diff --git a/ucw/trans.c b/ucw/trans.c index 1888ad89..a58d7d16 100644 --- a/ucw/trans.c +++ b/ucw/trans.c @@ -213,8 +213,9 @@ trans_throw(const char *id, void *object, const char *fmt, ...) void trans_vthrow(const char *id, void *object, const char *fmt, va_list args) { - trans_init(); struct mempool *mp = trans_get_pool(); + if (!mp) + vdie(fmt, args); struct exception *x = mp_alloc(mp, sizeof(*x)); x->id = id; x->object = object;