/*
* UCW Library -- Fast Buffered I/O on Files
*
- * (c) 1997--2004 Martin Mares <mj@ucw.cz>
+ * (c) 1997--2007 Martin Mares <mj@ucw.cz>
* (c) 2007 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
struct fb_file {
struct fastbuf fb;
int fd; /* File descriptor */
- int is_temp_file; /* 0=normal file, 1=temporary file, delete on close, -1=shared FD */
+ int is_temp_file;
int keep_back_buf; /* Optimize for backwards reading */
sh_off_t wpos; /* Real file position */
uns wlen; /* Window size */
if (F->wpos <= f->pos)
{
sh_off_t diff = f->pos - F->wpos;
- if (diff > ((sh_off_t)blen << 2)) /* FIXME: Formula for long forward seeks */
+ /* Formula for long forward seeks (prefer lseek()) */
+ if (diff > ((sh_off_t)blen << 2))
{
long_seek:
f->bptr = f->buffer + back;
f->bstop = f->buffer + blen;
goto seek;
}
- if ((uns)diff < back) /* Reuse part of previous window (also F->wpos == f->pos) */
- {
- uns keep = back - (uns)diff;
- if (keep >= F->wlen)
- back = diff + (keep = F->wlen);
- else
- memmove(f->buffer, f->buffer + F->wlen - keep, keep);
- read_len -= keep;
- read_ptr += keep;
- }
- else /* Short forward seek */
+ /* Short forward seek (prefer read() to skip data )*/
+ else if ((uns)diff >= back)
{
uns skip = diff - back;
F->wpos += skip;
skip -= l;
}
}
+ /* Reuse part of the previous window and append new data (also F->wpos == f->pos) */
+ else
+ {
+ uns keep = back - (uns)diff;
+ if (keep >= F->wlen)
+ back = diff + (keep = F->wlen);
+ else
+ memmove(f->buffer, f->buffer + F->wlen - keep, keep);
+ read_len -= keep;
+ read_ptr += keep;
+ }
f->bptr = f->buffer + back;
f->bstop = f->buffer + blen;
}
else
{
sh_off_t diff = F->wpos - f->pos;
- if (diff > ((sh_off_t)blen << 1)) /* FIXME: Formula for long backwards seeks */
+ /* Formula for long backwards seeks (keep smaller backbuffer than for shorter seeks ) */
+ if (diff > ((sh_off_t)blen << 1))
{
if ((sh_off_t)back > f->pos)
back = f->pos;
goto long_seek;
}
- if ((uns)diff <= F->wlen) /* Seek into previous window (for example brewind) */
+ /* Seek into previous window (do nothing... for example brewind) */
+ else if ((uns)diff <= F->wlen)
{
f->bstop = f->buffer + F->wlen;
f->bptr = f->bstop - diff;
if ((sh_off_t)back > f->pos)
back = f->pos;
f->bptr = f->buffer + back;
- read_len = back + diff - F->wlen;
- if (F->wlen && read_len < blen) /* Reuse part of previous window */
+ read_len = blen;
+ f->bstop = f->buffer + read_len;
+ /* Reuse part of previous window */
+ if (F->wlen && read_len <= back + diff && read_len > back + diff - F->wlen)
{
- uns keep = MIN(F->wlen, blen - read_len);
- memmove(f->buffer + read_len, f->buffer, keep);
- f->bstop = f->buffer + read_len + keep;
+ uns keep = read_len + F->wlen - back - diff;
+ memmove(f->buffer + read_len - keep, f->buffer, keep);
}
- else
- f->bstop = f->buffer + (read_len = blen);
seek:
+ /* Do lseek() */
F->wpos = f->pos + (f->buffer - f->bptr);
if (sh_seek(F->fd, F->wpos, SEEK_SET) < 0)
die("Error seeking %s: %m", f->name);
}
+ /* Read (part of) buffer */
do
{
int l = read(F->fd, read_ptr, read_len);
static void
bfd_spout(struct fastbuf *f)
{
+ /* Do delayed lseek() if needed */
if (FB_FILE(f)->wpos != f->pos && sh_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
die("Error seeking %s: %m", f->name);
int l = f->bptr - f->buffer;
byte *c = f->buffer;
+ /* Write the buffer */
FB_FILE(f)->wpos = (f->pos += l);
FB_FILE(f)->wlen = 0;
while (l)
static int
bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
{
+ /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */
sh_off_t l;
switch (whence)
{
static void
bfd_close(struct fastbuf *f)
{
- switch (FB_FILE(f)->is_temp_file)
- {
- case 1:
- if (unlink(f->name) < 0)
- log(L_ERROR, "unlink(%s): %m", f->name);
- case 0:
- close(FB_FILE(f)->fd);
- }
+ bclose_file_helper(f, FB_FILE(f)->fd, FB_FILE(f)->is_temp_file);
xfree(f);
}
static int
bfd_config(struct fastbuf *f, uns item, int value)
{
+ int orig;
+
switch (item)
{
case BCONFIG_IS_TEMP_FILE:
+ orig = FB_FILE(f)->is_temp_file;
FB_FILE(f)->is_temp_file = value;
- return 0;
+ return orig;
case BCONFIG_KEEP_BACK_BUF:
+ orig = FB_FILE(f)->keep_back_buf;
FB_FILE(f)->keep_back_buf = value;
- return 0;
+ return orig;
default:
return -1;
}
}
struct fastbuf *
-bfdopen_internal(int fd, byte *name, uns buflen)
+bfdopen_internal(int fd, const char *name, uns buflen)
{
ASSERT(buflen);
int namelen = strlen(name) + 1;
return f;
}
-struct fastbuf *
-bopen_try(byte *name, uns mode, uns buflen)
-{
- return bopen_file_try(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
-}
-
-struct fastbuf *
-bopen(byte *name, uns mode, uns buflen)
-{
- return bopen_file(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
-}
-
-struct fastbuf *
-bfdopen(int fd, uns buflen)
-{
- return bopen_fd(fd, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
-}
-
-struct fastbuf *
-bfdopen_shared(int fd, uns buflen)
-{
- struct fastbuf *f = bfdopen(fd, buflen);
- FB_FILE(f)->is_temp_file = -1;
- return f;
-}
-
void
bfilesync(struct fastbuf *b)
{
bflush(b);
if (fsync(FB_FILE(b)->fd) < 0)
- log(L_ERROR, "fsync(%s) failed: %m", b->name);
+ msg(L_ERROR, "fsync(%s) failed: %m", b->name);
}
#ifdef TEST
-int main(int argc UNUSED, char **argv UNUSED)
+int main(void)
{
struct fastbuf *f, *t;
-
- 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));
+ f = bopen_tmp(16);
+ t = bfdopen_shared(1, 13);
+ for (uns i = 0; i < 16; i++)
+ bwrite(f, "<hello>", 7);
+ bprintf(t, "%d\n", (int)btell(f));
+ brewind(f);
+ bbcopy(f, t, ~0U);
+ bprintf(t, "\n%d %d\n", (int)btell(f), (int)btell(t));
bclose(f);
bclose(t);
return 0;