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 DBG("FB: throwing %s", full_id);
37 snprintf(full_id, sizeof(full_id), "ucw.fb.%s", id);
38 ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
42 die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
44 f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
45 trans_vthrow(full_id, f, fmt, args);
48 int brefill(struct fastbuf *f, int allow_eof)
51 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
53 bthrow(f, "read", "Stream not readable");
56 ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
61 ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend);
62 if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
63 bthrow(f, "eof", "Unexpected EOF");
68 static void do_spout(struct fastbuf *f)
71 ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */
73 bthrow(f, "write", "Stream not writeable");
75 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
78 void bspout(struct fastbuf *f)
81 if (f->bstop == f->bufend)
84 ASSERT(f->bstop < f->bufend);
88 void bflush(struct fastbuf *f)
90 if (f->bptr > f->bstop)
93 f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */
97 static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence)
100 DBG("FB: seeking to pos=%lld whence=%d %p %p %p %p", (long long)pos, whence, f->buffer, f->bstop, f->bptr, f->bufend);
101 if (!f->seek || !f->seek(f, pos, whence))
102 bthrow(f, "seek", "Stream not seekable");
103 DBG("FB: seeked %p %p %p %p", f->buffer, f->bstop, f->bptr, f->bufend);
104 ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
105 if (whence == SEEK_SET)
106 ASSERT(pos == btell(f));
108 ASSERT(btell(f) >= 0);
111 inline void bsetpos(struct fastbuf *f, ucw_off_t pos)
113 /* We can optimize seeks only when reading */
114 if (f->bptr < f->bstop && pos <= f->pos && pos >= f->pos - (f->bstop - f->buffer)) /* If bptr == bstop, then [buffer, bstop] may be undefined */
115 f->bptr = f->bstop + (pos - f->pos);
116 else if (pos != btell(f))
119 bthrow(f, "seek", "Seek out of range");
120 do_seek(f, pos, SEEK_SET);
124 void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
132 bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */
136 bthrow(f, "seek", "Seek out of range");
137 do_seek(f, pos, SEEK_END);
140 die("bseek: invalid whence=%d", whence);
144 int bgetc_slow(struct fastbuf *f)
146 if (f->bptr < f->bstop)
153 int bpeekc_slow(struct fastbuf *f)
155 if (f->bptr < f->bstop)
162 int beof_slow(struct fastbuf *f)
164 return f->bptr >= f->bstop && !brefill(f, 1);
167 void bputc_slow(struct fastbuf *f, uns c)
169 if (f->bptr >= f->bufend)
174 uns bread_slow(struct fastbuf *f, void *b, uns l, uns check)
179 uns k = f->bstop - f->bptr;
184 k = f->bstop - f->bptr;
190 memcpy(b, f->bptr, k);
196 if (check && total && l)
197 bthrow(f, "eof", "breadb: short read");
201 void bwrite_slow(struct fastbuf *f, const void *b, uns l)
205 uns k = f->bufend - f->bptr;
210 k = f->bufend - f->bptr;
214 memcpy(f->bptr, b, k);
221 void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
226 uns favail, tavail, n;
228 favail = bdirect_read_prepare(f, &fptr);
233 bthrow(f, "eof", "bbcopy: source exhausted");
235 tavail = bdirect_write_prepare(t, &tptr);
238 memcpy(tptr, fptr, n);
239 bdirect_read_commit(f, fptr + n);
240 bdirect_write_commit(t, tptr + n);
246 int bconfig(struct fastbuf *f, uns item, int value)
248 return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1;
251 void brewind(struct fastbuf *f)
257 int bskip_slow(struct fastbuf *f, uns len)
262 uns l = bdirect_read_prepare(f, &buf);
266 bdirect_read_commit(f, buf+l);
272 ucw_off_t bfilesize(struct fastbuf *f)
278 ucw_off_t pos = btell(f);
280 if (!f->seek(f, 0, SEEK_END))
282 ucw_off_t len = btell(f);
289 static void fb_res_detach(struct resource *r)
291 struct fastbuf *f = r->priv;
295 static void fb_res_free(struct resource *r)
297 struct fastbuf *f = r->priv;
302 static void fb_res_dump(struct resource *r, uns indent UNUSED)
304 struct fastbuf *f = r->priv;
305 printf(" name=%s\n", f->name);
308 static const struct res_class fb_res_class = {
310 .detach = fb_res_detach,
315 struct fastbuf *fb_tie(struct fastbuf *f)
317 f->res = res_new(&fb_res_class, f);