435 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			435 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Generic reference iterator infrastructure. See refs-internal.h for
 | 
						|
 * documentation about the design and use of reference iterators.
 | 
						|
 */
 | 
						|
 | 
						|
#include "cache.h"
 | 
						|
#include "refs.h"
 | 
						|
#include "refs/refs-internal.h"
 | 
						|
#include "iterator.h"
 | 
						|
 | 
						|
int ref_iterator_advance(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	return ref_iterator->vtable->advance(ref_iterator);
 | 
						|
}
 | 
						|
 | 
						|
int ref_iterator_peel(struct ref_iterator *ref_iterator,
 | 
						|
		      struct object_id *peeled)
 | 
						|
{
 | 
						|
	return ref_iterator->vtable->peel(ref_iterator, peeled);
 | 
						|
}
 | 
						|
 | 
						|
int ref_iterator_abort(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	return ref_iterator->vtable->abort(ref_iterator);
 | 
						|
}
 | 
						|
 | 
						|
void base_ref_iterator_init(struct ref_iterator *iter,
 | 
						|
			    struct ref_iterator_vtable *vtable,
 | 
						|
			    int ordered)
 | 
						|
{
 | 
						|
	iter->vtable = vtable;
 | 
						|
	iter->ordered = !!ordered;
 | 
						|
	iter->refname = NULL;
 | 
						|
	iter->oid = NULL;
 | 
						|
	iter->flags = 0;
 | 
						|
}
 | 
						|
 | 
						|
void base_ref_iterator_free(struct ref_iterator *iter)
 | 
						|
{
 | 
						|
	/* Help make use-after-free bugs fail quickly: */
 | 
						|
	iter->vtable = NULL;
 | 
						|
	free(iter);
 | 
						|
}
 | 
						|
 | 
						|
struct empty_ref_iterator {
 | 
						|
	struct ref_iterator base;
 | 
						|
};
 | 
						|
 | 
						|
static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	return ref_iterator_abort(ref_iterator);
 | 
						|
}
 | 
						|
 | 
						|
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator,
 | 
						|
				   struct object_id *peeled)
 | 
						|
{
 | 
						|
	BUG("peel called for empty iterator");
 | 
						|
}
 | 
						|
 | 
						|
static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	base_ref_iterator_free(ref_iterator);
 | 
						|
	return ITER_DONE;
 | 
						|
}
 | 
						|
 | 
						|
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
 | 
						|
	empty_ref_iterator_advance,
 | 
						|
	empty_ref_iterator_peel,
 | 
						|
	empty_ref_iterator_abort
 | 
						|
};
 | 
						|
 | 
						|
struct ref_iterator *empty_ref_iterator_begin(void)
 | 
						|
{
 | 
						|
	struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
 | 
						|
	struct ref_iterator *ref_iterator = &iter->base;
 | 
						|
 | 
						|
	base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
 | 
						|
	return ref_iterator;
 | 
						|
}
 | 
						|
 | 
						|
int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	return ref_iterator->vtable == &empty_ref_iterator_vtable;
 | 
						|
}
 | 
						|
 | 
						|
struct merge_ref_iterator {
 | 
						|
	struct ref_iterator base;
 | 
						|
 | 
						|
	struct ref_iterator *iter0, *iter1;
 | 
						|
 | 
						|
	ref_iterator_select_fn *select;
 | 
						|
	void *cb_data;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * A pointer to iter0 or iter1 (whichever is supplying the
 | 
						|
	 * current value), or NULL if advance has not yet been called.
 | 
						|
	 */
 | 
						|
	struct ref_iterator **current;
 | 
						|
};
 | 
						|
 | 
						|
static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	struct merge_ref_iterator *iter =
 | 
						|
		(struct merge_ref_iterator *)ref_iterator;
 | 
						|
	int ok;
 | 
						|
 | 
						|
	if (!iter->current) {
 | 
						|
		/* Initialize: advance both iterators to their first entries */
 | 
						|
		if ((ok = ref_iterator_advance(iter->iter0)) != ITER_OK) {
 | 
						|
			iter->iter0 = NULL;
 | 
						|
			if (ok == ITER_ERROR)
 | 
						|
				goto error;
 | 
						|
		}
 | 
						|
		if ((ok = ref_iterator_advance(iter->iter1)) != ITER_OK) {
 | 
						|
			iter->iter1 = NULL;
 | 
						|
			if (ok == ITER_ERROR)
 | 
						|
				goto error;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/*
 | 
						|
		 * Advance the current iterator past the just-used
 | 
						|
		 * entry:
 | 
						|
		 */
 | 
						|
		if ((ok = ref_iterator_advance(*iter->current)) != ITER_OK) {
 | 
						|
			*iter->current = NULL;
 | 
						|
			if (ok == ITER_ERROR)
 | 
						|
				goto error;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Loop until we find an entry that we can yield. */
 | 
						|
	while (1) {
 | 
						|
		struct ref_iterator **secondary;
 | 
						|
		enum iterator_selection selection =
 | 
						|
			iter->select(iter->iter0, iter->iter1, iter->cb_data);
 | 
						|
 | 
						|
		if (selection == ITER_SELECT_DONE) {
 | 
						|
			return ref_iterator_abort(ref_iterator);
 | 
						|
		} else if (selection == ITER_SELECT_ERROR) {
 | 
						|
			ref_iterator_abort(ref_iterator);
 | 
						|
			return ITER_ERROR;
 | 
						|
		}
 | 
						|
 | 
						|
		if ((selection & ITER_CURRENT_SELECTION_MASK) == 0) {
 | 
						|
			iter->current = &iter->iter0;
 | 
						|
			secondary = &iter->iter1;
 | 
						|
		} else {
 | 
						|
			iter->current = &iter->iter1;
 | 
						|
			secondary = &iter->iter0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (selection & ITER_SKIP_SECONDARY) {
 | 
						|
			if ((ok = ref_iterator_advance(*secondary)) != ITER_OK) {
 | 
						|
				*secondary = NULL;
 | 
						|
				if (ok == ITER_ERROR)
 | 
						|
					goto error;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (selection & ITER_YIELD_CURRENT) {
 | 
						|
			iter->base.refname = (*iter->current)->refname;
 | 
						|
			iter->base.oid = (*iter->current)->oid;
 | 
						|
			iter->base.flags = (*iter->current)->flags;
 | 
						|
			return ITER_OK;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
error:
 | 
						|
	ref_iterator_abort(ref_iterator);
 | 
						|
	return ITER_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
 | 
						|
				   struct object_id *peeled)
 | 
						|
{
 | 
						|
	struct merge_ref_iterator *iter =
 | 
						|
		(struct merge_ref_iterator *)ref_iterator;
 | 
						|
 | 
						|
	if (!iter->current) {
 | 
						|
		BUG("peel called before advance for merge iterator");
 | 
						|
	}
 | 
						|
	return ref_iterator_peel(*iter->current, peeled);
 | 
						|
}
 | 
						|
 | 
						|
static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	struct merge_ref_iterator *iter =
 | 
						|
		(struct merge_ref_iterator *)ref_iterator;
 | 
						|
	int ok = ITER_DONE;
 | 
						|
 | 
						|
	if (iter->iter0) {
 | 
						|
		if (ref_iterator_abort(iter->iter0) != ITER_DONE)
 | 
						|
			ok = ITER_ERROR;
 | 
						|
	}
 | 
						|
	if (iter->iter1) {
 | 
						|
		if (ref_iterator_abort(iter->iter1) != ITER_DONE)
 | 
						|
			ok = ITER_ERROR;
 | 
						|
	}
 | 
						|
	base_ref_iterator_free(ref_iterator);
 | 
						|
	return ok;
 | 
						|
}
 | 
						|
 | 
						|
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
 | 
						|
	merge_ref_iterator_advance,
 | 
						|
	merge_ref_iterator_peel,
 | 
						|
	merge_ref_iterator_abort
 | 
						|
};
 | 
						|
 | 
						|
struct ref_iterator *merge_ref_iterator_begin(
 | 
						|
		int ordered,
 | 
						|
		struct ref_iterator *iter0, struct ref_iterator *iter1,
 | 
						|
		ref_iterator_select_fn *select, void *cb_data)
 | 
						|
{
 | 
						|
	struct merge_ref_iterator *iter = xcalloc(1, sizeof(*iter));
 | 
						|
	struct ref_iterator *ref_iterator = &iter->base;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We can't do the same kind of is_empty_ref_iterator()-style
 | 
						|
	 * optimization here as overlay_ref_iterator_begin() does,
 | 
						|
	 * because we don't know the semantics of the select function.
 | 
						|
	 * It might, for example, implement "intersect" by passing
 | 
						|
	 * references through only if they exist in both iterators.
 | 
						|
	 */
 | 
						|
 | 
						|
	base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
 | 
						|
	iter->iter0 = iter0;
 | 
						|
	iter->iter1 = iter1;
 | 
						|
	iter->select = select;
 | 
						|
	iter->cb_data = cb_data;
 | 
						|
	iter->current = NULL;
 | 
						|
	return ref_iterator;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * A ref_iterator_select_fn that overlays the items from front on top
 | 
						|
 * of those from back (like loose refs over packed refs). See
 | 
						|
 * overlay_ref_iterator_begin().
 | 
						|
 */
 | 
						|
static enum iterator_selection overlay_iterator_select(
 | 
						|
		struct ref_iterator *front, struct ref_iterator *back,
 | 
						|
		void *cb_data)
 | 
						|
{
 | 
						|
	int cmp;
 | 
						|
 | 
						|
	if (!back)
 | 
						|
		return front ? ITER_SELECT_0 : ITER_SELECT_DONE;
 | 
						|
	else if (!front)
 | 
						|
		return ITER_SELECT_1;
 | 
						|
 | 
						|
	cmp = strcmp(front->refname, back->refname);
 | 
						|
 | 
						|
	if (cmp < 0)
 | 
						|
		return ITER_SELECT_0;
 | 
						|
	else if (cmp > 0)
 | 
						|
		return ITER_SELECT_1;
 | 
						|
	else
 | 
						|
		return ITER_SELECT_0_SKIP_1;
 | 
						|
}
 | 
						|
 | 
						|
struct ref_iterator *overlay_ref_iterator_begin(
 | 
						|
		struct ref_iterator *front, struct ref_iterator *back)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Optimization: if one of the iterators is empty, return the
 | 
						|
	 * other one rather than incurring the overhead of wrapping
 | 
						|
	 * them.
 | 
						|
	 */
 | 
						|
	if (is_empty_ref_iterator(front)) {
 | 
						|
		ref_iterator_abort(front);
 | 
						|
		return back;
 | 
						|
	} else if (is_empty_ref_iterator(back)) {
 | 
						|
		ref_iterator_abort(back);
 | 
						|
		return front;
 | 
						|
	} else if (!front->ordered || !back->ordered) {
 | 
						|
		BUG("overlay_ref_iterator requires ordered inputs");
 | 
						|
	}
 | 
						|
 | 
						|
	return merge_ref_iterator_begin(1, front, back,
 | 
						|
					overlay_iterator_select, NULL);
 | 
						|
}
 | 
						|
 | 
						|
struct prefix_ref_iterator {
 | 
						|
	struct ref_iterator base;
 | 
						|
 | 
						|
	struct ref_iterator *iter0;
 | 
						|
	char *prefix;
 | 
						|
	int trim;
 | 
						|
};
 | 
						|
 | 
						|
/* Return -1, 0, 1 if refname is before, inside, or after the prefix. */
 | 
						|
static int compare_prefix(const char *refname, const char *prefix)
 | 
						|
{
 | 
						|
	while (*prefix) {
 | 
						|
		if (*refname != *prefix)
 | 
						|
			return ((unsigned char)*refname < (unsigned char)*prefix) ? -1 : +1;
 | 
						|
 | 
						|
		refname++;
 | 
						|
		prefix++;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	struct prefix_ref_iterator *iter =
 | 
						|
		(struct prefix_ref_iterator *)ref_iterator;
 | 
						|
	int ok;
 | 
						|
 | 
						|
	while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
 | 
						|
		int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
 | 
						|
 | 
						|
		if (cmp < 0)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (cmp > 0) {
 | 
						|
			/*
 | 
						|
			 * If the source iterator is ordered, then we
 | 
						|
			 * can stop the iteration as soon as we see a
 | 
						|
			 * refname that comes after the prefix:
 | 
						|
			 */
 | 
						|
			if (iter->iter0->ordered) {
 | 
						|
				ok = ref_iterator_abort(iter->iter0);
 | 
						|
				break;
 | 
						|
			} else {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (iter->trim) {
 | 
						|
			/*
 | 
						|
			 * It is nonsense to trim off characters that
 | 
						|
			 * you haven't already checked for via a
 | 
						|
			 * prefix check, whether via this
 | 
						|
			 * `prefix_ref_iterator` or upstream in
 | 
						|
			 * `iter0`). So if there wouldn't be at least
 | 
						|
			 * one character left in the refname after
 | 
						|
			 * trimming, report it as a bug:
 | 
						|
			 */
 | 
						|
			if (strlen(iter->iter0->refname) <= iter->trim)
 | 
						|
				BUG("attempt to trim too many characters");
 | 
						|
			iter->base.refname = iter->iter0->refname + iter->trim;
 | 
						|
		} else {
 | 
						|
			iter->base.refname = iter->iter0->refname;
 | 
						|
		}
 | 
						|
 | 
						|
		iter->base.oid = iter->iter0->oid;
 | 
						|
		iter->base.flags = iter->iter0->flags;
 | 
						|
		return ITER_OK;
 | 
						|
	}
 | 
						|
 | 
						|
	iter->iter0 = NULL;
 | 
						|
	if (ref_iterator_abort(ref_iterator) != ITER_DONE)
 | 
						|
		return ITER_ERROR;
 | 
						|
	return ok;
 | 
						|
}
 | 
						|
 | 
						|
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
 | 
						|
				    struct object_id *peeled)
 | 
						|
{
 | 
						|
	struct prefix_ref_iterator *iter =
 | 
						|
		(struct prefix_ref_iterator *)ref_iterator;
 | 
						|
 | 
						|
	return ref_iterator_peel(iter->iter0, peeled);
 | 
						|
}
 | 
						|
 | 
						|
static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
 | 
						|
{
 | 
						|
	struct prefix_ref_iterator *iter =
 | 
						|
		(struct prefix_ref_iterator *)ref_iterator;
 | 
						|
	int ok = ITER_DONE;
 | 
						|
 | 
						|
	if (iter->iter0)
 | 
						|
		ok = ref_iterator_abort(iter->iter0);
 | 
						|
	free(iter->prefix);
 | 
						|
	base_ref_iterator_free(ref_iterator);
 | 
						|
	return ok;
 | 
						|
}
 | 
						|
 | 
						|
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
 | 
						|
	prefix_ref_iterator_advance,
 | 
						|
	prefix_ref_iterator_peel,
 | 
						|
	prefix_ref_iterator_abort
 | 
						|
};
 | 
						|
 | 
						|
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 | 
						|
					       const char *prefix,
 | 
						|
					       int trim)
 | 
						|
{
 | 
						|
	struct prefix_ref_iterator *iter;
 | 
						|
	struct ref_iterator *ref_iterator;
 | 
						|
 | 
						|
	if (!*prefix && !trim)
 | 
						|
		return iter0; /* optimization: no need to wrap iterator */
 | 
						|
 | 
						|
	iter = xcalloc(1, sizeof(*iter));
 | 
						|
	ref_iterator = &iter->base;
 | 
						|
 | 
						|
	base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
 | 
						|
 | 
						|
	iter->iter0 = iter0;
 | 
						|
	iter->prefix = xstrdup(prefix);
 | 
						|
	iter->trim = trim;
 | 
						|
 | 
						|
	return ref_iterator;
 | 
						|
}
 | 
						|
 | 
						|
struct ref_iterator *current_ref_iter = NULL;
 | 
						|
 | 
						|
int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
 | 
						|
				  each_repo_ref_fn fn, void *cb_data)
 | 
						|
{
 | 
						|
	int retval = 0, ok;
 | 
						|
	struct ref_iterator *old_ref_iter = current_ref_iter;
 | 
						|
 | 
						|
	current_ref_iter = iter;
 | 
						|
	while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 | 
						|
		retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
 | 
						|
		if (retval) {
 | 
						|
			/*
 | 
						|
			 * If ref_iterator_abort() returns ITER_ERROR,
 | 
						|
			 * we ignore that error in deference to the
 | 
						|
			 * callback function's return value.
 | 
						|
			 */
 | 
						|
			ref_iterator_abort(iter);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
out:
 | 
						|
	current_ref_iter = old_ref_iter;
 | 
						|
	if (ok == ITER_ERROR)
 | 
						|
		return -1;
 | 
						|
	return retval;
 | 
						|
}
 |