]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
e5835574e84ff5be9f58d79b8acc6a4bb3bf39a5
[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, offset;
32   int allow_close;
33   struct fastbuf *fb;
34 };
35
36 static void
37 fbmulti_subbuf_get_end(struct subbuf *s)
38 {
39   ASSERT(s->fb->seek);
40   bseek(s->fb, 0, SEEK_END);
41   s->end = s->begin + btell(s->fb);
42 }
43
44 static void
45 fbmulti_get_ptrs(struct fastbuf *f)
46 {
47   f->buffer = FB_MULTI(f)->cur->fb->buffer;
48   f->bptr = FB_MULTI(f)->cur->fb->bptr;
49   f->bstop = FB_MULTI(f)->cur->fb->bstop;
50   f->bufend = FB_MULTI(f)->cur->fb->bufend;
51   f->pos = FB_MULTI(f)->cur->begin + FB_MULTI(f)->cur->fb->pos - FB_MULTI(f)->cur->offset;
52 }
53
54 static void
55 fbmulti_set_ptrs(struct fastbuf *f)
56 {
57   FB_MULTI(f)->cur->fb->bptr = f->bptr;
58 }
59
60 static int
61 fbmulti_subbuf_next(struct fastbuf *f)
62 {
63   struct subbuf *next = clist_next(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
64   if (next == NULL)
65     return 0;
66
67   // Check the end of current buf
68   if (f->seek)
69     {
70       FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, FB_MULTI(f)->cur->end - FB_MULTI(f)->cur->begin, SEEK_SET);
71       fbmulti_get_ptrs(f);
72       ASSERT(FB_MULTI(f)->cur->end == f->pos);
73     }
74   else
75     FB_MULTI(f)->cur->end = f->pos;
76
77   // Set the beginning of the next buf
78   if (next->fb->seek)
79     {
80       bsetpos(next->fb, 0);
81       next->offset = 0;
82     }
83   else
84     {
85       ASSERT(!f->seek);
86       next->offset = btell(next->fb);
87     }
88
89   next->begin = FB_MULTI(f)->cur->end;
90
91   // Set the pointers
92   FB_MULTI(f)->cur = next;
93   fbmulti_get_ptrs(f);
94
95   return 1;
96 }
97
98 static int
99 fbmulti_subbuf_prev(struct fastbuf *f)
100 {
101   // Called only when seeking, assuming everything seekable
102   struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
103   ASSERT(prev != NULL);
104
105   // Set pos to beginning, flush offset
106   bsetpos(prev->fb, 0);
107   prev->offset = 0;
108
109   // Set the pointers
110   FB_MULTI(f)->cur = prev;
111   fbmulti_get_ptrs(f);
112
113   return 1;
114 }
115
116 static int
117 fbmulti_refill(struct fastbuf *f)
118 {
119   fbmulti_set_ptrs(f);
120   // Refill the subbuf
121   uns len = FB_MULTI(f)->cur->fb->refill(FB_MULTI(f)->cur->fb);
122   if (len)
123     {
124       fbmulti_get_ptrs(f);
125       return len;
126     }
127
128   // Current buf returned EOF
129   // Update the information on end of this buffer
130   fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
131
132   // Take the next one if exists and redo
133   if (fbmulti_subbuf_next(f))
134     return fbmulti_refill(f);
135   else
136     return 0;
137 }
138
139 static void
140 fbmulti_get_len(struct fastbuf *f)
141 {
142   ucw_off_t pos = btell(f);
143   ASSERT(f->seek);
144   FB_MULTI(f)->len = 0;
145
146   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
147     {
148       n->begin = FB_MULTI(f)->len;
149       fbmulti_subbuf_get_end(n);
150       FB_MULTI(f)->len = n->end;
151     }
152   f->seek(f, pos, SEEK_SET); // XXX: f->seek is needed here instead of bsetpos as the FE assumptions about f's state may be completely wrong.
153 }
154
155 static int
156 fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
157 {
158   fbmulti_set_ptrs(f);
159   switch(whence)
160     {
161     case SEEK_SET:
162       if (pos > FB_MULTI(f)->len)
163         bthrow(f, "seek", "Seek out of range");
164
165       while (pos > FB_MULTI(f)->cur->end) // Moving forward
166         ASSERT(fbmulti_subbuf_next(f));
167
168       while (pos < FB_MULTI(f)->cur->begin) // Moving backwards
169         ASSERT(fbmulti_subbuf_prev(f));
170
171       // Now cur is the right buffer.
172       FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
173
174       fbmulti_get_ptrs(f);
175       return 1;
176       break;
177
178     case SEEK_END:
179       return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_SET);
180       break;
181
182     default:
183       ASSERT(0);
184     }
185 }
186
187 static void
188 fbmulti_update_capability(struct fastbuf *f)
189 {
190   // FB Multi is only a proxy to other fastbufs ... if any of them lacks
191   // support of any feature, FB Multi also provides no support of that feature
192   f->refill = fbmulti_refill;
193   f->seek = fbmulti_seek;
194
195   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
196     {
197       ASSERT(n->fb->refill);
198
199       if (!n->fb->seek)
200         f->seek = NULL;
201     }
202 }
203
204 static void
205 fbmulti_close(struct fastbuf *f)
206 {
207   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
208     if (n->allow_close)
209       bclose(n->fb);
210
211   mp_delete(FB_MULTI(f)->mp);
212 }
213
214 struct fastbuf *
215 fbmulti_create(uns bufsize, ...)
216 {
217   struct mempool *mp = mp_new(bufsize);
218   struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
219   FB_MULTI(fb_out)->mp = mp;
220
221   struct fastbuf *fb_in;
222   clist *subbufs = mp_alloc(mp, sizeof(clist));
223   clist_init(subbufs);
224   FB_MULTI(fb_out)->subbufs = subbufs;
225
226   va_list args;
227   va_start(args, bufsize);
228   while (fb_in = va_arg(args, struct fastbuf *))
229     fbmulti_append(fb_out, fb_in, 1);
230   
231   va_end(args);
232
233   fbmulti_update_capability(fb_out);
234
235   FB_MULTI(fb_out)->cur = clist_head(subbufs);
236   bsetpos(FB_MULTI(fb_out)->cur->fb, 0);
237
238   fbmulti_get_ptrs(fb_out);
239
240   // If seekable, get the length of each subbuf, the total length and boundaries
241   if (fb_out->seek)
242     {
243       fbmulti_get_len(fb_out);
244     }
245
246   fb_out->name = FB_MULTI_NAME;
247
248   fb_out->close = fbmulti_close;
249
250   return fb_out;
251 }
252
253 void
254 fbmulti_append(struct fastbuf *f, struct fastbuf *fb, int allow_close)
255 {
256   struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(struct subbuf));
257   sb->fb = fb;
258   sb->allow_close = allow_close;
259   clist_add_tail(FB_MULTI(f)->subbufs, &(sb->n));
260   fbmulti_update_capability(f);
261 }
262
263 void
264 fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
265 {
266   bflush(f);
267   uns pos = f->pos;
268   if (fb)
269     {
270       CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
271         if (fb == n->fb)
272           {
273             // Move the pointers to another buffer if this one was the active.
274             if (FB_MULTI(f)->cur == n)
275               {
276                 pos = n->begin;
277                 if (!fbmulti_subbuf_next(f))
278                   {
279                     struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
280                     if (prev == NULL)
281                       goto cleanup;
282
283                     FB_MULTI(f)->cur = prev;
284                     fbmulti_get_ptrs(f);
285                   }
286               }
287
288             if (n->end < pos)
289               pos -= (n->end - n->begin);
290
291             clist_remove(&(n->n));
292             fbmulti_update_capability(f);
293             fbmulti_get_len(f);
294             fbmulti_get_ptrs(f);
295             return;
296           };
297
298       die("Given fastbuf %p not in given fbmulti %p.", fb, f);
299     }
300   else
301     clist_init(FB_MULTI(f)->subbufs);
302
303 cleanup:
304   // The fbmulti is empty now, do some cleanup
305   fbmulti_update_capability(f);
306   fbmulti_get_len(f);
307   f->buffer = f->bufend = f->bptr = f->bstop = NULL;
308   f->pos = 0;
309 }
310
311 static void fbmulti_flatten_internal(struct fastbuf *f, clist *c, int allow_close)
312 {
313   CLIST_FOR_EACH(struct subbuf *, n, *c)
314     {
315       if (strcmp(n->fb->name, FB_MULTI_NAME))
316         fbmulti_append(f, n->fb, n->allow_close && allow_close);
317       
318       else
319         {
320           fbmulti_flatten_internal(f, FB_MULTI(n->fb)->subbufs, allow_close && n->allow_close);
321           if (allow_close && n->allow_close)
322             {
323               FB_MULTI(n->fb)->subbufs = mp_alloc(FB_MULTI(n->fb)->mp, sizeof(clist));
324               clist_init(FB_MULTI(n->fb)->subbufs);
325               bclose(n->fb);
326             }
327         }
328     }
329 }
330
331 void
332 fbmulti_flatten(struct fastbuf *f)
333 {
334   if (strcmp(f->name, FB_MULTI_NAME))
335     {
336       DBG("fbmulti: given fastbuf isn't fbmulti");
337       return;
338     }
339   
340   clist *c = FB_MULTI(f)->subbufs;
341   FB_MULTI(f)->subbufs = mp_alloc(FB_MULTI(f)->mp, sizeof(clist));
342   clist_init(FB_MULTI(f)->subbufs);
343
344   fbmulti_flatten_internal(f, c, 1);
345   FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
346   f->bptr = f->bstop = f->buffer;
347   f->pos = 0;
348 }
349
350 #ifdef TEST
351
352 int main(int argc, char **argv)
353 {
354   if (argc < 2)
355     {
356       fprintf(stderr, "You must specify a test (r, w, o)\n");
357       return 1;
358     }
359   switch (*argv[1])
360     {
361       case 'r':
362         {
363           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
364           struct fastbuf fb[ARRAY_SIZE(data)];
365           for (uns i=0;i<ARRAY_SIZE(data);i++)
366             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
367
368           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
369
370           char buffer[9];
371           while (bgets(f, buffer, 9))
372             puts(buffer);
373
374           bclose(f);
375           break;
376         }
377       case 'm':
378         {
379           char *data[] = { "Mnl", "ige" };
380           struct fastbuf fb[ARRAY_SIZE(data)];
381           for (uns i=0;i<ARRAY_SIZE(data);i++)
382             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
383
384           struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], NULL);
385
386           int pos[] = {0, 3, 1, 4, 2, 5};
387
388           for (uns i=0;i<ARRAY_SIZE(pos);i++)
389             {
390               bsetpos(f, pos[i]);
391               putchar(bgetc(f));
392             }
393
394           bclose(f);
395           break;
396         }
397       case 'i':
398         {
399           char *data = "Insae";
400           struct fastbuf fb[4];
401           fbbuf_init_read(&fb[0], data, 1, 0);
402           fbbuf_init_read(&fb[1], data + 1, 1, 0);
403           fbbuf_init_read(&fb[2], data + 2, 2, 0);
404           fbbuf_init_read(&fb[3], data + 4, 1, 0);
405
406           struct fastbuf *f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
407
408           char buffer[9];
409           while(bgets(f, buffer, 9))
410             puts(buffer);
411
412           bclose(f);
413           break;
414         }
415       case 'f':
416       case 'n':
417         {
418           char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
419           struct fastbuf fb[ARRAY_SIZE(data)];
420           for (uns i=0;i<ARRAY_SIZE(data);i++)
421             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
422
423           struct fastbuf sp;
424           fbbuf_init_read(&sp, " ", 1, 0);
425
426           struct fastbuf nl;
427           fbbuf_init_read(&nl, "\n", 1, 0);
428
429           struct fastbuf *f = fbmulti_create(4,
430               fbmulti_create(5,
431                 &fb[0],
432                 &sp,
433                 &fb[1],
434                 NULL),
435               &nl,
436               fbmulti_create(7,
437                 &fb[2],
438                 &sp,
439                 &fb[3],
440                 NULL),
441               &nl,
442               fbmulti_create(3,
443                 &fb[4],
444                 &sp,
445                 &fb[5],
446                 NULL),
447               &nl,
448               NULL);
449
450           if (*argv[1] == 'f')
451             fbmulti_flatten(f);
452
453           char buffer[20];
454           while (bgets(f, buffer, 20))
455             puts(buffer);
456
457           bclose(f);
458           break;
459         }
460     }
461   return 0;
462 }
463
464 #endif