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;
36 fbmulti_subbuf_get_end(struct subbuf *s)
39 bseek(s->fb, 0, SEEK_END);
40 s->end = s->begin + btell(s->fb);
44 fbmulti_get_ptrs(struct fastbuf *f)
46 struct subbuf *sb = FB_MULTI(f)->cur;
47 struct fastbuf *ff = sb->fb;
49 f->buffer = ff->buffer;
52 f->bufend = ff->bufend;
53 f->pos = sb->begin + (ff->pos - sb->offset);
57 fbmulti_set_ptrs(struct fastbuf *f)
59 FB_MULTI(f)->cur->fb->bptr = f->bptr;
63 fbmulti_subbuf_next(struct fastbuf *f)
65 struct subbuf *next = clist_next(&FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
69 // Check the end of current buf
72 FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, FB_MULTI(f)->cur->end - FB_MULTI(f)->cur->begin, SEEK_SET);
74 ASSERT(FB_MULTI(f)->cur->end == f->pos);
77 FB_MULTI(f)->cur->end = f->pos;
79 // Set the beginning of the next buf
88 next->offset = btell(next->fb);
91 next->begin = FB_MULTI(f)->cur->end;
94 FB_MULTI(f)->cur = next;
101 fbmulti_subbuf_prev(struct fastbuf *f)
103 // Called only when seeking, assuming everything seekable
104 struct subbuf *prev = clist_prev(&FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
105 ASSERT(prev != NULL);
107 // Set pos to beginning, flush offset
108 bsetpos(prev->fb, 0);
112 FB_MULTI(f)->cur = prev;
119 fbmulti_refill(struct fastbuf *f)
123 uns len = FB_MULTI(f)->cur->fb->refill(FB_MULTI(f)->cur->fb);
130 // Current buf returned EOF
131 // Update the information on end of this buffer
132 fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
134 // Take the next one if exists and redo
135 if (fbmulti_subbuf_next(f))
136 return fbmulti_refill(f);
142 fbmulti_get_len(struct fastbuf *f)
144 ucw_off_t pos = btell(f);
146 FB_MULTI(f)->len = 0;
148 CLIST_FOR_EACH(struct subbuf *, n, FB_MULTI(f)->subbufs)
150 n->begin = FB_MULTI(f)->len;
151 fbmulti_subbuf_get_end(n);
152 FB_MULTI(f)->len = n->end;
154 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.
158 fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
164 if (pos > FB_MULTI(f)->len)
165 bthrow(f, "seek", "Seek out of range");
167 while (pos > FB_MULTI(f)->cur->end) // Moving forward
168 ASSERT(fbmulti_subbuf_next(f));
170 while (pos < FB_MULTI(f)->cur->begin) // Moving backwards
171 ASSERT(fbmulti_subbuf_prev(f));
173 // Now cur is the right buffer.
174 FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
181 return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_SET);
190 fbmulti_close(struct fastbuf *f)
192 CLIST_FOR_EACH(struct subbuf *, n, FB_MULTI(f)->subbufs)
195 mp_delete(FB_MULTI(f)->mp);
201 struct mempool *mp = mp_new(bufsize);
202 struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
203 struct fbmulti *fbm = FB_MULTI(fb_out);
206 clist_init(&fbm->subbufs);
209 va_start(args, bufsize);
210 while (fb_in = va_arg(args, struct fastbuf *))
211 fbmulti_append(fb_out, fb_in);
215 fbmulti_update_capability(fb_out);
217 fbm->cur = clist_head(&fbm->subbufs);
218 bsetpos(fbm->cur->fb, 0);
220 fbmulti_get_ptrs(fb_out);
222 // If seekable, get the length of each subbuf, the total length and boundaries
225 fbmulti_get_len(fb_out);
228 fb_out->name = FB_MULTI_NAME;
229 f->refill = fbmulti_refill;
230 f->seek = fbmulti_seek;
232 fb_out->close = fbmulti_close;
238 fbmulti_append(struct fastbuf *f, struct fastbuf *fb)
244 struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(struct subbuf));
246 clist_add_tail(&FB_MULTI(f)->subbufs, &(sb->n));
250 fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
256 CLIST_FOR_EACH(struct subbuf *, n, FB_MULTI(f)->subbufs)
259 // Move the pointers to another buffer if this one was the active.
260 if (FB_MULTI(f)->cur == n)
263 if (!fbmulti_subbuf_next(f))
265 struct subbuf *prev = clist_prev(&FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
269 FB_MULTI(f)->cur = prev;
275 pos -= (n->end - n->begin);
277 clist_remove(&(n->n));
278 fbmulti_update_capability(f);
284 die("Given fastbuf %p not in given fbmulti %p.", fb, f);
287 clist_init(&FB_MULTI(f)->subbufs);
290 // The fbmulti is empty now, do some cleanup
291 fbmulti_update_capability(f);
293 f->buffer = f->bufend = f->bptr = f->bstop = NULL;
299 int main(int argc, char **argv)
303 fprintf(stderr, "You must specify a test (r, w, o)\n");
310 char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
311 struct fastbuf fb[ARRAY_SIZE(data)];
312 for (uns i=0;i<ARRAY_SIZE(data);i++)
313 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
315 struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
318 while (bgets(f, buffer, 9))
326 char *data[] = { "Mnl", "ige" };
327 struct fastbuf fb[ARRAY_SIZE(data)];
328 for (uns i=0;i<ARRAY_SIZE(data);i++)
329 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
331 struct fastbuf *f = fbmulti_create(4, &fb[0], &fb[1], NULL);
333 int pos[] = {0, 3, 1, 4, 2, 5};
335 for (uns i=0;i<ARRAY_SIZE(pos);i++)
346 char *data = "Insae";
347 struct fastbuf fb[4];
348 fbbuf_init_read(&fb[0], data, 1, 0);
349 fbbuf_init_read(&fb[1], data + 1, 1, 0);
350 fbbuf_init_read(&fb[2], data + 2, 2, 0);
351 fbbuf_init_read(&fb[3], data + 4, 1, 0);
353 struct fastbuf *f = fbmulti_create(8, &fb[0], &fb[1], &fb[2], &fb[1], &fb[3], NULL);
356 while(bgets(f, buffer, 9))
364 char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
365 struct fastbuf fb[ARRAY_SIZE(data)];
366 for (uns i=0;i<ARRAY_SIZE(data);i++)
367 fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
370 fbbuf_init_read(&sp, " ", 1, 0);
373 fbbuf_init_read(&nl, "\n", 1, 0);
375 struct fastbuf *f = fbmulti_create(4,
397 while (bgets(f, buffer, 20))