2 * UCW Library -- Fast Buffered I/O
4 * (c) 1997--2011 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include "ucw/fastbuf.h"
14 #include "ucw/respool.h"
15 #include "ucw/trans.h"
16 #include "ucw/stkstring.h"
21 void bclose(struct fastbuf *f)
31 DBG("FB: closing", f);
33 f->close(f); /* Should always free all internal resources, even if it throws an exception */
37 void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
39 ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
40 DBG("FB: throwing %s", id);
44 die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
46 f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
47 trans_vthrow(id, f, fmt, args);
50 int brefill(struct fastbuf *f, int allow_eof)
53 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
55 bthrow(f, "fb.read", "Stream not readable");
58 ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
63 ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend);
64 if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
65 bthrow(f, "fb.eof", "Unexpected EOF");
70 static void do_spout(struct fastbuf *f)
73 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */
75 bthrow(f, "fb.write", "Stream not writeable");
77 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
80 void bspout(struct fastbuf *f)
83 if (f->bstop == f->bufend)
86 ASSERT(f->bstop < f->bufend);
90 void bflush(struct fastbuf *f)
92 if (f->bptr > f->bstop)
95 f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */
99 static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence)
102 DBG("FB: seeking to pos=%lld whence=%d %p %p %p %p", (long long)pos, whence, f->buffer, f->bstop, f->bptr, f->bufend);
103 if (!f->seek || !f->seek(f, pos, whence))
104 bthrow(f, "fb.seek", "Stream not seekable");
105 DBG("FB: seeked %p %p %p %p", f->buffer, f->bstop, f->bptr, f->bufend);
106 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
107 if (whence == SEEK_SET)
108 ASSERT(pos == btell(f));
110 ASSERT(btell(f) >= 0);
113 inline void bsetpos(struct fastbuf *f, ucw_off_t pos)
115 /* We can optimize seeks only when reading */
116 if (f->bptr < f->bstop && pos <= f->pos && pos >= f->pos - (f->bstop - f->buffer)) /* If bptr == bstop, then [buffer, bstop] may be undefined */
117 f->bptr = f->bstop + (pos - f->pos);
118 else if (pos != btell(f))
121 bthrow(f, "fb.seek", "Seek out of range");
122 do_seek(f, pos, SEEK_SET);
126 void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
134 bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */
138 bthrow(f, "fb.seek", "Seek out of range");
139 do_seek(f, pos, SEEK_END);
142 die("bseek: invalid whence=%d", whence);
146 int bgetc_slow(struct fastbuf *f)
148 if (f->bptr < f->bstop)
155 int bpeekc_slow(struct fastbuf *f)
157 if (f->bptr < f->bstop)
164 int beof_slow(struct fastbuf *f)
166 return f->bptr >= f->bstop && !brefill(f, 1);
169 void bputc_slow(struct fastbuf *f, uns c)
171 if (f->bptr >= f->bufend)
176 uns bread_slow(struct fastbuf *f, void *b, uns l, uns check)
181 uns k = f->bstop - f->bptr;
186 k = f->bstop - f->bptr;
192 memcpy(b, f->bptr, k);
198 if (check && total && l)
199 bthrow(f, "fb.read", "breadb: short read");
203 void bwrite_slow(struct fastbuf *f, const void *b, uns l)
207 uns k = f->bufend - f->bptr;
212 k = f->bufend - f->bptr;
216 memcpy(f->bptr, b, k);
223 void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
228 uns favail, tavail, n;
230 favail = bdirect_read_prepare(f, &fptr);
235 bthrow(f, "fb.read", "bbcopy: source exhausted");
237 tavail = bdirect_write_prepare(t, &tptr);
240 memcpy(tptr, fptr, n);
241 bdirect_read_commit(f, fptr + n);
242 bdirect_write_commit(t, tptr + n);
248 int bconfig(struct fastbuf *f, uns item, int value)
250 return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1;
253 void brewind(struct fastbuf *f)
259 int bskip_slow(struct fastbuf *f, uns len)
264 uns l = bdirect_read_prepare(f, &buf);
268 bdirect_read_commit(f, buf+l);
274 ucw_off_t bfilesize(struct fastbuf *f)
280 ucw_off_t pos = btell(f);
282 if (!f->seek(f, 0, SEEK_END))
284 ucw_off_t len = btell(f);
291 static void fb_res_detach(struct resource *r)
293 struct fastbuf *f = r->priv;
297 static void fb_res_free(struct resource *r)
299 struct fastbuf *f = r->priv;
304 static void fb_res_dump(struct resource *r, uns indent UNUSED)
306 struct fastbuf *f = r->priv;
307 printf(" name=%s\n", f->name);
310 static const struct res_class fb_res_class = {
312 .detach = fb_res_detach,
317 struct fastbuf *fb_tie(struct fastbuf *f)
319 f->res = res_new(&fb_res_class, f);