]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-mem.c
Macros: CLAMP now accepts arbitrary types, not only ints
[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   uint blocksize;
17   uint uc;
18   struct msblock *first;
19 };
20
21 struct msblock {
22   struct msblock *next;
23   ucw_off_t pos;
24   uint 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))
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 = f->bstop = b->data;
116           f->bptr = b->data + (pos - b->pos);
117           f->bufend = b->data + b->size;
118           f->pos = b->pos;
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->bstop = f->bufend = NULL;
127       f->pos = 0;
128       FB_MEM(f)->block = NULL;
129       return 1;
130     }
131   bthrow(f, "seek", "fbmem_seek to an 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(uint 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   return f;
166 }
167
168 struct fastbuf *
169 fbmem_clone_read(struct fastbuf *b)
170 {
171   struct fastbuf *f = xmalloc_zero(sizeof(struct fb_mem));
172   struct memstream *s = FB_MEM(b)->stream;
173
174   bflush(b);
175   s->uc++;
176
177   FB_MEM(f)->stream = s;
178   f->name = "<fbmem-read>";
179   f->refill = fbmem_refill;
180   f->seek = fbmem_seek;
181   f->close = fbmem_close;
182   f->can_overwrite_buffer = 1;
183   return f;
184 }
185
186 #ifdef TEST
187
188 int main(void)
189 {
190   struct fastbuf *w, *r;
191   int t;
192
193   w = fbmem_create(7);
194   r = fbmem_clone_read(w);
195   bwrite(w, "12345", 5);
196   bwrite(w, "12345", 5);
197   printf("<%d>", (int)btell(w));
198   bflush(w);
199   printf("<%d>", (int)btell(w));
200   printf("<%d>", (int)btell(r));
201   while ((t = bgetc(r)) >= 0)
202     putchar(t);
203   printf("<%d>", (int)btell(r));
204   bwrite(w, "12345", 5);
205   bwrite(w, "12345", 5);
206   printf("<%d>", (int)btell(w));
207   bclose(w);
208   bsetpos(r, 0);
209   printf("<!%d>", (int)btell(r));
210   while ((t = bgetc(r)) >= 0)
211     putchar(t);
212   bsetpos(r, 3);
213   printf("<!%d>", (int)btell(r));
214   while ((t = bgetc(r)) >= 0)
215     putchar(t);
216   putchar('\n');
217   fflush(stdout);
218   bclose(r);
219   return 0;
220 }
221
222 #endif