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