X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/11680e1989c6c867f207be7197602776e05cae6a..16b0fea8ae1a581d568dbee2efa2932aa4e6fcce:/lib/hreader.c diff --git a/lib/hreader.c b/lib/hreader.c index 5973375..bdf3c07 100644 --- a/lib/hreader.c +++ b/lib/hreader.c @@ -18,54 +18,90 @@ /** @file lib/hreader.c * @brief Hands-off reader - read files without keeping them open */ +#include #include "hreader.h" #include "mem.h" #include #include +#include +#include +#include -void hreader_init(const char *path, struct hreader *h) { +static int hreader_fill(struct hreader *h, off_t offset); + +int hreader_init(const char *path, struct hreader *h) { + struct stat sb; + if(stat(path, &sb) < 0) + return -1; memset(h, 0, sizeof *h); h->path = xstrdup(path); + h->size = sb.st_size; h->bufsize = 65536; h->buffer = xmalloc_noptr(h->bufsize); + return 0; +} + +void hreader_close(struct hreader *h) { + xfree(h->path); + xfree(h->buffer); } int hreader_read(struct hreader *h, void *buffer, size_t n) { - if(h->consumed == h->bytes) { - int r; + int r = hreader_pread(h, buffer, n, h->read_offset); + if(r > 0) + h->read_offset += r; + return r; +} - if((r = hreader_fill(h)) <= 0) - return r; +int hreader_pread(struct hreader *h, void *buffer, size_t n, off_t offset) { + size_t bytes_read = 0; + + while(bytes_read < n) { + // If the desired byte range is outside the file, fetch new contents + if(offset < h->buf_offset || offset >= h->buf_offset + (off_t)h->bytes) { + int r = hreader_fill(h, offset); + if(r < 0) + return -1; /* disaster! */ + else if(r == 0) + break; /* end of file */ + } + // Figure out how much we can read this time round + size_t left = h->bytes - (offset - h->buf_offset); + // Truncate the read if we don't want that much + if(left > (n - bytes_read)) + left = n - bytes_read; + memcpy((char *)buffer + bytes_read, + h->buffer + (offset - h->buf_offset), + left); + offset += left; + bytes_read += left; } - if(n > h->bytes - h->consumed) - n = h->bytes - h->consumed; - memcpy(buffer, h->buffer + h->consumed, n); - h->consumed += n; - return n; + return bytes_read; } -int hreader_fill(struct hreader *h) { - if(h->consumed < h->bytes) - return h->bytes - h->consumed; - h->bytes = h->consumed = 0; +static int hreader_fill(struct hreader *h, off_t offset) { int fd = open(h->path, O_RDONLY); if(fd < 0) return -1; - if(lseek(fd, h->offset, SEEK_SET) < 0) { - close(fd); - return -1; - } - int n = read(fd, h->buffer, h->bufsize); + int n = pread(fd, h->buffer, h->bufsize, offset); close(fd); if(n < 0) return -1; + h->buf_offset = offset; h->bytes = n; - h->offset += n; return n; } -void hreader_consume(struct hreader *h, size_t n) { - h->consumed += n; +off_t hreader_seek(struct hreader *h, off_t offset, int whence) { + switch(whence) { + case SEEK_SET: break; + case SEEK_CUR: offset += h->read_offset; break; + case SEEK_END: offset += h->size; break; + default: einval: errno = EINVAL; return -1; + } + if(offset < 0) goto einval; + h->read_offset = offset; + return offset; } /*