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