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