]> mj.ucw.cz Git - libucw.git/blob - ucw/fastbuf.c
fb2cc49f88513f73e16d4035a0dc26c417baf5d2
[libucw.git] / ucw / fastbuf.c
1 /*
2  *      UCW Library -- Fast Buffered I/O
3  *
4  *      (c) 1997--2007 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 #include "ucw/lib.h"
11 #include "ucw/fastbuf.h"
12 #include "ucw/respool.h"
13 #include "ucw/trans.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 void bclose(struct fastbuf *f)
19 {
20   if (f)
21     {
22       if (!(f->flags & FB_DEAD))
23         bflush(f);
24       if (f->close)
25         f->close(f);
26       if (f->res)
27         res_drop(f->res);
28     }
29 }
30
31 void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
32 {
33   ASSERT(!(f->flags & FB_DEAD));
34   f->flags |= FB_DEAD;
35   va_list args;
36   va_start(args, fmt);
37   trans_vthrow(id, f, fmt, args);
38 }
39
40 int brefill(struct fastbuf *f, int allow_eof)
41 {
42   ASSERT(f->bptr >= f->bstop);
43   if (!f->refill)
44     bthrow(f, "fb.read", "Stream not readable");
45   if (f->refill(f))
46     {
47       ASSERT(f->bptr < f->bstop);
48       return 1;
49     }
50   else
51     {
52       if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
53         bthrow(f, "fb.eof", "Unexpected EOF");
54       ASSERT(f->bptr == f->bstop);
55       return 0;
56     }
57 }
58
59 void bspout(struct fastbuf *f)
60 {
61   ASSERT(f->bptr > f->bstop || f->bptr >= f->bufend);
62   if (!f->spout)
63     bthrow(f, "fb.write", "Stream not writeable");
64   f->spout(f);
65   ASSERT(f->bptr < f->bufend);
66 }
67
68 void bflush(struct fastbuf *f)
69 {
70   if (f->bptr > f->bstop)
71     bspout(f);
72   else if (f->bstop > f->buffer)
73     f->bptr = f->bstop = f->buffer;
74 }
75
76 inline void bsetpos(struct fastbuf *f, ucw_off_t pos)
77 {
78   /* We can optimize seeks only when reading */
79   if (pos >= f->pos - (f->bstop - f->buffer) && pos <= f->pos)
80     f->bptr = f->bstop + (pos - f->pos);
81   else
82     {
83       bflush(f);
84       if (!f->seek || !f->seek(f, pos, SEEK_SET))
85         bthrow(f, "fb.seek", "Stream not seekable");
86     }
87 }
88
89 void bseek(struct fastbuf *f, ucw_off_t pos, int whence)
90 {
91   switch (whence)
92     {
93     case SEEK_SET:
94       return bsetpos(f, pos);
95     case SEEK_CUR:
96       return bsetpos(f, btell(f) + pos);
97     case SEEK_END:
98       bflush(f);
99       if (!f->seek || !f->seek(f, pos, SEEK_END))
100         bthrow(f, "fb.seek", "Stream not seekable");
101       break;
102     default:
103       die("bseek: invalid whence=%d", whence);
104     }
105 }
106
107 int bgetc_slow(struct fastbuf *f)
108 {
109   if (f->bptr < f->bstop)
110     return *f->bptr++;
111   if (!brefill(f, 0))
112     return -1;
113   return *f->bptr++;
114 }
115
116 int bpeekc_slow(struct fastbuf *f)
117 {
118   if (f->bptr < f->bstop)
119     return *f->bptr;
120   if (!brefill(f, 0))
121     return -1;
122   return *f->bptr;
123 }
124
125 int beof_slow(struct fastbuf *f)
126 {
127   return f->bptr >= f->bstop && !brefill(f, 1);
128 }
129
130 void bputc_slow(struct fastbuf *f, uns c)
131 {
132   if (f->bptr >= f->bufend)
133     bspout(f);
134   *f->bptr++ = c;
135 }
136
137 uns bread_slow(struct fastbuf *f, void *b, uns l, uns check)
138 {
139   uns total = 0;
140   while (l)
141     {
142       uns k = f->bstop - f->bptr;
143
144       if (!k)
145         {
146           brefill(f, check);
147           k = f->bstop - f->bptr;
148           if (!k)
149             break;
150         }
151       if (k > l)
152         k = l;
153       memcpy(b, f->bptr, k);
154       f->bptr += k;
155       b = (byte *)b + k;
156       l -= k;
157       total += k;
158     }
159   if (check && total && l)
160     bthrow(f, "fb.read", "breadb: short read");
161   return total;
162 }
163
164 void bwrite_slow(struct fastbuf *f, const void *b, uns l)
165 {
166   while (l)
167     {
168       uns k = f->bufend - f->bptr;
169
170       if (!k)
171         {
172           bspout(f);
173           k = f->bufend - f->bptr;
174         }
175       if (k > l)
176         k = l;
177       memcpy(f->bptr, b, k);
178       f->bptr += k;
179       b = (byte *)b + k;
180       l -= k;
181     }
182 }
183
184 void
185 bbcopy_slow(struct fastbuf *f, struct fastbuf *t, uns l)
186 {
187   while (l)
188     {
189       byte *fptr, *tptr;
190       uns favail, tavail, n;
191
192       favail = bdirect_read_prepare(f, &fptr);
193       if (!favail)
194         {
195           if (l == ~0U)
196             return;
197           die("bbcopy: source exhausted");
198         }
199       tavail = bdirect_write_prepare(t, &tptr);
200       n = MIN(l, favail);
201       n = MIN(n, tavail);
202       memcpy(tptr, fptr, n);
203       bdirect_read_commit(f, fptr + n);
204       bdirect_write_commit(t, tptr + n);
205       if (l != ~0U)
206         l -= n;
207     }
208 }
209
210 int
211 bconfig(struct fastbuf *f, uns item, int value)
212 {
213   return f->config ? f->config(f, item, value) : -1;
214 }
215
216 void
217 brewind(struct fastbuf *f)
218 {
219   bflush(f);
220   bsetpos(f, 0);
221 }
222
223 int
224 bskip_slow(struct fastbuf *f, uns len)
225 {
226   while (len)
227     {
228       byte *buf;
229       uns l = bdirect_read_prepare(f, &buf);
230       if (!l)
231         return 0;
232       l = MIN(l, len);
233       bdirect_read_commit(f, buf+l);
234       len -= l;
235     }
236   return 1;
237 }
238
239 ucw_off_t
240 bfilesize(struct fastbuf *f)
241 {
242   if (!f)
243     return 0;
244   ucw_off_t pos = btell(f);
245   bflush(f);
246   if (!f->seek(f, 0, SEEK_END))
247     return -1;
248   ucw_off_t len = btell(f);
249   bsetpos(f, pos);
250   return len;
251 }
252
253 /* Resources */
254
255 static void
256 fb_res_detach(struct resource *r)
257 {
258   struct fastbuf *f = r->priv;
259   f->res = NULL;
260 }
261
262 static void
263 fb_res_free(struct resource *r)
264 {
265   struct fastbuf *f = r->priv;
266   f->res = NULL;
267   bclose(f);
268 }
269
270 static void
271 fb_res_dump(struct resource *r)
272 {
273   struct fastbuf *f = r->priv;
274   printf(" name=%s", f->name);
275 }
276
277 static const struct res_class fb_res_class = {
278   .name = "fastbuf",
279   .detach = fb_res_detach,
280   .dump = fb_res_dump,
281   .free = fb_res_free,
282 };
283
284 void
285 fb_tie(struct fastbuf *f)
286 {
287   f->res = res_new(&fb_res_class, f);
288 }