File: | tog/../lib/diff_main.c |
Warning: | line 250, column 25 Access to field 'left_count' results in a dereference of a null pointer (loaded from variable 'new_chunk') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Generic infrastructure to implement various diff algorithms (implementation). */ | ||||
2 | /* | ||||
3 | * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de> | ||||
4 | * | ||||
5 | * Permission to use, copy, modify, and distribute this software for any | ||||
6 | * purpose with or without fee is hereby granted, provided that the above | ||||
7 | * copyright notice and this permission notice appear in all copies. | ||||
8 | * | ||||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
16 | */ | ||||
17 | |||||
18 | |||||
19 | #include <sys/queue.h> | ||||
20 | #include <ctype.h> | ||||
21 | #include <errno(*__errno()).h> | ||||
22 | #include <stdint.h> | ||||
23 | #include <stdlib.h> | ||||
24 | #include <stdbool.h> | ||||
25 | #include <stdio.h> | ||||
26 | #include <string.h> | ||||
27 | #include <limits.h> | ||||
28 | #include <unistd.h> | ||||
29 | |||||
30 | #include <assert.h> | ||||
31 | |||||
32 | #include <arraylist.h> | ||||
33 | #include <diff_main.h> | ||||
34 | |||||
35 | #include "diff_internal.h" | ||||
36 | #include "diff_debug.h" | ||||
37 | |||||
38 | static int | ||||
39 | read_at(FILE *f, off_t at_pos, unsigned char *buf, size_t len) | ||||
40 | { | ||||
41 | int r; | ||||
42 | if (fseeko(f, at_pos, SEEK_SET0) == -1) | ||||
43 | return errno(*__errno()); | ||||
44 | r = fread(buf, sizeof(char), len, f); | ||||
45 | if ((r == 0 || r < len) && ferror(f)(!__isthreaded ? (((f)->_flags & 0x0040) != 0) : (ferror )(f))) | ||||
46 | return errno(*__errno()); | ||||
47 | if (r != len) | ||||
48 | return EIO5; | ||||
49 | return 0; | ||||
50 | } | ||||
51 | |||||
52 | static int | ||||
53 | buf_cmp(const unsigned char *left, size_t left_len, | ||||
54 | const unsigned char *right, size_t right_len, | ||||
55 | bool_Bool ignore_whitespace) | ||||
56 | { | ||||
57 | int cmp; | ||||
58 | |||||
59 | if (ignore_whitespace) { | ||||
60 | int il = 0, ir = 0; | ||||
61 | while (il < left_len && ir < right_len) { | ||||
62 | unsigned char cl = left[il]; | ||||
63 | unsigned char cr = right[ir]; | ||||
64 | |||||
65 | if (isspace(cl) && il < left_len) { | ||||
66 | il++; | ||||
67 | continue; | ||||
68 | } | ||||
69 | if (isspace(cr) && ir < right_len) { | ||||
70 | ir++; | ||||
71 | continue; | ||||
72 | } | ||||
73 | |||||
74 | if (cl > cr) | ||||
75 | return 1; | ||||
76 | if (cr > cl) | ||||
77 | return -1; | ||||
78 | il++; | ||||
79 | ir++; | ||||
80 | } | ||||
81 | while (il < left_len) { | ||||
82 | unsigned char cl = left[il++]; | ||||
83 | if (!isspace(cl)) | ||||
84 | return 1; | ||||
85 | } | ||||
86 | while (ir < right_len) { | ||||
87 | unsigned char cr = right[ir++]; | ||||
88 | if (!isspace(cr)) | ||||
89 | return -1; | ||||
90 | } | ||||
91 | |||||
92 | return 0; | ||||
93 | } | ||||
94 | |||||
95 | cmp = memcmp(left, right, MIN(left_len, right_len)((left_len)<(right_len)?(left_len):(right_len))); | ||||
96 | if (cmp) | ||||
97 | return cmp; | ||||
98 | if (left_len == right_len) | ||||
99 | return 0; | ||||
100 | return (left_len > right_len) ? 1 : -1; | ||||
101 | } | ||||
102 | |||||
103 | int | ||||
104 | diff_atom_cmp(int *cmp, | ||||
105 | const struct diff_atom *left, | ||||
106 | const struct diff_atom *right) | ||||
107 | { | ||||
108 | off_t remain_left, remain_right; | ||||
109 | int flags = (left->root->diff_flags | right->root->diff_flags); | ||||
110 | bool_Bool ignore_whitespace = (flags & DIFF_FLAG_IGNORE_WHITESPACE0x00000001); | ||||
111 | |||||
112 | if (!left->len && !right->len) { | ||||
113 | *cmp = 0; | ||||
114 | return 0; | ||||
115 | } | ||||
116 | if (!ignore_whitespace) { | ||||
117 | if (!right->len) { | ||||
118 | *cmp = 1; | ||||
119 | return 0; | ||||
120 | } | ||||
121 | if (!left->len) { | ||||
122 | *cmp = -1; | ||||
123 | return 0; | ||||
124 | } | ||||
125 | } | ||||
126 | |||||
127 | if (left->at != NULL((void *)0) && right->at != NULL((void *)0)) { | ||||
128 | *cmp = buf_cmp(left->at, left->len, right->at, right->len, | ||||
129 | ignore_whitespace); | ||||
130 | return 0; | ||||
131 | } | ||||
132 | |||||
133 | remain_left = left->len; | ||||
134 | remain_right = right->len; | ||||
135 | while (remain_left > 0 || remain_right > 0) { | ||||
136 | const size_t chunksz = 8192; | ||||
137 | unsigned char buf_left[chunksz], buf_right[chunksz]; | ||||
138 | const uint8_t *p_left, *p_right; | ||||
139 | off_t n_left, n_right; | ||||
140 | ssize_t r; | ||||
141 | |||||
142 | if (!remain_right) { | ||||
143 | *cmp = 1; | ||||
144 | return 0; | ||||
145 | } | ||||
146 | if (!remain_left) { | ||||
147 | *cmp = -1; | ||||
148 | return 0; | ||||
149 | } | ||||
150 | |||||
151 | n_left = MIN(chunksz, remain_left)((chunksz)<(remain_left)?(chunksz):(remain_left)); | ||||
152 | n_right = MIN(chunksz, remain_right)((chunksz)<(remain_right)?(chunksz):(remain_right)); | ||||
153 | |||||
154 | if (left->at == NULL((void *)0)) { | ||||
155 | r = read_at(left->root->f, | ||||
156 | left->pos + (left->len - remain_left), | ||||
157 | buf_left, n_left); | ||||
158 | if (r) { | ||||
159 | *cmp = 0; | ||||
160 | return r; | ||||
161 | } | ||||
162 | p_left = buf_left; | ||||
163 | } else { | ||||
164 | p_left = left->at + (left->len - remain_left); | ||||
165 | } | ||||
166 | |||||
167 | if (right->at == NULL((void *)0)) { | ||||
168 | r = read_at(right->root->f, | ||||
169 | right->pos + (right->len - remain_right), | ||||
170 | buf_right, n_right); | ||||
171 | if (r) { | ||||
172 | *cmp = 0; | ||||
173 | return r; | ||||
174 | } | ||||
175 | p_right = buf_right; | ||||
176 | } else { | ||||
177 | p_right = right->at + (right->len - remain_right); | ||||
178 | } | ||||
179 | |||||
180 | r = buf_cmp(p_left, n_left, p_right, n_right, | ||||
181 | ignore_whitespace); | ||||
182 | if (r) { | ||||
183 | *cmp = r; | ||||
184 | return 0; | ||||
185 | } | ||||
186 | |||||
187 | remain_left -= n_left; | ||||
188 | remain_right -= n_right; | ||||
189 | } | ||||
190 | |||||
191 | *cmp = 0; | ||||
192 | return 0; | ||||
193 | } | ||||
194 | |||||
195 | int | ||||
196 | diff_atom_same(bool_Bool *same, | ||||
197 | const struct diff_atom *left, | ||||
198 | const struct diff_atom *right) | ||||
199 | { | ||||
200 | int cmp; | ||||
201 | int r; | ||||
202 | if (left->hash != right->hash) { | ||||
203 | *same = false0; | ||||
204 | return 0; | ||||
205 | } | ||||
206 | r = diff_atom_cmp(&cmp, left, right); | ||||
207 | if (r) { | ||||
208 | *same = true1; | ||||
209 | return r; | ||||
210 | } | ||||
211 | *same = (cmp == 0); | ||||
212 | return 0; | ||||
213 | } | ||||
214 | |||||
215 | static struct diff_chunk * | ||||
216 | diff_state_add_solved_chunk(struct diff_state *state, | ||||
217 | const struct diff_chunk *chunk) | ||||
218 | { | ||||
219 | diff_chunk_arraylist_t *result; | ||||
220 | struct diff_chunk *new_chunk; | ||||
221 | enum diff_chunk_type last_t; | ||||
222 | enum diff_chunk_type new_t; | ||||
223 | struct diff_chunk *last; | ||||
224 | |||||
225 | /* Append to solved chunks; make sure that adjacent chunks of same type are combined, and that a minus chunk | ||||
226 | * never directly follows a plus chunk. */ | ||||
227 | result = &state->result->chunks; | ||||
228 | |||||
229 | last_t = result->len
| ||||
230 | : CHUNK_EMPTY; | ||||
231 | new_t = diff_chunk_type(chunk); | ||||
232 | |||||
233 | debug("ADD %s chunk #%u:\n", chunk->solved ? "solved" : "UNSOLVED", | ||||
234 | result->len); | ||||
235 | debug("L\n"); | ||||
236 | debug_dump_atoms(&state->left, chunk->left_start, chunk->left_count); | ||||
237 | debug("R\n"); | ||||
238 | debug_dump_atoms(&state->right, chunk->right_start, chunk->right_count); | ||||
239 | |||||
240 | if (result->len
| ||||
241 | last = &result->head[result->len - 1]; | ||||
242 | assert(chunk->left_start((chunk->left_start == last->left_start + last->left_count ) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c" , 243, __func__, "chunk->left_start == last->left_start + last->left_count" )) | ||||
243 | == last->left_start + last->left_count)((chunk->left_start == last->left_start + last->left_count ) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c" , 243, __func__, "chunk->left_start == last->left_start + last->left_count" )); | ||||
244 | assert(chunk->right_start((chunk->right_start == last->right_start + last->right_count ) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c" , 245, __func__, "chunk->right_start == last->right_start + last->right_count" )) | ||||
245 | == last->right_start + last->right_count)((chunk->right_start == last->right_start + last->right_count ) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c" , 245, __func__, "chunk->right_start == last->right_start + last->right_count" )); | ||||
246 | } | ||||
247 | |||||
248 | if (new_t == last_t) { | ||||
249 | new_chunk = &result->head[result->len - 1]; | ||||
250 | new_chunk->left_count += chunk->left_count; | ||||
| |||||
251 | new_chunk->right_count += chunk->right_count; | ||||
252 | debug(" - added chunk touches previous one of same type, joined:\n"); | ||||
253 | debug("L\n"); | ||||
254 | debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count); | ||||
255 | debug("R\n"); | ||||
256 | debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count); | ||||
257 | } else if (last_t == CHUNK_PLUS && new_t == CHUNK_MINUS) { | ||||
258 | enum diff_chunk_type prev_last_t = | ||||
259 | result->len > 1 ? | ||||
260 | diff_chunk_type(&result->head[result->len - 2]) | ||||
261 | : CHUNK_EMPTY; | ||||
262 | /* If a minus-chunk follows a plus-chunk, place it above the plus-chunk-> | ||||
263 | * Is the one before that also a minus? combine. */ | ||||
264 | if (prev_last_t == CHUNK_MINUS) { | ||||
265 | new_chunk = &result->head[result->len - 2]; | ||||
266 | new_chunk->left_count += chunk->left_count; | ||||
267 | new_chunk->right_count += chunk->right_count; | ||||
268 | |||||
269 | debug(" - added minus-chunk follows plus-chunk," | ||||
270 | " put before that plus-chunk and joined" | ||||
271 | " with preceding minus-chunk:\n"); | ||||
272 | debug("L\n"); | ||||
273 | debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count); | ||||
274 | debug("R\n"); | ||||
275 | debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count); | ||||
276 | } else { | ||||
277 | ARRAYLIST_INSERT(new_chunk, *result, result->len - 1)do { int _at_idx = (result->len - 1); do { if ((*result).len && !(*result).allocated) { new_chunk = ((void *)0); break ; } if ((*result).head == ((void *)0) || (*result).allocated < (*result).len + 1) { (*result).p = recallocarray((*result).head , (*result).len, (*result).allocated + ((*result).alloc_blocksize ? : 8), sizeof(*(*result).head)); if ((*result).p == ((void * )0)) { new_chunk = ((void *)0); break; } (*result).allocated += (*result).alloc_blocksize ? : 8; (*result).head = (*result). p; (*result).p = ((void *)0); }; if ((*result).head == ((void *)0) || (*result).allocated < (*result).len + 1) { new_chunk = ((void *)0); break; } (new_chunk) = &(*result).head[(* result).len]; (*result).len++; } while (0); if ((new_chunk) && _at_idx >= 0 && _at_idx < (*result).len) { memmove (&(*result).head[_at_idx + 1], &(*result).head[_at_idx ], ((*result).len - 1 - _at_idx) * sizeof(*(*result).head)); ( new_chunk) = &(*result).head[_at_idx]; }; } while (0); | ||||
278 | if (!new_chunk) | ||||
279 | return NULL((void *)0); | ||||
280 | *new_chunk = *chunk; | ||||
281 | |||||
282 | /* The new minus chunk indicates to which position on | ||||
283 | * the right it corresponds, even though it doesn't add | ||||
284 | * any lines on the right. By moving above a plus chunk, | ||||
285 | * that position on the right has shifted. */ | ||||
286 | last = &result->head[result->len - 1]; | ||||
287 | new_chunk->right_start = last->right_start; | ||||
288 | |||||
289 | debug(" - added minus-chunk follows plus-chunk," | ||||
290 | " put before that plus-chunk\n"); | ||||
291 | } | ||||
292 | |||||
293 | /* That last_t == CHUNK_PLUS indicates to which position on the | ||||
294 | * left it corresponds, even though it doesn't add any lines on | ||||
295 | * the left. By inserting/extending the prev_last_t == | ||||
296 | * CHUNK_MINUS, that position on the left has shifted. */ | ||||
297 | last = &result->head[result->len - 1]; | ||||
298 | last->left_start = new_chunk->left_start | ||||
299 | + new_chunk->left_count; | ||||
300 | |||||
301 | } else { | ||||
302 | ARRAYLIST_ADD(new_chunk, *result)do { if ((*result).len && !(*result).allocated) { new_chunk = ((void *)0); break; } if ((*result).head == ((void *)0) || (*result).allocated < (*result).len + 1) { (*result).p = recallocarray ((*result).head, (*result).len, (*result).allocated + ((*result ).alloc_blocksize ? : 8), sizeof(*(*result).head)); if ((*result ).p == ((void *)0)) { new_chunk = ((void *)0); break; } (*result ).allocated += (*result).alloc_blocksize ? : 8; (*result).head = (*result).p; (*result).p = ((void *)0); }; if ((*result).head == ((void *)0) || (*result).allocated < (*result).len + 1 ) { new_chunk = ((void *)0); break; } (new_chunk) = &(*result ).head[(*result).len]; (*result).len++; } while (0); | ||||
303 | if (!new_chunk) | ||||
304 | return NULL((void *)0); | ||||
305 | *new_chunk = *chunk; | ||||
306 | } | ||||
307 | return new_chunk; | ||||
308 | } | ||||
309 | |||||
310 | /* Even if a left or right side is empty, diff output may need to know the | ||||
311 | * position in that file. | ||||
312 | * So left_start or right_start must never be NULL -- pass left_count or | ||||
313 | * right_count as zero to indicate staying at that position without consuming | ||||
314 | * any lines. */ | ||||
315 | struct diff_chunk * | ||||
316 | diff_state_add_chunk(struct diff_state *state, bool_Bool solved, | ||||
317 | struct diff_atom *left_start, unsigned int left_count, | ||||
318 | struct diff_atom *right_start, unsigned int right_count) | ||||
319 | { | ||||
320 | struct diff_chunk *new_chunk; | ||||
321 | struct diff_chunk chunk = { | ||||
322 | .solved = solved, | ||||
323 | .left_start = left_start, | ||||
324 | .left_count = left_count, | ||||
325 | .right_start = right_start, | ||||
326 | .right_count = right_count, | ||||
327 | }; | ||||
328 | |||||
329 | /* An unsolved chunk means store as intermediate result for later | ||||
330 | * re-iteration. | ||||
331 | * If there already are intermediate results, that means even a | ||||
332 | * following solved chunk needs to go to intermediate results, so that | ||||
333 | * it is later put in the final correct position in solved chunks. | ||||
334 | */ | ||||
335 | if (!solved
| ||||
336 | /* Append to temp_result */ | ||||
337 | debug("ADD %s chunk to temp result:\n", | ||||
338 | chunk.solved ? "solved" : "UNSOLVED"); | ||||
339 | debug("L\n"); | ||||
340 | debug_dump_atoms(&state->left, left_start, left_count); | ||||
341 | debug("R\n"); | ||||
342 | debug_dump_atoms(&state->right, right_start, right_count); | ||||
343 | ARRAYLIST_ADD(new_chunk, state->temp_result)do { if ((state->temp_result).len && !(state->temp_result ).allocated) { new_chunk = ((void *)0); break; } if ((state-> temp_result).head == ((void *)0) || (state->temp_result).allocated < (state->temp_result).len + 1) { (state->temp_result ).p = recallocarray((state->temp_result).head, (state-> temp_result).len, (state->temp_result).allocated + ((state ->temp_result).alloc_blocksize ? : 8), sizeof(*(state-> temp_result).head)); if ((state->temp_result).p == ((void * )0)) { new_chunk = ((void *)0); break; } (state->temp_result ).allocated += (state->temp_result).alloc_blocksize ? : 8; (state->temp_result).head = (state->temp_result).p; (state ->temp_result).p = ((void *)0); }; if ((state->temp_result ).head == ((void *)0) || (state->temp_result).allocated < (state->temp_result).len + 1) { new_chunk = ((void *)0); break ; } (new_chunk) = &(state->temp_result).head[(state-> temp_result).len]; (state->temp_result).len++; } while (0); | ||||
344 | if (!new_chunk) | ||||
345 | return NULL((void *)0); | ||||
346 | *new_chunk = chunk; | ||||
347 | return new_chunk; | ||||
348 | } | ||||
349 | |||||
350 | return diff_state_add_solved_chunk(state, &chunk); | ||||
351 | } | ||||
352 | |||||
353 | void | ||||
354 | diff_data_init_root(struct diff_data *d, FILE *f, const uint8_t *data, | ||||
355 | unsigned long long len, int diff_flags) | ||||
356 | { | ||||
357 | *d = (struct diff_data){ | ||||
358 | .f = f, | ||||
359 | .pos = 0, | ||||
360 | .data = data, | ||||
361 | .len = len, | ||||
362 | .root = d, | ||||
363 | .diff_flags = diff_flags, | ||||
364 | }; | ||||
365 | } | ||||
366 | |||||
367 | void | ||||
368 | diff_data_init_subsection(struct diff_data *d, struct diff_data *parent, | ||||
369 | struct diff_atom *from_atom, unsigned int atoms_count) | ||||
370 | { | ||||
371 | struct diff_atom *last_atom; | ||||
372 | |||||
373 | debug("diff_data %p parent %p from_atom %p atoms_count %u\n", | ||||
374 | d, parent, from_atom, atoms_count); | ||||
375 | debug(" from_atom "); | ||||
376 | debug_dump_atom(parent, NULL, from_atom); | ||||
377 | |||||
378 | if (atoms_count == 0) { | ||||
379 | *d = (struct diff_data){ | ||||
380 | .f = NULL((void *)0), | ||||
381 | .pos = 0, | ||||
382 | .data = NULL((void *)0), | ||||
383 | .len = 0, | ||||
384 | .root = parent->root, | ||||
385 | .atoms.head = NULL((void *)0), | ||||
386 | .atoms.len = atoms_count, | ||||
387 | }; | ||||
388 | |||||
389 | return; | ||||
390 | } | ||||
391 | |||||
392 | last_atom = from_atom + atoms_count - 1; | ||||
393 | *d = (struct diff_data){ | ||||
394 | .f = NULL((void *)0), | ||||
395 | .pos = from_atom->pos, | ||||
396 | .data = from_atom->at, | ||||
397 | .len = (last_atom->pos + last_atom->len) - from_atom->pos, | ||||
398 | .root = parent->root, | ||||
399 | .atoms.head = from_atom, | ||||
400 | .atoms.len = atoms_count, | ||||
401 | }; | ||||
402 | |||||
403 | debug("subsection:\n"); | ||||
404 | debug_dump(d); | ||||
405 | } | ||||
406 | |||||
407 | void | ||||
408 | diff_data_free(struct diff_data *diff_data) | ||||
409 | { | ||||
410 | if (!diff_data) | ||||
411 | return; | ||||
412 | if (diff_data->atoms.allocated) | ||||
413 | ARRAYLIST_FREE(diff_data->atoms)do { if ((diff_data->atoms).head && (diff_data-> atoms).allocated) free((diff_data->atoms).head); do { (diff_data ->atoms).head = ((void *)0); (diff_data->atoms).len = 0 ; (diff_data->atoms).allocated = 0; (diff_data->atoms). alloc_blocksize = (diff_data->atoms).alloc_blocksize; } while (0); } while(0); | ||||
414 | } | ||||
415 | |||||
416 | int | ||||
417 | diff_algo_none(const struct diff_algo_config *algo_config, | ||||
418 | struct diff_state *state) | ||||
419 | { | ||||
420 | debug("\n** %s\n", __func__); | ||||
421 | debug("left:\n"); | ||||
422 | debug_dump(&state->left); | ||||
423 | debug("right:\n"); | ||||
424 | debug_dump(&state->right); | ||||
425 | debug_dump_myers_graph(&state->left, &state->right, NULL, NULL, 0, NULL, | ||||
426 | 0); | ||||
427 | |||||
428 | /* Add a chunk of equal lines, if any */ | ||||
429 | struct diff_atom *l = state->left.atoms.head; | ||||
430 | unsigned int l_len = state->left.atoms.len; | ||||
431 | struct diff_atom *r = state->right.atoms.head; | ||||
432 | unsigned int r_len = state->right.atoms.len; | ||||
433 | unsigned int equal_atoms_start = 0; | ||||
434 | unsigned int equal_atoms_end = 0; | ||||
435 | unsigned int l_idx = 0; | ||||
436 | unsigned int r_idx = 0; | ||||
437 | |||||
438 | while (equal_atoms_start
| ||||
439 | && equal_atoms_start
| ||||
440 | int err; | ||||
441 | bool_Bool same; | ||||
442 | err = diff_atom_same(&same, &l[equal_atoms_start], | ||||
443 | &r[equal_atoms_start]); | ||||
444 | if (err
| ||||
445 | return err; | ||||
446 | if (!same
| ||||
447 | break; | ||||
448 | equal_atoms_start++; | ||||
449 | } | ||||
450 | while (equal_atoms_end < (l_len - equal_atoms_start) | ||||
451 | && equal_atoms_end < (r_len - equal_atoms_start)) { | ||||
452 | int err; | ||||
453 | bool_Bool same; | ||||
454 | err = diff_atom_same(&same, &l[l_len - 1 - equal_atoms_end], | ||||
455 | &r[r_len - 1 - equal_atoms_end]); | ||||
456 | if (err
| ||||
457 | return err; | ||||
458 | if (!same
| ||||
459 | break; | ||||
460 | equal_atoms_end++; | ||||
461 | } | ||||
462 | |||||
463 | /* Add a chunk of equal lines at the start */ | ||||
464 | if (equal_atoms_start
| ||||
465 | if (!diff_state_add_chunk(state, true1, | ||||
466 | l, equal_atoms_start, | ||||
467 | r, equal_atoms_start)) | ||||
468 | return ENOMEM12; | ||||
469 | l_idx += equal_atoms_start; | ||||
470 | r_idx += equal_atoms_start; | ||||
471 | } | ||||
472 | |||||
473 | /* Add a "minus" chunk with all lines from the left. */ | ||||
474 | if (equal_atoms_start + equal_atoms_end < l_len) { | ||||
475 | unsigned int add_len = l_len - equal_atoms_start - equal_atoms_end; | ||||
476 | if (!diff_state_add_chunk(state, true1, | ||||
477 | &l[l_idx], add_len, | ||||
478 | &r[r_idx], 0)) | ||||
479 | return ENOMEM12; | ||||
480 | l_idx += add_len; | ||||
481 | } | ||||
482 | |||||
483 | /* Add a "plus" chunk with all lines from the right. */ | ||||
484 | if (equal_atoms_start + equal_atoms_end < r_len) { | ||||
485 | unsigned int add_len = r_len - equal_atoms_start - equal_atoms_end; | ||||
486 | if (!diff_state_add_chunk(state, true1, | ||||
487 | &l[l_idx], 0, | ||||
488 | &r[r_idx], add_len)) | ||||
489 | return ENOMEM12; | ||||
490 | r_idx += add_len; | ||||
491 | } | ||||
492 | |||||
493 | /* Add a chunk of equal lines at the end */ | ||||
494 | if (equal_atoms_end) { | ||||
495 | if (!diff_state_add_chunk(state, true1, | ||||
496 | &l[l_idx], equal_atoms_end, | ||||
497 | &r[r_idx], equal_atoms_end)) | ||||
498 | return ENOMEM12; | ||||
499 | } | ||||
500 | |||||
501 | return DIFF_RC_OK0; | ||||
502 | } | ||||
503 | |||||
504 | int | ||||
505 | diff_run_algo(const struct diff_algo_config *algo_config, | ||||
506 | struct diff_state *state) | ||||
507 | { | ||||
508 | int rc; | ||||
509 | |||||
510 | if (!algo_config || !algo_config->impl | ||||
511 | || !state->recursion_depth_left | ||||
512 | || !state->left.atoms.len || !state->right.atoms.len) { | ||||
513 | debug("Fall back to diff_algo_none():%s%s%s\n", | ||||
514 | (!algo_config || !algo_config->impl) ? " no-cfg" : "", | ||||
515 | (!state->recursion_depth_left) ? " max-depth" : "", | ||||
516 | (!state->left.atoms.len || !state->right.atoms.len)? | ||||
517 | " trivial" : ""); | ||||
518 | return diff_algo_none(algo_config, state); | ||||
519 | } | ||||
520 | |||||
521 | ARRAYLIST_FREE(state->temp_result)do { if ((state->temp_result).head && (state->temp_result ).allocated) free((state->temp_result).head); do { (state-> temp_result).head = ((void *)0); (state->temp_result).len = 0; (state->temp_result).allocated = 0; (state->temp_result ).alloc_blocksize = (state->temp_result).alloc_blocksize; } while(0); } while(0); | ||||
522 | ARRAYLIST_INIT(state->temp_result, DIFF_RESULT_ALLOC_BLOCKSIZE)do { (state->temp_result).head = ((void *)0); (state->temp_result ).len = 0; (state->temp_result).allocated = 0; (state-> temp_result).alloc_blocksize = 128; } while(0); | ||||
523 | rc = algo_config->impl(algo_config, state); | ||||
524 | switch (rc) { | ||||
525 | case DIFF_RC_USE_DIFF_ALGO_FALLBACK-1: | ||||
526 | debug("Got DIFF_RC_USE_DIFF_ALGO_FALLBACK (%p)\n", | ||||
527 | algo_config->fallback_algo); | ||||
528 | rc = diff_run_algo(algo_config->fallback_algo, state); | ||||
529 | goto return_rc; | ||||
530 | |||||
531 | case DIFF_RC_OK0: | ||||
532 | /* continue below */ | ||||
533 | break; | ||||
534 | |||||
535 | default: | ||||
536 | /* some error happened */ | ||||
537 | goto return_rc; | ||||
538 | } | ||||
539 | |||||
540 | /* Pick up any diff chunks that are still unsolved and feed to | ||||
541 | * inner_algo. inner_algo will solve unsolved chunks and append to | ||||
542 | * result, and subsequent solved chunks on this level are then appended | ||||
543 | * to result afterwards. */ | ||||
544 | int i; | ||||
545 | for (i = 0; i < state->temp_result.len; i++) { | ||||
546 | struct diff_chunk *c = &state->temp_result.head[i]; | ||||
547 | if (c->solved) { | ||||
548 | diff_state_add_solved_chunk(state, c); | ||||
549 | continue; | ||||
550 | } | ||||
551 | |||||
552 | /* c is an unsolved chunk, feed to inner_algo */ | ||||
553 | struct diff_state inner_state = { | ||||
554 | .result = state->result, | ||||
555 | .recursion_depth_left = state->recursion_depth_left - 1, | ||||
556 | .kd_buf = state->kd_buf, | ||||
557 | .kd_buf_size = state->kd_buf_size, | ||||
558 | }; | ||||
559 | diff_data_init_subsection(&inner_state.left, &state->left, | ||||
560 | c->left_start, c->left_count); | ||||
561 | diff_data_init_subsection(&inner_state.right, &state->right, | ||||
562 | c->right_start, c->right_count); | ||||
563 | |||||
564 | rc = diff_run_algo(algo_config->inner_algo, &inner_state); | ||||
565 | state->kd_buf = inner_state.kd_buf; | ||||
566 | state->kd_buf_size = inner_state.kd_buf_size; | ||||
567 | if (rc != DIFF_RC_OK0) | ||||
568 | goto return_rc; | ||||
569 | } | ||||
570 | |||||
571 | rc = DIFF_RC_OK0; | ||||
572 | return_rc: | ||||
573 | ARRAYLIST_FREE(state->temp_result)do { if ((state->temp_result).head && (state->temp_result ).allocated) free((state->temp_result).head); do { (state-> temp_result).head = ((void *)0); (state->temp_result).len = 0; (state->temp_result).allocated = 0; (state->temp_result ).alloc_blocksize = (state->temp_result).alloc_blocksize; } while(0); } while(0); | ||||
574 | return rc; | ||||
575 | } | ||||
576 | |||||
577 | int | ||||
578 | diff_atomize_file(struct diff_data *d, | ||||
579 | const struct diff_config *config, | ||||
580 | FILE *f, const uint8_t *data, off_t len, int diff_flags) | ||||
581 | { | ||||
582 | if (!config->atomize_func) | ||||
583 | return EINVAL22; | ||||
584 | |||||
585 | diff_data_init_root(d, f, data, len, diff_flags); | ||||
586 | |||||
587 | return config->atomize_func(config->atomize_func_data, d); | ||||
588 | |||||
589 | } | ||||
590 | |||||
591 | struct diff_result * | ||||
592 | diff_main(const struct diff_config *config, struct diff_data *left, | ||||
593 | struct diff_data *right) | ||||
594 | { | ||||
595 | struct diff_result *result = malloc(sizeof(struct diff_result)); | ||||
596 | if (!result) | ||||
| |||||
597 | return NULL((void *)0); | ||||
598 | |||||
599 | *result = (struct diff_result){}; | ||||
600 | result->left = left; | ||||
601 | result->right = right; | ||||
602 | |||||
603 | struct diff_state state = { | ||||
604 | .result = result, | ||||
605 | .recursion_depth_left = config->max_recursion_depth ? : UINT_MAX(2147483647 *2U +1U), | ||||
606 | .kd_buf = NULL((void *)0), | ||||
607 | .kd_buf_size = 0, | ||||
608 | }; | ||||
609 | diff_data_init_subsection(&state.left, left, | ||||
610 | left->atoms.head, | ||||
611 | left->atoms.len); | ||||
612 | diff_data_init_subsection(&state.right, right, | ||||
613 | right->atoms.head, | ||||
614 | right->atoms.len); | ||||
615 | |||||
616 | result->rc = diff_run_algo(config->algo, &state); | ||||
617 | free(state.kd_buf); | ||||
618 | |||||
619 | return result; | ||||
620 | } | ||||
621 | |||||
622 | void | ||||
623 | diff_result_free(struct diff_result *result) | ||||
624 | { | ||||
625 | if (!result) | ||||
626 | return; | ||||
627 | ARRAYLIST_FREE(result->chunks)do { if ((result->chunks).head && (result->chunks ).allocated) free((result->chunks).head); do { (result-> chunks).head = ((void *)0); (result->chunks).len = 0; (result ->chunks).allocated = 0; (result->chunks).alloc_blocksize = (result->chunks).alloc_blocksize; } while(0); } while(0 ); | ||||
628 | free(result); | ||||
629 | } |