Code

vcs-svn: parse svndiff0 window header
[git.git] / vcs-svn / svndiff.c
1 /*
2  * Licensed under a two-clause BSD-style license.
3  * See LICENSE for details.
4  */
6 #include "git-compat-util.h"
7 #include "line_buffer.h"
8 #include "svndiff.h"
10 /*
11  * svndiff0 applier
12  *
13  * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
14  *
15  * svndiff0 ::= 'SVN\0' window*
16  * window ::= int int int int int instructions inline_data;
17  * int ::= highdigit* lowdigit;
18  * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
19  * lowdigit ::= # 7 bit value;
20  */
22 #define VLI_CONTINUE    0x80
23 #define VLI_DIGIT_MASK  0x7f
24 #define VLI_BITS_PER_DIGIT 7
26 static int error_short_read(struct line_buffer *input)
27 {
28         if (buffer_ferror(input))
29                 return error("error reading delta: %s", strerror(errno));
30         return error("invalid delta: unexpected end of file");
31 }
33 static int read_magic(struct line_buffer *in, off_t *len)
34 {
35         static const char magic[] = {'S', 'V', 'N', '\0'};
36         struct strbuf sb = STRBUF_INIT;
38         if (*len < sizeof(magic) ||
39             buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic)) {
40                 error_short_read(in);
41                 strbuf_release(&sb);
42                 return -1;
43         }
45         if (memcmp(sb.buf, magic, sizeof(magic))) {
46                 strbuf_release(&sb);
47                 return error("invalid delta: unrecognized file type");
48         }
50         *len -= sizeof(magic);
51         strbuf_release(&sb);
52         return 0;
53 }
55 static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
56 {
57         uintmax_t rv = 0;
58         off_t sz;
59         for (sz = *len; sz; sz--) {
60                 const int ch = buffer_read_char(in);
61                 if (ch == EOF)
62                         break;
64                 rv <<= VLI_BITS_PER_DIGIT;
65                 rv += (ch & VLI_DIGIT_MASK);
66                 if (ch & VLI_CONTINUE)
67                         continue;
69                 *result = rv;
70                 *len = sz - 1;
71                 return 0;
72         }
73         return error_short_read(in);
74 }
76 static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
77 {
78         uintmax_t val;
79         if (read_int(in, &val, len))
80                 return -1;
81         if (val > maximum_signed_value_of_type(off_t))
82                 return error("unrepresentable offset in delta: %"PRIuMAX"", val);
83         *result = val;
84         return 0;
85 }
87 static int read_length(struct line_buffer *in, size_t *result, off_t *len)
88 {
89         uintmax_t val;
90         if (read_int(in, &val, len))
91                 return -1;
92         if (val > SIZE_MAX)
93                 return error("unrepresentable length in delta: %"PRIuMAX"", val);
94         *result = val;
95         return 0;
96 }
98 static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
99 {
100         size_t out_len;
101         size_t instructions_len;
102         size_t data_len;
103         assert(delta_len);
105         /* "source view" offset and length already handled; */
106         if (read_length(delta, &out_len, delta_len) ||
107             read_length(delta, &instructions_len, delta_len) ||
108             read_length(delta, &data_len, delta_len))
109                 return -1;
110         if (instructions_len)
111                 return error("What do you think I am?  A delta applier?");
112         if (data_len)
113                 return error("No support for inline data yet");
114         return 0;
117 int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
118                         struct sliding_view *preimage, FILE *postimage)
120         assert(delta && preimage && postimage);
122         if (read_magic(delta, &delta_len))
123                 return -1;
124         while (delta_len) {     /* For each window: */
125                 off_t pre_off;
126                 size_t pre_len;
128                 if (read_offset(delta, &pre_off, &delta_len) ||
129                     read_length(delta, &pre_len, &delta_len) ||
130                     apply_one_window(delta, &delta_len))
131                         return -1;
132         }
133         return 0;