]> mj.ucw.cz Git - libucw.git/commitdiff
Major cleanup of fastbufs:
authorMartin Mares <mj@ucw.cz>
Mon, 23 Sep 2002 12:07:15 +0000 (12:07 +0000)
committerMartin Mares <mj@ucw.cz>
Mon, 23 Sep 2002 12:07:15 +0000 (12:07 +0000)
  o  Split generic fastbuf from low-level routines. `struct fastbuf'
     no longer contains low-level data like `fd' or `is_temp_file'.
  o  Introduced safe type casting macros to avoid programming errors.
  o  `struct fastbuf' is no longer freed by the high-level code.
  o  Documented behaviour of bflush() between reads and writes.
  o  Redefined semantics of fastbuf->pos: it now corresponds to `bstop'
     instead of `buffer', hence it always coincides with real file
     position, making `fdpos' unnecessary.

lib/fastbuf.h
lib/fb-file.c
lib/fb-mem.c
lib/fb-temp.c

index 69df9b710bc4f565a38c85b65143fdf7f84dba41..f3a05952c9db60a16021f66da5e1eaf2d9e2badd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Sherlock Library -- Fast Buffered I/O
  *
- *     (c) 1997--2000 Martin Mares <mj@ucw.cz>
+ *     (c) 1997--2002 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
@@ -19,7 +19,8 @@
 #include "lib/unaligned.h"
 
 /*
- *  Generic buffered I/O on a top of buffer swapping functions.
+ *  Generic buffered I/O. You supply hooks to be called for low-level operations
+ *  (swapping of buffers, seeking and closing), we do the rest.
  *
  *  Buffer layout when reading:
  *
  *  +----------------+---------------------------+
  *  ^                 ^                           ^
  *  buffer=bstop      bptr                        bufend
+ *
+ *  Dirty tricks:
+ *
+ *    - You can mix reads and writes on the same stream, but you must
+ *     call bflush() in between and remember that the file position
+ *     points after the flushed buffer which is not necessarily the same
+ *     as after the data you've read.
+ *    - The spout/refill hooks can change not only bptr and bstop, but also
+ *     the location of the buffer; fb-mem.c takes advantage of it.
  */
 
 struct fastbuf {
+  byte is_fastbuf[0];                  /* Dummy field for checking of type casts */
   byte *bptr, *bstop;                  /* Access pointers */
   byte *buffer, *bufend;               /* Start and end of the buffer */
   byte *name;                          /* File name for error messages */
-  uns buflen;                          /* Size of the buffer */
-  sh_off_t pos;                                /* Position of buffer start in the file */
-  sh_off_t fdpos;                      /* Current position in the non-buffered file */
-  int fd;                              /* File descriptor, -1 if not a real file */
-  int is_temp_file;                    /* Is a temporary file, delete on close */
-  void *lldata;                                /* Data private to access functions below */
-  void *llpos;                         /* ... continued ... */
+  sh_off_t pos;                                /* Position of bstop in the file */
   int (*refill)(struct fastbuf *);     /* Get a buffer with new data */
   void (*spout)(struct fastbuf *);     /* Write buffer data to the file */
   void (*seek)(struct fastbuf *, sh_off_t, int);  /* Slow path for bseek(), buffer already flushed */
@@ -61,10 +66,18 @@ struct fastbuf {
 
 /* FastIO on standard files */
 
+struct fb_file {
+  struct fastbuf fb;
+  int fd;                              /* File descriptor, -1 if not a real file */
+  int is_temp_file;                    /* Is a temporary file, delete on close */
+};
+#define FB_FILE(f) ((struct fb_file *)(f)->is_fastbuf)
+
 struct fastbuf *bopen(byte *name, uns mode, uns buffer);
 struct fastbuf *bopen_tmp(uns buffer);
 struct fastbuf *bfdopen(int fd, uns buffer);
 void bbcopy(struct fastbuf *f, struct fastbuf *t, uns l);
+#define FB_IS_TEMP_FILE(f) FB_FILE(f)->is_temp_file
 
 /* FastIO on in-memory streams */
 
@@ -80,7 +93,7 @@ void bsetpos(struct fastbuf *f, sh_off_t pos);
 
 static inline sh_off_t btell(struct fastbuf *f)
 {
-  return f->pos + (f->bptr - f->buffer);
+  return f->pos + (f->bptr - f->bstop);
 }
 
 int bgetc_slow(struct fastbuf *f);
index 31c3bf180674b677d0e65e8e3aa7370148b7302d..1fc85de5ed500445ec178a237961ce9bdd63a974 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Sherlock Library -- Fast Buffered I/O on Files
  *
- *     (c) 1997--2000 Martin Mares <mj@ucw.cz>
+ *     (c) 1997--2002 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
@@ -11,7 +11,7 @@
 #include "lib/fastbuf.h"
 #include "lib/lfs.h"
 
-#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 static int
 bfd_refill(struct fastbuf *f)
 {
-  int l = read(f->fd, f->buffer, f->buflen);
-
+  int l = read(FB_FILE(f)->fd, f->buffer, f->bufend-f->buffer);
   if (l < 0)
     die("Error reading %s: %m", f->name);
   f->bptr = f->buffer;
   f->bstop = f->buffer + l;
-  f->pos = f->fdpos;
-  f->fdpos += l;
+  f->pos += l;
   return l;
 }
 
@@ -36,17 +34,16 @@ bfd_spout(struct fastbuf *f)
   int l = f->bptr - f->buffer;
   char *c = f->buffer;
 
+  f->pos += l;
   while (l)
     {
-      int z = write(f->fd, c, l);
+      int z = write(FB_FILE(f)->fd, c, l);
       if (z <= 0)
        die("Error writing %s: %m", f->name);
-      f->fdpos += z;
       l -= z;
       c += z;
     }
   f->bptr = f->buffer;
-  f->pos = f->fdpos;
 }
 
 static void
@@ -54,41 +51,43 @@ bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
 {
   sh_off_t l;
 
-  if (whence == SEEK_SET && pos == f->fdpos)
+  if (whence == SEEK_SET && pos == f->pos)
     return;
 
-  l = sh_seek(f->fd, pos, whence);
+  l = sh_seek(FB_FILE(f)->fd, pos, whence);
   if (l < 0)
     die("lseek on %s: %m", f->name);
-  f->fdpos = f->pos = l;
+  f->pos = l;
 }
 
 static void
 bfd_close(struct fastbuf *f)
 {
-  close(f->fd);
-  if (f->is_temp_file && unlink(f->name) < 0)
+  close(FB_FILE(f)->fd);
+  if (FB_FILE(f)->is_temp_file && unlink(f->name) < 0)
     die("unlink(%s): %m", f->name);
+  xfree(f);
 }
 
 static struct fastbuf *
 bfdopen_internal(int fd, uns buflen, byte *name)
 {
   int namelen = strlen(name) + 1;
-  struct fastbuf *b = xmalloc_zero(sizeof(struct fastbuf) + buflen + namelen);
-
-  b->buflen = buflen;
-  b->buffer = (char *)(b+1);
-  b->bptr = b->bstop = b->buffer;
-  b->bufend = b->buffer + buflen;
-  b->name = b->bufend;
-  strcpy(b->name, name);
-  b->fd = fd;
-  b->refill = bfd_refill;
-  b->spout = bfd_spout;
-  b->seek = bfd_seek;
-  b->close = bfd_close;
-  return b;
+  struct fb_file *F = xmalloc(sizeof(struct fb_file) + buflen + namelen);
+  struct fastbuf *f = &F->fb;
+
+  bzero(F, sizeof(*F));
+  f->buffer = (char *)(F+1);
+  f->bptr = f->bstop = f->buffer;
+  f->bufend = f->buffer + buflen;
+  f->name = f->bufend;
+  memcpy(f->name, name, namelen);
+  F->fd = fd;
+  f->refill = bfd_refill;
+  f->spout = bfd_spout;
+  f->seek = bfd_seek;
+  f->close = bfd_close;
+  return f;
 }
 
 struct fastbuf *
@@ -117,26 +116,30 @@ bfdopen(int fd, uns buffer)
 void bbcopy(struct fastbuf *f, struct fastbuf *t, uns l)
 {
   uns rf = f->bstop - f->bptr;
+  uns tbuflen = t->bufend - t->buffer;
 
+  ASSERT(f->close == bfd_close);
+  ASSERT(t->close == bfd_close);
   if (!l)
     return;
   if (rf)
     {
-      uns k = (rf <= l) ? rf : l;
+      uns k = MIN(rf, l);
       bwrite(t, f->bptr, k);
       f->bptr += k;
       l -= k;
+      if (!l)
+       return;
     }
-  while (l >= t->buflen)
+  while (l >= tbuflen)
     {
       t->spout(t);
-      if ((uns) read(f->fd, t->buffer, t->buflen) != t->buflen)
+      if ((uns) read(FB_FILE(f)->fd, t->buffer, tbuflen) != tbuflen)
        die("bbcopy: %s exhausted", f->name);
-      f->pos = f->fdpos;
-      f->fdpos += t->buflen;
+      f->pos += tbuflen;
       f->bstop = f->bptr = f->buffer;
       t->bptr = t->bufend;
-      l -= t->buflen;
+      l -= tbuflen;
     }
   while (l)
     {
@@ -160,13 +163,14 @@ void bbcopy(struct fastbuf *f, struct fastbuf *t, uns l)
 int main(int argc, char **argv)
 {
   struct fastbuf *f, *t;
-  int c;
 
   f = bopen("/etc/profile", O_RDONLY, 16);
   t = bfdopen(1, 13);
   bbcopy(f, t, 100);
+  printf("%d %d\n", (int)btell(f), (int)btell(t));
   bclose(f);
   bclose(t);
+  return 0;
 }
 
 #endif
index 41d0a6918d2f5a8ffe21e3b74d0fe473ac0cd612..0f3158c379dbc9e7ae666c35d90e0d91c6bdfb32 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Sherlock Library -- Fast Buffered I/O on Memory Streams
  *
- *     (c) 1997--2000 Martin Mares <mj@ucw.cz>
+ *     (c) 1997--2002 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
@@ -10,7 +10,6 @@
 #include "lib/lib.h"
 #include "lib/fastbuf.h"
 
-#include <stdio.h>
 #include <stdlib.h>
 
 struct memstream {
@@ -21,15 +20,23 @@ struct memstream {
 
 struct msblock {
   struct msblock *next;
+  sh_off_t pos;
   unsigned size;
   byte data[0];
 };
 
+struct fb_mem {
+  struct fastbuf fb;
+  struct memstream *stream;
+  struct msblock *block;
+};
+#define FB_MEM(f) ((struct fb_mem *)(f)->is_fastbuf)
+
 static int
 fbmem_refill(struct fastbuf *f)
 {
-  struct memstream *s = f->lldata;
-  struct msblock *b = f->llpos;
+  struct memstream *s = FB_MEM(f)->stream;
+  struct msblock *b = FB_MEM(f)->block;
 
   if (!b)
     {
@@ -40,28 +47,27 @@ fbmem_refill(struct fastbuf *f)
   else if (f->buffer == b->data && f->bstop < b->data + b->size)
     {
       f->bstop = b->data + b->size;
+      f->pos = b->pos + b->size;
       return 1;
     }
+  else if (!b->next)
+    return 0;
   else
-    {
-      if (!b->next)
-       return 0;
-      f->pos += b->size;
-      b = b->next;
-    }
+    b = b->next;
   if (!b->size)
     return 0;
   f->buffer = f->bptr = b->data;
   f->bufend = f->bstop = b->data + b->size;
-  f->llpos = b;
+  f->pos = b->pos + b->size;
+  FB_MEM(f)->block = b;
   return 1;
 }
 
 static void
 fbmem_spout(struct fastbuf *f)
 {
-  struct memstream *s = f->lldata;
-  struct msblock *b = f->llpos;
+  struct memstream *s = FB_MEM(f)->stream;
+  struct msblock *b = FB_MEM(f)->block;
   struct msblock *bb;
 
   if (b)
@@ -69,52 +75,50 @@ fbmem_spout(struct fastbuf *f)
       b->size = f->bptr - b->data;
       if (b->size < s->blocksize)
        return;
-      f->pos += b->size;
     }
   bb = xmalloc(sizeof(struct msblock) + s->blocksize);
   if (b)
-    b->next = bb;
+    {
+      b->next = bb;
+      bb->pos = b->pos + b->size;
+    }
   else
-    s->first = bb;
+    {
+      s->first = bb;
+      bb->pos = 0;
+    }
   bb->next = NULL;
   bb->size = 0;
   f->buffer = f->bptr = f->bstop = bb->data;
   f->bufend = bb->data + s->blocksize;
-  f->llpos = bb;
+  f->pos = bb->pos;
+  FB_MEM(f)->block = bb;
 }
 
 static void
 fbmem_seek(struct fastbuf *f, sh_off_t pos, int whence)
 {
-  struct memstream *m = f->lldata;
+  struct memstream *m = FB_MEM(f)->stream;
   struct msblock *b;
-  unsigned int p = 0;
 
-  /* FIXME: Milan's quick hack to allow SEEK_END */
+  ASSERT(whence == SEEK_SET || whence == SEEK_END);
   if (whence == SEEK_END)
-  {
-         for (b=m->first; b; b=b->next)
-                 p += b->size;
-         pos += p;
-         p=0;
-         whence = SEEK_SET;
-  }
-  /* EOQH */
-  
-  if (whence != SEEK_SET)
-    die("fbmem_seek: only SEEK_SET supported");
+    {
+      for (b=m->first; b; b=b->next)
+       pos += b->size;
+    }
+  /* Yes, this is linear. But considering the average number of buckets, it doesn't matter. */
   for (b=m->first; b; b=b->next)
     {
-      if ((unsigned) pos <= p + b->size) /* <=, because we need to be able to seek just after file end */
+      if (pos <= b->pos + b->size) /* <=, because we need to be able to seek just after file end */
        {
-         f->pos = p;
          f->buffer = b->data;
-         f->bptr = b->data + (pos - p);
+         f->bptr = b->data + (pos - b->pos);
          f->bufend = f->bstop = b->data + b->size;
-         f->llpos = b;
+         f->pos = b->pos + b->size;
+         FB_MEM(f)->block = b;
          return;
        }
-      p += b->size;
     }
   die("fbmem_seek to invalid offset");
 }
@@ -122,31 +126,32 @@ fbmem_seek(struct fastbuf *f, sh_off_t pos, int whence)
 static void
 fbmem_close(struct fastbuf *f)
 {
-  struct memstream *m = f->lldata;
+  struct memstream *m = FB_MEM(f)->stream;
   struct msblock *b;
 
-  if (--m->uc)
-    return;
-
-  while (b = m->first)
+  if (!--m->uc)
     {
-      m->first = b->next;
-      xfree(b);
+      while (b = m->first)
+       {
+         m->first = b->next;
+         xfree(b);
+       }
+      xfree(m);
     }
-  xfree(m);
+  xfree(f);
 }
 
 struct fastbuf *
 fbmem_create(unsigned blocksize)
 {
-  struct fastbuf *f = xmalloc_zero(sizeof(struct fastbuf));
-  struct memstream *m = xmalloc_zero(sizeof(struct memstream));
+  struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
+  struct memstream *s = xmalloc_zero(sizeof(struct memstream));
 
-  m->blocksize = blocksize;
-  m->uc = 1;
+  s->blocksize = blocksize;
+  s->uc = 1;
 
+  FB_MEM(f)->stream = s;
   f->name = "<fbmem-write>";
-  f->lldata = m;
   f->spout = fbmem_spout;
   f->close = fbmem_close;
   return f;
@@ -155,14 +160,14 @@ fbmem_create(unsigned blocksize)
 struct fastbuf *
 fbmem_clone_read(struct fastbuf *b)
 {
-  struct fastbuf *f = xmalloc_zero(sizeof(struct fastbuf));
-  struct memstream *s = b->lldata;
+  struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
+  struct memstream *s = FB_MEM(b)->stream;
 
   bflush(b);
   s->uc++;
 
+  FB_MEM(f)->stream = s;
   f->name = "<fbmem-read>";
-  f->lldata = s;
   f->refill = fbmem_refill;
   f->seek = fbmem_seek;
   f->close = fbmem_close;
@@ -180,23 +185,23 @@ int main(void)
   r = fbmem_clone_read(w);
   bwrite(w, "12345", 5);
   bwrite(w, "12345", 5);
-  printf("<%d>", btell(w));
+  printf("<%d>", (int)btell(w));
   bflush(w);
-  printf("<%d>", btell(w));
-  printf("<%d>", btell(r));
+  printf("<%d>", (int)btell(w));
+  printf("<%d>", (int)btell(r));
   while ((t = bgetc(r)) >= 0)
     putchar(t);
-  printf("<%d>", btell(r));
+  printf("<%d>", (int)btell(r));
   bwrite(w, "12345", 5);
   bwrite(w, "12345", 5);
-  printf("<%d>", btell(w));
+  printf("<%d>", (int)btell(w));
   bclose(w);
   bsetpos(r, 0);
-  printf("<!%d>", btell(r));
+  printf("<!%d>", (int)btell(r));
   while ((t = bgetc(r)) >= 0)
     putchar(t);
   bsetpos(r, 3);
-  printf("<!%d>", btell(r));
+  printf("<!%d>", (int)btell(r));
   while ((t = bgetc(r)) >= 0)
     putchar(t);
   fflush(stdout);
index 650e8a091d45c34c87c4a69b4b3109793de33cc3..c6daec31bbbbb73ac8eba3654c090fc41d61ad4c 100644 (file)
@@ -36,6 +36,6 @@ bopen_tmp(uns bufsize)
 
   sprintf(buf, temp_template, (int) getpid(), temp_counter++);
   f = bopen(buf, O_RDWR | O_CREAT | O_EXCL, bufsize);
-  f->is_temp_file = 1;
+  FB_IS_TEMP_FILE(f) = 1;
   return f;
 }