]> mj.ucw.cz Git - libucw.git/blob - lib/fb-file.c
Merge with git+ssh://git.ucw.cz/projects/sherlock/GIT/sherlock.git
[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 = back + diff - F->wlen;
106       /* Reuse part of previous window */
107       if (F->wlen && read_len < blen)
108         {
109           uns keep = MIN(F->wlen, blen - read_len);
110           memmove(f->buffer + read_len, f->buffer, keep);
111           f->bstop = f->buffer + read_len + keep;
112         }
113       else
114         f->bstop = f->buffer + (read_len = blen);
115 seek:
116       /* Do lseek() */
117       F->wpos = f->pos + (f->buffer - f->bptr);
118       if (sh_seek(F->fd, F->wpos, SEEK_SET) < 0)
119         die("Error seeking %s: %m", f->name);
120     }
121   /* Read (part of) buffer */
122   do
123     {
124       int l = read(F->fd, read_ptr, read_len);
125       if (unlikely(l < 0))
126         die("Error reading %s: %m", f->name);
127       if (!l)
128         if (unlikely(read_ptr < f->bptr))
129           goto eof;
130         else
131           break; /* Incomplete read because of EOF */
132       read_ptr += l;
133       read_len -= l;
134       F->wpos += l;
135     }
136   while (read_ptr <= f->bptr);
137   if (read_len)
138     f->bstop = read_ptr;
139   f->pos += f->bstop - f->bptr;
140   F->wlen = f->bstop - f->buffer;
141   return f->bstop - f->bptr;
142 eof:
143   /* Seeked behind EOF */
144   f->bptr = f->bstop = f->buffer;
145   F->wlen = 0;
146   return 0;
147 }
148
149 static void
150 bfd_spout(struct fastbuf *f)
151 {
152   /* Do delayed lseek() if needed */
153   if (FB_FILE(f)->wpos != f->pos && sh_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
154     die("Error seeking %s: %m", f->name);
155
156   int l = f->bptr - f->buffer;
157   byte *c = f->buffer;
158
159   /* Write the buffer */
160   FB_FILE(f)->wpos = (f->pos += l);
161   FB_FILE(f)->wlen = 0;
162   while (l)
163     {
164       int z = write(FB_FILE(f)->fd, c, l);
165       if (z <= 0)
166         die("Error writing %s: %m", f->name);
167       l -= z;
168       c += z;
169     }
170   f->bptr = f->buffer = FB_BUFFER(f);
171 }
172
173 static int
174 bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
175 {
176   /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */
177   sh_off_t l;
178   switch (whence)
179     {
180       case SEEK_SET:
181         f->pos = pos;
182         return 1;
183       case SEEK_CUR:
184         l = f->pos + pos;
185         if ((pos > 0) ^ (l > f->pos))
186           return 0;
187         f->pos = l;
188         return 1;
189       case SEEK_END:
190         l = sh_seek(FB_FILE(f)->fd, pos, SEEK_END);
191         if (l < 0)
192           return 0;
193         FB_FILE(f)->wpos = f->pos = l;
194         FB_FILE(f)->wlen = 0;
195         return 1;
196       default:
197         ASSERT(0);
198     }
199 }
200
201 static void
202 bfd_close(struct fastbuf *f)
203 {
204   switch (FB_FILE(f)->is_temp_file)
205     {
206     case 1:
207       if (unlink(f->name) < 0)
208         log(L_ERROR, "unlink(%s): %m", f->name);
209     case 0:
210       if (close(FB_FILE(f)->fd))
211         die("close(%s): %m", f->name);
212     }
213   xfree(f);
214 }
215
216 static int
217 bfd_config(struct fastbuf *f, uns item, int value)
218 {
219   switch (item)
220     {
221       case BCONFIG_IS_TEMP_FILE:
222         FB_FILE(f)->is_temp_file = value;
223         return 0;
224       case BCONFIG_KEEP_BACK_BUF:
225         FB_FILE(f)->keep_back_buf = value;
226         return 0;
227       default:
228         return -1;
229     }
230 }
231
232 struct fastbuf *
233 bfdopen_internal(int fd, byte *name, uns buflen)
234 {
235   ASSERT(buflen);
236   int namelen = strlen(name) + 1;
237   struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
238   struct fastbuf *f = &F->fb;
239
240   bzero(F, sizeof(*F));
241   f->buffer = (byte *)(F+1);
242   f->bptr = f->bstop = f->buffer;
243   f->bufend = f->buffer + buflen;
244   f->name = f->bufend;
245   memcpy(f->name, name, namelen);
246   F->fd = fd;
247   f->refill = bfd_refill;
248   f->spout = bfd_spout;
249   f->seek = bfd_seek;
250   f->close = bfd_close;
251   f->config = bfd_config;
252   f->can_overwrite_buffer = 2;
253   return f;
254 }
255
256 struct fastbuf *
257 bopen_try(byte *name, uns mode, uns buflen)
258 {
259   return bopen_file_try(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
260 }
261
262 struct fastbuf *
263 bopen(byte *name, uns mode, uns buflen)
264 {
265   return bopen_file(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
266 }
267
268 struct fastbuf *
269 bfdopen(int fd, uns buflen)
270 {
271   return bopen_fd(fd, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
272 }
273
274 struct fastbuf *
275 bfdopen_shared(int fd, uns buflen)
276 {
277   struct fastbuf *f = bfdopen(fd, buflen);
278   FB_FILE(f)->is_temp_file = -1;
279   return f;
280 }
281
282 void
283 bfilesync(struct fastbuf *b)
284 {
285   bflush(b);
286   if (fsync(FB_FILE(b)->fd) < 0)
287     log(L_ERROR, "fsync(%s) failed: %m", b->name);
288 }
289
290 #ifdef TEST
291
292 int main(void)
293 {
294   struct fastbuf *f, *t;
295   f = bopen_tmp(16);
296   t = bfdopen(1, 13);
297   for (uns i = 0; i < 16; i++)
298     bwrite(f, "<hello>", 7);
299   bprintf(t, "%d\n", (int)btell(f));
300   brewind(f);
301   bbcopy(f, t, ~0U);
302   bprintf(t, "\n%d %d\n", (int)btell(f), (int)btell(t));
303   bclose(f);
304   bclose(t);
305   return 0;
306 }
307
308 #endif