*
* This backend is seekable iff all of the supplied fastbufs are seekable.
*
- * Please note that no cleanup of underlying fastbufs is provided.
- *
* Also, please be aware of direct operations on the underlying buffers. The
- * fbmulti backend doesn't expect it.
+ * fbmulti backend doesn't expect you doing something directly on them.
+ *
+ * You may init a fbmulti by @fbmulti_create(bufsize, fb1, fb2, ..., NULL).
+ * This call returns a fastbuf that concatenates all the given fastbufs.
+ * The last parameter of @fbmulti_create must be NULL.
+ *
+ * By default, if @bclose() is called on fbmulti, all the underlying buffers
+ * get closed recursively.
+ *
+ * You may init a fbmulti by @fbmulti_create(bufsize) with no underlying buffers
+ * and then append the underlying buffers one by one. If allow_close is set to 0,
+ * the fastbuf doesn't get closed at @bclose() and you have to do the cleanup on
+ * yourself.
+ *
+ * If used in some formatter, you'll probably have a large and deep structure
+ * of nested fastbufs. Just before reading from the fbmulti, you may call
+ * @fbmulti_flatten() to flatten the structure. After @fbmulti_flatten(), the
+ * fbmulti is seeked to the beginning, flushed and ready to read the whole buffer.
*
- * The last parameter must be NULL.
+ * For performance reasons, use @fbmulti_flatten() only once, just before reading.
***/
struct fastbuf* fbmulti_create(uns bufsize, ...) SENTINEL_CHECK;
+void fbmulti_append(struct fastbuf *f, struct fastbuf *fa, int allow_close);
+void fbmulti_flatten(struct fastbuf *f);
/*** === Configuring stream parameters [[bconfig]] ***/
#include <stdio.h>
+#define FB_MULTI_NAME "<multi>"
+
struct fb_multi {
struct fastbuf fb;
struct mempool* mp;
struct subbuf {
cnode n;
ucw_off_t begin, end;
+ int allow_close;
struct fastbuf* fb;
};
#define SUBBUF(f) ((struct subbuf *)(f))
static void
fbmulti_close(struct fastbuf *f) {
CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
- bclose(n->fb);
+ if (n->allow_close)
+ bclose(n->fb);
mp_delete(FB_MULTI(f)->mp);
}
va_list args;
va_start(args, bufsize);
while (fb_in = va_arg(args, struct fastbuf *)) {
- struct subbuf *sb = mp_alloc(mp, sizeof(struct subbuf));
- sb->fb = fb_in;
- clist_add_tail(subbufs, &(sb->n));
+ fbmulti_append(fb_out, fb_in, 1);
}
va_end(args);
fb_out->buffer = mp_alloc(mp, bufsize);
fb_out->bptr = fb_out->bstop = fb_out->buffer;
fb_out->bufend = fb_out->buffer + bufsize;
- fb_out->name = "<multi>";
+ fb_out->name = FB_MULTI_NAME;
fbmulti_update_capability(fb_out);
fb_out->close = fbmulti_close;
return fb_out;
}
+void
+fbmulti_append(struct fastbuf *f, struct fastbuf *fb, int allow_close) {
+ struct subbuf *sb = mp_alloc(FB_MULTI(f)->mp, sizeof(struct subbuf));
+ sb->fb = fb;
+ sb->allow_close = allow_close;
+ clist_add_tail(FB_MULTI(f)->subbufs, &(sb->n));
+}
+
+static void fbmulti_flatten_internal(struct fastbuf *f, clist* c, int allow_close) {
+ CLIST_FOR_EACH(struct subbuf *, n, *c) {
+ if (strcmp(n->fb->name, FB_MULTI_NAME))
+ fbmulti_append(f, n->fb, n->allow_close && allow_close);
+ else {
+ fbmulti_flatten_internal(f, FB_MULTI(n->fb)->subbufs, allow_close && n->allow_close);
+ if (allow_close && n->allow_close) {
+ FB_MULTI(n->fb)->subbufs = mp_alloc(FB_MULTI(n->fb)->mp, sizeof(clist));
+ clist_init(FB_MULTI(n->fb)->subbufs);
+ bclose(n->fb);
+ }
+ }
+ }
+}
+
+void
+fbmulti_flatten(struct fastbuf *f) {
+ if (strcmp(f->name, FB_MULTI_NAME)) {
+ DBG("fbmulti: given fastbuf isn't fbmulti");
+ return;
+ }
+
+ clist* c = FB_MULTI(f)->subbufs;
+ FB_MULTI(f)->subbufs = mp_alloc(FB_MULTI(f)->mp, sizeof(clist));
+ clist_init(FB_MULTI(f)->subbufs);
+
+ fbmulti_flatten_internal(f, c, 1);
+ FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
+ f->bptr = f->bstop = f->buffer;
+ f->pos = 0;
+}
+
#ifdef TEST
int main(int argc, char ** argv)
bclose(f);
break;
}
+ case 'f':
case 'n':
{
char *data[] = { "Nested", "Data", "As", "In", "Real", "Usage", };
&nl,
NULL);
+ if (*argv[1] == 'f')
+ fbmulti_flatten(f);
+
char buffer[20];
while (bgets(f, buffer, 20))
puts(buffer);