]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-file.c
Doc: Updated the list of contributors
[libucw.git] / ucw / fb-file.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on Files
3  *
4  *      (c) 1997--2007 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 <ucw/lib.h>
12 #include <ucw/fastbuf.h>
13 #include <ucw/io.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;
24   int keep_back_buf;                    /* Optimize for backwards reading */
25   ucw_off_t wpos;                       /* Real file position */
26   uns wlen;                             /* Window size */
27 };
28 #define FB_FILE(f) ((struct fb_file *)(f))
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       ucw_off_t diff = f->pos - F->wpos;
41       /* Formula for long forward seeks (prefer lseek()) */
42       if (diff > ((ucw_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                   bthrow(f, "read", "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       ucw_off_t diff = F->wpos - f->pos;
86       /* Formula for long backwards seeks (keep smaller backbuffer than for shorter seeks ) */
87       if (diff > ((ucw_off_t)blen << 1))
88         {
89           if ((ucw_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 ((ucw_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 (ucw_seek(F->fd, F->wpos, SEEK_SET) < 0)
117         bthrow(f, "read", "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         bthrow(f, "read", "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 && ucw_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
152     bthrow(f, "write", "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         bthrow(f, "write", "Error writing %s: %m", f->name);
165       l -= z;
166       c += z;
167     }
168   f->bptr = f->bstop = f->buffer = FB_BUFFER(f);
169 }
170
171 static int
172 bfd_seek(struct fastbuf *f, ucw_off_t pos, int whence)
173 {
174   ASSERT(f->bptr == f->bstop);
175   /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */
176   switch (whence)
177     {
178       case SEEK_SET:
179         f->pos = pos;
180         return 1;
181       case SEEK_END: ;
182         ucw_off_t l = ucw_seek(FB_FILE(f)->fd, pos, SEEK_END);
183         if (l < 0)
184           bthrow(f, "seek", "Error seeking %s: %m", f->name);
185         FB_FILE(f)->wpos = f->pos = l;
186         FB_FILE(f)->wlen = 0;
187         return 1;
188       default:
189         ASSERT(0);
190     }
191 }
192
193 static void
194 bfd_close(struct fastbuf *f)
195 {
196   bclose_file_helper(f, FB_FILE(f)->fd, FB_FILE(f)->is_temp_file);
197   xfree(f);
198 }
199
200 static int
201 bfd_config(struct fastbuf *f, uns item, int value)
202 {
203   int orig;
204
205   switch (item)
206     {
207       case BCONFIG_IS_TEMP_FILE:
208         orig = FB_FILE(f)->is_temp_file;
209         FB_FILE(f)->is_temp_file = value;
210         return orig;
211       case BCONFIG_KEEP_BACK_BUF:
212         orig = FB_FILE(f)->keep_back_buf;
213         FB_FILE(f)->keep_back_buf = value;
214         return orig;
215       default:
216         return -1;
217     }
218 }
219
220 struct fastbuf *
221 bfdopen_internal(int fd, const char *name, uns buflen)
222 {
223   ASSERT(buflen);
224   int namelen = strlen(name) + 1;
225   struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
226   struct fastbuf *f = &F->fb;
227
228   bzero(F, sizeof(*F));
229   f->buffer = (byte *)(F+1);
230   f->bptr = f->bstop = f->buffer;
231   f->bufend = f->buffer + buflen;
232   f->name = f->bufend;
233   memcpy(f->name, name, namelen);
234   F->fd = fd;
235   f->refill = bfd_refill;
236   f->spout = bfd_spout;
237   f->seek = bfd_seek;
238   f->close = bfd_close;
239   f->config = bfd_config;
240   f->can_overwrite_buffer = 2;
241   return f;
242 }
243
244 void
245 bfilesync(struct fastbuf *b)
246 {
247   bflush(b);
248   if (fsync(FB_FILE(b)->fd) < 0)
249     msg(L_ERROR, "fsync(%s) failed: %m", b->name);
250 }
251
252 #ifdef TEST
253
254 int main(void)
255 {
256   struct fastbuf *f, *t;
257   f = bopen_tmp(16);
258   t = bfdopen_shared(1, 13);
259   for (uns i = 0; i < 16; i++)
260     bwrite(f, "<hello>", 7);
261   bprintf(t, "%d\n", (int)btell(f));
262   brewind(f);
263   bbcopy(f, t, ~0U);
264   bprintf(t, "\n%d %d\n", (int)btell(f), (int)btell(t));
265   bclose(f);
266   bclose(t);
267   return 0;
268 }
269
270 #endif