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