xref: /freebsd-src/contrib/libdiff/lib/diff_main.c (revision 0549218b43f028bb5bde430d7fcc6ead6843aa61)
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