vcs-svn: read inline data from deltas
authorJonathan Nieder <jrnieder@gmail.com>
Wed, 13 Oct 2010 09:35:59 +0000 (04:35 -0500)
committerJonathan Nieder <jrnieder@gmail.com>
Mon, 28 Mar 2011 03:51:00 +0000 (22:51 -0500)
Each window of an svndiff0-format delta includes a section for novel
text to be copied to the postimage (in the order it appears in the
window, possibly interspersed with other data).

Slurp in this data when encountering it. It is not actually necessary
to do so --- it would be just as easy to copy from delta to output
as part of interpreting the relevant instructions --- but this way,
the code that interprets svndiff0 instructions can proceed very
quickly because it does not require I/O.

Subversion's svndiff0 parser rejects deltas that do not consume all
the novel text that was provided. Omit that check for now so we can
test the new functionality right away, rather than waiting to learn
instructions that consume data.

Do check for truncated data sections. Subversion's parser rejects
deltas that end in the middle of a declared novel-text section, so it
should be safe for us to reject them, too.

Improved-by: Ramkumar Ramachandra <artagnon@gmail.com>
Improved-by: David Barr <david.barr@cordelta.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Acked-by: Ramkumar Ramachandra <artagnon@gmail.com>
t/t9011-svn-da.sh
vcs-svn/svndiff.c
index 90b6058ab67d38edcd576ec040e1dd3cee2368d0..26a4a3694a8f322e579d7a3678d13e61b077e317 100755 (executable)
@@ -121,4 +121,16 @@ test_expect_success 'preimage view: reject truncated preimage' '
        test_must_fail test-svn-fe -d preimage clear.longread 9
 '
 
+test_expect_success 'inline data' '
+       printf "SVNQ%b%s%b%s" "QQQQ\003" "bar" "QQQQ\001" "x" |
+               q_to_nul >inline.clear &&
+       test-svn-fe -d preimage inline.clear 18 >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success 'reject truncated inline data' '
+       printf "SVNQ%b%s" "QQQQ\003" "b" | q_to_nul >inline.trunc &&
+       test_must_fail test-svn-fe -d preimage inline.trunc 10
+'
+
 test_done
index b7c2c8bf53aabfe29af32be3b2b11bc20f2e7b86..175168f599839c4db471540cb0f0759c065b7429 100644 (file)
 #define VLI_DIGIT_MASK 0x7f
 #define VLI_BITS_PER_DIGIT 7
 
+struct window {
+       struct strbuf data;
+};
+
+#define WINDOW_INIT    { STRBUF_INIT }
+
+static void window_release(struct window *ctx)
+{
+       strbuf_release(&ctx->data);
+}
+
 static int error_short_read(struct line_buffer *input)
 {
        if (buffer_ferror(input))
@@ -31,24 +42,30 @@ static int error_short_read(struct line_buffer *input)
        return error("invalid delta: unexpected end of file");
 }
 
+static int read_chunk(struct line_buffer *delta, off_t *delta_len,
+                     struct strbuf *buf, size_t len)
+{
+       strbuf_reset(buf);
+       if (len > *delta_len ||
+           buffer_read_binary(delta, buf, len) != len)
+               return error_short_read(delta);
+       *delta_len -= buf->len;
+       return 0;
+}
+
 static int read_magic(struct line_buffer *in, off_t *len)
 {
        static const char magic[] = {'S', 'V', 'N', '\0'};
        struct strbuf sb = STRBUF_INIT;
 
-       if (*len < sizeof(magic) ||
-           buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic)) {
-               error_short_read(in);
+       if (read_chunk(in, len, &sb, sizeof(magic))) {
                strbuf_release(&sb);
                return -1;
        }
-
        if (memcmp(sb.buf, magic, sizeof(magic))) {
                strbuf_release(&sb);
                return error("invalid delta: unrecognized file type");
        }
-
-       *len -= sizeof(magic);
        strbuf_release(&sb);
        return 0;
 }
@@ -98,6 +115,7 @@ static int read_length(struct line_buffer *in, size_t *result, off_t *len)
 
 static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
 {
+       struct window ctx = WINDOW_INIT;
        size_t out_len;
        size_t instructions_len;
        size_t data_len;
@@ -107,12 +125,18 @@ static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
        if (read_length(delta, &out_len, delta_len) ||
            read_length(delta, &instructions_len, delta_len) ||
            read_length(delta, &data_len, delta_len))
-               return -1;
-       if (instructions_len)
-               return error("What do you think I am?  A delta applier?");
-       if (data_len)
-               return error("No support for inline data yet");
+               goto error_out;
+       if (instructions_len) {
+               error("What do you think I am?  A delta applier?");
+               goto error_out;
+       }
+       if (read_chunk(delta, delta_len, &ctx.data, data_len))
+               goto error_out;
+       window_release(&ctx);
        return 0;
+error_out:
+       window_release(&ctx);
+       return -1;
 }
 
 int svndiff0_apply(struct line_buffer *delta, off_t delta_len,