159c8e88eSDag-Erling Smørgrav /* Generic infrastructure to implement various diff algorithms (implementation). */
259c8e88eSDag-Erling Smørgrav /*
359c8e88eSDag-Erling Smørgrav * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
459c8e88eSDag-Erling Smørgrav *
559c8e88eSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any
659c8e88eSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above
759c8e88eSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies.
859c8e88eSDag-Erling Smørgrav *
959c8e88eSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1059c8e88eSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1159c8e88eSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1259c8e88eSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1359c8e88eSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459c8e88eSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1559c8e88eSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1659c8e88eSDag-Erling Smørgrav */
1759c8e88eSDag-Erling Smørgrav
1859c8e88eSDag-Erling Smørgrav
1959c8e88eSDag-Erling Smørgrav #include <sys/queue.h>
2059c8e88eSDag-Erling Smørgrav #include <ctype.h>
2159c8e88eSDag-Erling Smørgrav #include <errno.h>
2259c8e88eSDag-Erling Smørgrav #include <stdint.h>
2359c8e88eSDag-Erling Smørgrav #include <stdlib.h>
2459c8e88eSDag-Erling Smørgrav #include <stdbool.h>
2559c8e88eSDag-Erling Smørgrav #include <stdio.h>
2659c8e88eSDag-Erling Smørgrav #include <string.h>
2759c8e88eSDag-Erling Smørgrav #include <limits.h>
2859c8e88eSDag-Erling Smørgrav #include <unistd.h>
2959c8e88eSDag-Erling Smørgrav
3059c8e88eSDag-Erling Smørgrav #include <assert.h>
3159c8e88eSDag-Erling Smørgrav
3259c8e88eSDag-Erling Smørgrav #include <arraylist.h>
3359c8e88eSDag-Erling Smørgrav #include <diff_main.h>
3459c8e88eSDag-Erling Smørgrav
3559c8e88eSDag-Erling Smørgrav #include "diff_internal.h"
3659c8e88eSDag-Erling Smørgrav #include "diff_debug.h"
3759c8e88eSDag-Erling Smørgrav
3859c8e88eSDag-Erling Smørgrav inline enum diff_chunk_type
diff_chunk_type(const struct diff_chunk * chunk)3959c8e88eSDag-Erling Smørgrav diff_chunk_type(const struct diff_chunk *chunk)
4059c8e88eSDag-Erling Smørgrav {
4159c8e88eSDag-Erling Smørgrav if (!chunk->left_count && !chunk->right_count)
4259c8e88eSDag-Erling Smørgrav return CHUNK_EMPTY;
4359c8e88eSDag-Erling Smørgrav if (!chunk->solved)
4459c8e88eSDag-Erling Smørgrav return CHUNK_ERROR;
4559c8e88eSDag-Erling Smørgrav if (!chunk->right_count)
4659c8e88eSDag-Erling Smørgrav return CHUNK_MINUS;
4759c8e88eSDag-Erling Smørgrav if (!chunk->left_count)
4859c8e88eSDag-Erling Smørgrav return CHUNK_PLUS;
4959c8e88eSDag-Erling Smørgrav if (chunk->left_count != chunk->right_count)
5059c8e88eSDag-Erling Smørgrav return CHUNK_ERROR;
5159c8e88eSDag-Erling Smørgrav return CHUNK_SAME;
5259c8e88eSDag-Erling Smørgrav }
5359c8e88eSDag-Erling Smørgrav
5459c8e88eSDag-Erling Smørgrav static int
read_at(FILE * f,off_t at_pos,unsigned char * buf,size_t len)5559c8e88eSDag-Erling Smørgrav read_at(FILE *f, off_t at_pos, unsigned char *buf, size_t len)
5659c8e88eSDag-Erling Smørgrav {
57*0549218bSDag-Erling Smørgrav ssize_t r;
58*0549218bSDag-Erling Smørgrav
5959c8e88eSDag-Erling Smørgrav if (fseeko(f, at_pos, SEEK_SET) == -1)
6059c8e88eSDag-Erling Smørgrav return errno;
6159c8e88eSDag-Erling Smørgrav r = fread(buf, sizeof(char), len, f);
6259c8e88eSDag-Erling Smørgrav if ((r == 0 || r < len) && ferror(f))
6359c8e88eSDag-Erling Smørgrav return EIO;
6459c8e88eSDag-Erling Smørgrav if (r != len)
6559c8e88eSDag-Erling Smørgrav return EIO;
6659c8e88eSDag-Erling Smørgrav return 0;
6759c8e88eSDag-Erling Smørgrav }
6859c8e88eSDag-Erling Smørgrav
6959c8e88eSDag-Erling Smørgrav static int
buf_cmp(const unsigned char * left,size_t left_len,const unsigned char * right,size_t right_len,bool ignore_whitespace)7059c8e88eSDag-Erling Smørgrav buf_cmp(const unsigned char *left, size_t left_len,
7159c8e88eSDag-Erling Smørgrav const unsigned char *right, size_t right_len,
7259c8e88eSDag-Erling Smørgrav bool ignore_whitespace)
7359c8e88eSDag-Erling Smørgrav {
7459c8e88eSDag-Erling Smørgrav int cmp;
7559c8e88eSDag-Erling Smørgrav
7659c8e88eSDag-Erling Smørgrav if (ignore_whitespace) {
7759c8e88eSDag-Erling Smørgrav int il = 0, ir = 0;
7859c8e88eSDag-Erling Smørgrav while (il < left_len && ir < right_len) {
7959c8e88eSDag-Erling Smørgrav unsigned char cl = left[il];
8059c8e88eSDag-Erling Smørgrav unsigned char cr = right[ir];
8159c8e88eSDag-Erling Smørgrav
8259c8e88eSDag-Erling Smørgrav if (isspace((unsigned char)cl) && il < left_len) {
8359c8e88eSDag-Erling Smørgrav il++;
8459c8e88eSDag-Erling Smørgrav continue;
8559c8e88eSDag-Erling Smørgrav }
8659c8e88eSDag-Erling Smørgrav if (isspace((unsigned char)cr) && ir < right_len) {
8759c8e88eSDag-Erling Smørgrav ir++;
8859c8e88eSDag-Erling Smørgrav continue;
8959c8e88eSDag-Erling Smørgrav }
9059c8e88eSDag-Erling Smørgrav
9159c8e88eSDag-Erling Smørgrav if (cl > cr)
9259c8e88eSDag-Erling Smørgrav return 1;
9359c8e88eSDag-Erling Smørgrav if (cr > cl)
9459c8e88eSDag-Erling Smørgrav return -1;
9559c8e88eSDag-Erling Smørgrav il++;
9659c8e88eSDag-Erling Smørgrav ir++;
9759c8e88eSDag-Erling Smørgrav }
9859c8e88eSDag-Erling Smørgrav while (il < left_len) {
9959c8e88eSDag-Erling Smørgrav unsigned char cl = left[il++];
10059c8e88eSDag-Erling Smørgrav if (!isspace((unsigned char)cl))
10159c8e88eSDag-Erling Smørgrav return 1;
10259c8e88eSDag-Erling Smørgrav }
10359c8e88eSDag-Erling Smørgrav while (ir < right_len) {
10459c8e88eSDag-Erling Smørgrav unsigned char cr = right[ir++];
10559c8e88eSDag-Erling Smørgrav if (!isspace((unsigned char)cr))
10659c8e88eSDag-Erling Smørgrav return -1;
10759c8e88eSDag-Erling Smørgrav }
10859c8e88eSDag-Erling Smørgrav
10959c8e88eSDag-Erling Smørgrav return 0;
11059c8e88eSDag-Erling Smørgrav }
11159c8e88eSDag-Erling Smørgrav
11259c8e88eSDag-Erling Smørgrav cmp = memcmp(left, right, MIN(left_len, right_len));
11359c8e88eSDag-Erling Smørgrav if (cmp)
11459c8e88eSDag-Erling Smørgrav return cmp;
11559c8e88eSDag-Erling Smørgrav if (left_len == right_len)
11659c8e88eSDag-Erling Smørgrav return 0;
11759c8e88eSDag-Erling Smørgrav return (left_len > right_len) ? 1 : -1;
11859c8e88eSDag-Erling Smørgrav }
11959c8e88eSDag-Erling Smørgrav
12059c8e88eSDag-Erling Smørgrav int
diff_atom_cmp(int * cmp,const struct diff_atom * left,const struct diff_atom * right)12159c8e88eSDag-Erling Smørgrav diff_atom_cmp(int *cmp,
12259c8e88eSDag-Erling Smørgrav const struct diff_atom *left,
12359c8e88eSDag-Erling Smørgrav const struct diff_atom *right)
12459c8e88eSDag-Erling Smørgrav {
12559c8e88eSDag-Erling Smørgrav off_t remain_left, remain_right;
12659c8e88eSDag-Erling Smørgrav int flags = (left->root->diff_flags | right->root->diff_flags);
12759c8e88eSDag-Erling Smørgrav bool ignore_whitespace = (flags & DIFF_FLAG_IGNORE_WHITESPACE);
12859c8e88eSDag-Erling Smørgrav
12959c8e88eSDag-Erling Smørgrav if (!left->len && !right->len) {
13059c8e88eSDag-Erling Smørgrav *cmp = 0;
13159c8e88eSDag-Erling Smørgrav return 0;
13259c8e88eSDag-Erling Smørgrav }
13359c8e88eSDag-Erling Smørgrav if (!ignore_whitespace) {
13459c8e88eSDag-Erling Smørgrav if (!right->len) {
13559c8e88eSDag-Erling Smørgrav *cmp = 1;
13659c8e88eSDag-Erling Smørgrav return 0;
13759c8e88eSDag-Erling Smørgrav }
13859c8e88eSDag-Erling Smørgrav if (!left->len) {
13959c8e88eSDag-Erling Smørgrav *cmp = -1;
14059c8e88eSDag-Erling Smørgrav return 0;
14159c8e88eSDag-Erling Smørgrav }
14259c8e88eSDag-Erling Smørgrav }
14359c8e88eSDag-Erling Smørgrav
14459c8e88eSDag-Erling Smørgrav if (left->at != NULL && right->at != NULL) {
14559c8e88eSDag-Erling Smørgrav *cmp = buf_cmp(left->at, left->len, right->at, right->len,
14659c8e88eSDag-Erling Smørgrav ignore_whitespace);
14759c8e88eSDag-Erling Smørgrav return 0;
14859c8e88eSDag-Erling Smørgrav }
14959c8e88eSDag-Erling Smørgrav
15059c8e88eSDag-Erling Smørgrav remain_left = left->len;
15159c8e88eSDag-Erling Smørgrav remain_right = right->len;
15259c8e88eSDag-Erling Smørgrav while (remain_left > 0 || remain_right > 0) {
15359c8e88eSDag-Erling Smørgrav const size_t chunksz = 8192;
15459c8e88eSDag-Erling Smørgrav unsigned char buf_left[chunksz], buf_right[chunksz];
15559c8e88eSDag-Erling Smørgrav const uint8_t *p_left, *p_right;
15659c8e88eSDag-Erling Smørgrav off_t n_left, n_right;
157*0549218bSDag-Erling Smørgrav int r;
15859c8e88eSDag-Erling Smørgrav
15959c8e88eSDag-Erling Smørgrav if (!remain_right) {
16059c8e88eSDag-Erling Smørgrav *cmp = 1;
16159c8e88eSDag-Erling Smørgrav return 0;
16259c8e88eSDag-Erling Smørgrav }
16359c8e88eSDag-Erling Smørgrav if (!remain_left) {
16459c8e88eSDag-Erling Smørgrav *cmp = -1;
16559c8e88eSDag-Erling Smørgrav return 0;
16659c8e88eSDag-Erling Smørgrav }
16759c8e88eSDag-Erling Smørgrav
16859c8e88eSDag-Erling Smørgrav n_left = MIN(chunksz, remain_left);
16959c8e88eSDag-Erling Smørgrav n_right = MIN(chunksz, remain_right);
17059c8e88eSDag-Erling Smørgrav
17159c8e88eSDag-Erling Smørgrav if (left->at == NULL) {
17259c8e88eSDag-Erling Smørgrav r = read_at(left->root->f,
17359c8e88eSDag-Erling Smørgrav left->pos + (left->len - remain_left),
17459c8e88eSDag-Erling Smørgrav buf_left, n_left);
17559c8e88eSDag-Erling Smørgrav if (r) {
17659c8e88eSDag-Erling Smørgrav *cmp = 0;
17759c8e88eSDag-Erling Smørgrav return r;
17859c8e88eSDag-Erling Smørgrav }
17959c8e88eSDag-Erling Smørgrav p_left = buf_left;
18059c8e88eSDag-Erling Smørgrav } else {
18159c8e88eSDag-Erling Smørgrav p_left = left->at + (left->len - remain_left);
18259c8e88eSDag-Erling Smørgrav }
18359c8e88eSDag-Erling Smørgrav
18459c8e88eSDag-Erling Smørgrav if (right->at == NULL) {
18559c8e88eSDag-Erling Smørgrav r = read_at(right->root->f,
18659c8e88eSDag-Erling Smørgrav right->pos + (right->len - remain_right),
18759c8e88eSDag-Erling Smørgrav buf_right, n_right);
18859c8e88eSDag-Erling Smørgrav if (r) {
18959c8e88eSDag-Erling Smørgrav *cmp = 0;
19059c8e88eSDag-Erling Smørgrav return r;
19159c8e88eSDag-Erling Smørgrav }
19259c8e88eSDag-Erling Smørgrav p_right = buf_right;
19359c8e88eSDag-Erling Smørgrav } else {
19459c8e88eSDag-Erling Smørgrav p_right = right->at + (right->len - remain_right);
19559c8e88eSDag-Erling Smørgrav }
19659c8e88eSDag-Erling Smørgrav
19759c8e88eSDag-Erling Smørgrav r = buf_cmp(p_left, n_left, p_right, n_right,
19859c8e88eSDag-Erling Smørgrav ignore_whitespace);
19959c8e88eSDag-Erling Smørgrav if (r) {
20059c8e88eSDag-Erling Smørgrav *cmp = r;
20159c8e88eSDag-Erling Smørgrav return 0;
20259c8e88eSDag-Erling Smørgrav }
20359c8e88eSDag-Erling Smørgrav
20459c8e88eSDag-Erling Smørgrav remain_left -= n_left;
20559c8e88eSDag-Erling Smørgrav remain_right -= n_right;
20659c8e88eSDag-Erling Smørgrav }
20759c8e88eSDag-Erling Smørgrav
20859c8e88eSDag-Erling Smørgrav *cmp = 0;
20959c8e88eSDag-Erling Smørgrav return 0;
21059c8e88eSDag-Erling Smørgrav }
21159c8e88eSDag-Erling Smørgrav
21259c8e88eSDag-Erling Smørgrav int
diff_atom_same(bool * same,const struct diff_atom * left,const struct diff_atom * right)21359c8e88eSDag-Erling Smørgrav diff_atom_same(bool *same,
21459c8e88eSDag-Erling Smørgrav const struct diff_atom *left,
21559c8e88eSDag-Erling Smørgrav const struct diff_atom *right)
21659c8e88eSDag-Erling Smørgrav {
21759c8e88eSDag-Erling Smørgrav int cmp;
21859c8e88eSDag-Erling Smørgrav int r;
21959c8e88eSDag-Erling Smørgrav if (left->hash != right->hash) {
22059c8e88eSDag-Erling Smørgrav *same = false;
22159c8e88eSDag-Erling Smørgrav return 0;
22259c8e88eSDag-Erling Smørgrav }
22359c8e88eSDag-Erling Smørgrav r = diff_atom_cmp(&cmp, left, right);
22459c8e88eSDag-Erling Smørgrav if (r) {
22559c8e88eSDag-Erling Smørgrav *same = true;
22659c8e88eSDag-Erling Smørgrav return r;
22759c8e88eSDag-Erling Smørgrav }
22859c8e88eSDag-Erling Smørgrav *same = (cmp == 0);
22959c8e88eSDag-Erling Smørgrav return 0;
23059c8e88eSDag-Erling Smørgrav }
23159c8e88eSDag-Erling Smørgrav
23259c8e88eSDag-Erling Smørgrav static struct diff_chunk *
diff_state_add_solved_chunk(struct diff_state * state,const struct diff_chunk * chunk)23359c8e88eSDag-Erling Smørgrav diff_state_add_solved_chunk(struct diff_state *state,
23459c8e88eSDag-Erling Smørgrav const struct diff_chunk *chunk)
23559c8e88eSDag-Erling Smørgrav {
23659c8e88eSDag-Erling Smørgrav diff_chunk_arraylist_t *result;
23759c8e88eSDag-Erling Smørgrav struct diff_chunk *new_chunk;
23859c8e88eSDag-Erling Smørgrav enum diff_chunk_type last_t;
23959c8e88eSDag-Erling Smørgrav enum diff_chunk_type new_t;
24059c8e88eSDag-Erling Smørgrav struct diff_chunk *last;
24159c8e88eSDag-Erling Smørgrav
24259c8e88eSDag-Erling Smørgrav /* Append to solved chunks; make sure that adjacent chunks of same type are combined, and that a minus chunk
24359c8e88eSDag-Erling Smørgrav * never directly follows a plus chunk. */
24459c8e88eSDag-Erling Smørgrav result = &state->result->chunks;
24559c8e88eSDag-Erling Smørgrav
24659c8e88eSDag-Erling Smørgrav last_t = result->len ? diff_chunk_type(&result->head[result->len - 1])
24759c8e88eSDag-Erling Smørgrav : CHUNK_EMPTY;
24859c8e88eSDag-Erling Smørgrav new_t = diff_chunk_type(chunk);
24959c8e88eSDag-Erling Smørgrav
25059c8e88eSDag-Erling Smørgrav debug("ADD %s chunk #%u:\n", chunk->solved ? "solved" : "UNSOLVED",
25159c8e88eSDag-Erling Smørgrav result->len);
25259c8e88eSDag-Erling Smørgrav debug("L\n");
25359c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->left, chunk->left_start, chunk->left_count);
25459c8e88eSDag-Erling Smørgrav debug("R\n");
25559c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->right, chunk->right_start, chunk->right_count);
25659c8e88eSDag-Erling Smørgrav
25759c8e88eSDag-Erling Smørgrav if (result->len) {
25859c8e88eSDag-Erling Smørgrav last = &result->head[result->len - 1];
25959c8e88eSDag-Erling Smørgrav assert(chunk->left_start
26059c8e88eSDag-Erling Smørgrav == last->left_start + last->left_count);
26159c8e88eSDag-Erling Smørgrav assert(chunk->right_start
26259c8e88eSDag-Erling Smørgrav == last->right_start + last->right_count);
26359c8e88eSDag-Erling Smørgrav }
26459c8e88eSDag-Erling Smørgrav
26559c8e88eSDag-Erling Smørgrav if (new_t == last_t) {
26659c8e88eSDag-Erling Smørgrav new_chunk = &result->head[result->len - 1];
26759c8e88eSDag-Erling Smørgrav new_chunk->left_count += chunk->left_count;
26859c8e88eSDag-Erling Smørgrav new_chunk->right_count += chunk->right_count;
26959c8e88eSDag-Erling Smørgrav debug(" - added chunk touches previous one of same type, joined:\n");
27059c8e88eSDag-Erling Smørgrav debug("L\n");
27159c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
27259c8e88eSDag-Erling Smørgrav debug("R\n");
27359c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
27459c8e88eSDag-Erling Smørgrav } else if (last_t == CHUNK_PLUS && new_t == CHUNK_MINUS) {
27559c8e88eSDag-Erling Smørgrav enum diff_chunk_type prev_last_t =
27659c8e88eSDag-Erling Smørgrav result->len > 1 ?
27759c8e88eSDag-Erling Smørgrav diff_chunk_type(&result->head[result->len - 2])
27859c8e88eSDag-Erling Smørgrav : CHUNK_EMPTY;
27959c8e88eSDag-Erling Smørgrav /* If a minus-chunk follows a plus-chunk, place it above the plus-chunk->
28059c8e88eSDag-Erling Smørgrav * Is the one before that also a minus? combine. */
28159c8e88eSDag-Erling Smørgrav if (prev_last_t == CHUNK_MINUS) {
28259c8e88eSDag-Erling Smørgrav new_chunk = &result->head[result->len - 2];
28359c8e88eSDag-Erling Smørgrav new_chunk->left_count += chunk->left_count;
28459c8e88eSDag-Erling Smørgrav new_chunk->right_count += chunk->right_count;
28559c8e88eSDag-Erling Smørgrav
28659c8e88eSDag-Erling Smørgrav debug(" - added minus-chunk follows plus-chunk,"
28759c8e88eSDag-Erling Smørgrav " put before that plus-chunk and joined"
28859c8e88eSDag-Erling Smørgrav " with preceding minus-chunk:\n");
28959c8e88eSDag-Erling Smørgrav debug("L\n");
29059c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
29159c8e88eSDag-Erling Smørgrav debug("R\n");
29259c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
29359c8e88eSDag-Erling Smørgrav } else {
29459c8e88eSDag-Erling Smørgrav ARRAYLIST_INSERT(new_chunk, *result, result->len - 1);
29559c8e88eSDag-Erling Smørgrav if (!new_chunk)
29659c8e88eSDag-Erling Smørgrav return NULL;
29759c8e88eSDag-Erling Smørgrav *new_chunk = *chunk;
29859c8e88eSDag-Erling Smørgrav
29959c8e88eSDag-Erling Smørgrav /* The new minus chunk indicates to which position on
30059c8e88eSDag-Erling Smørgrav * the right it corresponds, even though it doesn't add
30159c8e88eSDag-Erling Smørgrav * any lines on the right. By moving above a plus chunk,
30259c8e88eSDag-Erling Smørgrav * that position on the right has shifted. */
30359c8e88eSDag-Erling Smørgrav last = &result->head[result->len - 1];
30459c8e88eSDag-Erling Smørgrav new_chunk->right_start = last->right_start;
30559c8e88eSDag-Erling Smørgrav
30659c8e88eSDag-Erling Smørgrav debug(" - added minus-chunk follows plus-chunk,"
30759c8e88eSDag-Erling Smørgrav " put before that plus-chunk\n");
30859c8e88eSDag-Erling Smørgrav }
30959c8e88eSDag-Erling Smørgrav
31059c8e88eSDag-Erling Smørgrav /* That last_t == CHUNK_PLUS indicates to which position on the
31159c8e88eSDag-Erling Smørgrav * left it corresponds, even though it doesn't add any lines on
31259c8e88eSDag-Erling Smørgrav * the left. By inserting/extending the prev_last_t ==
31359c8e88eSDag-Erling Smørgrav * CHUNK_MINUS, that position on the left has shifted. */
31459c8e88eSDag-Erling Smørgrav last = &result->head[result->len - 1];
31559c8e88eSDag-Erling Smørgrav last->left_start = new_chunk->left_start
31659c8e88eSDag-Erling Smørgrav + new_chunk->left_count;
31759c8e88eSDag-Erling Smørgrav
31859c8e88eSDag-Erling Smørgrav } else {
31959c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(new_chunk, *result);
32059c8e88eSDag-Erling Smørgrav if (!new_chunk)
32159c8e88eSDag-Erling Smørgrav return NULL;
32259c8e88eSDag-Erling Smørgrav *new_chunk = *chunk;
32359c8e88eSDag-Erling Smørgrav }
32459c8e88eSDag-Erling Smørgrav return new_chunk;
32559c8e88eSDag-Erling Smørgrav }
32659c8e88eSDag-Erling Smørgrav
32759c8e88eSDag-Erling Smørgrav /* Even if a left or right side is empty, diff output may need to know the
32859c8e88eSDag-Erling Smørgrav * position in that file.
32959c8e88eSDag-Erling Smørgrav * So left_start or right_start must never be NULL -- pass left_count or
33059c8e88eSDag-Erling Smørgrav * right_count as zero to indicate staying at that position without consuming
33159c8e88eSDag-Erling Smørgrav * any lines. */
33259c8e88eSDag-Erling Smørgrav struct diff_chunk *
diff_state_add_chunk(struct diff_state * state,bool solved,struct diff_atom * left_start,unsigned int left_count,struct diff_atom * right_start,unsigned int right_count)33359c8e88eSDag-Erling Smørgrav diff_state_add_chunk(struct diff_state *state, bool solved,
33459c8e88eSDag-Erling Smørgrav struct diff_atom *left_start, unsigned int left_count,
33559c8e88eSDag-Erling Smørgrav struct diff_atom *right_start, unsigned int right_count)
33659c8e88eSDag-Erling Smørgrav {
33759c8e88eSDag-Erling Smørgrav struct diff_chunk *new_chunk;
33859c8e88eSDag-Erling Smørgrav struct diff_chunk chunk = {
33959c8e88eSDag-Erling Smørgrav .solved = solved,
34059c8e88eSDag-Erling Smørgrav .left_start = left_start,
34159c8e88eSDag-Erling Smørgrav .left_count = left_count,
34259c8e88eSDag-Erling Smørgrav .right_start = right_start,
34359c8e88eSDag-Erling Smørgrav .right_count = right_count,
34459c8e88eSDag-Erling Smørgrav };
34559c8e88eSDag-Erling Smørgrav
34659c8e88eSDag-Erling Smørgrav /* An unsolved chunk means store as intermediate result for later
34759c8e88eSDag-Erling Smørgrav * re-iteration.
34859c8e88eSDag-Erling Smørgrav * If there already are intermediate results, that means even a
34959c8e88eSDag-Erling Smørgrav * following solved chunk needs to go to intermediate results, so that
35059c8e88eSDag-Erling Smørgrav * it is later put in the final correct position in solved chunks.
35159c8e88eSDag-Erling Smørgrav */
35259c8e88eSDag-Erling Smørgrav if (!solved || state->temp_result.len) {
35359c8e88eSDag-Erling Smørgrav /* Append to temp_result */
35459c8e88eSDag-Erling Smørgrav debug("ADD %s chunk to temp result:\n",
35559c8e88eSDag-Erling Smørgrav chunk.solved ? "solved" : "UNSOLVED");
35659c8e88eSDag-Erling Smørgrav debug("L\n");
35759c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->left, left_start, left_count);
35859c8e88eSDag-Erling Smørgrav debug("R\n");
35959c8e88eSDag-Erling Smørgrav debug_dump_atoms(&state->right, right_start, right_count);
36059c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(new_chunk, state->temp_result);
36159c8e88eSDag-Erling Smørgrav if (!new_chunk)
36259c8e88eSDag-Erling Smørgrav return NULL;
36359c8e88eSDag-Erling Smørgrav *new_chunk = chunk;
36459c8e88eSDag-Erling Smørgrav return new_chunk;
36559c8e88eSDag-Erling Smørgrav }
36659c8e88eSDag-Erling Smørgrav
36759c8e88eSDag-Erling Smørgrav return diff_state_add_solved_chunk(state, &chunk);
36859c8e88eSDag-Erling Smørgrav }
36959c8e88eSDag-Erling Smørgrav
37059c8e88eSDag-Erling Smørgrav static void
diff_data_init_root(struct diff_data * d,FILE * f,const uint8_t * data,unsigned long long len,int diff_flags)37159c8e88eSDag-Erling Smørgrav diff_data_init_root(struct diff_data *d, FILE *f, const uint8_t *data,
37259c8e88eSDag-Erling Smørgrav unsigned long long len, int diff_flags)
37359c8e88eSDag-Erling Smørgrav {
37459c8e88eSDag-Erling Smørgrav *d = (struct diff_data){
37559c8e88eSDag-Erling Smørgrav .f = f,
37659c8e88eSDag-Erling Smørgrav .pos = 0,
37759c8e88eSDag-Erling Smørgrav .data = data,
37859c8e88eSDag-Erling Smørgrav .len = len,
37959c8e88eSDag-Erling Smørgrav .root = d,
38059c8e88eSDag-Erling Smørgrav .diff_flags = diff_flags,
38159c8e88eSDag-Erling Smørgrav };
38259c8e88eSDag-Erling Smørgrav }
38359c8e88eSDag-Erling Smørgrav
38459c8e88eSDag-Erling Smørgrav void
diff_data_init_subsection(struct diff_data * d,struct diff_data * parent,struct diff_atom * from_atom,unsigned int atoms_count)38559c8e88eSDag-Erling Smørgrav diff_data_init_subsection(struct diff_data *d, struct diff_data *parent,
38659c8e88eSDag-Erling Smørgrav struct diff_atom *from_atom, unsigned int atoms_count)
38759c8e88eSDag-Erling Smørgrav {
38859c8e88eSDag-Erling Smørgrav struct diff_atom *last_atom;
38959c8e88eSDag-Erling Smørgrav
39059c8e88eSDag-Erling Smørgrav debug("diff_data %p parent %p from_atom %p atoms_count %u\n",
39159c8e88eSDag-Erling Smørgrav d, parent, from_atom, atoms_count);
39259c8e88eSDag-Erling Smørgrav debug(" from_atom ");
39359c8e88eSDag-Erling Smørgrav debug_dump_atom(parent, NULL, from_atom);
39459c8e88eSDag-Erling Smørgrav
39559c8e88eSDag-Erling Smørgrav if (atoms_count == 0) {
39659c8e88eSDag-Erling Smørgrav *d = (struct diff_data){
39759c8e88eSDag-Erling Smørgrav .f = NULL,
39859c8e88eSDag-Erling Smørgrav .pos = 0,
39959c8e88eSDag-Erling Smørgrav .data = NULL,
40059c8e88eSDag-Erling Smørgrav .len = 0,
40159c8e88eSDag-Erling Smørgrav .root = parent->root,
40259c8e88eSDag-Erling Smørgrav .atoms.head = NULL,
40359c8e88eSDag-Erling Smørgrav .atoms.len = atoms_count,
40459c8e88eSDag-Erling Smørgrav };
40559c8e88eSDag-Erling Smørgrav
40659c8e88eSDag-Erling Smørgrav return;
40759c8e88eSDag-Erling Smørgrav }
40859c8e88eSDag-Erling Smørgrav
40959c8e88eSDag-Erling Smørgrav last_atom = from_atom + atoms_count - 1;
41059c8e88eSDag-Erling Smørgrav *d = (struct diff_data){
41159c8e88eSDag-Erling Smørgrav .f = NULL,
41259c8e88eSDag-Erling Smørgrav .pos = from_atom->pos,
41359c8e88eSDag-Erling Smørgrav .data = from_atom->at,
41459c8e88eSDag-Erling Smørgrav .len = (last_atom->pos + last_atom->len) - from_atom->pos,
41559c8e88eSDag-Erling Smørgrav .root = parent->root,
41659c8e88eSDag-Erling Smørgrav .atoms.head = from_atom,
41759c8e88eSDag-Erling Smørgrav .atoms.len = atoms_count,
41859c8e88eSDag-Erling Smørgrav };
41959c8e88eSDag-Erling Smørgrav
42059c8e88eSDag-Erling Smørgrav debug("subsection:\n");
42159c8e88eSDag-Erling Smørgrav debug_dump(d);
42259c8e88eSDag-Erling Smørgrav }
42359c8e88eSDag-Erling Smørgrav
42459c8e88eSDag-Erling Smørgrav void
diff_data_free(struct diff_data * diff_data)42559c8e88eSDag-Erling Smørgrav diff_data_free(struct diff_data *diff_data)
42659c8e88eSDag-Erling Smørgrav {
42759c8e88eSDag-Erling Smørgrav if (!diff_data)
42859c8e88eSDag-Erling Smørgrav return;
42959c8e88eSDag-Erling Smørgrav if (diff_data->atoms.allocated)
43059c8e88eSDag-Erling Smørgrav ARRAYLIST_FREE(diff_data->atoms);
43159c8e88eSDag-Erling Smørgrav }
43259c8e88eSDag-Erling Smørgrav
43359c8e88eSDag-Erling Smørgrav int
diff_algo_none(const struct diff_algo_config * algo_config,struct diff_state * state)43459c8e88eSDag-Erling Smørgrav diff_algo_none(const struct diff_algo_config *algo_config,
43559c8e88eSDag-Erling Smørgrav struct diff_state *state)
43659c8e88eSDag-Erling Smørgrav {
43759c8e88eSDag-Erling Smørgrav debug("\n** %s\n", __func__);
43859c8e88eSDag-Erling Smørgrav debug("left:\n");
43959c8e88eSDag-Erling Smørgrav debug_dump(&state->left);
44059c8e88eSDag-Erling Smørgrav debug("right:\n");
44159c8e88eSDag-Erling Smørgrav debug_dump(&state->right);
44259c8e88eSDag-Erling Smørgrav debug_dump_myers_graph(&state->left, &state->right, NULL, NULL, 0, NULL,
44359c8e88eSDag-Erling Smørgrav 0);
44459c8e88eSDag-Erling Smørgrav
44559c8e88eSDag-Erling Smørgrav /* Add a chunk of equal lines, if any */
44659c8e88eSDag-Erling Smørgrav struct diff_atom *l = state->left.atoms.head;
44759c8e88eSDag-Erling Smørgrav unsigned int l_len = state->left.atoms.len;
44859c8e88eSDag-Erling Smørgrav struct diff_atom *r = state->right.atoms.head;
44959c8e88eSDag-Erling Smørgrav unsigned int r_len = state->right.atoms.len;
45059c8e88eSDag-Erling Smørgrav unsigned int equal_atoms_start = 0;
45159c8e88eSDag-Erling Smørgrav unsigned int equal_atoms_end = 0;
45259c8e88eSDag-Erling Smørgrav unsigned int l_idx = 0;
45359c8e88eSDag-Erling Smørgrav unsigned int r_idx = 0;
45459c8e88eSDag-Erling Smørgrav
45559c8e88eSDag-Erling Smørgrav while (equal_atoms_start < l_len
45659c8e88eSDag-Erling Smørgrav && equal_atoms_start < r_len) {
45759c8e88eSDag-Erling Smørgrav int err;
45859c8e88eSDag-Erling Smørgrav bool same;
45959c8e88eSDag-Erling Smørgrav err = diff_atom_same(&same, &l[equal_atoms_start],
46059c8e88eSDag-Erling Smørgrav &r[equal_atoms_start]);
46159c8e88eSDag-Erling Smørgrav if (err)
46259c8e88eSDag-Erling Smørgrav return err;
46359c8e88eSDag-Erling Smørgrav if (!same)
46459c8e88eSDag-Erling Smørgrav break;
46559c8e88eSDag-Erling Smørgrav equal_atoms_start++;
46659c8e88eSDag-Erling Smørgrav }
46759c8e88eSDag-Erling Smørgrav while (equal_atoms_end < (l_len - equal_atoms_start)
46859c8e88eSDag-Erling Smørgrav && equal_atoms_end < (r_len - equal_atoms_start)) {
46959c8e88eSDag-Erling Smørgrav int err;
47059c8e88eSDag-Erling Smørgrav bool same;
47159c8e88eSDag-Erling Smørgrav err = diff_atom_same(&same, &l[l_len - 1 - equal_atoms_end],
47259c8e88eSDag-Erling Smørgrav &r[r_len - 1 - equal_atoms_end]);
47359c8e88eSDag-Erling Smørgrav if (err)
47459c8e88eSDag-Erling Smørgrav return err;
47559c8e88eSDag-Erling Smørgrav if (!same)
47659c8e88eSDag-Erling Smørgrav break;
47759c8e88eSDag-Erling Smørgrav equal_atoms_end++;
47859c8e88eSDag-Erling Smørgrav }
47959c8e88eSDag-Erling Smørgrav
48059c8e88eSDag-Erling Smørgrav /* Add a chunk of equal lines at the start */
48159c8e88eSDag-Erling Smørgrav if (equal_atoms_start) {
48259c8e88eSDag-Erling Smørgrav if (!diff_state_add_chunk(state, true,
48359c8e88eSDag-Erling Smørgrav l, equal_atoms_start,
48459c8e88eSDag-Erling Smørgrav r, equal_atoms_start))
48559c8e88eSDag-Erling Smørgrav return ENOMEM;
48659c8e88eSDag-Erling Smørgrav l_idx += equal_atoms_start;
48759c8e88eSDag-Erling Smørgrav r_idx += equal_atoms_start;
48859c8e88eSDag-Erling Smørgrav }
48959c8e88eSDag-Erling Smørgrav
49059c8e88eSDag-Erling Smørgrav /* Add a "minus" chunk with all lines from the left. */
49159c8e88eSDag-Erling Smørgrav if (equal_atoms_start + equal_atoms_end < l_len) {
49259c8e88eSDag-Erling Smørgrav unsigned int add_len = l_len - equal_atoms_start - equal_atoms_end;
49359c8e88eSDag-Erling Smørgrav if (!diff_state_add_chunk(state, true,
49459c8e88eSDag-Erling Smørgrav &l[l_idx], add_len,
49559c8e88eSDag-Erling Smørgrav &r[r_idx], 0))
49659c8e88eSDag-Erling Smørgrav return ENOMEM;
49759c8e88eSDag-Erling Smørgrav l_idx += add_len;
49859c8e88eSDag-Erling Smørgrav }
49959c8e88eSDag-Erling Smørgrav
50059c8e88eSDag-Erling Smørgrav /* Add a "plus" chunk with all lines from the right. */
50159c8e88eSDag-Erling Smørgrav if (equal_atoms_start + equal_atoms_end < r_len) {
50259c8e88eSDag-Erling Smørgrav unsigned int add_len = r_len - equal_atoms_start - equal_atoms_end;
50359c8e88eSDag-Erling Smørgrav if (!diff_state_add_chunk(state, true,
50459c8e88eSDag-Erling Smørgrav &l[l_idx], 0,
50559c8e88eSDag-Erling Smørgrav &r[r_idx], add_len))
50659c8e88eSDag-Erling Smørgrav return ENOMEM;
50759c8e88eSDag-Erling Smørgrav r_idx += add_len;
50859c8e88eSDag-Erling Smørgrav }
50959c8e88eSDag-Erling Smørgrav
51059c8e88eSDag-Erling Smørgrav /* Add a chunk of equal lines at the end */
51159c8e88eSDag-Erling Smørgrav if (equal_atoms_end) {
51259c8e88eSDag-Erling Smørgrav if (!diff_state_add_chunk(state, true,
51359c8e88eSDag-Erling Smørgrav &l[l_idx], equal_atoms_end,
51459c8e88eSDag-Erling Smørgrav &r[r_idx], equal_atoms_end))
51559c8e88eSDag-Erling Smørgrav return ENOMEM;
51659c8e88eSDag-Erling Smørgrav }
51759c8e88eSDag-Erling Smørgrav
51859c8e88eSDag-Erling Smørgrav return DIFF_RC_OK;
51959c8e88eSDag-Erling Smørgrav }
52059c8e88eSDag-Erling Smørgrav
52159c8e88eSDag-Erling Smørgrav static int
diff_run_algo(const struct diff_algo_config * algo_config,struct diff_state * state)52259c8e88eSDag-Erling Smørgrav diff_run_algo(const struct diff_algo_config *algo_config,
52359c8e88eSDag-Erling Smørgrav struct diff_state *state)
52459c8e88eSDag-Erling Smørgrav {
52559c8e88eSDag-Erling Smørgrav int rc;
52659c8e88eSDag-Erling Smørgrav
52759c8e88eSDag-Erling Smørgrav if (!algo_config || !algo_config->impl
52859c8e88eSDag-Erling Smørgrav || !state->recursion_depth_left
52959c8e88eSDag-Erling Smørgrav || !state->left.atoms.len || !state->right.atoms.len) {
53059c8e88eSDag-Erling Smørgrav debug("Fall back to diff_algo_none():%s%s%s\n",
53159c8e88eSDag-Erling Smørgrav (!algo_config || !algo_config->impl) ? " no-cfg" : "",
53259c8e88eSDag-Erling Smørgrav (!state->recursion_depth_left) ? " max-depth" : "",
53359c8e88eSDag-Erling Smørgrav (!state->left.atoms.len || !state->right.atoms.len)?
53459c8e88eSDag-Erling Smørgrav " trivial" : "");
53559c8e88eSDag-Erling Smørgrav return diff_algo_none(algo_config, state);
53659c8e88eSDag-Erling Smørgrav }
53759c8e88eSDag-Erling Smørgrav
53859c8e88eSDag-Erling Smørgrav ARRAYLIST_FREE(state->temp_result);
53959c8e88eSDag-Erling Smørgrav ARRAYLIST_INIT(state->temp_result, DIFF_RESULT_ALLOC_BLOCKSIZE);
54059c8e88eSDag-Erling Smørgrav rc = algo_config->impl(algo_config, state);
54159c8e88eSDag-Erling Smørgrav switch (rc) {
54259c8e88eSDag-Erling Smørgrav case DIFF_RC_USE_DIFF_ALGO_FALLBACK:
54359c8e88eSDag-Erling Smørgrav debug("Got DIFF_RC_USE_DIFF_ALGO_FALLBACK (%p)\n",
54459c8e88eSDag-Erling Smørgrav algo_config->fallback_algo);
54559c8e88eSDag-Erling Smørgrav rc = diff_run_algo(algo_config->fallback_algo, state);
54659c8e88eSDag-Erling Smørgrav goto return_rc;
54759c8e88eSDag-Erling Smørgrav
54859c8e88eSDag-Erling Smørgrav case DIFF_RC_OK:
54959c8e88eSDag-Erling Smørgrav /* continue below */
55059c8e88eSDag-Erling Smørgrav break;
55159c8e88eSDag-Erling Smørgrav
55259c8e88eSDag-Erling Smørgrav default:
55359c8e88eSDag-Erling Smørgrav /* some error happened */
55459c8e88eSDag-Erling Smørgrav goto return_rc;
55559c8e88eSDag-Erling Smørgrav }
55659c8e88eSDag-Erling Smørgrav
55759c8e88eSDag-Erling Smørgrav /* Pick up any diff chunks that are still unsolved and feed to
55859c8e88eSDag-Erling Smørgrav * inner_algo. inner_algo will solve unsolved chunks and append to
55959c8e88eSDag-Erling Smørgrav * result, and subsequent solved chunks on this level are then appended
56059c8e88eSDag-Erling Smørgrav * to result afterwards. */
56159c8e88eSDag-Erling Smørgrav int i;
56259c8e88eSDag-Erling Smørgrav for (i = 0; i < state->temp_result.len; i++) {
56359c8e88eSDag-Erling Smørgrav struct diff_chunk *c = &state->temp_result.head[i];
56459c8e88eSDag-Erling Smørgrav if (c->solved) {
56559c8e88eSDag-Erling Smørgrav diff_state_add_solved_chunk(state, c);
56659c8e88eSDag-Erling Smørgrav continue;
56759c8e88eSDag-Erling Smørgrav }
56859c8e88eSDag-Erling Smørgrav
56959c8e88eSDag-Erling Smørgrav /* c is an unsolved chunk, feed to inner_algo */
57059c8e88eSDag-Erling Smørgrav struct diff_state inner_state = {
57159c8e88eSDag-Erling Smørgrav .result = state->result,
57259c8e88eSDag-Erling Smørgrav .recursion_depth_left = state->recursion_depth_left - 1,
57359c8e88eSDag-Erling Smørgrav .kd_buf = state->kd_buf,
57459c8e88eSDag-Erling Smørgrav .kd_buf_size = state->kd_buf_size,
57559c8e88eSDag-Erling Smørgrav };
57659c8e88eSDag-Erling Smørgrav diff_data_init_subsection(&inner_state.left, &state->left,
57759c8e88eSDag-Erling Smørgrav c->left_start, c->left_count);
57859c8e88eSDag-Erling Smørgrav diff_data_init_subsection(&inner_state.right, &state->right,
57959c8e88eSDag-Erling Smørgrav c->right_start, c->right_count);
58059c8e88eSDag-Erling Smørgrav
58159c8e88eSDag-Erling Smørgrav rc = diff_run_algo(algo_config->inner_algo, &inner_state);
58259c8e88eSDag-Erling Smørgrav state->kd_buf = inner_state.kd_buf;
58359c8e88eSDag-Erling Smørgrav state->kd_buf_size = inner_state.kd_buf_size;
58459c8e88eSDag-Erling Smørgrav if (rc != DIFF_RC_OK)
58559c8e88eSDag-Erling Smørgrav goto return_rc;
58659c8e88eSDag-Erling Smørgrav }
58759c8e88eSDag-Erling Smørgrav
58859c8e88eSDag-Erling Smørgrav rc = DIFF_RC_OK;
58959c8e88eSDag-Erling Smørgrav return_rc:
59059c8e88eSDag-Erling Smørgrav ARRAYLIST_FREE(state->temp_result);
59159c8e88eSDag-Erling Smørgrav return rc;
59259c8e88eSDag-Erling Smørgrav }
59359c8e88eSDag-Erling Smørgrav
59459c8e88eSDag-Erling Smørgrav int
diff_atomize_file(struct diff_data * d,const struct diff_config * config,FILE * f,const uint8_t * data,off_t len,int diff_flags)59559c8e88eSDag-Erling Smørgrav diff_atomize_file(struct diff_data *d,
59659c8e88eSDag-Erling Smørgrav const struct diff_config *config,
59759c8e88eSDag-Erling Smørgrav FILE *f, const uint8_t *data, off_t len, int diff_flags)
59859c8e88eSDag-Erling Smørgrav {
59959c8e88eSDag-Erling Smørgrav if (!config->atomize_func)
60059c8e88eSDag-Erling Smørgrav return EINVAL;
60159c8e88eSDag-Erling Smørgrav
60259c8e88eSDag-Erling Smørgrav diff_data_init_root(d, f, data, len, diff_flags);
60359c8e88eSDag-Erling Smørgrav
60459c8e88eSDag-Erling Smørgrav return config->atomize_func(config->atomize_func_data, d);
60559c8e88eSDag-Erling Smørgrav
60659c8e88eSDag-Erling Smørgrav }
60759c8e88eSDag-Erling Smørgrav
60859c8e88eSDag-Erling Smørgrav struct diff_result *
diff_main(const struct diff_config * config,struct diff_data * left,struct diff_data * right)60959c8e88eSDag-Erling Smørgrav diff_main(const struct diff_config *config, struct diff_data *left,
61059c8e88eSDag-Erling Smørgrav struct diff_data *right)
61159c8e88eSDag-Erling Smørgrav {
61259c8e88eSDag-Erling Smørgrav struct diff_result *result = malloc(sizeof(struct diff_result));
61359c8e88eSDag-Erling Smørgrav if (!result)
61459c8e88eSDag-Erling Smørgrav return NULL;
61559c8e88eSDag-Erling Smørgrav
61659c8e88eSDag-Erling Smørgrav *result = (struct diff_result){};
61759c8e88eSDag-Erling Smørgrav result->left = left;
61859c8e88eSDag-Erling Smørgrav result->right = right;
61959c8e88eSDag-Erling Smørgrav
62059c8e88eSDag-Erling Smørgrav struct diff_state state = {
62159c8e88eSDag-Erling Smørgrav .result = result,
62259c8e88eSDag-Erling Smørgrav .recursion_depth_left = config->max_recursion_depth ?
62359c8e88eSDag-Erling Smørgrav config->max_recursion_depth : UINT_MAX,
62459c8e88eSDag-Erling Smørgrav .kd_buf = NULL,
62559c8e88eSDag-Erling Smørgrav .kd_buf_size = 0,
62659c8e88eSDag-Erling Smørgrav };
62759c8e88eSDag-Erling Smørgrav diff_data_init_subsection(&state.left, left,
62859c8e88eSDag-Erling Smørgrav left->atoms.head,
62959c8e88eSDag-Erling Smørgrav left->atoms.len);
63059c8e88eSDag-Erling Smørgrav diff_data_init_subsection(&state.right, right,
63159c8e88eSDag-Erling Smørgrav right->atoms.head,
63259c8e88eSDag-Erling Smørgrav right->atoms.len);
63359c8e88eSDag-Erling Smørgrav
63459c8e88eSDag-Erling Smørgrav result->rc = diff_run_algo(config->algo, &state);
63559c8e88eSDag-Erling Smørgrav free(state.kd_buf);
63659c8e88eSDag-Erling Smørgrav
63759c8e88eSDag-Erling Smørgrav return result;
63859c8e88eSDag-Erling Smørgrav }
63959c8e88eSDag-Erling Smørgrav
64059c8e88eSDag-Erling Smørgrav void
diff_result_free(struct diff_result * result)64159c8e88eSDag-Erling Smørgrav diff_result_free(struct diff_result *result)
64259c8e88eSDag-Erling Smørgrav {
64359c8e88eSDag-Erling Smørgrav if (!result)
64459c8e88eSDag-Erling Smørgrav return;
64559c8e88eSDag-Erling Smørgrav ARRAYLIST_FREE(result->chunks);
64659c8e88eSDag-Erling Smørgrav free(result);
64759c8e88eSDag-Erling Smørgrav }
64859c8e88eSDag-Erling Smørgrav
64959c8e88eSDag-Erling Smørgrav int
diff_result_contains_printable_chunks(struct diff_result * result)65059c8e88eSDag-Erling Smørgrav diff_result_contains_printable_chunks(struct diff_result *result)
65159c8e88eSDag-Erling Smørgrav {
65259c8e88eSDag-Erling Smørgrav struct diff_chunk *c;
65359c8e88eSDag-Erling Smørgrav enum diff_chunk_type t;
65459c8e88eSDag-Erling Smørgrav int i;
65559c8e88eSDag-Erling Smørgrav
65659c8e88eSDag-Erling Smørgrav for (i = 0; i < result->chunks.len; i++) {
65759c8e88eSDag-Erling Smørgrav c = &result->chunks.head[i];
65859c8e88eSDag-Erling Smørgrav t = diff_chunk_type(c);
65959c8e88eSDag-Erling Smørgrav if (t == CHUNK_MINUS || t == CHUNK_PLUS)
66059c8e88eSDag-Erling Smørgrav return 1;
66159c8e88eSDag-Erling Smørgrav }
66259c8e88eSDag-Erling Smørgrav
66359c8e88eSDag-Erling Smørgrav return 0;
66459c8e88eSDag-Erling Smørgrav }
665