/*
* 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.
#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 */
/* 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 */
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);
/*
* 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.
#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;
}
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
{
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 *
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)
{
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
/*
* 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.
#include "lib/lib.h"
#include "lib/fastbuf.h"
-#include <stdio.h>
#include <stdlib.h>
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)
{
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)
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");
}
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;
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;
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);
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;
}