@matheusbsbs / matheus-bagliano / commits / ec71aa2e1f
Fork 0

vcs-svn: implement copyfrom_data delta instruction

The copyfrom_data instruction copies a few bytes verbatim from the novel text section of a window to the postimage. [jn: with memory leak fix from David] Improved-by: David Barr <david.barr@cordelta.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Acked-by: Ramkumar Ramachandra <artagnon@gmail.com>

Jonathan Nieder committed Oct 13, 2010 at 04:39 UTC ec71aa2e1f229b90092e6678ac7c2dca3d15b5f3
2 files changed +139 -7
t/t9011-svn-da.sh
+31
@@ -138,4 +138,35 @@ test_expect_success 'reject truncated inline data (after instruction section)' '
138 test_must_fail test-svn-fe -d preimage insn.trunc 11
139 '
140
141 +test_expect_success 'copyfrom_data' '
142 + echo hi >expect &&
143 + printf "SVNQ%b%b%b" "QQ\003\001\003" "\0203" "hi\n" | q_to_nul >copydat &&
144 + test-svn-fe -d preimage copydat 13 >actual &&
145 + test_cmp expect actual
146 +'
147 +
148 +test_expect_success 'multiple copyfrom_data' '
149 + echo hi >expect &&
150 + printf "SVNQ%b%b%b%b%b" "QQ\003\002\003" "\0201\0202" "hi\n" \
151 + "QQQ\002Q" "\0200Q" | q_to_nul >copy.multi &&
152 + len=$(wc -c <copy.multi) &&
153 + test-svn-fe -d preimage copy.multi $len >actual &&
154 + test_cmp expect actual
155 +'
156 +
157 +test_expect_success 'incomplete multiple insn' '
158 + printf "SVNQ%b%b%b" "QQ\003\002\003" "\0203\0200" "hi\n" |
159 + q_to_nul >copy.partial &&
160 + len=$(wc -c <copy.partial) &&
161 + test_must_fail test-svn-fe -d preimage copy.partial $len
162 +'
163 +
164 +test_expect_success 'catch attempt to copy missing data' '
165 + printf "SVNQ%b%b%s%b%s" "QQ\002\002\001" "\0201\0201" "X" \
166 + "QQQQ\002" "YZ" |
167 + q_to_nul >copy.incomplete &&
168 + len=$(wc -c <copy.incomplete) &&
169 + test_must_fail test-svn-fe -d preimage copy.incomplete $len
170 +'
171 +
172 test_done
vcs-svn/svndiff.c
+108 -7
@@ -15,28 +15,49 @@
15 *
16 * svndiff0 ::= 'SVN\0' window*
17 * window ::= int int int int int instructions inline_data;
18 + * instructions ::= instruction*;
19 + * instruction ::= view_selector int int
20 + * | copyfrom_data int
21 + * | packed_view_selector int
22 + * | packed_copyfrom_data
23 + * ;
24 + * copyfrom_data ::= # binary 10 000000;
25 + * packed_copyfrom_data ::= # copyfrom_data OR-ed with 6 bit value;
26 * int ::= highdigit* lowdigit;
27 * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
28 * lowdigit ::= # 7 bit value;
29 */
30
31 +#define INSN_MASK 0xc0
32 +#define INSN_COPYFROM_DATA 0x80
33 +#define OPERAND_MASK 0x3f
34 +
35 #define VLI_CONTINUE 0x80
36 #define VLI_DIGIT_MASK 0x7f
37 #define VLI_BITS_PER_DIGIT 7
38
39 struct window {
40 + struct strbuf out;
41 struct strbuf instructions;
42 struct strbuf data;
43 };
44
32 -#define WINDOW_INIT { STRBUF_INIT, STRBUF_INIT }
45 +#define WINDOW_INIT { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }
46
47 static void window_release(struct window *ctx)
48 {
49 + strbuf_release(&ctx->out);
50 strbuf_release(&ctx->instructions);
51 strbuf_release(&ctx->data);
52 }
53
54 +static int write_strbuf(struct strbuf *sb, FILE *out)
55 +{
56 + if (fwrite(sb->buf, 1, sb->len, out) == sb->len) /* Success. */
57 + return 0;
58 + return error("cannot write delta postimage: %s", strerror(errno));
59 +}
60 +
61 static int error_short_read(struct line_buffer *input)
62 {
63 if (buffer_ferror(input))
@@ -93,6 +114,25 @@ static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
114 return error_short_read(in);
115 }
116
117 +static int parse_int(const char **buf, size_t *result, const char *end)
118 +{
119 + size_t rv = 0;
120 + const char *pos;
121 + for (pos = *buf; pos != end; pos++) {
122 + unsigned char ch = *pos;
123 +
124 + rv <<= VLI_BITS_PER_DIGIT;
125 + rv += (ch & VLI_DIGIT_MASK);
126 + if (ch & VLI_CONTINUE)
127 + continue;
128 +
129 + *result = rv;
130 + *buf = pos + 1;
131 + return 0;
132 + }
133 + return error("invalid delta: unexpected end of instructions section");
134 +}
135 +
136 static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
137 {
138 uintmax_t val;
@@ -115,7 +155,64 @@ static int read_length(struct line_buffer *in, size_t *result, off_t *len)
155 return 0;
156 }
157
118 -static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
158 +static int copyfrom_data(struct window *ctx, size_t *data_pos, size_t nbytes)
159 +{
160 + const size_t pos = *data_pos;
161 + if (unsigned_add_overflows(pos, nbytes) ||
162 + pos + nbytes > ctx->data.len)
163 + return error("invalid delta: copies unavailable inline data");
164 + strbuf_add(&ctx->out, ctx->data.buf + pos, nbytes);
165 + *data_pos += nbytes;
166 + return 0;
167 +}
168 +
169 +static int parse_first_operand(const char **buf, size_t *out, const char *end)
170 +{
171 + size_t result = (unsigned char) *(*buf)++ & OPERAND_MASK;
172 + if (result) { /* immediate operand */
173 + *out = result;
174 + return 0;
175 + }
176 + return parse_int(buf, out, end);
177 +}
178 +
179 +static int execute_one_instruction(struct window *ctx,
180 + const char **instructions, size_t *data_pos)
181 +{
182 + unsigned int instruction;
183 + const char *insns_end = ctx->instructions.buf + ctx->instructions.len;
184 + size_t nbytes;
185 + assert(ctx);
186 + assert(instructions && *instructions);
187 + assert(data_pos);
188 +
189 + instruction = (unsigned char) **instructions;
190 + if (parse_first_operand(instructions, &nbytes, insns_end))
191 + return -1;
192 + if ((instruction & INSN_MASK) != INSN_COPYFROM_DATA)
193 + return error("Unknown instruction %x", instruction);
194 + return copyfrom_data(ctx, data_pos, nbytes);
195 +}
196 +
197 +static int apply_window_in_core(struct window *ctx)
198 +{
199 + const char *instructions;
200 + size_t data_pos = 0;
201 +
202 + /*
203 + * Fill ctx->out.buf using data from the source, target,
204 + * and inline data views.
205 + */
206 + for (instructions = ctx->instructions.buf;
207 + instructions != ctx->instructions.buf + ctx->instructions.len;
208 + )
209 + if (execute_one_instruction(ctx, &instructions, &data_pos))
210 + return -1;
211 + return 0;
212 +}
213 +
214 +static int apply_one_window(struct line_buffer *delta, off_t *delta_len,
215 + FILE *out)
216 {
217 struct window ctx = WINDOW_INIT;
218 size_t out_len;
@@ -127,13 +224,17 @@ static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
224 if (read_length(delta, &out_len, delta_len) ||
225 read_length(delta, &instructions_len, delta_len) ||
226 read_length(delta, &data_len, delta_len) ||
130 - read_chunk(delta, delta_len, &ctx.instructions, instructions_len))
227 + read_chunk(delta, delta_len, &ctx.instructions, instructions_len) ||
228 + read_chunk(delta, delta_len, &ctx.data, data_len))
229 + goto error_out;
230 + strbuf_grow(&ctx.out, out_len);
231 + if (apply_window_in_core(&ctx))
232 goto error_out;
132 - if (instructions_len) {
133 - error("What do you think I am? A delta applier?");
233 + if (ctx.out.len != out_len) {
234 + error("invalid delta: incorrect postimage length");
235 goto error_out;
236 }
136 - if (read_chunk(delta, delta_len, &ctx.data, data_len))
237 + if (write_strbuf(&ctx.out, out))
238 goto error_out;
239 window_release(&ctx);
240 return 0;
@@ -156,7 +257,7 @@ int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
257 if (read_offset(delta, &pre_off, &delta_len) ||
258 read_length(delta, &pre_len, &delta_len) ||
259 move_window(preimage, pre_off, pre_len) ||
159 - apply_one_window(delta, &delta_len))
260 + apply_one_window(delta, &delta_len, postimage))
261 return -1;
262 }
263 return 0;