]> mj.ucw.cz Git - libucw.git/blob - lib/fb-mem.c
A better implementation of overwritable buffers.
[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 };
34 #define FB_MEM(f) ((struct fb_mem *)(f)->is_fastbuf)
35
36 static int
37 fbmem_refill(struct fastbuf *f)
38 {
39   struct memstream *s = FB_MEM(f)->stream;
40   struct msblock *b = FB_MEM(f)->block;
41
42   if (!b)
43     {
44       b = s->first;
45       if (!b)
46         return 0;
47     }
48   else if (f->buffer == b->data && f->bstop < b->data + b->size)
49     {
50       f->bstop = b->data + b->size;
51       f->pos = b->pos + b->size;
52       return 1;
53     }
54   else if (!b->next)
55     return 0;
56   else
57     b = b->next;
58   if (!b->size)
59     return 0;
60   f->buffer = f->bptr = b->data;
61   f->bufend = f->bstop = b->data + b->size;
62   f->pos = b->pos + b->size;
63   FB_MEM(f)->block = b;
64   return 1;
65 }
66
67 static void
68 fbmem_spout(struct fastbuf *f)
69 {
70   struct memstream *s = FB_MEM(f)->stream;
71   struct msblock *b = FB_MEM(f)->block;
72   struct msblock *bb;
73
74   if (b)
75     {
76       b->size = f->bptr - b->data;
77       if (b->size < s->blocksize)
78         return;
79     }
80   bb = xmalloc(sizeof(struct msblock) + s->blocksize);
81   if (b)
82     {
83       b->next = bb;
84       bb->pos = b->pos + b->size;
85     }
86   else
87     {
88       s->first = bb;
89       bb->pos = 0;
90     }
91   bb->next = NULL;
92   bb->size = 0;
93   f->buffer = f->bptr = f->bstop = bb->data;
94   f->bufend = bb->data + s->blocksize;
95   f->pos = bb->pos;
96   FB_MEM(f)->block = bb;
97 }
98
99 static void
100 fbmem_seek(struct fastbuf *f, sh_off_t pos, int whence)
101 {
102   struct memstream *m = FB_MEM(f)->stream;
103   struct msblock *b;
104
105   ASSERT(whence == SEEK_SET || whence == SEEK_END);
106   if (whence == SEEK_END)
107     {
108       for (b=m->first; b; b=b->next)
109         pos += b->size;
110     }
111   /* Yes, this is linear. But considering the average number of buckets, it doesn't matter. */
112   for (b=m->first; b; b=b->next)
113     {
114       if (pos <= b->pos + (sh_off_t)b->size) /* <=, because we need to be able to seek just after file end */
115         {
116           f->buffer = b->data;
117           f->bptr = b->data + (pos - b->pos);
118           f->bufend = f->bstop = b->data + b->size;
119           f->pos = b->pos + b->size;
120           FB_MEM(f)->block = b;
121           return;
122         }
123     }
124   if (!m->first && !pos)
125     {
126       /* Seeking to offset 0 in an empty file needs an exception */
127       f->buffer = f->bptr = f->bufend = NULL;
128       f->pos = 0;
129       FB_MEM(f)->block = NULL;
130       return;
131     }
132   die("fbmem_seek to invalid offset");
133 }
134
135 static void
136 fbmem_close(struct fastbuf *f)
137 {
138   struct memstream *m = FB_MEM(f)->stream;
139   struct msblock *b;
140
141   if (!--m->uc)
142     {
143       while (b = m->first)
144         {
145           m->first = b->next;
146           xfree(b);
147         }
148       xfree(m);
149     }
150   xfree(f);
151 }
152
153 struct fastbuf *
154 fbmem_create(unsigned blocksize)
155 {
156   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
157   struct memstream *s = xmalloc_zero(sizeof(struct memstream));
158
159   s->blocksize = blocksize;
160   s->uc = 1;
161
162   FB_MEM(f)->stream = s;
163   f->name = "<fbmem-write>";
164   f->spout = fbmem_spout;
165   f->close = fbmem_close;
166   return f;
167 }
168
169 struct fastbuf *
170 fbmem_clone_read(struct fastbuf *b)
171 {
172   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
173   struct memstream *s = FB_MEM(b)->stream;
174
175   bflush(b);
176   s->uc++;
177
178   FB_MEM(f)->stream = s;
179   f->name = "<fbmem-read>";
180   f->refill = fbmem_refill;
181   f->seek = fbmem_seek;
182   f->close = fbmem_close;
183   f->can_overwrite_buffer = 1;
184   return f;
185 }
186
187 #ifdef TEST
188
189 int main(void)
190 {
191   struct fastbuf *w, *r;
192   int t;
193
194   w = fbmem_create(7);
195   r = fbmem_clone_read(w);
196   bwrite(w, "12345", 5);
197   bwrite(w, "12345", 5);
198   printf("<%d>", (int)btell(w));
199   bflush(w);
200   printf("<%d>", (int)btell(w));
201   printf("<%d>", (int)btell(r));
202   while ((t = bgetc(r)) >= 0)
203     putchar(t);
204   printf("<%d>", (int)btell(r));
205   bwrite(w, "12345", 5);
206   bwrite(w, "12345", 5);
207   printf("<%d>", (int)btell(w));
208   bclose(w);
209   bsetpos(r, 0);
210   printf("<!%d>", (int)btell(r));
211   while ((t = bgetc(r)) >= 0)
212     putchar(t);
213   bsetpos(r, 3);
214   printf("<!%d>", (int)btell(r));
215   while ((t = bgetc(r)) >= 0)
216     putchar(t);
217   fflush(stdout);
218   bclose(r);
219   return 0;
220 }
221
222 #endif