]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
Fastbuf: fbmulti_remove
[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   fbmulti_update_capability(f);
214 }
215
216 void
217 fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
218 {
219   if (fb)
220     {
221       CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
222         if (fb == n->fb)
223           {
224             // TODO: fix seek positions
225             clist_remove(&(n->n));
226             fbmulti_update_capability(f);
227             return;
228           }
229
230       die("Given fastbuf %p not in given fbmulti %p.", fb, f);
231     }
232   else
233     clist_init(FB_MULTI(f)->subbufs);
234
235   fbmulti_update_capability(f);
236 }
237
238 static void fbmulti_flatten_internal(struct fastbuf *f, clist *c, int allow_close)
239 {
240   CLIST_FOR_EACH(struct subbuf *, n, *c)
241     {
242       if (strcmp(n->fb->name, FB_MULTI_NAME))
243         fbmulti_append(f, n->fb, n->allow_close && allow_close);
244       
245       else
246         {
247           fbmulti_flatten_internal(f, FB_MULTI(n->fb)->subbufs, allow_close && n->allow_close);
248           if (allow_close && n->allow_close)
249             {
250               FB_MULTI(n->fb)->subbufs = mp_alloc(FB_MULTI(n->fb)->mp, sizeof(clist));
251               clist_init(FB_MULTI(n->fb)->subbufs);
252               bclose(n->fb);
253             }
254         }
255     }
256 }
257
258 void
259 fbmulti_flatten(struct fastbuf *f)
260 {
261   if (strcmp(f->name, FB_MULTI_NAME))
262     {
263       DBG("fbmulti: given fastbuf isn't fbmulti");
264       return;
265     }
266   
267   clist *c = FB_MULTI(f)->subbufs;
268   FB_MULTI(f)->subbufs = mp_alloc(FB_MULTI(f)->mp, sizeof(clist));
269   clist_init(FB_MULTI(f)->subbufs);
270
271   fbmulti_flatten_internal(f, c, 1);
272   FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
273   f->bptr = f->bstop = f->buffer;
274   f->pos = 0;
275 }
276
277 #ifdef TEST
278
279 int main(int argc, char **argv)
280 {
281   if (argc < 2)
282     {
283       fprintf(stderr, "You must specify a test (r, w, o)\n");
284       return 1;
285     }
286   switch (*argv[1])
287     {
288       case 'r':
289         {
290           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
291           struct fastbuf fb[ARRAY_SIZE(data)];
292           for (uns i=0;i<ARRAY_SIZE(data);i++)
293             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
294
295           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
296
297           char buffer[9];
298           while (bgets(f, buffer, 9))
299             puts(buffer);
300
301           bclose(f);
302           break;
303         }
304       case 'm':
305         {
306           char *data[] = { "Mnl", "ige" };
307           struct fastbuf fb[ARRAY_SIZE(data)];
308           for (uns i=0;i<ARRAY_SIZE(data);i++)
309             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
310
311           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], NULL);
312
313           int pos[] = {0, 3, 1, 4, 2, 5};
314
315           for (uns i=0;i<ARRAY_SIZE(pos);i++)
316             {
317               bsetpos(f, pos[i]);
318               putchar(bgetc(f));
319             }
320
321           bclose(f);
322           break;
323         }
324       case 'i':
325         {
326           char *data = "Insae";
327           struct fastbuf fb[4];
328           fbbuf_init_read(&fb[0], data, 1, 0);
329           fbbuf_init_read(&fb[1], data + 1, 1, 0);
330           fbbuf_init_read(&fb[2], data + 2, 2, 0);
331           fbbuf_init_read(&fb[3], data + 4, 1, 0);
332
333           struct fastbuf *f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
334
335           char buffer[9];
336           while(bgets(f, buffer, 9))
337             puts(buffer);
338
339           bclose(f);
340           break;
341         }
342       case 'f':
343       case 'n':
344         {
345           char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
346           struct fastbuf fb[ARRAY_SIZE(data)];
347           for (uns i=0;i<ARRAY_SIZE(data);i++)
348             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
349
350           struct fastbuf sp;
351           fbbuf_init_read(&sp, " ", 1, 0);
352
353           struct fastbuf nl;
354           fbbuf_init_read(&nl, "\n", 1, 0);
355
356           struct fastbuf *f = fbmulti_create(4,
357               fbmulti_create(5,
358                 &fb[0],
359                 &sp,
360                 &fb[1],
361                 NULL),
362               &nl,
363               fbmulti_create(7,
364                 &fb[2],
365                 &sp,
366                 &fb[3],
367                 NULL),
368               &nl,
369               fbmulti_create(3,
370                 &fb[4],
371                 &sp,
372                 &fb[5],
373                 NULL),
374               &nl,
375               NULL);
376
377           if (*argv[1] == 'f')
378             fbmulti_flatten(f);
379
380           char buffer[20];
381           while (bgets(f, buffer, 20))
382             puts(buffer);
383
384           bclose(f);
385           break;
386         }
387     }
388   return 0;
389 }
390
391 #endif