From cf83e06e4158617e981fef5bf3878315b23bf7f9 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 23 Sep 2002 12:07:15 +0000 Subject: [PATCH] Major cleanup of fastbufs: 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 | 33 ++++++++++---- lib/fb-file.c | 74 ++++++++++++++++-------------- lib/fb-mem.c | 123 ++++++++++++++++++++++++++------------------------ lib/fb-temp.c | 2 +- 4 files changed, 127 insertions(+), 105 deletions(-) diff --git a/lib/fastbuf.h b/lib/fastbuf.h index 69df9b71..f3a05952 100644 --- a/lib/fastbuf.h +++ b/lib/fastbuf.h @@ -1,7 +1,7 @@ /* * Sherlock Library -- Fast Buffered I/O * - * (c) 1997--2000 Martin Mares + * (c) 1997--2002 Martin Mares * * 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: * @@ -40,19 +41,23 @@ * +----------------+---------------------------+ * ^ ^ ^ * 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); diff --git a/lib/fb-file.c b/lib/fb-file.c index 31c3bf18..1fc85de5 100644 --- a/lib/fb-file.c +++ b/lib/fb-file.c @@ -1,7 +1,7 @@ /* * Sherlock Library -- Fast Buffered I/O on Files * - * (c) 1997--2000 Martin Mares + * (c) 1997--2002 Martin Mares * * 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 +#include #include #include #include @@ -19,14 +19,12 @@ 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 diff --git a/lib/fb-mem.c b/lib/fb-mem.c index 41d0a691..0f3158c3 100644 --- a/lib/fb-mem.c +++ b/lib/fb-mem.c @@ -1,7 +1,7 @@ /* * Sherlock Library -- Fast Buffered I/O on Memory Streams * - * (c) 1997--2000 Martin Mares + * (c) 1997--2002 Martin Mares * * 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 #include 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 = ""; - 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 = ""; - 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("", btell(r)); + printf("", (int)btell(r)); while ((t = bgetc(r)) >= 0) putchar(t); bsetpos(r, 3); - printf("", btell(r)); + printf("", (int)btell(r)); while ((t = bgetc(r)) >= 0) putchar(t); fflush(stdout); diff --git a/lib/fb-temp.c b/lib/fb-temp.c index 650e8a09..c6daec31 100644 --- a/lib/fb-temp.c +++ b/lib/fb-temp.c @@ -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; } -- 2.39.2