2 * UCW Library -- Fast Buffered I/O on itself
4 * (c) 2012 Jan Moskyto Matejka <mq@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 #include <ucw/clists.h>
12 #include <ucw/fastbuf.h>
13 #include <ucw/mempool.h>
17 #define FB_MULTI_NAME "<multi>"
27 #define FB_MULTI(f) ((struct fb_multi *)(f))
31 ucw_off_t begin, end, offset;
37 fbmulti_subbuf_get_end(struct subbuf *s)
40 bseek(s->fb, 0, SEEK_END);
41 s->end = s->begin + btell(s->fb);
45 fbmulti_get_ptrs(struct fastbuf *f)
47 f->buffer = FB_MULTI(f)->cur->fb->buffer;
48 f->bptr = FB_MULTI(f)->cur->fb->bptr;
49 f->bstop = FB_MULTI(f)->cur->fb->bstop;
50 f->bufend = FB_MULTI(f)->cur->fb->bufend;
51 f->pos = FB_MULTI(f)->cur->begin + FB_MULTI(f)->cur->fb->pos - FB_MULTI(f)->cur->offset;
55 fbmulti_set_ptrs(struct fastbuf *f)
57 FB_MULTI(f)->cur->fb->bptr = f->bptr;
61 fbmulti_subbuf_next(struct fastbuf *f)
63 struct subbuf *next = clist_next(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
67 // Check the end of current buf
70 FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, FB_MULTI(f)->cur->end - FB_MULTI(f)->cur->begin, SEEK_SET);
72 ASSERT(FB_MULTI(f)->cur->end == f->pos);
75 FB_MULTI(f)->cur->end = f->pos;
77 // Set the beginning of the next buf
86 next->offset = btell(next->fb);
89 next->begin = FB_MULTI(f)->cur->end;
92 FB_MULTI(f)->cur = next;
99 fbmulti_subbuf_prev(struct fastbuf *f)
101 // Called only when seeking, assuming everything seekable
102 struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
103 ASSERT(prev != NULL);
105 // Set pos to beginning, flush offset
106 bsetpos(prev->fb, 0);
110 FB_MULTI(f)->cur = prev;
117 fbmulti_refill(struct fastbuf *f)
121 uns len = FB_MULTI(f)->cur->fb->refill(FB_MULTI(f)->cur->fb);
128 // Current buf returned EOF
129 // Update the information on end of this buffer
130 fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
132 // Take the next one if exists and redo
133 if (fbmulti_subbuf_next(f))
134 return fbmulti_refill(f);
140 fbmulti_get_len(struct fastbuf *f)
142 ucw_off_t pos = btell(f);
144 FB_MULTI(f)->len = 0;
146 CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
148 n->begin = FB_MULTI(f)->len;
149 fbmulti_subbuf_get_end(n);
150 FB_MULTI(f)->len = n->end;
152 f->seek(f, pos, SEEK_SET); // XXX: f->seek is needed here instead of bsetpos as the FE assumptions about f's state may be completely wrong.
156 fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
162 if (pos > FB_MULTI(f)->len)
163 bthrow(f, "seek", "Seek out of range");
165 while (pos > FB_MULTI(f)->cur->end) // Moving forward
166 ASSERT(fbmulti_subbuf_next(f));
168 while (pos < FB_MULTI(f)->cur->begin) // Moving backwards
169 ASSERT(fbmulti_subbuf_prev(f));
171 // Now cur is the right buffer.
172 FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
179 return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_SET);
188 fbmulti_update_capability(struct fastbuf *f)
190 // FB Multi is only a proxy to other fastbufs ... if any of them lacks
191 // support of any feature, FB Multi also provides no support of that feature
192 f->refill = fbmulti_refill;
193 f->seek = fbmulti_seek;
195 CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
197 ASSERT(n->fb->refill);
205 fbmulti_close(struct fastbuf *f)
207 CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
211 mp_delete(FB_MULTI(f)->mp);
215 fbmulti_create(uns bufsize, ...)
217 struct mempool *mp = mp_new(bufsize);
218 struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
219 FB_MULTI(fb_out)->mp = mp;
221 struct fastbuf *fb_in;
222 clist *subbufs = mp_alloc(mp, sizeof(clist));
224 FB_MULTI(fb_out)->subbufs = subbufs;
227 va_start(args, bufsize);
228 while (fb_in = va_arg(args, struct fastbuf *))
229 fbmulti_append(fb_out, fb_in, 1);
233 fbmulti_update_capability(fb_out);
235 FB_MULTI(fb_out)->cur = clist_head(subbufs);
236 bsetpos(FB_MULTI(fb_out)->cur->fb, 0);
238 fbmulti_get_ptrs(fb_out);
240 // If seekable, get the length of each subbuf, the total length and boundaries
243 fbmulti_get_len(fb_out);
246 fb_out->name = FB_MULTI_NAME;
248 fb_out->close = fbmulti_close;
254 fbmulti_append(struct fastbuf *f, struct fastbuf *fb, int allow_close)
256 struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(struct subbuf));
258 sb->allow_close = allow_close;
259 clist_add_tail(FB_MULTI(f)->subbufs, &(sb->n));
260 fbmulti_update_capability(f);
264 fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
270 CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
273 // Move the pointers to another buffer if this one was the active.
274 if (FB_MULTI(f)->cur == n)
277 if (!fbmulti_subbuf_next(f))
279 struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
283 FB_MULTI(f)->cur = prev;
289 pos -= (n->end - n->begin);
291 clist_remove(&(n->n));
292 fbmulti_update_capability(f);
298 die("Given fastbuf %p not in given fbmulti %p.", fb, f);
301 clist_init(FB_MULTI(f)->subbufs);
304 // The fbmulti is empty now, do some cleanup
305 fbmulti_update_capability(f);
307 f->buffer = f->bufend = f->bptr = f->bstop = NULL;
311 static void fbmulti_flatten_internal(struct fastbuf *f, clist *c, int allow_close)
313 CLIST_FOR_EACH(struct subbuf *, n, *c)
315 if (strcmp(n->fb->name, FB_MULTI_NAME))
316 fbmulti_append(f, n->fb, n->allow_close && allow_close);
320 fbmulti_flatten_internal(f, FB_MULTI(n->fb)->subbufs, allow_close && n->allow_close);
321 if (allow_close && n->allow_close)
323 FB_MULTI(n->fb)->subbufs = mp_alloc(FB_MULTI(n->fb)->mp, sizeof(clist));
324 clist_init(FB_MULTI(n->fb)->subbufs);
332 fbmulti_flatten(struct fastbuf *f)
334 if (strcmp(f->name, FB_MULTI_NAME))
336 DBG("fbmulti: given fastbuf isn't fbmulti");
340 clist *c = FB_MULTI(f)->subbufs;
341 FB_MULTI(f)->subbufs = mp_alloc(FB_MULTI(f)->mp, sizeof(clist));
342 clist_init(FB_MULTI(f)->subbufs);
344 fbmulti_flatten_internal(f, c, 1);
345 FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
346 f->bptr = f->bstop = f->buffer;
352 int main(int argc, char **argv)
356 fprintf(stderr, "You must specify a test (r, w, o)\n");
363 char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
364 struct fastbuf fb[ARRAY_SIZE(data)];
365 for (uns i=0;i<ARRAY_SIZE(data);i++)
366 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
368 struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
371 while (bgets(f, buffer, 9))
379 char *data[] = { "Mnl", "ige" };
380 struct fastbuf fb[ARRAY_SIZE(data)];
381 for (uns i=0;i<ARRAY_SIZE(data);i++)
382 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
384 struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], NULL);
386 int pos[] = {0, 3, 1, 4, 2, 5};
388 for (uns i=0;i<ARRAY_SIZE(pos);i++)
399 char *data = "Insae";
400 struct fastbuf fb[4];
401 fbbuf_init_read(&fb[0], data, 1, 0);
402 fbbuf_init_read(&fb[1], data + 1, 1, 0);
403 fbbuf_init_read(&fb[2], data + 2, 2, 0);
404 fbbuf_init_read(&fb[3], data + 4, 1, 0);
406 struct fastbuf *f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
409 while(bgets(f, buffer, 9))
418 char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
419 struct fastbuf fb[ARRAY_SIZE(data)];
420 for (uns i=0;i<ARRAY_SIZE(data);i++)
421 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
424 fbbuf_init_read(&sp, " ", 1, 0);
427 fbbuf_init_read(&nl, "\n", 1, 0);
429 struct fastbuf *f = fbmulti_create(4,
454 while (bgets(f, buffer, 20))