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