]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
LibUCW: Fixed broken DARY_ALLOC() on 64bit architectures.
[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 = 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         {
136           int r = fbmulti_subbuf_next(f);
137           ASSERT(r);
138         }
139
140       while (pos < FB_MULTI(f)->cur->begin) // Moving backwards
141         {
142           int r = fbmulti_subbuf_prev(f);
143           ASSERT(r);
144         }
145
146       // Now cur is the right buffer.
147       FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
148
149       fbmulti_get_ptrs(f);
150       return 1;
151
152     case SEEK_END:
153       return fbmulti_seek(f, FB_MULTI(f)->len + pos, SEEK_SET);
154
155     default:
156       ASSERT(0);
157     }
158 }
159
160 static void
161 fbmulti_close(struct fastbuf *f)
162 {
163   CLIST_FOR_EACH(struct subbuf *, n, FB_MULTI(f)->subbufs)
164     bclose(n->fb);
165
166   mp_delete(FB_MULTI(f)->mp);
167 }
168
169 struct fastbuf *
170 fbmulti_create(void)
171 {
172   struct mempool *mp = mp_new(1024);
173   struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
174   struct fb_multi *fbm = FB_MULTI(fb_out);
175
176   fbm->mp = mp;
177   fbm->len = 0;
178
179   clist_init(&fbm->subbufs);
180
181   fb_out->name = FB_MULTI_NAME;
182   fb_out->refill = fbmulti_refill;
183   fb_out->seek = fbmulti_seek;
184   fb_out->close = fbmulti_close;
185
186   return fb_out;
187 }
188
189 void
190 fbmulti_append(struct fastbuf *f, struct fastbuf *fb)
191 {
192   struct subbuf *last = clist_tail(&FB_MULTI(f)->subbufs);
193
194   struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(*sb));
195   sb->fb = fb;
196   clist_add_tail(&FB_MULTI(f)->subbufs, &(sb->n));
197
198   ASSERT(fb->refill);
199
200   if (fb->seek)
201     {
202       if (f->seek)
203         {
204           sb->begin = last ? last->end : 0;
205           bseek(fb, 0, SEEK_END);
206           FB_MULTI(f)->len = sb->end = sb->begin + btell(fb);
207         }
208
209       bsetpos(fb, 0);
210     }
211
212   else
213     {
214       f->seek = NULL;
215       sb->offset = btell(fb);
216       sb->begin = 0;
217       sb->end = 0;
218     }
219
220   if (!last)
221     {
222       FB_MULTI(f)->cur = sb;
223       fbmulti_get_ptrs(f);
224     }
225 }
226
227 void
228 fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
229 {
230   bflush(f);
231   if (fb)
232     {
233       CLIST_FOR_EACH(struct subbuf *, n, FB_MULTI(f)->subbufs)
234         if (fb == n->fb)
235           {
236             clist_remove(&(n->n));
237             return;
238           }
239
240       die("Given fastbuf %p not in given fbmulti %p", fb, f);
241     }
242   else
243     clist_init(&FB_MULTI(f)->subbufs);
244 }
245
246 #ifdef TEST
247
248 int main(int argc, char **argv)
249 {
250   if (argc < 2)
251     {
252       fprintf(stderr, "You must specify a test (r, m, i, n)\n");
253       return 1;
254     }
255   switch (*argv[1])
256     {
257       case 'r':
258         {
259           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
260           struct fastbuf fb[ARRAY_SIZE(data)];
261           for (uns i=0;i<ARRAY_SIZE(data);i++)
262             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
263
264           struct fastbuf *f = fbmulti_create();
265           fbmulti_append(f, &fb[0]);
266           fbmulti_append(f, &fb[1]);
267           fbmulti_append(f, &fb[2]);
268
269           char buffer[9];
270           while (bgets(f, buffer, 9))
271             puts(buffer);
272
273           bclose(f);
274           break;
275         }
276       case 'm':
277         {
278           char *data[] = { "Mnl", "ige" };
279           struct fastbuf fb[ARRAY_SIZE(data)];
280           for (uns i=0;i<ARRAY_SIZE(data);i++)
281             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
282
283           struct fastbuf *f = fbmulti_create();
284           fbmulti_append(f, &fb[0]);
285           fbmulti_append(f, &fb[1]);
286
287           int pos[] = {0, 3, 1, 4, 2, 5};
288
289           for (uns i=0;i<ARRAY_SIZE(pos);i++)
290             {
291               bsetpos(f, pos[i]);
292               putchar(bgetc(f));
293             }
294
295           bclose(f);
296           break;
297         }
298       case 'i':
299         {
300           char *data = "Insae";
301           struct fastbuf fb[4];
302           fbbuf_init_read(&fb[0], data, 1, 0);
303           fbbuf_init_read(&fb[1], data + 1, 1, 0);
304           fbbuf_init_read(&fb[2], data + 2, 2, 0);
305           fbbuf_init_read(&fb[3], data + 4, 1, 0);
306
307           struct fastbuf *f = fbmulti_create();
308           fbmulti_append(f, &fb[0]);
309           fbmulti_append(f, &fb[1]);
310           fbmulti_append(f, &fb[2]);
311           fbmulti_append(f, &fb[1]);
312           fbmulti_append(f, &fb[3]);
313
314           char buffer[9];
315           while(bgets(f, buffer, 9))
316             puts(buffer);
317
318           bclose(f);
319           break;
320         }
321       case 'n':
322         {
323           char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
324           struct fastbuf fb[ARRAY_SIZE(data)];
325           for (uns i=0;i<ARRAY_SIZE(data);i++)
326             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
327
328           struct fastbuf sp;
329           fbbuf_init_read(&sp, " ", 1, 0);
330
331           struct fastbuf nl;
332           fbbuf_init_read(&nl, "\n", 1, 0);
333
334           struct fastbuf *f = fbmulti_create();
335           struct fastbuf *ff;
336
337           ff = fbmulti_create();
338           fbmulti_append(ff, &fb[0]);
339           fbmulti_append(ff, &sp);
340           fbmulti_append(ff, &fb[1]);
341           fbmulti_append(f, ff);
342           fbmulti_append(f, &nl);
343
344           ff = fbmulti_create();
345           fbmulti_append(ff, &fb[2]);
346           fbmulti_append(ff, &sp);
347           fbmulti_append(ff, &fb[3]);
348           fbmulti_append(f, ff);
349           fbmulti_append(f, &nl);
350
351           ff = fbmulti_create();
352           fbmulti_append(ff, &fb[4]);
353           fbmulti_append(ff, &sp);
354           fbmulti_append(ff, &fb[5]);
355           fbmulti_append(f, ff);
356           fbmulti_append(f, &nl);
357
358           char buffer[20];
359           while (bgets(f, buffer, 20))
360             puts(buffer);
361
362           bclose(f);
363           break;
364         }
365     }
366   return 0;
367 }
368
369 #endif