]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-mem.c
Automatic tying of fastbufs to resources.
[libucw.git] / ucw / 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 "ucw/lib.h"
11 #include "ucw/fastbuf.h"
12
13 #include <stdio.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   ucw_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 int
99 fbmem_seek(struct fastbuf *f, ucw_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 + (ucw_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 1;
121         }
122     }
123   if (!m->first && !pos)
124     {
125       /* Seeking to offset 0 in an empty file needs an exception */
126       f->buffer = f->bptr = f->bufend = NULL;
127       f->pos = 0;
128       FB_MEM(f)->block = NULL;
129       return 1;
130     }
131   die("fbmem_seek to invalid offset");
132 }
133
134 static void
135 fbmem_close(struct fastbuf *f)
136 {
137   struct memstream *m = FB_MEM(f)->stream;
138   struct msblock *b;
139
140   if (!--m->uc)
141     {
142       while (b = m->first)
143         {
144           m->first = b->next;
145           xfree(b);
146         }
147       xfree(m);
148     }
149   xfree(f);
150 }
151
152 struct fastbuf *
153 fbmem_create(unsigned blocksize)
154 {
155   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
156   struct memstream *s = xmalloc_zero(sizeof(struct memstream));
157
158   s->blocksize = blocksize;
159   s->uc = 1;
160
161   FB_MEM(f)->stream = s;
162   f->name = "<fbmem-write>";
163   f->spout = fbmem_spout;
164   f->close = fbmem_close;
165   fb_tie(f);
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   fb_tie(f);
185   return f;
186 }
187
188 #ifdef TEST
189
190 int main(void)
191 {
192   struct fastbuf *w, *r;
193   int t;
194
195   w = fbmem_create(7);
196   r = fbmem_clone_read(w);
197   bwrite(w, "12345", 5);
198   bwrite(w, "12345", 5);
199   printf("<%d>", (int)btell(w));
200   bflush(w);
201   printf("<%d>", (int)btell(w));
202   printf("<%d>", (int)btell(r));
203   while ((t = bgetc(r)) >= 0)
204     putchar(t);
205   printf("<%d>", (int)btell(r));
206   bwrite(w, "12345", 5);
207   bwrite(w, "12345", 5);
208   printf("<%d>", (int)btell(w));
209   bclose(w);
210   bsetpos(r, 0);
211   printf("<!%d>", (int)btell(r));
212   while ((t = bgetc(r)) >= 0)
213     putchar(t);
214   bsetpos(r, 3);
215   printf("<!%d>", (int)btell(r));
216   while ((t = bgetc(r)) >= 0)
217     putchar(t);
218   putchar('\n');
219   fflush(stdout);
220   bclose(r);
221   return 0;
222 }
223
224 #endif