]> mj.ucw.cz Git - libucw.git/blob - lib/fb-mem.c
82fe251e9ec28413e155e7149c85bccac78873fd
[libucw.git] / lib / fb-mem.c
1 /*
2  *      Sherlock Library -- Fast Buffered I/O on Memory Streams
3  *
4  *      (c) 1997--2002 Martin Mares <mj@ucw.cz>
5  *      (c) 2004 Robert Spalek <robert@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "lib/lib.h"
12 #include "lib/fastbuf.h"
13
14 #include <stdlib.h>
15
16 struct memstream {
17   unsigned blocksize;
18   unsigned uc;
19   struct msblock *first;
20 };
21
22 struct msblock {
23   struct msblock *next;
24   sh_off_t pos;
25   unsigned size;
26   byte data[0];
27 };
28
29 struct fb_mem {
30   struct fastbuf fb;
31   struct memstream *stream;
32   struct msblock *block;
33   int can_overwrite;
34 };
35 #define FB_MEM(f) ((struct fb_mem *)(f)->is_fastbuf)
36
37 static int
38 fbmem_refill(struct fastbuf *f)
39 {
40   struct memstream *s = FB_MEM(f)->stream;
41   struct msblock *b = FB_MEM(f)->block;
42
43   if (!b)
44     {
45       b = s->first;
46       if (!b)
47         return 0;
48     }
49   else if (f->buffer == b->data && f->bstop < b->data + b->size)
50     {
51       f->bstop = b->data + b->size;
52       f->pos = b->pos + b->size;
53       return 1;
54     }
55   else if (!b->next)
56     return 0;
57   else
58     b = b->next;
59   if (!b->size)
60     return 0;
61   f->buffer = f->bptr = b->data;
62   f->bufend = f->bstop = b->data + b->size;
63   f->pos = b->pos + b->size;
64   FB_MEM(f)->block = b;
65   return 1;
66 }
67
68 static void
69 fbmem_spout(struct fastbuf *f)
70 {
71   struct memstream *s = FB_MEM(f)->stream;
72   struct msblock *b = FB_MEM(f)->block;
73   struct msblock *bb;
74
75   if (b)
76     {
77       b->size = f->bptr - b->data;
78       if (b->size < s->blocksize)
79         return;
80     }
81   bb = xmalloc(sizeof(struct msblock) + s->blocksize);
82   if (b)
83     {
84       b->next = bb;
85       bb->pos = b->pos + b->size;
86     }
87   else
88     {
89       s->first = bb;
90       bb->pos = 0;
91     }
92   bb->next = NULL;
93   bb->size = 0;
94   f->buffer = f->bptr = f->bstop = bb->data;
95   f->bufend = bb->data + s->blocksize;
96   f->pos = bb->pos;
97   FB_MEM(f)->block = bb;
98 }
99
100 static void
101 fbmem_seek(struct fastbuf *f, sh_off_t pos, int whence)
102 {
103   struct memstream *m = FB_MEM(f)->stream;
104   struct msblock *b;
105
106   ASSERT(whence == SEEK_SET || whence == SEEK_END);
107   if (whence == SEEK_END)
108     {
109       for (b=m->first; b; b=b->next)
110         pos += b->size;
111     }
112   /* Yes, this is linear. But considering the average number of buckets, it doesn't matter. */
113   for (b=m->first; b; b=b->next)
114     {
115       if (pos <= b->pos + (sh_off_t)b->size) /* <=, because we need to be able to seek just after file end */
116         {
117           f->buffer = b->data;
118           f->bptr = b->data + (pos - b->pos);
119           f->bufend = f->bstop = b->data + b->size;
120           f->pos = b->pos + b->size;
121           FB_MEM(f)->block = b;
122           return;
123         }
124     }
125   if (!m->first && !pos)
126     {
127       /* Seeking to offset 0 in an empty file needs an exception */
128       f->buffer = f->bptr = f->bufend = NULL;
129       f->pos = 0;
130       FB_MEM(f)->block = NULL;
131       return;
132     }
133   die("fbmem_seek to invalid offset");
134 }
135
136 static void
137 fbmem_close(struct fastbuf *f)
138 {
139   struct memstream *m = FB_MEM(f)->stream;
140   struct msblock *b;
141
142   if (!--m->uc)
143     {
144       while (b = m->first)
145         {
146           m->first = b->next;
147           xfree(b);
148         }
149       xfree(m);
150     }
151   xfree(f);
152 }
153
154 static int
155 fbmem_config(struct fastbuf *f, uns item, int value)
156 {
157   switch (item)
158     {
159     case BCONFIG_CAN_OVERWRITE: ;
160       int old_value = FB_MEM(f)->can_overwrite;
161       if (value >= 0 && value <= 1)
162         FB_MEM(f)->can_overwrite = value;
163       return old_value;
164     default:
165       return -1;
166     }
167 }
168
169 struct fastbuf *
170 fbmem_create(unsigned blocksize)
171 {
172   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
173   struct memstream *s = xmalloc_zero(sizeof(struct memstream));
174
175   s->blocksize = blocksize;
176   s->uc = 1;
177
178   FB_MEM(f)->stream = s;
179   f->name = "<fbmem-write>";
180   f->spout = fbmem_spout;
181   f->close = fbmem_close;
182   f->config = fbmem_config;
183   return f;
184 }
185
186 struct fastbuf *
187 fbmem_clone_read(struct fastbuf *b)
188 {
189   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
190   struct memstream *s = FB_MEM(b)->stream;
191
192   bflush(b);
193   s->uc++;
194
195   FB_MEM(f)->stream = s;
196   f->name = "<fbmem-read>";
197   f->refill = fbmem_refill;
198   f->seek = fbmem_seek;
199   f->close = fbmem_close;
200   f->config = fbmem_config;
201   FB_MEM(f)->can_overwrite = 1;
202   return f;
203 }
204
205 #ifdef TEST
206
207 int main(void)
208 {
209   struct fastbuf *w, *r;
210   int t;
211
212   w = fbmem_create(7);
213   r = fbmem_clone_read(w);
214   bwrite(w, "12345", 5);
215   bwrite(w, "12345", 5);
216   printf("<%d>", (int)btell(w));
217   bflush(w);
218   printf("<%d>", (int)btell(w));
219   printf("<%d>", (int)btell(r));
220   while ((t = bgetc(r)) >= 0)
221     putchar(t);
222   printf("<%d>", (int)btell(r));
223   bwrite(w, "12345", 5);
224   bwrite(w, "12345", 5);
225   printf("<%d>", (int)btell(w));
226   bclose(w);
227   bsetpos(r, 0);
228   printf("<!%d>", (int)btell(r));
229   while ((t = bgetc(r)) >= 0)
230     putchar(t);
231   bsetpos(r, 3);
232   printf("<!%d>", (int)btell(r));
233   while ((t = bgetc(r)) >= 0)
234     putchar(t);
235   fflush(stdout);
236   bclose(r);
237   return 0;
238 }
239
240 #endif