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.

320 lines
6.9 KiB

2 months ago
/*
* UCW Library -- Fast Buffered I/O
*
* (c) 1997--2011 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
*/
#undef LOCAL_DEBUG
#include <ucw/lib.h>
#include <ucw/fastbuf.h>
#include <ucw/resource.h>
#include <ucw/trans.h>
#include <ucw/stkstring.h>
#include <stdio.h>
#include <stdlib.h>
void bclose(struct fastbuf *f)
{
if (f)
{
bflush(f);
res_detach(f->res);
DBG("FB: closing");
if (f->close)
f->close(f); /* Should always free all internal resources, even if it throws an exception */
}
}
void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
{
char full_id[16];
snprintf(full_id, sizeof(full_id), "ucw.fb.%s", id);
DBG("FB: throwing %s", full_id);
ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
va_list args;
va_start(args, fmt);
if (!f->res)
die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
f->flags |= FB_DEAD;
f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
trans_vthrow(full_id, f, fmt, args);
}
int brefill(struct fastbuf *f, int allow_eof)
{
DBG("FB: refill");
ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
if (!f->refill)
bthrow(f, "read", "Stream not readable");
if (f->refill(f))
{
ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
return 1;
}
else
{
ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend);
if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
bthrow(f, "eof", "Unexpected EOF");
return 0;
}
}
static void do_spout(struct fastbuf *f)
{
DBG("FB: spout");
ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */
if (!f->spout)
bthrow(f, "write", "Stream not writeable");
f->spout(f);
ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
}
void bspout(struct fastbuf *f)
{
do_spout(f);
if (f->bstop == f->bufend)
{
do_spout(f);
ASSERT(f->bstop < f->bufend);
}
}
void bflush(struct fastbuf *f)
{
if (f->bptr > f->bstop)
do_spout(f);
else
f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */
DBG("FB: flushed");
}
static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence)
{
bflush(f);
DBG("FB: seeking to pos=%lld whence=%d %p %p %p %p", (long long)pos, whence, f->buffer, f->bstop, f->bptr, f->bufend);
if (!f->seek || !f->seek(f, pos, whence))
bthrow(f, "seek", "Stream not seekable");
DBG("FB: seeked %p %p %p %p", f->buffer, f->bstop, f->bptr, f->bufend);
ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
if (whence == SEEK_SET)
ASSERT(pos == btell(f));
else
ASSERT(btell(f) >= 0);
}
void bsetpos(struct fastbuf *f, ucw_off_t pos)
{
/* We can optimize seeks only when reading */
if (f->bptr < f->bstop && pos <= f->pos && pos >= f->pos - (f->bstop - f->buffer)) /* If bptr == bstop, then [buffer, bstop] may be undefined */
f->bptr = f->bstop + (pos - f->pos);
else if (pos != btell(f))
{
if (pos < 0)
bthrow(f, "seek", "Seek out of range");
do_seek(f, pos, SEEK_SET);
}
}
void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
{
switch (whence)
{
case SEEK_SET:
bsetpos(f, pos);
break;
case SEEK_CUR:
bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */
break;
case SEEK_END:
if (pos > 0)
bthrow(f, "seek", "Seek out of range");
do_seek(f, pos, SEEK_END);
break;
default:
die("bseek: invalid whence=%d", whence);
}
}
int bgetc_slow(struct fastbuf *f)
{
if (f->bptr < f->bstop)
return *f->bptr++;
if (!brefill(f, 0))
return -1;
return *f->bptr++;
}
int bpeekc_slow(struct fastbuf *f)
{
if (f->bptr < f->bstop)
return *f->bptr;
if (!brefill(f, 0))
return -1;
return *f->bptr;
}
int beof_slow(struct fastbuf *f)
{
return f->bptr >= f->bstop && !brefill(f, 1);
}
void bputc_slow(struct fastbuf *f, uint c)
{
if (f->bptr >= f->bufend)
bspout(f);
*f->bptr++ = c;
}
uint bread_slow(struct fastbuf *f, void *b, uint l, uint check)
{
uint total = 0;
while (l)
{
uint k = f->bstop - f->bptr;
if (!k)
{
brefill(f, check);
k = f->bstop - f->bptr;
if (!k)
break;
}
if (k > l)
k = l;
memcpy(b, f->bptr, k);
f->bptr += k;
b = (byte *)b + k;
l -= k;
total += k;
}
if (check && total && l)
bthrow(f, "eof", "breadb: short read");
return total;
}
void bwrite_slow(struct fastbuf *f, const void *b, uint l)
{
while (l)
{
uint k = f->bufend - f->bptr;
if (!k)
{
bspout(f);
k = f->bufend - f->bptr;
}
if (k > l)
k = l;
memcpy(f->bptr, b, k);
f->bptr += k;
b = (byte *)b + k;
l -= k;
}
}
void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uint l)
{
while (l)
{
byte *fptr, *tptr;
uint favail, tavail, n;
favail = bdirect_read_prepare(f, &fptr);
if (!favail)
{
if (l == ~0U)
return;
bthrow(f, "eof", "bbcopy: source exhausted");
}
tavail = bdirect_write_prepare(t, &tptr);
n = MIN(l, favail);
n = MIN(n, tavail);
memcpy(tptr, fptr, n);
bdirect_read_commit(f, fptr + n);
bdirect_write_commit(t, tptr + n);
if (l != ~0U)
l -= n;
}
}
int bconfig(struct fastbuf *f, uint item, int value)
{
return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1;
}
void brewind(struct fastbuf *f)
{
bflush(f);
bsetpos(f, 0);
}
int bskip_slow(struct fastbuf *f, uint len)
{
while (len)
{
byte *buf;
uint l = bdirect_read_prepare(f, &buf);
if (!l)
return 0;
l = MIN(l, len);
bdirect_read_commit(f, buf+l);
len -= l;
}
return 1;
}
ucw_off_t bfilesize(struct fastbuf *f)
{
if (!f)
return 0;
if (!f->seek)
return -1;
ucw_off_t pos = btell(f);
bflush(f);
if (!f->seek(f, 0, SEEK_END))
return -1;
ucw_off_t len = btell(f);
bsetpos(f, pos);
return len;
}
/* Resources */
static void fb_res_detach(struct resource *r)
{
struct fastbuf *f = r->priv;
f->res = NULL;
}
static void fb_res_free(struct resource *r)
{
struct fastbuf *f = r->priv;
f->res = NULL;
bclose(f);
}
static void fb_res_dump(struct resource *r, uint indent UNUSED)
{
struct fastbuf *f = r->priv;
printf(" name=%s\n", f->name);
}
static const struct res_class fb_res_class = {
.name = "fastbuf",
.detach = fb_res_detach,
.dump = fb_res_dump,
.free = fb_res_free,
};
struct fastbuf *fb_tie(struct fastbuf *f)
{
f->res = res_new(&fb_res_class, f);
return f;
}