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