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