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