]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-multi.c
Fastbuf: fbmulti backend
[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 struct fb_multi {
18   struct fastbuf fb;
19   struct mempool* mp;
20   struct subbuf* cur;
21   ucw_off_t len;
22   clist* subbufs;
23 };
24 #define FB_MULTI(f) ((struct fb_multi *)(f))
25
26 struct subbuf {
27   cnode n;
28   ucw_off_t begin, end;
29   struct fastbuf* fb;
30 };
31 #define SUBBUF(f) ((struct subbuf *)(f))
32
33 static inline void
34 fbmulti_subbuf_get_end(struct subbuf *s)
35 {
36   if (s->fb->seek) {
37     bseek(s->fb, 0, SEEK_END);
38     s->end = s->begin + btell(s->fb);
39   }
40 }
41
42 static inline int
43 fbmulti_subbuf_next(struct fastbuf *f)
44 {
45   struct subbuf* next = clist_next(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
46   if (next == NULL)
47     return 0;
48   
49   if (f->seek)
50     next->begin = FB_MULTI(f)->cur->end;
51   FB_MULTI(f)->cur = next;
52   return 1;
53 }
54
55 static int
56 fbmulti_refill(struct fastbuf *f)
57 {
58   if (f->bufend == f->bstop)
59     f->bptr = f->bstop = f->buffer;
60   uns len = bread(FB_MULTI(f)->cur->fb, f->bstop, (f->bufend - f->bstop));
61   f->bstop += len;
62   f->pos += len;
63   if (len)
64     return len;
65
66   // Current buf returned EOF
67   // Update the information on end of this buffer
68   fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
69
70   // Take the next one if exists
71   if (fbmulti_subbuf_next(f))
72     return fbmulti_refill(f);
73   else
74     return 0;
75 }
76
77 static void
78 fbmulti_get_len(struct fastbuf *f)
79 {
80   ASSERT (f->seek);
81   FB_MULTI(f)->len = 0;
82   
83   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
84     {
85       n->begin = FB_MULTI(f)->len;
86       fbmulti_subbuf_get_end(n);
87       FB_MULTI(f)->len = n->end;
88     }
89 }
90
91 static int
92 fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
93 {
94   switch(whence)
95     {
96     case SEEK_SET:
97       if (f->pos > pos) {
98         FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
99         FB_MULTI(f)->cur->begin = 0;
100         f->pos = 0;
101         return fbmulti_seek(f, pos, SEEK_SET);
102       }
103
104       do {
105         fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
106         if (pos < FB_MULTI(f)->cur->end)
107           break;
108
109         if (!fbmulti_subbuf_next(f))
110           bthrow(f, "seek", "Seek out of range");
111
112       } while (1);
113
114       bseek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
115       f->pos = pos;
116       f->bptr = f->bstop = f->buffer;
117       return 1;
118       break;
119
120     case SEEK_END:
121       fbmulti_get_len(f);
122       return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_CUR);
123       break;
124
125     default:
126       ASSERT(0);
127     }
128 }
129
130 static void
131 fbmulti_update_capability(struct fastbuf *f) {
132   // FB Multi is only a proxy to other fastbufs ... if any of them lacks
133   // support of any feature, FB Multi also provides no support of that feature
134   f->refill = fbmulti_refill;
135   f->seek = fbmulti_seek;
136
137   CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs)) {
138     if (!n->fb->refill)
139       f->refill = NULL;
140
141     if (!n->fb->seek)
142       f->seek = NULL;
143   }
144 }
145
146 struct fastbuf*
147 fbmulti_create(uns bufsize, ...)
148 {
149   struct mempool *mp = mp_new(bufsize);
150   struct fastbuf *fb_out = mp_alloc(mp, sizeof(struct fb_multi));
151   FB_MULTI(fb_out)->mp = mp;
152
153   struct fastbuf *fb_in;
154   clist* subbufs = mp_alloc(mp, sizeof(clist));
155   clist_init(subbufs);
156   FB_MULTI(fb_out)->subbufs = subbufs;
157
158   va_list args;
159   va_start(args, bufsize);
160   while (fb_in = va_arg(args, struct fastbuf *)) {
161     struct subbuf *sb = mp_alloc(mp, sizeof(struct subbuf));
162     sb->fb = fb_in;
163     clist_add_tail(subbufs, &(sb->n));
164   }
165   va_end(args);
166
167   FB_MULTI(fb_out)->cur = clist_head(subbufs);
168
169   fb_out->buffer = mp_alloc(mp, bufsize);
170   fb_out->bptr = fb_out->bstop = fb_out->buffer;
171   fb_out->bufend = fb_out->buffer + bufsize;
172   fb_out->name = "<multi>";
173
174   fbmulti_update_capability(fb_out);
175
176   return fb_out;
177 }
178
179 #ifdef TEST
180
181 int main(int argc, char ** argv)
182 {
183   if (argc < 2)
184     {
185       fprintf(stderr, "You must specify a test (r, w, o)\n");
186       return 1;
187     }
188   switch (*argv[1])
189     {
190       case 'r':
191         {
192           char *data[] = { "One\nLine", "Two\nLines", "Th\nreeLi\nnes\n" };
193           struct fastbuf fb[ARRAY_SIZE(data)];
194           for (uns i=0;i<ARRAY_SIZE(data);i++)
195             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
196
197           struct fastbuf* f = fbmulti_create(4, &fb[0], &fb[1], &fb[2], NULL);
198
199           char buffer[9];
200           while (bgets(f, buffer, 9))
201             puts(buffer);
202
203           bclose(f);
204           break;
205         }
206       case 'm':
207         {
208           char *data[] = { "Mnl", "ige" };
209           struct fastbuf fb[ARRAY_SIZE(data)];
210           for (uns i=0;i<ARRAY_SIZE(data);i++)
211             fbbuf_init_read(&fb[i], data[i], strlen(data[i]), 0);
212
213           struct fastbuf* f = fbmulti_create(4, &fb[0], &fb[1], NULL);
214
215           int pos[] = {0, 3, 1, 4, 2, 5};
216
217           for (uns i=0;i<ARRAY_SIZE(pos);i++) {
218             bseek(f, pos[i], SEEK_SET);
219             putchar(bgetc(f));
220           }
221
222           bclose(f);
223           break;
224         }
225     }
226   return 0;
227 }
228
229 #endif