]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
0bf6d9e4a0cdacf92ee3874a07313b07e1341634
[libucw.git] / ucw / fb-multi.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on itself
3  *
4  *      (c) 2012 Jan Moskyto Matejka <mq@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/clists.h>
12 #include <ucw/fastbuf.h>
13 #include <ucw/mempool.h>
14
15 #include <stdio.h>
16
17 #define FB_MULTI_NAME "<multi>"
18
19 struct fb_multi {
20   struct fastbuf fb;
21   struct mempool *mp;
22   struct subbuf *cur;
23   ucw_off_t len;
24   clist *subbufs;
25 };
26
27 #define FB_MULTI(f) ((struct fb_multi *)(f))
28
29 struct subbuf {
30   cnode n;
31   ucw_off_t begin, end;
32   int allow_close;
33   struct fastbuf *fb;
34 };
35
36 static void
37 fbmulti_subbuf_get_end(struct subbuf *s)
38 {
39   if (s->fb->seek)
40     {
41       bseek(s->fb, 0, SEEK_END);
42       s->end = s->begin + btell(s->fb);
43     }
44 }
45
46 static int
47 fbmulti_subbuf_next(struct fastbuf *f)
48 {
49   struct subbuf *next = clist_next(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
50   if (next == NULL)
51     return 0;
52   
53   if (f->seek)
54     {
55       bsetpos(next->fb, 0);
56       next->begin = FB_MULTI(f)->cur->end;
57     }
58
59   FB_MULTI(f)->cur = next;
60   return 1;
61 }
62
63 static int
64 fbmulti_refill(struct fastbuf *f)
65 {
66   if (f->bufend == f->bstop)
67     f->bptr = f->bstop = f->buffer;
68
69   uns len = bread(FB_MULTI(f)->cur->fb, f->bstop, (f->bufend - f->bstop));
70   f->bstop += len;
71   f->pos += len;
72   if (len)
73     return len;
74
75   // Current buf returned EOF
76   // Update the information on end of this buffer
77   fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
78
79   // Take the next one if exists
80   if (fbmulti_subbuf_next(f))
81     return fbmulti_refill(f);
82   else
83     return 0;
84 }
85
86 static void
87 fbmulti_get_len(struct fastbuf *f)
88 {
89   ASSERT(f->seek);
90   FB_MULTI(f)->len = 0;
91   
92   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
93     {
94       n->begin = FB_MULTI(f)->len;
95       fbmulti_subbuf_get_end(n);
96       FB_MULTI(f)->len = n->end;
97     }
98 }
99
100 static int
101 fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
102 {
103   switch(whence)
104     {
105     case SEEK_SET:
106       if (f->pos > pos)
107         {
108           FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
109           FB_MULTI(f)->cur->begin = 0;
110           f->pos = 0;
111           return fbmulti_seek(f, pos, SEEK_SET);
112         }
113
114       do
115         {
116           fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
117           if (pos < FB_MULTI(f)->cur->end)
118             break;
119
120           if (!fbmulti_subbuf_next(f))
121             {
122               if (pos == FB_MULTI(f)->cur->end)
123                 break;
124               else
125                 bthrow(f, "seek", "Seek out of range");
126             }
127
128         }
129       while (1);
130
131       bsetpos(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin));
132       f->pos = pos;
133       f->bptr = f->bstop = f->buffer;
134       return 1;
135       break;
136
137     case SEEK_END:
138       fbmulti_get_len(f);
139       return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_SET);
140       break;
141
142     default:
143       ASSERT(0);
144     }
145 }
146
147 static void
148 fbmulti_update_capability(struct fastbuf *f)
149 {
150   // FB Multi is only a proxy to other fastbufs ... if any of them lacks
151   // support of any feature, FB Multi also provides no support of that feature
152   f->refill = fbmulti_refill;
153   f->seek = fbmulti_seek;
154
155   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
156     {
157       ASSERT(n->fb->refill)
158
159       if (!n->fb->seek)
160         f->seek = NULL;
161     }
162 }
163
164 static void
165 fbmulti_close(struct fastbuf *f)
166 {
167   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
168     if (n->allow_close)
169       bclose(n->fb);
170
171   mp_delete(FB_MULTI(f)->mp);
172 }
173
174 struct fastbuf *
175 fbmulti_create(uns bufsize, ...)
176 {
177   struct mempool *mp = mp_new(bufsize);
178   struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
179   FB_MULTI(fb_out)->mp = mp;
180
181   struct fastbuf *fb_in;
182   clist *subbufs = mp_alloc(mp, sizeof(clist));
183   clist_init(subbufs);
184   FB_MULTI(fb_out)->subbufs = subbufs;
185
186   va_list args;
187   va_start(args, bufsize);
188   while (fb_in = va_arg(args, struct fastbuf *))
189     fbmulti_append(fb_out, fb_in, 1);
190   
191   va_end(args);
192
193   FB_MULTI(fb_out)->cur = clist_head(subbufs);
194
195   fb_out->buffer = mp_alloc(mp, bufsize);
196   fb_out->bptr = fb_out->bstop = fb_out->buffer;
197   fb_out->bufend = fb_out->buffer + bufsize;
198   fb_out->name = FB_MULTI_NAME;
199
200   fbmulti_update_capability(fb_out);
201   fb_out->close = fbmulti_close;
202
203   return fb_out;
204 }
205
206 void
207 fbmulti_append(struct fastbuf *f, struct fastbuf *fb, int allow_close)
208 {
209   struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(struct subbuf));
210   sb->fb = fb;
211   sb->allow_close = allow_close;
212   clist_add_tail(FB_MULTI(f)->subbufs, &(sb->n));
213 }
214
215 static void fbmulti_flatten_internal(struct fastbuf *f, clist *c, int allow_close)
216 {
217   CLIST_FOR_EACH(struct subbuf *, n, *c)
218     {
219       if (strcmp(n->fb->name, FB_MULTI_NAME))
220         fbmulti_append(f, n->fb, n->allow_close && allow_close);
221       
222       else
223         {
224           fbmulti_flatten_internal(f, FB_MULTI(n->fb)->subbufs, allow_close && n->allow_close);
225           if (allow_close && n->allow_close)
226             {
227               FB_MULTI(n->fb)->subbufs = mp_alloc(FB_MULTI(n->fb)->mp, sizeof(clist));
228               clist_init(FB_MULTI(n->fb)->subbufs);
229               bclose(n->fb);
230             }
231         }
232     }
233 }
234
235 void
236 fbmulti_flatten(struct fastbuf *f)
237 {
238   if (strcmp(f->name, FB_MULTI_NAME))
239     {
240       DBG("fbmulti: given fastbuf isn't fbmulti");
241       return;
242     }
243   
244   clist *c = FB_MULTI(f)->subbufs;
245   FB_MULTI(f)->subbufs = mp_alloc(FB_MULTI(f)->mp, sizeof(clist));
246   clist_init(FB_MULTI(f)->subbufs);
247
248   fbmulti_flatten_internal(f, c, 1);
249   FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
250   f->bptr = f->bstop = f->buffer;
251   f->pos = 0;
252 }
253
254 #ifdef TEST
255
256 int main(int argc, char **argv)
257 {
258   if (argc < 2)
259     {
260       fprintf(stderr, "You must specify a test (r, w, o)\n");
261       return 1;
262     }
263   switch (*argv[1])
264     {
265       case 'r':
266         {
267           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
268           struct fastbuf fb[ARRAY_SIZE(data)];
269           for (uns i=0;i<ARRAY_SIZE(data);i++)
270             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
271
272           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
273
274           char buffer[9];
275           while (bgets(f, buffer, 9))
276             puts(buffer);
277
278           bclose(f);
279           break;
280         }
281       case 'm':
282         {
283           char *data[] = { "Mnl", "ige" };
284           struct fastbuf fb[ARRAY_SIZE(data)];
285           for (uns i=0;i<ARRAY_SIZE(data);i++)
286             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
287
288           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], NULL);
289
290           int pos[] = {0, 3, 1, 4, 2, 5};
291
292           for (uns i=0;i<ARRAY_SIZE(pos);i++)
293             {
294               bsetpos(f, pos[i]);
295               putchar(bgetc(f));
296             }
297
298           bclose(f);
299           break;
300         }
301       case 'i':
302         {
303           char *data = "Insae";
304           struct fastbuf fb[4];
305           fbbuf_init_read(&fb[0], data, 1, 0);
306           fbbuf_init_read(&fb[1], data + 1, 1, 0);
307           fbbuf_init_read(&fb[2], data + 2, 2, 0);
308           fbbuf_init_read(&fb[3], data + 4, 1, 0);
309
310           struct fastbuf *f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
311
312           char buffer[9];
313           while(bgets(f, buffer, 9))
314             puts(buffer);
315
316           bclose(f);
317           break;
318         }
319       case 'f':
320       case 'n':
321         {
322           char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
323           struct fastbuf fb[ARRAY_SIZE(data)];
324           for (uns i=0;i<ARRAY_SIZE(data);i++)
325             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
326
327           struct fastbuf sp;
328           fbbuf_init_read(&sp, " ", 1, 0);
329
330           struct fastbuf nl;
331           fbbuf_init_read(&nl, "\n", 1, 0);
332
333           struct fastbuf *f = fbmulti_create(4,
334               fbmulti_create(5,
335                 &fb[0],
336                 &sp,
337                 &fb[1],
338                 NULL),
339               &nl,
340               fbmulti_create(7,
341                 &fb[2],
342                 &sp,
343                 &fb[3],
344                 NULL),
345               &nl,
346               fbmulti_create(3,
347                 &fb[4],
348                 &sp,
349                 &fb[5],
350                 NULL),
351               &nl,
352               NULL);
353
354           if (*argv[1] == 'f')
355             fbmulti_flatten(f);
356
357           char buffer[20];
358           while (bgets(f, buffer, 20))
359             puts(buffer);
360
361           bclose(f);
362           break;
363         }
364     }
365   return 0;
366 }
367
368 #endif