37#define DIFF_OUT_PADDING 256
39#define DIFF_HEADER_BUF 128
61 size_t len = 0, nb = 1, idx = 0;
68 if (!text || !text[0])
return TRUE;
71 for (
size_t i = 0; i < len; i++) {
72 if (text[i] ==
'\n') nb++;
74 if (text[len - 1] ==
'\n') nb--;
75 if (nb == 0)
return TRUE;
78 if (!t->
buf)
return FALSE;
79 memcpy(t->
buf, text, len + 1);
88 for (
size_t i = 0; i <= len && idx < nb; i++) {
89 if (t->
buf[i] ==
'\n' || t->
buf[i] ==
'\0') {
93 start = t->
buf + i + 1;
114 if (!
line)
return FALSE;
117 line->line_a = line_a;
118 line->line_b = line_b;
119 line->text = strdup(text ? text :
"");
144static int _myers(
N_DIFF_RESULT* result,
char** a,
int n,
char** b,
int m,
size_t base_a,
size_t base_b,
int max_edits) {
148 int voff = 0, found = -1, ret = -1;
150 if (maxd > max_edits) maxd = max_edits;
153 Malloc(v,
int, (
size_t)(2 * maxd + 3));
155 Malloc(trace,
int*, (
size_t)(maxd + 1));
161 for (
int d = 0; d <= maxd; d++) {
165 Malloc(snap,
int, (
size_t)(2 * d + 3));
166 if (!snap)
goto cleanup;
167 for (
int k = -d - 1; k <= d + 1; k++) {
168 snap[k + d + 1] = v[k + voff];
172 for (
int k = -d; k <= d; k += 2) {
174 if (k == -d || (k != d && v[k - 1 + voff] < v[k + 1 + voff])) {
179 x = v[k - 1 + voff] + 1;
182 while (x < n && y < m && strcmp(a[x], b[y]) == 0) {
187 if (x >= n && y >= m) {
192 if (found >= 0)
break;
202 for (
int d = found; d >= 0; d--) {
203 const int* snap = trace[d];
205 int prev_k = 0, prev_x = 0, prev_y = 0;
208 if (k == -d || (k != d && snap[k - 1 + d + 1] < snap[k + 1 + d + 1])) {
213 prev_x = snap[prev_k + d + 1];
214 prev_y = prev_x - prev_k;
215 if (prev_x < 0 || prev_x > n || prev_y < 0 || prev_y > m) {
216 n_log(
LOG_ERR,
"n_diff: inconsistent trace at d %d, giving up", d);
223 while (x > prev_x && y > prev_y) {
226 if (!
_emit(result,
N_DIFF_COMMON, a[x], base_a + (
size_t)x, base_b + (
size_t)y))
goto cleanup;
243 for (
int d = 0; d <= maxd; d++) {
255 size_t pre = 0, suf = 0, mid_n = 0, mid_m = 0;
268 if (!result)
goto fail;
270 if (!result->
lines)
goto fail;
279 while (suf < ta.
count - pre && suf < tb.
count - pre &&
283 mid_n = ta.
count - pre - suf;
284 mid_m = tb.
count - pre - suf;
287 for (
size_t i = 0; i < suf; i++) {
288 size_t ia = ta.
count - 1 - i;
289 size_t ib = tb.
count - 1 - i;
293 if (mid_n == 0 && mid_m == 0) {
295 }
else if (mid_n > INT_MAX / 2 || mid_m > INT_MAX / 2) {
299 rc =
_myers(result, ta.
line + pre, (
int)mid_n, tb.
line + pre, (
int)mid_m, pre, pre, (
int)max_edits);
301 if (rc < 0)
goto fail;
308 for (
size_t i = mid_m; i > 0; i--) {
309 size_t ib = pre + i - 1;
312 for (
size_t i = mid_n; i > 0; i--) {
313 size_t ia = pre + i - 1;
318 for (
size_t i = pre; i > 0; i--) {
334 if (!result || !*result)
return;
335 if ((*result)->lines) {
353 if (n == 0)
return TRUE;
386 if (!
_out_mem(o, &marker, 1))
return FALSE;
394 size_t* a_before = NULL;
395 size_t* b_before = NULL;
396 size_t count = 0, idx = 0, seen_a = 0, seen_b = 0;
401 memset(&out, 0,
sizeof(out));
418 if (!
line)
goto fail;
419 Malloc(a_before,
size_t, count);
420 if (!a_before)
goto fail;
421 Malloc(b_before,
size_t, count);
422 if (!b_before)
goto fail;
430 a_before[idx] = seen_a;
431 b_before[idx] = seen_b;
445 while (idx < count) {
446 size_t start = 0, end = 0,
last = 0, scan = 0;
447 size_t len_a = 0, len_b = 0, start_a = 0, start_b = 0;
455 start = (idx > context) ? (idx - context) : 0;
457 for (scan = idx; scan < count; scan++) {
460 }
else if (scan -
last > 2 * context) {
466 end =
last + context;
467 if (end >= count) end = count - 1;
469 for (scan = start; scan <= end; scan++) {
473 start_a = len_a ? a_before[start] + 1 : a_before[start];
474 start_b = len_b ? b_before[start] + 1 : b_before[start];
476 snprintf(header,
sizeof(header),
"@@ -%zu,%zu +%zu,%zu @@\n", start_a, len_a, start_b, len_b);
477 if (!
_out_str(&out, header))
goto fail;
478 for (scan = start; scan <= end; scan++) {
static N_GUI_MOUSE_EVENT last
the last event the widget was told of, and how many
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
size_t nb_items
number of item currently in the list
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
int list_unshift(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer at the start of the list.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
int truncated
1 when the edit limit was hit and the differing region was reported as one delete run followed by one...
size_t nb_deleted
number of N_DIFF_DELETE lines
size_t nb_inserted
number of N_DIFF_INSERT lines
size_t nb_common
number of N_DIFF_COMMON lines
int op
N_DIFF_COMMON, N_DIFF_DELETE or N_DIFF_INSERT.
LIST * lines
LIST of N_DIFF_LINE*, in output order.
void n_diff_result_free(N_DIFF_RESULT **result)
Free an edit script and every line it holds.
#define N_DIFF_MAX_EDITS_CEILING
hard ceiling n_diff_lines() clamps max_edits to, keeping the worst case trace under about 64 MB
#define N_DIFF_NO_LINE
N_DIFF_LINE line_a / line_b value when the line has no counterpart there.
#define N_DIFF_DELETE
the line is only in the first text
#define N_DIFF_COMMON
the line is present in both texts
#define N_DIFF_MAX_EDITS_DEFAULT
n_diff_lines() default ceiling on the number of differing lines searched
#define N_DIFF_INSERT
the line is only in the second text
#define N_DIFF_CONTEXT_FULL
n_diff_to_unified() context asking for every common line, without hunk headers
N_DIFF_RESULT * n_diff_lines(const char *text_a, const char *text_b, size_t max_edits)
Compare two texts line by line.
N_STR * n_diff_to_unified(const N_DIFF_RESULT *result, size_t context)
Render an edit script as unified diff text.
one line of an edit script
edit script turning one text into another
size_t NSTRBYTE
N_STR base unit.
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
N_STR * char_to_nstr_nocopy(char *src)
Convert a char into a N_STR, direct use of linked source pointer.
int write_and_fit_ex(char **dest, NSTRBYTE *size, NSTRBYTE *written, const char *src, NSTRBYTE src_size, NSTRBYTE additional_padding)
Append src_size bytes from src to *dest, updating size/written, growing the buffer if needed.
A box including a string and his lenght.
Common headers and low-level functions & define.
static int _out_mem(DIFF_OUT *o, const char *s, size_t n)
append n bytes of s to the output, TRUE on success
static void _text_free(DIFF_TEXT *t)
release a DIFF_TEXT, leaving it empty
static int _myers(N_DIFF_RESULT *result, char **a, int n, char **b, int m, size_t base_a, size_t base_b, int max_edits)
Myers greedy forward pass with a stored trace, then a backtrack emitting the script in reverse.
#define DIFF_OUT_PADDING
growth headroom so consecutive short appends share one realloc
static int _text_load(DIFF_TEXT *t, const char *text)
split text into lines.
static void _line_free(void *ptr)
destructor for the N_DIFF_LINE entries of N_DIFF_RESULT.lines
static N_STR * _out_finish(DIFF_OUT *o)
hand the buffer over to a new N_STR (no copy); the DIFF_OUT is emptied
static int _emit(N_DIFF_RESULT *result, int op, const char *text, size_t line_a, size_t line_b)
prepend one line to the script, which is built back to front.
char ** line
start of each line inside buf
static int _out_line(DIFF_OUT *o, const N_DIFF_LINE *line)
append one script line as "<marker>text\n", TRUE on success
NSTRBYTE written
meaningful bytes, excluding the terminating '\0'
char * buf
buffer, reallocated on growth
static char _op_char(int op)
unified diff marker for an operation
#define DIFF_HEADER_BUF
widest "@@ -a,b +c,d @@" header we can produce
size_t count
number of lines
char * buf
copy of the input with every ' ' turned into '\0'
NSTRBYTE size
total allocation in bytes
static int _out_str(DIFF_OUT *o, const char *s)
append a NULL terminated string to the output, TRUE on success
growing output buffer fed by write_and_fit_ex
a text split into lines, all pointing into one owned buffer
Line oriented text comparison (Myers shortest edit script).