]> mj.ucw.cz Git - libucw.git/commitdiff
* Fixed several bugs in fastbufs. * Implemented vdie(). * fb-grow can be allocated...
authorPavel Charvat <pchar@ucw.cz>
Thu, 13 Nov 2008 14:21:44 +0000 (15:21 +0100)
committerMartin Mares <mj@ucw.cz>
Tue, 29 Mar 2011 10:55:05 +0000 (12:55 +0200)
ucw/fastbuf.c
ucw/fastbuf.h
ucw/fb-buffer.c
ucw/fb-file.c
ucw/fb-grow.c
ucw/fb-mem.c
ucw/fb-pool.c
ucw/lib.h
ucw/log.c
ucw/trans.c

index fb2cc49f88513f73e16d4035a0dc26c417baf5d2..4cd8bdfd1ccb467cce5bd37d61f75b1d47aa61d5 100644 (file)
@@ -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 <stdio.h>
 #include <stdlib.h>
@@ -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 ? : "<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);
     }
 }
 
@@ -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);
 }
index 90c7648eafeac8ff28509a07892a198d7fec5e80..41f339eef813e5ed7f51e09a81248ed8eb9552f9 100644 (file)
@@ -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;
index 7fff1a1cfd94574bf4c1ce7f554c2cc0e827f471..e0350f374f30fb99745cbeda3fd2cf899b9c304b 100644 (file)
 #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
@@ -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;
 }
 
index 46b7e71bc256d6689243798a66b120cbd235e9f7..35adbae49c8ffd1a13d3094afe6afabd16082cec 100644 (file)
@@ -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;
index 7d4a8f84237601eba702e2285ec0ebf8d30ba1ad..27b5bf038b0a8329b8e9ca3e939b838a4762d7c4 100644 (file)
 
 #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
index 2356e86bd128729175fe4c95b62babbfa5379ad0..38fa9001d374050f3289ce36243816c0fc88a1ed 100644 (file)
@@ -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;
index e889c8e9459f79400262137590b66e68e66339d0..17a9c6dc5ed7de6a96150ebd3a2cce830bff2e87 100644 (file)
 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;
     }
 }
index 65ee86c0d539f5faf727aa70e512a134610ac359..f114dbe978587e6036ad96f11cf32c677a8251bd 100644 (file)
--- 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. **/
index 7a870261d8ac2379f61ecf69aa0cfd296d15eea7..31252c9ee2b0f1f083d6eb2bfb29d3b06c9a8e17 100644 (file)
--- 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)
 {
index 1888ad89e6a2abbfdb72ea5f99bbf234fa68fd53..a58d7d166b7e1cac8407766d048d2229772fda2b 100644 (file)
@@ -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;