| File: | got/../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/got/../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/got/../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/got/../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/got/../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 | } |