/* See LICENSE file for copyright and license details. */ #include "common.h" #define REPETITION_MAX ((1U << (sizeof(int) * CHAR_BIT - CHAR_BIT)) - 1U) static int allocate(struct libpatch_diff2 **diff, size_t *difflen, size_t *r, size_t *w, int change) { #define GROWTH 32U void *new; size_t size; if (*w == *r) { if (GROWTH > SIZE_MAX / sizeof(**diff) - *difflen) { errno = ENOMEM; return -1; } size = (*difflen + GROWTH) * sizeof(**diff); new = realloc(*diff, size); if (!new) return -1; *diff = new; memmove(&(*diff)[*r + GROWTH], &(*diff)[*r], *difflen - *r); *difflen += GROWTH; *r += GROWTH; } (*diff)[*w].change = (unsigned char)change; (*diff)[*w].repetition = 0; *w += 1; return 0; } static int putchange(struct libpatch_diff2 **diff, size_t *difflen, size_t *r, size_t *w, int change, size_t count) { if (!count) return 0; if (*w && (*diff)[*w - 1].change == change && (*diff)[*w - 1].repetition < REPETITION_MAX) goto start; do { if (allocate(diff, difflen, r, w, change)) return -1; start: if (count > (size_t)(REPETITION_MAX - (*diff)[*w - 1].repetition)) { count -= (size_t)(REPETITION_MAX - (*diff)[*w - 1].repetition); (*diff)[*w - 1].repetition = REPETITION_MAX; } else { (*diff)[*w - 1].repetition += count; break; } } while (count); return 0; } static int put(struct libpatch_diff2 **diff, size_t *difflen, const struct libpatch_diff2_spec *spec, size_t *r, size_t *w, size_t common, size_t onlyfile1, size_t onlyfile2, size_t changed) { int a, b; size_t an, bn, i; size_t context, a_context, z_context; if (common) { if (spec->full_context) { return putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, common); } else if (*w > 0 && *r < *difflen) { context = MAX(spec->context_after + spec->context_before, spec->context_between); if (common <= context) return putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, common); common -= a_context = MIN(spec->context_after, common); common -= z_context = MIN(spec->context_before, common); return -(putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, a_context) || putchange(diff, difflen, r, w, LIBPATCH_DIFF2_SKIP, common) || putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, z_context)); } else if (*w > 0) { common -= context = MIN(spec->context_after, common); return -(putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, context) || putchange(diff, difflen, r, w, LIBPATCH_DIFF2_SKIP, common)); } else if (*r < *difflen) { common -= context = MIN(spec->context_before, common); return -(putchange(diff, difflen, r, w, LIBPATCH_DIFF2_SKIP, common) || putchange(diff, difflen, r, w, LIBPATCH_DIFF2_CONTEXT, context)); } else { return putchange(diff, difflen, r, w, LIBPATCH_DIFF2_SKIP, common); } } else { if (!spec->keep_split_changes) { changed += MIN(onlyfile1, onlyfile2); onlyfile1 -= changed; onlyfile2 -= changed; } if (spec->single_file_change_order == LIBPATCH_DIFF2_FILE1_ONLY_FIRST) { a = LIBPATCH_DIFF2_FILE1_ONLY; an = onlyfile1; b = LIBPATCH_DIFF2_FILE2_ONLY; bn = onlyfile2; } else { a = LIBPATCH_DIFF2_FILE2_ONLY; an = onlyfile2; b = LIBPATCH_DIFF2_FILE1_ONLY; bn = onlyfile1; } if (spec->two_file_change_order == LIBPATCH_DIFF2_TWO_FILE_CHANGES_BEFORE_SINGLE_FILE) if (putchange(diff, difflen, r, w, LIBPATCH_DIFF2_TWO_FILE_CHANGE, changed)) return -1; if (putchange(diff, difflen, r, w, a, an)) return -1; if (spec->two_file_change_order == LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES) { if (putchange(diff, difflen, r, w, a, changed)) return -1; if (putchange(diff, difflen, r, w, b, changed)) return -1; } else if (spec->two_file_change_order == LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES_INTERLEAVE) { for (i = 0; i < changed; i++) { if (putchange(diff, difflen, r, w, a, 1)) return -1; if (putchange(diff, difflen, r, w, b, 1)) return -1; } } if (spec->two_file_change_order == LIBPATCH_DIFF2_TWO_FILE_CHANGES_BETWEEN_SINGLE_FILE) if (putchange(diff, difflen, r, w, LIBPATCH_DIFF2_TWO_FILE_CHANGE, changed)) return -1; if (putchange(diff, difflen, r, w, b, bn)) return -1; if (spec->two_file_change_order == LIBPATCH_DIFF2_TWO_FILE_CHANGES_AFTER_SINGLE_FILE) if (putchange(diff, difflen, r, w, LIBPATCH_DIFF2_TWO_FILE_CHANGE, changed)) return -1; return 0; } } int libpatch_rewrite_diff2(struct libpatch_diff2 **diff, size_t *difflen, const struct libpatch_diff2_spec *spec) { size_t r, w = 0; size_t saved0 = 0, saved1 = 0, saved2 = 0, saved3 = 0; for (r = 0; r < *difflen; r++) { if ((*diff)[r].change == LIBPATCH_DIFF2_SKIP || (*diff)[r].change == LIBPATCH_DIFF2_CONTEXT) { if (saved1 || saved2) { if (put(diff, difflen, spec, &r, &w, saved0, saved1, saved2, saved3)) return -1; saved1 = 0; saved2 = 0; saved3 = 0; } saved0 += (size_t)(*diff)[r].repetition; } else { if (saved0) { if (put(diff, difflen, spec, &r, &w, saved0, saved1, saved2, saved3)) return -1; saved0 = 0; } if (spec->keep_split_changes) { if ((*diff)[r].change == LIBPATCH_DIFF2_FILE1_ONLY) saved1 += (size_t)(*diff)[r].repetition; else if ((*diff)[r].change == LIBPATCH_DIFF2_FILE2_ONLY) saved2 += (size_t)(*diff)[r].repetition; else saved3 += (size_t)(*diff)[r].repetition; } else { if ((*diff)[r].change != LIBPATCH_DIFF2_FILE2_ONLY) saved1 += (size_t)(*diff)[r].repetition; if ((*diff)[r].change != LIBPATCH_DIFF2_FILE1_ONLY) saved2 += (size_t)(*diff)[r].repetition; } } } if (put(diff, difflen, spec, &r, &w, saved0, saved1, saved2, saved3)) return -1; *difflen = w; return 0; }