]> mj.ucw.cz Git - libucw.git/blob - ucw/fastbuf.c
83c3b0790a60a8915d18ecfaed6e23ae8c282687
[libucw.git] / ucw / fastbuf.c
1 /*
2  *      UCW Library -- Fast Buffered I/O
3  *
4  *      (c) 1997--2011 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "ucw/lib.h"
13 #include "ucw/fastbuf.h"
14 #include "ucw/respool.h"
15 #include "ucw/trans.h"
16 #include "ucw/stkstring.h"
17
18 #include <stdio.h>
19 #include <stdlib.h>
20
21 void bclose(struct fastbuf *f)
22 {
23   if (f)
24     {
25       bflush(f);
26       if (f->res)
27         {
28           res_drop(f->res);
29           f->res = NULL;
30         }
31       DBG("FB: closing", f);
32       if (f->close)
33         f->close(f); /* Should always free all internal resources, even if it throws an exception */
34     }
35 }
36
37 void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
38 {
39   ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
40   DBG("FB: throwing %s", id);
41   va_list args;
42   va_start(args, fmt);
43   if (!f->res)
44     die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
45   f->flags |= FB_DEAD;
46   f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
47   trans_vthrow(id, f, fmt, args);
48 }
49
50 int brefill(struct fastbuf *f, int allow_eof)
51 {
52   DBG("FB: refill");
53   ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
54   if (!f->refill)
55     bthrow(f, "fb.read", "Stream not readable");
56   if (f->refill(f))
57     {
58       ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
59       return 1;
60     }
61   else
62     {
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");
66       return 0;
67     }
68 }
69
70 static void do_spout(struct fastbuf *f)
71 {
72   DBG("FB: spout");
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 */
74   if (!f->spout)
75     bthrow(f, "fb.write", "Stream not writeable");
76   f->spout(f);
77   ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
78 }
79
80 void bspout(struct fastbuf *f)
81 {
82   do_spout(f);
83   if (f->bstop == f->bufend)
84     {
85       do_spout(f);
86       ASSERT(f->bstop < f->bufend);
87     }
88 }
89
90 void bflush(struct fastbuf *f)
91 {
92   if (f->bptr > f->bstop)
93     do_spout(f);
94   else
95     f->bptr = f->bstop; /* XXX: Skip the rest of the reading buffer ==> it breaks the position of the FE cursor */
96   DBG("FB: flushed");
97 }
98
99 static void do_seek(struct fastbuf *f, ucw_off_t pos, int whence)
100 {
101   bflush(f);
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));
109   else
110     ASSERT(btell(f) >= 0);
111 }
112
113 inline void bsetpos(struct fastbuf *f, ucw_off_t pos)
114 {
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))
119     {
120       if (pos < 0)
121         bthrow(f, "fb.seek", "Seek out of range");
122       do_seek(f, pos, SEEK_SET);
123     }
124 }
125
126 void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
127 {
128   switch (whence)
129     {
130     case SEEK_SET:
131       bsetpos(f, pos);
132       break;
133     case SEEK_CUR:
134       bsetpos(f, btell(f) + pos); /* btell() is non-negative, so an overflow will always throw "Seek out of range" in bsetpos() */
135       break;
136     case SEEK_END:
137       if (pos > 0)
138         bthrow(f, "fb.seek", "Seek out of range");
139       do_seek(f, pos, SEEK_END);
140       break;
141     default:
142       die("bseek: invalid whence=%d", whence);
143     }
144 }
145
146 int bgetc_slow(struct fastbuf *f)
147 {
148   if (f->bptr < f->bstop)
149     return *f->bptr++;
150   if (!brefill(f, 0))
151     return -1;
152   return *f->bptr++;
153 }
154
155 int bpeekc_slow(struct fastbuf *f)
156 {
157   if (f->bptr < f->bstop)
158     return *f->bptr;
159   if (!brefill(f, 0))
160     return -1;
161   return *f->bptr;
162 }
163
164 int beof_slow(struct fastbuf *f)
165 {
166   return f->bptr >= f->bstop && !brefill(f, 1);
167 }
168
169 void bputc_slow(struct fastbuf *f, uns c)
170 {
171   if (f->bptr >= f->bufend)
172     bspout(f);
173   *f->bptr++ = c;
174 }
175
176 uns bread_slow(struct fastbuf *f, void *b, uns l, uns check)
177 {
178   uns total = 0;
179   while (l)
180     {
181       uns k = f->bstop - f->bptr;
182
183       if (!k)
184         {
185           brefill(f, check);
186           k = f->bstop - f->bptr;
187           if (!k)
188             break;
189         }
190       if (k > l)
191         k = l;
192       memcpy(b, f->bptr, k);
193       f->bptr += k;
194       b = (byte *)b + k;
195       l -= k;
196       total += k;
197     }
198   if (check && total && l)
199     bthrow(f, "fb.read", "breadb: short read");
200   return total;
201 }
202
203 void bwrite_slow(struct fastbuf *f, const void *b, uns l)
204 {
205   while (l)
206     {
207       uns k = f->bufend - f->bptr;
208
209       if (!k)
210         {
211           bspout(f);
212           k = f->bufend - f->bptr;
213         }
214       if (k > l)
215         k = l;
216       memcpy(f->bptr, b, k);
217       f->bptr += k;
218       b = (byte *)b + k;
219       l -= k;
220     }
221 }
222
223 void bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
224 {
225   while (l)
226     {
227       byte *fptr, *tptr;
228       uns favail, tavail, n;
229
230       favail = bdirect_read_prepare(f, &fptr);
231       if (!favail)
232         {
233           if (l == ~0U)
234             return;
235           bthrow(f, "fb.read", "bbcopy: source exhausted");
236         }
237       tavail = bdirect_write_prepare(t, &tptr);
238       n = MIN(l, favail);
239       n = MIN(n, tavail);
240       memcpy(tptr, fptr, n);
241       bdirect_read_commit(f, fptr + n);
242       bdirect_write_commit(t, tptr + n);
243       if (l != ~0U)
244         l -= n;
245     }
246 }
247
248 int bconfig(struct fastbuf *f, uns item, int value)
249 {
250   return (f->config && !(f->flags & FB_DEAD)) ? f->config(f, item, value) : -1;
251 }
252
253 void brewind(struct fastbuf *f)
254 {
255   bflush(f);
256   bsetpos(f, 0);
257 }
258
259 int bskip_slow(struct fastbuf *f, uns len)
260 {
261   while (len)
262     {
263       byte *buf;
264       uns l = bdirect_read_prepare(f, &buf);
265       if (!l)
266         return 0;
267       l = MIN(l, len);
268       bdirect_read_commit(f, buf+l);
269       len -= l;
270     }
271   return 1;
272 }
273
274 ucw_off_t bfilesize(struct fastbuf *f)
275 {
276   if (!f)
277     return 0;
278   if (!f->seek)
279     return -1;
280   ucw_off_t pos = btell(f);
281   bflush(f);
282   if (!f->seek(f, 0, SEEK_END))
283     return -1;
284   ucw_off_t len = btell(f);
285   bsetpos(f, pos);
286   return len;
287 }
288
289 /* Resources */
290
291 static void fb_res_detach(struct resource *r)
292 {
293   struct fastbuf *f = r->priv;
294   f->res = NULL;
295 }
296
297 static void fb_res_free(struct resource *r)
298 {
299   struct fastbuf *f = r->priv;
300   f->res = NULL;
301   bclose(f);
302 }
303
304 static void fb_res_dump(struct resource *r)
305 {
306   struct fastbuf *f = r->priv;
307   printf(" name=%s", f->name);
308 }
309
310 static const struct res_class fb_res_class = {
311   .name = "fastbuf",
312   .detach = fb_res_detach,
313   .dump = fb_res_dump,
314   .free = fb_res_free,
315 };
316
317 struct fastbuf *fb_tie(struct fastbuf *f)
318 {
319   f->res = res_new(&fb_res_class, f);
320   return f;
321 }