Workshop o mikrokontrolérech na SKSP 2024.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

271 lines
6.3 KiB

2 months ago
/*
* UCW Library -- Fast Buffered I/O on Files
*
* (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
* of the GNU Lesser General Public License.
*/
#include <ucw/lib.h>
#include <ucw/fastbuf.h>
#include <ucw/io.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
struct fb_file {
struct fastbuf fb;
int fd; /* File descriptor */
int is_temp_file;
int keep_back_buf; /* Optimize for backwards reading */
ucw_off_t wpos; /* Real file position */
uint wlen; /* Window size */
};
#define FB_FILE(f) ((struct fb_file *)(f))
#define FB_BUFFER(f) (byte *)(FB_FILE(f) + 1)
static int
bfd_refill(struct fastbuf *f)
{
struct fb_file *F = FB_FILE(f);
byte *read_ptr = (f->buffer = FB_BUFFER(f));
uint blen = f->bufend - f->buffer, back = F->keep_back_buf ? blen >> 2 : 0, read_len = blen;
/* Forward or no seek */
if (F->wpos <= f->pos)
{
ucw_off_t diff = f->pos - F->wpos;
/* Formula for long forward seeks (prefer lseek()) */
if (diff > ((ucw_off_t)blen << 2))
{
long_seek:
f->bptr = f->buffer + back;
f->bstop = f->buffer + blen;
goto seek;
}
/* Short forward seek (prefer read() to skip data )*/
else if ((uint)diff >= back)
{
uint skip = diff - back;
F->wpos += skip;
while (skip)
{
int l = read(F->fd, f->buffer, MIN(skip, blen));
if (unlikely(l <= 0))
if (l < 0)
bthrow(f, "read", "Error reading %s: %m", f->name);
else
{
F->wpos -= skip;
goto eof;
}
skip -= l;
}
}
/* Reuse part of the previous window and append new data (also F->wpos == f->pos) */
else
{
uint keep = back - (uint)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;
}
/* Backwards seek */
else
{
ucw_off_t diff = F->wpos - f->pos;
/* Formula for long backwards seeks (keep smaller backbuffer than for shorter seeks ) */
if (diff > ((ucw_off_t)blen << 1))
{
if ((ucw_off_t)back > f->pos)
back = f->pos;
goto long_seek;
}
/* Seek into previous window (do nothing... for example brewind) */
else if ((uint)diff <= F->wlen)
{
f->bstop = f->buffer + F->wlen;
f->bptr = f->bstop - diff;
f->pos = F->wpos;
return 1;
}
back *= 3;
if ((ucw_off_t)back > f->pos)
back = f->pos;
f->bptr = f->buffer + back;
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)
{
uint keep = read_len + F->wlen - back - diff;
memmove(f->buffer + read_len - keep, f->buffer, keep);
}
seek:
/* Do lseek() */
F->wpos = f->pos + (f->buffer - f->bptr);
if (ucw_seek(F->fd, F->wpos, SEEK_SET) < 0)
bthrow(f, "read", "Error seeking %s: %m", f->name);
}
/* Read (part of) buffer */
do
{
int l = read(F->fd, read_ptr, read_len);
if (unlikely(l < 0))
bthrow(f, "read", "Error reading %s: %m", f->name);
if (!l)
if (unlikely(read_ptr < f->bptr))
goto eof;
else
break; /* Incomplete read because of EOF */
read_ptr += l;
read_len -= l;
F->wpos += l;
}
while (read_ptr <= f->bptr);
if (read_len)
f->bstop = read_ptr;
f->pos += f->bstop - f->bptr;
F->wlen = f->bstop - f->buffer;
return f->bstop - f->bptr;
eof:
/* Seeked behind EOF */
f->bptr = f->bstop = f->buffer;
F->wlen = 0;
return 0;
}
static void
bfd_spout(struct fastbuf *f)
{
/* Do delayed lseek() if needed */
if (FB_FILE(f)->wpos != f->pos && ucw_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
bthrow(f, "write", "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)
{
int z = write(FB_FILE(f)->fd, c, l);
if (z <= 0)
bthrow(f, "write", "Error writing %s: %m", f->name);
l -= z;
c += z;
}
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). */
switch (whence)
{
case SEEK_SET:
f->pos = pos;
return 1;
case SEEK_END: ;
ucw_off_t l = ucw_seek(FB_FILE(f)->fd, pos, SEEK_END);
if (l < 0)
bthrow(f, "seek", "Error seeking %s: %m", f->name);
FB_FILE(f)->wpos = f->pos = l;
FB_FILE(f)->wlen = 0;
return 1;
default:
ASSERT(0);
}
}
static void
bfd_close(struct fastbuf *f)
{
bclose_file_helper(f, FB_FILE(f)->fd, FB_FILE(f)->is_temp_file);
xfree(f);
}
static int
bfd_config(struct fastbuf *f, uint 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 orig;
case BCONFIG_KEEP_BACK_BUF:
orig = FB_FILE(f)->keep_back_buf;
FB_FILE(f)->keep_back_buf = value;
return orig;
default:
return -1;
}
}
struct fastbuf *
bfdopen_internal(int fd, const char *name, uint buflen)
{
ASSERT(buflen);
int namelen = strlen(name) + 1;
struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
struct fastbuf *f = &F->fb;
bzero(F, sizeof(*F));
f->buffer = (byte *)(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;
f->config = bfd_config;
f->can_overwrite_buffer = 2;
return f;
}
void
bfilesync(struct fastbuf *b)
{
bflush(b);
if (fsync(FB_FILE(b)->fd) < 0)
msg(L_ERROR, "fsync(%s) failed: %m", b->name);
}
#ifdef TEST
int main(void)
{
struct fastbuf *f, *t;
f = bopen_tmp(16);
t = bfdopen_shared(1, 13);
for (uint 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;
}
#endif