chiark / gitweb /
disorder.h: more consistent approach to function attributes
[disorder] / lib / hreader.c
index 6390d693049ceef5d9b50388671b417a30e691a5..bdf3c074c7ed5a741070c03721040ee7a4e4281c 100644 (file)
 /** @file lib/hreader.c
  * @brief Hands-off reader - read files without keeping them open
  */
+#include <config.h>
 #include "hreader.h"
 #include "mem.h"
 #include <string.h>
 #include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
 
-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;
   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;
 }
 
 /*