Subversion's delta format has the convenient property that applying each section of the delta only requires examining (and keeping in memory) a small portion of the preimage. At any moment, this portion begins at a well-defined file offset and has a well-defined length, and as the delta is applied, it moves from the beginning to the end of the file. Add a move_window() function to keep track of such a window into a file. You can use it like this: struct line_buffer preimage = LINE_BUFFER_INIT; buffer_init(&preimage, NULL); struct view window = {&preimage, 0, STRBUF_INIT}; move_window(&window, 3, 7); /* (1) */ move_window(&window, 5, 5); /* (2) */ move_window(&window, 12, 2); /* (3) */ strbuf_release(&window.buf); buffer_deinit(&preimage); 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. Whenever move_window() returns, the file position indicator is at position window->off + window->buf.len and the data from positions window->off to the current file position are stored in window->buf. This function does only sequential access and never seeks, so it can be safely used on pipes and sockets. On end-of-file, move_window() just silently reads less than the caller requested. On other errors, it prints a message to stderr and returns -1. Helped-by: David Barr <david.barr@xxxxxxxxxxxx> Signed-off-by: Jonathan Nieder <jrnieder@xxxxxxxxx> --- Makefile | 5 ++- vcs-svn/sliding_window.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ vcs-svn/sliding_window.h | 14 ++++++++++ vcs-svn/LICENSE | 2 + 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 vcs-svn/sliding_window.c create mode 100644 vcs-svn/sliding_window.h diff --git a/Makefile b/Makefile index 1f1ce04..d99da33 100644 --- a/Makefile +++ b/Makefile @@ -1765,7 +1765,8 @@ endif XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ xdiff/xmerge.o xdiff/xpatience.o VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \ - vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o + vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o \ + vcs-svn/sliding_window.o OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS) dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d) @@ -1892,7 +1893,7 @@ xdiff-interface.o $(XDIFF_OBJS): \ $(VCSSVN_OBJS): \ vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h \ vcs-svn/line_buffer.h vcs-svn/repo_tree.h vcs-svn/fast_export.h \ - vcs-svn/svndump.h + vcs-svn/svndump.h vcs-svn/sliding_window.h endif exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \ diff --git a/vcs-svn/sliding_window.c b/vcs-svn/sliding_window.c new file mode 100644 index 0000000..8273970 --- /dev/null +++ b/vcs-svn/sliding_window.c @@ -0,0 +1,65 @@ +/* + * 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 void strbuf_remove_from_left(struct strbuf *sb, size_t nbytes) +{ + assert(nbytes <= sb->len); + memmove(sb->buf, sb->buf + nbytes, sb->len - nbytes); + strbuf_setlen(sb, sb->len - nbytes); +} + +static int check_overflow(off_t a, size_t b) +{ + if ((off_t) b < 0) + return error("Unrepresentable length: " + "%"PRIu64" > OFF_MAX", (uint64_t) b); + if (signed_add_overflows(a, (off_t) b)) + return error("Unrepresentable offset: " + "%"PRIu64" + %"PRIu64" > OFF_MAX", + (uint64_t) a, (uint64_t) b); + return 0; +} + +int move_window(struct view *view, off_t off, size_t len) +{ + off_t file_offset; + assert(view && view->file); + assert(!check_overflow(view->off, view->buf.len)); + + if (check_overflow(off, len)) + return -1; + if (off < view->off || off + len < view->off + view->buf.len) + 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_from_left(&view->buf, off - view->off); + else + strbuf_setlen(&view->buf, 0); + if (off > file_offset) { + /* Seek ahead to skip the gap. */ + const off_t gap = off - file_offset; + const off_t nread = buffer_skip_bytes(view->file, gap); + if (nread != gap) { + if (!buffer_ferror(view->file)) /* View ends early. */ + goto done; + return error("Cannot seek forward in input: %s", + strerror(errno)); + } + file_offset += gap; + } + buffer_read_binary(&view->buf, len - view->buf.len, view->file); + if (buffer_ferror(view->file)) + return error("Cannot read preimage: %s", strerror(errno)); + done: + view->off = off; + return 0; +} diff --git a/vcs-svn/sliding_window.h b/vcs-svn/sliding_window.h new file mode 100644 index 0000000..b9f0552 --- /dev/null +++ b/vcs-svn/sliding_window.h @@ -0,0 +1,14 @@ +#ifndef SLIDING_WINDOW_H_ +#define SLIDING_WINDOW_H_ + +#include "strbuf.h" + +struct view { + struct line_buffer *file; + off_t off; + struct strbuf buf; +}; + +extern int move_window(struct view *view, off_t off, size_t len); + +#endif diff --git a/vcs-svn/LICENSE b/vcs-svn/LICENSE index 0a5e3c4..805882c 100644 --- a/vcs-svn/LICENSE +++ b/vcs-svn/LICENSE @@ -1,6 +1,8 @@ Copyright (C) 2010 David Barr <david.barr@xxxxxxxxxxxx>. All rights reserved. +Copyright (C) 2010 Jonathan Nieder <jrnieder@xxxxxxxxx>. + Copyright (C) 2008 Jason Evans <jasone@xxxxxxxxxxxxx>. All rights reserved. -- 1.7.2.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html