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