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)
27 DBG("FB: closing", f);
29 f->close(f); /* Should always free all internal resources, even if it throws an exception */
33 void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
35 ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
36 DBG("FB: throwing %s", id);
40 die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
42 f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
43 trans_vthrow(id, f, fmt, args);
46 int brefill(struct fastbuf *f, int allow_eof)
49 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
51 bthrow(f, "fb.read", "Stream not readable");
54 ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
59 ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend);
60 if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
61 bthrow(f, "fb.eof", "Unexpected EOF");
66 static void do_spout(struct fastbuf *f)
69 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */
71 bthrow(f, "fb.write", "Stream not writeable");
73 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
76 void bspout(struct fastbuf *f)
79 if (f->bstop == f->bufend)
82 ASSERT(f->bstop < f->bufend);
86 void bflush(struct fastbuf *f)
88 if (f->bptr > f->bstop)
91 f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */
95 static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence)
98 DBG("FB: seeking to pos=%lld whence=%d %p %p %p %p", (long long)pos, whence, f->buffer, f->bstop, f->bptr, f->bufend);
99 if (!f->seek || !f->seek(f, pos, whence))
100 bthrow(f, "fb.seek", "Stream not seekable");
101 DBG("FB: seeked %p %p %p %p", f->buffer, f->bstop, f->bptr, f->bufend);
102 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
103 if (whence == SEEK_SET)
104 ASSERT(pos == btell(f));
106 ASSERT(btell(f) >= 0);
109 inline void bsetpos(struct fastbuf *f, ucw_off_t pos)
111 /* We can optimize seeks only when reading */
112 if (f->bptr < f->bstop && pos <= f->pos && pos >= f->pos - (f->bstop - f->buffer)) /* If bptr == bstop, then [buffer, bstop] may be undefined */
113 f->bptr = f->bstop + (pos - f->pos);
114 else if (pos != btell(f))
117 bthrow(f, "fb.seek", "Seek out of range");
118 do_seek(f, pos, SEEK_SET);
122 void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
130 bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */
134 bthrow(f, "fb.seek", "Seek out of range");
135 do_seek(f, pos, SEEK_END);
138 die("bseek: invalid whence=%d", whence);
142 int bgetc_slow(struct fastbuf *f)
144 if (f->bptr < f->bstop)
151 int bpeekc_slow(struct fastbuf *f)
153 if (f->bptr < f->bstop)
160 int beof_slow(struct fastbuf *f)
162 return f->bptr >= f->bstop && !brefill(f, 1);
165 void bputc_slow(struct fastbuf *f, uns c)
167 if (f->bptr >= f->bufend)
172 uns bread_slow(struct fastbuf *f, void *b, uns l, uns check)
177 uns k = f->bstop - f->bptr;
182 k = f->bstop - f->bptr;
188 memcpy(b, f->bptr, k);
194 if (check && total && l)
195 bthrow(f, "fb.read", "breadb: short read");
199 void bwrite_slow(struct fastbuf *f, const void *b, uns l)
203 uns k = f->bufend - f->bptr;
208 k = f->bufend - f->bptr;
212 memcpy(f->bptr, b, k);
219 void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
224 uns favail, tavail, n;
226 favail = bdirect_read_prepare(f, &fptr);
231 bthrow(f, "fb.read", "bbcopy: source exhausted");
233 tavail = bdirect_write_prepare(t, &tptr);
236 memcpy(tptr, fptr, n);
237 bdirect_read_commit(f, fptr + n);
238 bdirect_write_commit(t, tptr + n);
244 int bconfig(struct fastbuf *f, uns item, int value)
246 return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1;
249 void brewind(struct fastbuf *f)
255 int bskip_slow(struct fastbuf *f, uns len)
260 uns l = bdirect_read_prepare(f, &buf);
264 bdirect_read_commit(f, buf+l);
270 ucw_off_t bfilesize(struct fastbuf *f)
276 ucw_off_t pos = btell(f);
278 if (!f->seek(f, 0, SEEK_END))
280 ucw_off_t len = btell(f);
287 static void fb_res_detach(struct resource *r)
289 struct fastbuf *f = r->priv;
293 static void fb_res_free(struct resource *r)
295 struct fastbuf *f = r->priv;
300 static void fb_res_dump(struct resource *r, uns indent UNUSED)
302 struct fastbuf *f = r->priv;
303 printf(" name=%s\n", f->name);
306 static const struct res_class fb_res_class = {
308 .detach = fb_res_detach,
313 struct fastbuf *fb_tie(struct fastbuf *f)
315 f->res = res_new(&fb_res_class, f);