1515 *
1616 * svndiff0 ::= 'SVN\0' window*
1717 * 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;
1826 * int ::= highdigit* lowdigit;
1927 * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
2028 * lowdigit ::= # 7 bit value;
2129 */
2230
31+ #define INSN_MASK 0xc0
32+ #define INSN_COPYFROM_DATA 0x80
33+ #define OPERAND_MASK 0x3f
34+
2335#define VLI_CONTINUE 0x80
2436#define VLI_DIGIT_MASK 0x7f
2537#define VLI_BITS_PER_DIGIT 7
2638
2739struct window {
40+ struct strbuf out ;
2841 struct strbuf instructions ;
2942 struct strbuf data ;
3043};
3144
32- #define WINDOW_INIT { STRBUF_INIT, STRBUF_INIT }
45+ #define WINDOW_INIT { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }
3346
3447static void window_release (struct window * ctx )
3548{
49+ strbuf_release (& ctx -> out );
3650 strbuf_release (& ctx -> instructions );
3751 strbuf_release (& ctx -> data );
3852}
3953
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+
4061static int error_short_read (struct line_buffer * input )
4162{
4263 if (buffer_ferror (input ))
@@ -93,6 +114,25 @@ static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
93114 return error_short_read (in );
94115}
95116
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+
96136static int read_offset (struct line_buffer * in , off_t * result , off_t * len )
97137{
98138 uintmax_t val ;
@@ -115,7 +155,64 @@ static int read_length(struct line_buffer *in, size_t *result, off_t *len)
115155 return 0 ;
116156}
117157
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 )
119216{
120217 struct window ctx = WINDOW_INIT ;
121218 size_t out_len ;
@@ -127,13 +224,17 @@ static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
127224 if (read_length (delta , & out_len , delta_len ) ||
128225 read_length (delta , & instructions_len , delta_len ) ||
129226 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 ))
131232 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 " );
134235 goto error_out ;
135236 }
136- if (read_chunk ( delta , delta_len , & ctx .data , data_len ))
237+ if (write_strbuf ( & ctx .out , out ))
137238 goto error_out ;
138239 window_release (& ctx );
139240 return 0 ;
@@ -156,7 +257,7 @@ int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
156257 if (read_offset (delta , & pre_off , & delta_len ) ||
157258 read_length (delta , & pre_len , & delta_len ) ||
158259 move_window (preimage , pre_off , pre_len ) ||
159- apply_one_window (delta , & delta_len ))
260+ apply_one_window (delta , & delta_len , postimage ))
160261 return -1 ;
161262 }
162263 return 0 ;
0 commit comments