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