2 * UCW Library -- Fast Buffered I/O on Files
4 * (c) 1997--2004 Martin Mares <mj@ucw.cz>
5 * (c) 2007 Pavel Charvat <pchar@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
12 #include "lib/fastbuf.h"
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 */
28 #define FB_FILE(f) ((struct fb_file *)(f)->is_fastbuf)
29 #define FB_BUFFER(f) (byte *)(FB_FILE(f) + 1)
32 bfd_refill(struct fastbuf *f)
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)
40 sh_off_t diff = f->pos - F->wpos;
41 if (diff > ((sh_off_t)blen << 2)) /* FIXME: Formula for long forward seeks */
44 f->bptr = f->buffer + back;
45 f->bstop = f->buffer + blen;
48 if ((uns)diff < back) /* Reuse part of previous window (also F->wpos == f->pos) */
50 uns keep = back - (uns)diff;
52 back = diff + (keep = F->wlen);
54 memmove(f->buffer, f->buffer + F->wlen - keep, keep);
58 else /* Short forward seek */
60 uns skip = diff - back;
64 int l = read(F->fd, f->buffer, MIN(skip, blen));
67 die("Error reading %s: %m", f->name);
76 f->bptr = f->buffer + back;
77 f->bstop = f->buffer + blen;
82 sh_off_t diff = F->wpos - f->pos;
83 if (diff > ((sh_off_t)blen << 1)) /* FIXME: Formula for long backwards seeks */
85 if ((sh_off_t)back > f->pos)
89 if ((uns)diff <= F->wlen) /* Seek into previous window (for example brewind) */
91 f->bstop = f->buffer + F->wlen;
92 f->bptr = f->bstop - diff;
97 if ((sh_off_t)back > f->pos)
99 f->bptr = f->buffer + back;
100 read_len = back + diff - F->wlen;
101 if (F->wlen && read_len < blen) /* Reuse part of previous window */
103 uns keep = MIN(F->wlen, blen - read_len);
104 memmove(f->buffer + read_len, f->buffer, keep);
105 f->bstop = f->buffer + read_len + keep;
108 f->bstop = f->buffer + (read_len = blen);
110 F->wpos = f->pos + (f->buffer - f->bptr);
111 if (sh_seek(F->fd, F->wpos, SEEK_SET) < 0)
112 die("Error seeking %s: %m", f->name);
116 int l = read(F->fd, read_ptr, read_len);
118 die("Error reading %s: %m", f->name);
120 if (unlikely(read_ptr < f->bptr))
123 break; /* Incomplete read because of EOF */
128 while (read_ptr <= f->bptr);
131 f->pos += f->bstop - f->bptr;
132 F->wlen = f->bstop - f->buffer;
133 return f->bstop - f->bptr;
135 /* Seeked behind EOF */
136 f->bptr = f->bstop = f->buffer;
142 bfd_spout(struct fastbuf *f)
144 if (FB_FILE(f)->wpos != f->pos && sh_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
145 die("Error seeking %s: %m", f->name);
147 int l = f->bptr - f->buffer;
150 FB_FILE(f)->wpos = (f->pos += l);
151 FB_FILE(f)->wlen = 0;
154 int z = write(FB_FILE(f)->fd, c, l);
156 die("Error writing %s: %m", f->name);
160 f->bptr = f->buffer = FB_BUFFER(f);
164 bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
174 if ((pos > 0) ^ (l > f->pos))
179 l = sh_seek(FB_FILE(f)->fd, pos, SEEK_END);
182 FB_FILE(f)->wpos = f->pos = l;
183 FB_FILE(f)->wlen = 0;
191 bfd_close(struct fastbuf *f)
193 switch (FB_FILE(f)->is_temp_file)
196 if (unlink(f->name) < 0)
197 log(L_ERROR, "unlink(%s): %m", f->name);
199 close(FB_FILE(f)->fd);
205 bfd_config(struct fastbuf *f, uns item, int value)
209 case BCONFIG_IS_TEMP_FILE:
210 FB_FILE(f)->is_temp_file = value;
212 case BCONFIG_KEEP_BACK_BUF:
213 FB_FILE(f)->keep_back_buf = value;
221 bfdopen_internal(int fd, byte *name, uns 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;
228 bzero(F, sizeof(*F));
229 f->buffer = (byte *)(F+1);
230 f->bptr = f->bstop = f->buffer;
231 f->bufend = f->buffer + buflen;
233 memcpy(f->name, name, namelen);
235 f->refill = bfd_refill;
236 f->spout = bfd_spout;
238 f->close = bfd_close;
239 f->config = bfd_config;
240 f->can_overwrite_buffer = 2;
245 bopen_try(byte *name, uns mode, uns buflen)
247 return bopen_file_try(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
251 bopen(byte *name, uns mode, uns buflen)
253 return bopen_file(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
257 bfdopen(int fd, uns buflen)
259 return bopen_fd(fd, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
263 bfdopen_shared(int fd, uns buflen)
265 struct fastbuf *f = bfdopen(fd, buflen);
266 FB_FILE(f)->is_temp_file = -1;
271 bfilesync(struct fastbuf *b)
274 if (fsync(FB_FILE(b)->fd) < 0)
275 log(L_ERROR, "fsync(%s) failed: %m", b->name);
280 int main(int argc UNUSED, char **argv UNUSED)
282 struct fastbuf *f, *t;
284 f = bopen("/etc/profile", O_RDONLY, 16);
287 printf("%d %d\n", (int)btell(f), (int)btell(t));