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