]> mj.ucw.cz Git - libucw.git/blob - lib/fb-file.c
Let bconfig() return the original value.
[libucw.git] / lib / fb-file.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on Files
3  *
4  *      (c) 1997--2004 Martin Mares <mj@ucw.cz>
5  *      (c) 2007 Pavel Charvat <pchar@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 #include "lib/lfs.h"
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 struct fb_file {
21   struct fastbuf fb;
22   int fd;                               /* File descriptor */
23   int is_temp_file;                     /* 0=normal file, 1=temporary file, delete on close, -1=shared FD */
24   int keep_back_buf;                    /* Optimize for backwards reading */
25   sh_off_t wpos;                        /* Real file position */
26   uns wlen;                             /* Window size */
27 };
28 #define FB_FILE(f) ((struct fb_file *)(f)->is_fastbuf)
29 #define FB_BUFFER(f) (byte *)(FB_FILE(f) + 1)
30
31 static int
32 bfd_refill(struct fastbuf *f)
33 {
34   struct fb_file *F = FB_FILE(f);
35   byte *read_ptr = (f->buffer = FB_BUFFER(f));
36   uns blen = f->bufend - f->buffer, back = F->keep_back_buf ? blen >> 2 : 0, read_len = blen;
37   /* Forward or no seek */
38   if (F->wpos <= f->pos)
39     {
40       sh_off_t diff = f->pos - F->wpos;
41       /* Formula for long forward seeks (prefer lseek()) */
42       if (diff > ((sh_off_t)blen << 2))
43         {
44 long_seek:
45           f->bptr = f->buffer + back;
46           f->bstop = f->buffer + blen;
47           goto seek;
48         }
49       /* Short forward seek (prefer read() to skip data )*/
50       else if ((uns)diff >= back)
51         {
52           uns skip = diff - back;
53           F->wpos += skip;
54           while (skip)
55             {
56               int l = read(F->fd, f->buffer, MIN(skip, blen));
57               if (unlikely(l <= 0))
58                 if (l < 0)
59                   die("Error reading %s: %m", f->name);
60                 else
61                   {
62                     F->wpos -= skip;
63                     goto eof;
64                   }
65               skip -= l;
66             }
67         }
68       /* Reuse part of the previous window and append new data (also F->wpos == f->pos) */
69       else
70         {
71           uns keep = back - (uns)diff;
72           if (keep >= F->wlen)
73             back = diff + (keep = F->wlen);
74           else
75             memmove(f->buffer, f->buffer + F->wlen - keep, keep);
76           read_len -= keep;
77           read_ptr += keep;
78         }
79       f->bptr = f->buffer + back;
80       f->bstop = f->buffer + blen;
81     }
82   /* Backwards seek */
83   else
84     {
85       sh_off_t diff = F->wpos - f->pos;
86       /* Formula for long backwards seeks (keep smaller backbuffer than for shorter seeks ) */
87       if (diff > ((sh_off_t)blen << 1))
88         {
89           if ((sh_off_t)back > f->pos)
90             back = f->pos;
91           goto long_seek;
92         }
93       /* Seek into previous window (do nothing... for example brewind) */
94       else if ((uns)diff <= F->wlen) 
95         {
96           f->bstop = f->buffer + F->wlen;
97           f->bptr = f->bstop - diff;
98           f->pos = F->wpos;
99           return 1;
100         }
101       back *= 3;
102       if ((sh_off_t)back > f->pos)
103         back = f->pos;
104       f->bptr = f->buffer + back;
105       read_len = blen;
106       f->bstop = f->buffer + read_len;
107       /* Reuse part of previous window */
108       if (F->wlen && read_len <= back + diff && read_len > back + diff - F->wlen)
109         {
110           uns keep = read_len + F->wlen - back - diff;
111           memmove(f->buffer + read_len - keep, f->buffer, keep);
112         }
113 seek:
114       /* Do lseek() */
115       F->wpos = f->pos + (f->buffer - f->bptr);
116       if (sh_seek(F->fd, F->wpos, SEEK_SET) < 0)
117         die("Error seeking %s: %m", f->name);
118     }
119   /* Read (part of) buffer */
120   do
121     {
122       int l = read(F->fd, read_ptr, read_len);
123       if (unlikely(l < 0))
124         die("Error reading %s: %m", f->name);
125       if (!l)
126         if (unlikely(read_ptr < f->bptr))
127           goto eof;
128         else
129           break; /* Incomplete read because of EOF */
130       read_ptr += l;
131       read_len -= l;
132       F->wpos += l;
133     }
134   while (read_ptr <= f->bptr);
135   if (read_len)
136     f->bstop = read_ptr;
137   f->pos += f->bstop - f->bptr;
138   F->wlen = f->bstop - f->buffer;
139   return f->bstop - f->bptr;
140 eof:
141   /* Seeked behind EOF */
142   f->bptr = f->bstop = f->buffer;
143   F->wlen = 0;
144   return 0;
145 }
146
147 static void
148 bfd_spout(struct fastbuf *f)
149 {
150   /* Do delayed lseek() if needed */
151   if (FB_FILE(f)->wpos != f->pos && sh_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
152     die("Error seeking %s: %m", f->name);
153
154   int l = f->bptr - f->buffer;
155   byte *c = f->buffer;
156
157   /* Write the buffer */
158   FB_FILE(f)->wpos = (f->pos += l);
159   FB_FILE(f)->wlen = 0;
160   while (l)
161     {
162       int z = write(FB_FILE(f)->fd, c, l);
163       if (z <= 0)
164         die("Error writing %s: %m", f->name);
165       l -= z;
166       c += z;
167     }
168   f->bptr = f->buffer = FB_BUFFER(f);
169 }
170
171 static int
172 bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
173 {
174   /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */
175   sh_off_t l;
176   switch (whence)
177     {
178       case SEEK_SET:
179         f->pos = pos;
180         return 1;
181       case SEEK_CUR:
182         l = f->pos + pos;
183         if ((pos > 0) ^ (l > f->pos))
184           return 0;
185         f->pos = l;
186         return 1;
187       case SEEK_END:
188         l = sh_seek(FB_FILE(f)->fd, pos, SEEK_END);
189         if (l < 0)
190           return 0;
191         FB_FILE(f)->wpos = f->pos = l;
192         FB_FILE(f)->wlen = 0;
193         return 1;
194       default:
195         ASSERT(0);
196     }
197 }
198
199 static void
200 bfd_close(struct fastbuf *f)
201 {
202   switch (FB_FILE(f)->is_temp_file)
203     {
204     case 1:
205       if (unlink(f->name) < 0)
206         msg(L_ERROR, "unlink(%s): %m", f->name);
207     case 0:
208       if (close(FB_FILE(f)->fd))
209         die("close(%s): %m", f->name);
210     }
211   xfree(f);
212 }
213
214 static int
215 bfd_config(struct fastbuf *f, uns item, int value)
216 {
217   int orig;
218
219   switch (item)
220     {
221       case BCONFIG_IS_TEMP_FILE:
222         orig = FB_FILE(f)->is_temp_file;
223         FB_FILE(f)->is_temp_file = value;
224         return orig;
225       case BCONFIG_KEEP_BACK_BUF:
226         orig = FB_FILE(f)->keep_back_buf;
227         FB_FILE(f)->keep_back_buf = value;
228         return orig;
229       default:
230         return -1;
231     }
232 }
233
234 struct fastbuf *
235 bfdopen_internal(int fd, const char *name, uns buflen)
236 {
237   ASSERT(buflen);
238   int namelen = strlen(name) + 1;
239   struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
240   struct fastbuf *f = &F->fb;
241
242   bzero(F, sizeof(*F));
243   f->buffer = (byte *)(F+1);
244   f->bptr = f->bstop = f->buffer;
245   f->bufend = f->buffer + buflen;
246   f->name = f->bufend;
247   memcpy(f->name, name, namelen);
248   F->fd = fd;
249   f->refill = bfd_refill;
250   f->spout = bfd_spout;
251   f->seek = bfd_seek;
252   f->close = bfd_close;
253   f->config = bfd_config;
254   f->can_overwrite_buffer = 2;
255   return f;
256 }
257
258 struct fastbuf *
259 bopen_try(const char *name, uns mode, uns buflen)
260 {
261   return bopen_file_try(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
262 }
263
264 struct fastbuf *
265 bopen(const char *name, uns mode, uns buflen)
266 {
267   return bopen_file(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
268 }
269
270 struct fastbuf *
271 bfdopen(int fd, uns buflen)
272 {
273   return bopen_fd(fd, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
274 }
275
276 struct fastbuf *
277 bfdopen_shared(int fd, uns buflen)
278 {
279   struct fastbuf *f = bfdopen(fd, buflen);
280   FB_FILE(f)->is_temp_file = -1;
281   return f;
282 }
283
284 void
285 bfilesync(struct fastbuf *b)
286 {
287   bflush(b);
288   if (fsync(FB_FILE(b)->fd) < 0)
289     msg(L_ERROR, "fsync(%s) failed: %m", b->name);
290 }
291
292 #ifdef TEST
293
294 int main(void)
295 {
296   struct fastbuf *f, *t;
297   f = bopen_tmp(16);
298   t = bfdopen(1, 13);
299   for (uns i = 0; i < 16; i++)
300     bwrite(f, "<hello>", 7);
301   bprintf(t, "%d\n", (int)btell(f));
302   brewind(f);
303   bbcopy(f, t, ~0U);
304   bprintf(t, "\n%d %d\n", (int)btell(f), (int)btell(t));
305   bclose(f);
306   bclose(t);
307   return 0;
308 }
309
310 #endif