]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
23a45611d5e049e6dc5c6a4a19514ec4735478b9
[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 static void
154 fbmulti_close(struct fastbuf *f) {
155   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
156     bclose(n->fb);
157
158   mp_delete(FB_MULTI(f)->mp);
159 }
160
161 struct fastbuf*
162 fbmulti_create(uns bufsize, ...)
163 {
164   struct mempool *mp = mp_new(bufsize);
165   struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
166   FB_MULTI(fb_out)->mp = mp;
167
168   struct fastbuf *fb_in;
169   clist* subbufs = mp_alloc(mp, sizeof(clist));
170   clist_init(subbufs);
171   FB_MULTI(fb_out)->subbufs = subbufs;
172
173   va_list args;
174   va_start(args, bufsize);
175   while (fb_in = va_arg(args, struct fastbuf *)) {
176     struct subbuf *sb = mp_alloc(mp, sizeof(struct subbuf));
177     sb->fb = fb_in;
178     clist_add_tail(subbufs, &(sb->n));
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 = "<multi>";
188
189   fbmulti_update_capability(fb_out);
190   fb_out->close = fbmulti_close;
191
192   return fb_out;
193 }
194
195 #ifdef TEST
196
197 int main(int argc, char ** argv)
198 {
199   if (argc < 2)
200     {
201       fprintf(stderr, "You must specify a test (r, w, o)\n");
202       return 1;
203     }
204   switch (*argv[1])
205     {
206       case 'r':
207         {
208           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
209           struct fastbuf fb[ARRAY_SIZE(data)];
210           for (uns i=0;i<ARRAY_SIZE(data);i++)
211             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
212
213           struct fastbuf* f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
214
215           char buffer[9];
216           while (bgets(f, buffer, 9))
217             puts(buffer);
218
219           bclose(f);
220           break;
221         }
222       case 'm':
223         {
224           char *data[] = { "Mnl", "ige" };
225           struct fastbuf fb[ARRAY_SIZE(data)];
226           for (uns i=0;i<ARRAY_SIZE(data);i++)
227             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
228
229           struct fastbuf* f = fbmulti_create(4, &fb[0], &fb[1], NULL);
230
231           int pos[] = {0, 3, 1, 4, 2, 5};
232
233           for (uns i=0;i<ARRAY_SIZE(pos);i++) {
234             bseek(f, pos[i], SEEK_SET);
235             putchar(bgetc(f));
236           }
237
238           bclose(f);
239           break;
240         }
241       case 'i':
242         {
243           char *data = "Insae";
244           struct fastbuf fb[4];
245           fbbuf_init_read(&fb[0], data, 1, 0);
246           fbbuf_init_read(&fb[1], data + 1, 1, 0);
247           fbbuf_init_read(&fb[2], data + 2, 2, 0);
248           fbbuf_init_read(&fb[3], data + 4, 1, 0);
249
250           struct fastbuf* f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
251
252           char buffer[9];
253           while(bgets(f, buffer, 9))
254             puts(buffer);
255
256           bclose(f);
257           break;
258         }
259       case 'n':
260         {
261           char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
262           struct fastbuf fb[ARRAY_SIZE(data)];
263           for (uns i=0;i<ARRAY_SIZE(data);i++)
264             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
265
266           struct fastbuf sp;
267           fbbuf_init_read(&sp, " ", 1, 0);
268
269           struct fastbuf nl;
270           fbbuf_init_read(&nl, "\n", 1, 0);
271
272           struct fastbuf *f = fbmulti_create(4,
273               fbmulti_create(5,
274                 &fb[0],
275                 &sp,
276                 &fb[1],
277                 NULL),
278               &nl,
279               fbmulti_create(7,
280                 &fb[2],
281                 &sp,
282                 &fb[3],
283                 NULL),
284               &nl,
285               fbmulti_create(3,
286                 &fb[4],
287                 &sp,
288                 &fb[5],
289                 NULL),
290               &nl,
291               NULL);
292
293           char buffer[20];
294           while (bgets(f, buffer, 20))
295             puts(buffer);
296
297           bclose(f);
298           break;
299         }
300     }
301   return 0;
302 }
303
304 #endif