summaryrefslogtreecommitdiff
path: root/lib/fsaf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fsaf.c')
-rw-r--r--lib/fsaf.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/lib/fsaf.c b/lib/fsaf.c
new file mode 100644
index 0000000..07a4979
--- /dev/null
+++ b/lib/fsaf.c
@@ -0,0 +1,260 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "align.h"
+#include "fsaf.h"
+#include "msg.h"
+
+#if !defined(_POSIX_MAPPED_FILES) || _POSIX_MAPPED_FILES == -1
+# warning "No mmap support detected."
+#endif
+
+#define READAHEAD 1
+
+bool fsaf_mmap;
+
+FSAF * fsaf_fdopen(int fd, char const *fname)
+{
+ FSAF * rv = malloc(sizeof *rv);
+ if (rv == 0) { errno = ENOMEM; return 0; }
+
+ rv->fname = strdup(fname);
+ if (rv->fname == NULL) { errno = ENOMEM; free(rv); return 0; }
+ rv->fd = fd;
+ rv->eof_mark = (size_t)(-1);
+ rv->buf = 0;
+ rv->buf_capacity = 0;
+ rv->buf_offset = 0;
+ rv->buf_size = 0;
+ rv->invalid_mark = 0;
+#ifdef _POSIX_MAPPED_FILES
+ rv->mapped = false;
+ rv->topread = 0;
+#endif
+
+ struct stat stat;
+ int res = fstat(fd, &stat);
+ if (res == -1) goto fail;
+
+ if (S_ISREG(stat.st_mode)) {
+ rv->eof_mark = stat.st_size;
+#ifdef _POSIX_MAPPED_FILES
+ if (fsaf_mmap) {
+ /* try mmapping, it is a regular file */
+ size_t eof_pagebound = pg_align(rv->eof_mark, true);
+ char * buf = mmap(0, eof_pagebound, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (buf != MAP_FAILED) {
+ debug_message("mmapping", 0);
+ rv->mapped = 1;
+ rv->buf = buf;
+ rv->buf_capacity = eof_pagebound;
+ rv->buf_offset = 0;
+ rv->buf_size = rv->eof_mark;
+ //madvise(rv->buf, rv->buf_capacity,
+ // MADV_SEQUENTIAL);
+ }
+ }
+#endif
+ }
+
+#ifdef _POSIX_MAPPED_FILES
+ if (!rv->mapped)
+#endif
+ {
+ rv->buf_capacity = 65536;
+ rv->buf = malloc(rv->buf_capacity);
+ if (rv->buf == 0) rv->buf_capacity = 0;
+ }
+
+ return rv;
+fail:
+ free(rv);
+ return 0;
+}
+
+void fsaf_close(FSAF * fp)
+{
+ assert(fp != 0);
+
+#if _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+ munmap(fp->buf, fp->buf_capacity);
+ free(fp);
+ return;
+ }
+#endif
+ free(fp->fname);
+ free(fp->buf);
+ free(fp);
+}
+
+
+static void slide(FSAF *fp)
+{
+ assert(fp->invalid_mark >= fp->buf_offset);
+ size_t delta = fp->invalid_mark - fp->buf_offset;
+ assert(fp->buf_capacity > delta);
+ if (delta == 0) return;
+ memmove(fp->buf, fp->buf + delta, fp->buf_size - delta);
+ fp->buf_offset += delta;
+ fp->buf_size -= delta;
+}
+
+static void slurp(FSAF * fp, size_t len)
+{
+ assert(fp != 0);
+ assert(len > 0);
+ if (fp->invalid_mark - fp->buf_offset > fp->buf_size / 2) {
+ slide(fp);
+ }
+ if (fp->buf_size + len > fp->buf_capacity) {
+ size_t nc = fp->buf_capacity;
+ if (nc == 0) nc = 256;
+ while (nc < fp->buf_size + len) nc *= 2;
+ char * nb = realloc(fp->buf, nc);
+ if (nb != 0) {
+ fp->buf = nb;
+ fp->buf_capacity = nc;
+ } else {
+ slide(fp);
+ if (fp->buf_size > fp->buf_capacity) {
+ fatal_enomem(fp->fname);
+ }
+ if (fp->buf_size + len > fp->buf_capacity) {
+ len = fp->buf_capacity - fp->buf_size;
+ }
+ }
+ }
+ assert(len > 0);
+ assert(fp->buf_size + len <= fp->buf_capacity);
+
+ ssize_t res;
+ do {
+// res = read(fp->fd, fp->buf + fp->buf_size, len);
+ res = read(fp->fd, fp->buf + fp->buf_size, fp->buf_capacity - fp->buf_size);
+ } while (res == -1 && errno == EINTR);
+ if (res == 0) {
+ fp->eof_mark = fp->buf_offset + fp->buf_size;
+ }
+ fp->buf_size += res;
+}
+
+//static inline
+struct fsaf_read_rv fsaf_read(FSAF * fp, size_t offset, size_t len)
+{
+ struct fsaf_read_rv rv;
+
+ /* Reading nothing - since offset can be bogus in this
+ * situation, this could foul up our assumptions later, so
+ * return already here. */
+ if (len == 0) {
+ rv.b = "";
+ rv.len = 0;
+ return rv;
+ }
+
+ /* Make sure we don't read past the EOF mark. */
+ if (offset + len > fp->eof_mark) len = fp->eof_mark - offset;
+
+#if _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+#if 0 /* madvise seems not to give a noteworthy efficiency boost */
+ /* Notify the kernel that we will soon want more of
+ * the data, but do this only once for each page! */
+ if (offset + len >= fp->topread) {
+ size_t base = align(offset - fp->buf_offset, 1);
+ size_t rl = READAHEAD * pagesize;
+ madvise(fp->buf + base, rl, MADV_WILLNEED);
+ fp->topread += rl;
+ }
+#endif
+ } else
+#endif
+ {
+ /* Ensure that we have enough data in the buffer.
+ * This is only executed if we are not dealing with a
+ * mapped file. */
+ assert(offset >= fp->buf_offset);
+ if (offset - fp->buf_offset + len > fp->buf_size) {
+ slurp(fp, offset - fp->buf_offset + len - fp->buf_size);
+ if (offset - fp->buf_offset + len > fp->buf_size) {
+ len = fp->buf_size - (offset - fp->buf_offset);
+ }
+ }
+
+ }
+
+ assert(offset - fp->buf_offset + len <= fp->buf_size);
+ assert(offset + len <= fp->eof_mark);
+ rv.b = fp->buf + (offset - fp->buf_offset);
+ rv.len = len;
+ return rv;
+}
+
+void fsaf_invalidate(FSAF * fp, size_t offset)
+{
+ assert(fp->eof_mark >= offset);
+ if (fp->invalid_mark >= offset) return;
+
+#ifdef _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+ size_t old = pg_align(fp->eof_mark - fp->buf_offset, 0);
+ size_t new = pg_align(offset - fp->buf_offset, 0);
+ madvise(fp->buf + old, new - old, MADV_DONTNEED);
+ }
+#endif
+
+ fp->invalid_mark = offset;
+}
+
+#ifdef TESTMAIN
+#include <stdio.h>
+volatile char c;
+int main(int argc, char * argv[])
+{
+ if (argc == 1) {
+ set_loglevel(L_DEBUG);
+ FSAF * fp = fsaf_fdopen(STDIN_FILENO);
+ assert(fp != 0);
+ size_t i = 0;
+ while (i < fsaf_eof(fp)) {
+ struct fsaf_read_rv rr = fsaf_read(fp, i, 1);
+ //fwrite(rr.b, 1, rr.len, stdout);
+ c = rr.b[0];
+ i += rr.len;
+ }
+ fsaf_close(fp);
+ } else {
+ int ch;
+ while ((ch = getchar()) != EOF) {
+ c = ch;
+ }
+ }
+ return 0;
+}
+#endif