* 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 <stdio.h>
#include <stdlib.h>
{
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 ? : "<fb>", 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);
}
}
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);
}
}
-void
-bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
+void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
{
while (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);
}
}
-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)
{
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))
/* 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);
.free = fb_res_free,
};
-void
-fb_tie(struct fastbuf *f)
+void fb_tie(struct fastbuf *f)
{
f->res = res_new(&fb_res_class, f);
}
* 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). **/
* 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;
#include <stdlib.h>
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
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;
}
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;
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;
#include "ucw/lib.h"
#include "ucw/fastbuf.h"
+#include "ucw/mempool.h"
#include <stdio.h>
#include <stdlib.h>
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 = "<fbgbuf>";
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
{
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;
}
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;
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;
}
}
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. **/
}
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)
{
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;