vcs-svn: learn to maintain a sliding view of a file
authorJonathan Nieder <jrnieder@gmail.com>
Mon, 3 Jan 2011 03:54:58 +0000 (21:54 -0600)
committerJonathan Nieder <jrnieder@gmail.com>
Mon, 28 Mar 2011 01:23:32 +0000 (20:23 -0500)
Each section of a Subversion-format delta only requires examining (and
keeping in random-access memory) a small portion of the preimage.  At
any moment, this portion starts at a certain file offset and has a
well-defined length, and as the delta is applied, the portion advances
from the beginning to the end of the preimage.  Add a move_window
function to keep track of this view into the preimage.

You can use it like this:

buffer_init(f, NULL);
struct sliding_view window = SLIDING_VIEW_INIT(f);
move_window(&window, 3, 7); /* (1) */
move_window(&window, 5, 5); /* (2) */
move_window(&window, 12, 2); /* (3) */
strbuf_release(&window.buf);
buffer_deinit(f);

The data structure is called sliding_view instead of _window to
prevent confusion with svndiff0 Windows.

In this example, (1) reads 10 bytes and discards the first 3;
(2) discards the first 2, which are not needed any more; and (3) skips
2 bytes and reads 2 new bytes to work with.

When move_window returns, the file position indicator is at position
window->off + window->width and the data from positions window->off to
the current file position are stored in window->buf.

This function performs only sequential access from the input file and
never seeks, so it can be safely used on pipes and sockets.

On end-of-file, move_window silently reads less than the caller
requested.  On other errors, it prints a message and returns -1.

Helped-by: David Barr <david.barr@cordelta.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Makefile
vcs-svn/LICENSE
vcs-svn/sliding_window.c [new file with mode: 0644]
vcs-svn/sliding_window.h [new file with mode: 0644]

index dd1af362c720c54299106b70afa8f0d63e6e5a6c..5cd141d5afb16b7cc2dcef3ab15d14895af1d0b8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1851,6 +1851,7 @@ XDIFF_OBJS += xdiff/xpatience.o
 
 VCSSVN_OBJS += vcs-svn/string_pool.o
 VCSSVN_OBJS += vcs-svn/line_buffer.o
+VCSSVN_OBJS += vcs-svn/sliding_window.o
 VCSSVN_OBJS += vcs-svn/repo_tree.o
 VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndump.o
@@ -1993,6 +1994,7 @@ VCSSVN_H += vcs-svn/obj_pool.h
 VCSSVN_H += vcs-svn/trp.h
 VCSSVN_H += vcs-svn/string_pool.h
 VCSSVN_H += vcs-svn/line_buffer.h
+VCSSVN_H += vcs-svn/sliding_window.h
 VCSSVN_H += vcs-svn/repo_tree.h
 VCSSVN_H += vcs-svn/fast_export.h
 VCSSVN_H += vcs-svn/svndump.h
index 0a5e3c43a0bfc69345c6e2ab6ec4d7830f468b37..805882c8383e9258e883dfd528baaa02ecfbf064 100644 (file)
@@ -1,6 +1,8 @@
 Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
 All rights reserved.
 
+Copyright (C) 2010 Jonathan Nieder <jrnieder@gmail.com>.
+
 Copyright (C) 2008 Jason Evans <jasone@canonware.com>.
 All rights reserved.
 
diff --git a/vcs-svn/sliding_window.c b/vcs-svn/sliding_window.c
new file mode 100644 (file)
index 0000000..1b8d987
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "sliding_window.h"
+#include "line_buffer.h"
+#include "strbuf.h"
+
+static int input_error(struct line_buffer *file)
+{
+       if (!buffer_ferror(file))
+               return error("delta preimage ends early");
+       return error("cannot read delta preimage: %s", strerror(errno));
+}
+
+static int skip_or_whine(struct line_buffer *file, off_t gap)
+{
+       if (buffer_skip_bytes(file, gap) != gap)
+               return input_error(file);
+       return 0;
+}
+
+static int read_to_fill_or_whine(struct line_buffer *file,
+                               struct strbuf *buf, size_t width)
+{
+       buffer_read_binary(file, buf, width - buf->len);
+       if (buf->len != width)
+               return input_error(file);
+       return 0;
+}
+
+static int check_overflow(off_t a, size_t b)
+{
+       if (b > maximum_signed_value_of_type(off_t))
+               return error("unrepresentable length in delta: "
+                               "%"PRIuMAX" > OFF_MAX", (uintmax_t) b);
+       if (signed_add_overflows(a, (off_t) b))
+               return error("unrepresentable offset in delta: "
+                               "%"PRIuMAX" + %"PRIuMAX" > OFF_MAX",
+                               (uintmax_t) a, (uintmax_t) b);
+       return 0;
+}
+
+int move_window(struct sliding_view *view, off_t off, size_t width)
+{
+       off_t file_offset;
+       assert(view);
+       assert(view->width <= view->buf.len);
+       assert(!check_overflow(view->off, view->buf.len));
+
+       if (check_overflow(off, width))
+               return -1;
+       if (off < view->off || off + width < view->off + view->width)
+               return error("invalid delta: window slides left");
+
+       file_offset = view->off + view->buf.len;
+       if (off < file_offset) {
+               /* Move the overlapping region into place. */
+               strbuf_remove(&view->buf, 0, off - view->off);
+       } else {
+               /* Seek ahead to skip the gap. */
+               if (skip_or_whine(view->file, off - file_offset))
+                       return -1;
+               strbuf_setlen(&view->buf, 0);
+       }
+
+       if (view->buf.len > width)
+               ; /* Already read. */
+       else if (read_to_fill_or_whine(view->file, &view->buf, width))
+               return -1;
+
+       view->off = off;
+       view->width = width;
+       return 0;
+}
diff --git a/vcs-svn/sliding_window.h b/vcs-svn/sliding_window.h
new file mode 100644 (file)
index 0000000..ed0bfdd
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SLIDING_WINDOW_H_
+#define SLIDING_WINDOW_H_
+
+#include "strbuf.h"
+
+struct sliding_view {
+       struct line_buffer *file;
+       off_t off;
+       size_t width;
+       struct strbuf buf;
+};
+
+#define SLIDING_VIEW_INIT(input)       { (input), 0, 0, STRBUF_INIT }
+
+extern int move_window(struct sliding_view *view, off_t off, size_t width);
+
+#endif