| #include "../cache.h" | 
 | #include "../refs.h" | 
 | #include "refs-internal.h" | 
 | #include "ref-cache.h" | 
 | #include "../iterator.h" | 
 |  | 
 | void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry) | 
 | { | 
 | 	ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc); | 
 | 	dir->entries[dir->nr++] = entry; | 
 | 	/* optimize for the case that entries are added in order */ | 
 | 	if (dir->nr == 1 || | 
 | 	    (dir->nr == dir->sorted + 1 && | 
 | 	     strcmp(dir->entries[dir->nr - 2]->name, | 
 | 		    dir->entries[dir->nr - 1]->name) < 0)) | 
 | 		dir->sorted = dir->nr; | 
 | } | 
 |  | 
 | struct ref_dir *get_ref_dir(struct ref_entry *entry) | 
 | { | 
 | 	struct ref_dir *dir; | 
 | 	assert(entry->flag & REF_DIR); | 
 | 	dir = &entry->u.subdir; | 
 | 	if (entry->flag & REF_INCOMPLETE) { | 
 | 		if (!dir->cache->fill_ref_dir) | 
 | 			BUG("incomplete ref_store without fill_ref_dir function"); | 
 |  | 
 | 		dir->cache->fill_ref_dir(dir->cache->ref_store, dir, entry->name); | 
 | 		entry->flag &= ~REF_INCOMPLETE; | 
 | 	} | 
 | 	return dir; | 
 | } | 
 |  | 
 | struct ref_entry *create_ref_entry(const char *refname, | 
 | 				   const struct object_id *oid, int flag) | 
 | { | 
 | 	struct ref_entry *ref; | 
 |  | 
 | 	FLEX_ALLOC_STR(ref, name, refname); | 
 | 	oidcpy(&ref->u.value.oid, oid); | 
 | 	ref->flag = flag; | 
 | 	return ref; | 
 | } | 
 |  | 
 | struct ref_cache *create_ref_cache(struct ref_store *refs, | 
 | 				   fill_ref_dir_fn *fill_ref_dir) | 
 | { | 
 | 	struct ref_cache *ret = xcalloc(1, sizeof(*ret)); | 
 |  | 
 | 	ret->ref_store = refs; | 
 | 	ret->fill_ref_dir = fill_ref_dir; | 
 | 	ret->root = create_dir_entry(ret, "", 0, 1); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void clear_ref_dir(struct ref_dir *dir); | 
 |  | 
 | static void free_ref_entry(struct ref_entry *entry) | 
 | { | 
 | 	if (entry->flag & REF_DIR) { | 
 | 		/* | 
 | 		 * Do not use get_ref_dir() here, as that might | 
 | 		 * trigger the reading of loose refs. | 
 | 		 */ | 
 | 		clear_ref_dir(&entry->u.subdir); | 
 | 	} | 
 | 	free(entry); | 
 | } | 
 |  | 
 | void free_ref_cache(struct ref_cache *cache) | 
 | { | 
 | 	free_ref_entry(cache->root); | 
 | 	free(cache); | 
 | } | 
 |  | 
 | /* | 
 |  * Clear and free all entries in dir, recursively. | 
 |  */ | 
 | static void clear_ref_dir(struct ref_dir *dir) | 
 | { | 
 | 	int i; | 
 | 	for (i = 0; i < dir->nr; i++) | 
 | 		free_ref_entry(dir->entries[i]); | 
 | 	FREE_AND_NULL(dir->entries); | 
 | 	dir->sorted = dir->nr = dir->alloc = 0; | 
 | } | 
 |  | 
 | struct ref_entry *create_dir_entry(struct ref_cache *cache, | 
 | 				   const char *dirname, size_t len, | 
 | 				   int incomplete) | 
 | { | 
 | 	struct ref_entry *direntry; | 
 |  | 
 | 	FLEX_ALLOC_MEM(direntry, name, dirname, len); | 
 | 	direntry->u.subdir.cache = cache; | 
 | 	direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); | 
 | 	return direntry; | 
 | } | 
 |  | 
 | static int ref_entry_cmp(const void *a, const void *b) | 
 | { | 
 | 	struct ref_entry *one = *(struct ref_entry **)a; | 
 | 	struct ref_entry *two = *(struct ref_entry **)b; | 
 | 	return strcmp(one->name, two->name); | 
 | } | 
 |  | 
 | static void sort_ref_dir(struct ref_dir *dir); | 
 |  | 
 | struct string_slice { | 
 | 	size_t len; | 
 | 	const char *str; | 
 | }; | 
 |  | 
 | static int ref_entry_cmp_sslice(const void *key_, const void *ent_) | 
 | { | 
 | 	const struct string_slice *key = key_; | 
 | 	const struct ref_entry *ent = *(const struct ref_entry * const *)ent_; | 
 | 	int cmp = strncmp(key->str, ent->name, key->len); | 
 | 	if (cmp) | 
 | 		return cmp; | 
 | 	return '\0' - (unsigned char)ent->name[key->len]; | 
 | } | 
 |  | 
 | int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len) | 
 | { | 
 | 	struct ref_entry **r; | 
 | 	struct string_slice key; | 
 |  | 
 | 	if (refname == NULL || !dir->nr) | 
 | 		return -1; | 
 |  | 
 | 	sort_ref_dir(dir); | 
 | 	key.len = len; | 
 | 	key.str = refname; | 
 | 	r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries), | 
 | 		    ref_entry_cmp_sslice); | 
 |  | 
 | 	if (r == NULL) | 
 | 		return -1; | 
 |  | 
 | 	return r - dir->entries; | 
 | } | 
 |  | 
 | /* | 
 |  * Search for a directory entry directly within dir (without | 
 |  * recursing).  Sort dir if necessary.  subdirname must be a directory | 
 |  * name (i.e., end in '/').  If mkdir is set, then create the | 
 |  * directory if it is missing; otherwise, return NULL if the desired | 
 |  * directory cannot be found.  dir must already be complete. | 
 |  */ | 
 | static struct ref_dir *search_for_subdir(struct ref_dir *dir, | 
 | 					 const char *subdirname, size_t len, | 
 | 					 int mkdir) | 
 | { | 
 | 	int entry_index = search_ref_dir(dir, subdirname, len); | 
 | 	struct ref_entry *entry; | 
 | 	if (entry_index == -1) { | 
 | 		if (!mkdir) | 
 | 			return NULL; | 
 | 		/* | 
 | 		 * Since dir is complete, the absence of a subdir | 
 | 		 * means that the subdir really doesn't exist; | 
 | 		 * therefore, create an empty record for it but mark | 
 | 		 * the record complete. | 
 | 		 */ | 
 | 		entry = create_dir_entry(dir->cache, subdirname, len, 0); | 
 | 		add_entry_to_dir(dir, entry); | 
 | 	} else { | 
 | 		entry = dir->entries[entry_index]; | 
 | 	} | 
 | 	return get_ref_dir(entry); | 
 | } | 
 |  | 
 | /* | 
 |  * If refname is a reference name, find the ref_dir within the dir | 
 |  * tree that should hold refname. If refname is a directory name | 
 |  * (i.e., it ends in '/'), then return that ref_dir itself. dir must | 
 |  * represent the top-level directory and must already be complete. | 
 |  * Sort ref_dirs and recurse into subdirectories as necessary. If | 
 |  * mkdir is set, then create any missing directories; otherwise, | 
 |  * return NULL if the desired directory cannot be found. | 
 |  */ | 
 | static struct ref_dir *find_containing_dir(struct ref_dir *dir, | 
 | 					   const char *refname, int mkdir) | 
 | { | 
 | 	const char *slash; | 
 | 	for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { | 
 | 		size_t dirnamelen = slash - refname + 1; | 
 | 		struct ref_dir *subdir; | 
 | 		subdir = search_for_subdir(dir, refname, dirnamelen, mkdir); | 
 | 		if (!subdir) { | 
 | 			dir = NULL; | 
 | 			break; | 
 | 		} | 
 | 		dir = subdir; | 
 | 	} | 
 |  | 
 | 	return dir; | 
 | } | 
 |  | 
 | struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) | 
 | { | 
 | 	int entry_index; | 
 | 	struct ref_entry *entry; | 
 | 	dir = find_containing_dir(dir, refname, 0); | 
 | 	if (!dir) | 
 | 		return NULL; | 
 | 	entry_index = search_ref_dir(dir, refname, strlen(refname)); | 
 | 	if (entry_index == -1) | 
 | 		return NULL; | 
 | 	entry = dir->entries[entry_index]; | 
 | 	return (entry->flag & REF_DIR) ? NULL : entry; | 
 | } | 
 |  | 
 | int remove_entry_from_dir(struct ref_dir *dir, const char *refname) | 
 | { | 
 | 	int refname_len = strlen(refname); | 
 | 	int entry_index; | 
 | 	struct ref_entry *entry; | 
 | 	int is_dir = refname[refname_len - 1] == '/'; | 
 | 	if (is_dir) { | 
 | 		/* | 
 | 		 * refname represents a reference directory.  Remove | 
 | 		 * the trailing slash; otherwise we will get the | 
 | 		 * directory *representing* refname rather than the | 
 | 		 * one *containing* it. | 
 | 		 */ | 
 | 		char *dirname = xmemdupz(refname, refname_len - 1); | 
 | 		dir = find_containing_dir(dir, dirname, 0); | 
 | 		free(dirname); | 
 | 	} else { | 
 | 		dir = find_containing_dir(dir, refname, 0); | 
 | 	} | 
 | 	if (!dir) | 
 | 		return -1; | 
 | 	entry_index = search_ref_dir(dir, refname, refname_len); | 
 | 	if (entry_index == -1) | 
 | 		return -1; | 
 | 	entry = dir->entries[entry_index]; | 
 |  | 
 | 	MOVE_ARRAY(&dir->entries[entry_index], | 
 | 		   &dir->entries[entry_index + 1], dir->nr - entry_index - 1); | 
 | 	dir->nr--; | 
 | 	if (dir->sorted > entry_index) | 
 | 		dir->sorted--; | 
 | 	free_ref_entry(entry); | 
 | 	return dir->nr; | 
 | } | 
 |  | 
 | int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref) | 
 | { | 
 | 	dir = find_containing_dir(dir, ref->name, 1); | 
 | 	if (!dir) | 
 | 		return -1; | 
 | 	add_entry_to_dir(dir, ref); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Emit a warning and return true iff ref1 and ref2 have the same name | 
 |  * and the same oid. Die if they have the same name but different | 
 |  * oids. | 
 |  */ | 
 | static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2) | 
 | { | 
 | 	if (strcmp(ref1->name, ref2->name)) | 
 | 		return 0; | 
 |  | 
 | 	/* Duplicate name; make sure that they don't conflict: */ | 
 |  | 
 | 	if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR)) | 
 | 		/* This is impossible by construction */ | 
 | 		die("Reference directory conflict: %s", ref1->name); | 
 |  | 
 | 	if (!oideq(&ref1->u.value.oid, &ref2->u.value.oid)) | 
 | 		die("Duplicated ref, and SHA1s don't match: %s", ref1->name); | 
 |  | 
 | 	warning("Duplicated ref: %s", ref1->name); | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* | 
 |  * Sort the entries in dir non-recursively (if they are not already | 
 |  * sorted) and remove any duplicate entries. | 
 |  */ | 
 | static void sort_ref_dir(struct ref_dir *dir) | 
 | { | 
 | 	int i, j; | 
 | 	struct ref_entry *last = NULL; | 
 |  | 
 | 	/* | 
 | 	 * This check also prevents passing a zero-length array to qsort(), | 
 | 	 * which is a problem on some platforms. | 
 | 	 */ | 
 | 	if (dir->sorted == dir->nr) | 
 | 		return; | 
 |  | 
 | 	QSORT(dir->entries, dir->nr, ref_entry_cmp); | 
 |  | 
 | 	/* Remove any duplicates: */ | 
 | 	for (i = 0, j = 0; j < dir->nr; j++) { | 
 | 		struct ref_entry *entry = dir->entries[j]; | 
 | 		if (last && is_dup_ref(last, entry)) | 
 | 			free_ref_entry(entry); | 
 | 		else | 
 | 			last = dir->entries[i++] = entry; | 
 | 	} | 
 | 	dir->sorted = dir->nr = i; | 
 | } | 
 |  | 
 | enum prefix_state { | 
 | 	/* All refs within the directory would match prefix: */ | 
 | 	PREFIX_CONTAINS_DIR, | 
 |  | 
 | 	/* Some, but not all, refs within the directory might match prefix: */ | 
 | 	PREFIX_WITHIN_DIR, | 
 |  | 
 | 	/* No refs within the directory could possibly match prefix: */ | 
 | 	PREFIX_EXCLUDES_DIR | 
 | }; | 
 |  | 
 | /* | 
 |  * Return a `prefix_state` constant describing the relationship | 
 |  * between the directory with the specified `dirname` and `prefix`. | 
 |  */ | 
 | static enum prefix_state overlaps_prefix(const char *dirname, | 
 | 					 const char *prefix) | 
 | { | 
 | 	while (*prefix && *dirname == *prefix) { | 
 | 		dirname++; | 
 | 		prefix++; | 
 | 	} | 
 | 	if (!*prefix) | 
 | 		return PREFIX_CONTAINS_DIR; | 
 | 	else if (!*dirname) | 
 | 		return PREFIX_WITHIN_DIR; | 
 | 	else | 
 | 		return PREFIX_EXCLUDES_DIR; | 
 | } | 
 |  | 
 | /* | 
 |  * Load all of the refs from `dir` (recursively) that could possibly | 
 |  * contain references matching `prefix` into our in-memory cache. If | 
 |  * `prefix` is NULL, prime unconditionally. | 
 |  */ | 
 | static void prime_ref_dir(struct ref_dir *dir, const char *prefix) | 
 | { | 
 | 	/* | 
 | 	 * The hard work of loading loose refs is done by get_ref_dir(), so we | 
 | 	 * just need to recurse through all of the sub-directories. We do not | 
 | 	 * even need to care about sorting, as traversal order does not matter | 
 | 	 * to us. | 
 | 	 */ | 
 | 	int i; | 
 | 	for (i = 0; i < dir->nr; i++) { | 
 | 		struct ref_entry *entry = dir->entries[i]; | 
 | 		if (!(entry->flag & REF_DIR)) { | 
 | 			/* Not a directory; no need to recurse. */ | 
 | 		} else if (!prefix) { | 
 | 			/* Recurse in any case: */ | 
 | 			prime_ref_dir(get_ref_dir(entry), NULL); | 
 | 		} else { | 
 | 			switch (overlaps_prefix(entry->name, prefix)) { | 
 | 			case PREFIX_CONTAINS_DIR: | 
 | 				/* | 
 | 				 * Recurse, and from here down we | 
 | 				 * don't have to check the prefix | 
 | 				 * anymore: | 
 | 				 */ | 
 | 				prime_ref_dir(get_ref_dir(entry), NULL); | 
 | 				break; | 
 | 			case PREFIX_WITHIN_DIR: | 
 | 				prime_ref_dir(get_ref_dir(entry), prefix); | 
 | 				break; | 
 | 			case PREFIX_EXCLUDES_DIR: | 
 | 				/* No need to prime this directory. */ | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * A level in the reference hierarchy that is currently being iterated | 
 |  * through. | 
 |  */ | 
 | struct cache_ref_iterator_level { | 
 | 	/* | 
 | 	 * The ref_dir being iterated over at this level. The ref_dir | 
 | 	 * is sorted before being stored here. | 
 | 	 */ | 
 | 	struct ref_dir *dir; | 
 |  | 
 | 	enum prefix_state prefix_state; | 
 |  | 
 | 	/* | 
 | 	 * The index of the current entry within dir (which might | 
 | 	 * itself be a directory). If index == -1, then the iteration | 
 | 	 * hasn't yet begun. If index == dir->nr, then the iteration | 
 | 	 * through this level is over. | 
 | 	 */ | 
 | 	int index; | 
 | }; | 
 |  | 
 | /* | 
 |  * Represent an iteration through a ref_dir in the memory cache. The | 
 |  * iteration recurses through subdirectories. | 
 |  */ | 
 | struct cache_ref_iterator { | 
 | 	struct ref_iterator base; | 
 |  | 
 | 	/* | 
 | 	 * The number of levels currently on the stack. This is always | 
 | 	 * at least 1, because when it becomes zero the iteration is | 
 | 	 * ended and this struct is freed. | 
 | 	 */ | 
 | 	size_t levels_nr; | 
 |  | 
 | 	/* The number of levels that have been allocated on the stack */ | 
 | 	size_t levels_alloc; | 
 |  | 
 | 	/* | 
 | 	 * Only include references with this prefix in the iteration. | 
 | 	 * The prefix is matched textually, without regard for path | 
 | 	 * component boundaries. | 
 | 	 */ | 
 | 	const char *prefix; | 
 |  | 
 | 	/* | 
 | 	 * A stack of levels. levels[0] is the uppermost level that is | 
 | 	 * being iterated over in this iteration. (This is not | 
 | 	 * necessary the top level in the references hierarchy. If we | 
 | 	 * are iterating through a subtree, then levels[0] will hold | 
 | 	 * the ref_dir for that subtree, and subsequent levels will go | 
 | 	 * on from there.) | 
 | 	 */ | 
 | 	struct cache_ref_iterator_level *levels; | 
 | }; | 
 |  | 
 | static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) | 
 | { | 
 | 	struct cache_ref_iterator *iter = | 
 | 		(struct cache_ref_iterator *)ref_iterator; | 
 |  | 
 | 	while (1) { | 
 | 		struct cache_ref_iterator_level *level = | 
 | 			&iter->levels[iter->levels_nr - 1]; | 
 | 		struct ref_dir *dir = level->dir; | 
 | 		struct ref_entry *entry; | 
 | 		enum prefix_state entry_prefix_state; | 
 |  | 
 | 		if (level->index == -1) | 
 | 			sort_ref_dir(dir); | 
 |  | 
 | 		if (++level->index == level->dir->nr) { | 
 | 			/* This level is exhausted; pop up a level */ | 
 | 			if (--iter->levels_nr == 0) | 
 | 				return ref_iterator_abort(ref_iterator); | 
 |  | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		entry = dir->entries[level->index]; | 
 |  | 
 | 		if (level->prefix_state == PREFIX_WITHIN_DIR) { | 
 | 			entry_prefix_state = overlaps_prefix(entry->name, iter->prefix); | 
 | 			if (entry_prefix_state == PREFIX_EXCLUDES_DIR) | 
 | 				continue; | 
 | 		} else { | 
 | 			entry_prefix_state = level->prefix_state; | 
 | 		} | 
 |  | 
 | 		if (entry->flag & REF_DIR) { | 
 | 			/* push down a level */ | 
 | 			ALLOC_GROW(iter->levels, iter->levels_nr + 1, | 
 | 				   iter->levels_alloc); | 
 |  | 
 | 			level = &iter->levels[iter->levels_nr++]; | 
 | 			level->dir = get_ref_dir(entry); | 
 | 			level->prefix_state = entry_prefix_state; | 
 | 			level->index = -1; | 
 | 		} else { | 
 | 			iter->base.refname = entry->name; | 
 | 			iter->base.oid = &entry->u.value.oid; | 
 | 			iter->base.flags = entry->flag; | 
 | 			return ITER_OK; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, | 
 | 				   struct object_id *peeled) | 
 | { | 
 | 	return peel_object(ref_iterator->oid, peeled); | 
 | } | 
 |  | 
 | static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) | 
 | { | 
 | 	struct cache_ref_iterator *iter = | 
 | 		(struct cache_ref_iterator *)ref_iterator; | 
 |  | 
 | 	free((char *)iter->prefix); | 
 | 	free(iter->levels); | 
 | 	base_ref_iterator_free(ref_iterator); | 
 | 	return ITER_DONE; | 
 | } | 
 |  | 
 | static struct ref_iterator_vtable cache_ref_iterator_vtable = { | 
 | 	cache_ref_iterator_advance, | 
 | 	cache_ref_iterator_peel, | 
 | 	cache_ref_iterator_abort | 
 | }; | 
 |  | 
 | struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, | 
 | 					      const char *prefix, | 
 | 					      int prime_dir) | 
 | { | 
 | 	struct ref_dir *dir; | 
 | 	struct cache_ref_iterator *iter; | 
 | 	struct ref_iterator *ref_iterator; | 
 | 	struct cache_ref_iterator_level *level; | 
 |  | 
 | 	dir = get_ref_dir(cache->root); | 
 | 	if (prefix && *prefix) | 
 | 		dir = find_containing_dir(dir, prefix, 0); | 
 | 	if (!dir) | 
 | 		/* There's nothing to iterate over. */ | 
 | 		return empty_ref_iterator_begin(); | 
 |  | 
 | 	if (prime_dir) | 
 | 		prime_ref_dir(dir, prefix); | 
 |  | 
 | 	iter = xcalloc(1, sizeof(*iter)); | 
 | 	ref_iterator = &iter->base; | 
 | 	base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1); | 
 | 	ALLOC_GROW(iter->levels, 10, iter->levels_alloc); | 
 |  | 
 | 	iter->levels_nr = 1; | 
 | 	level = &iter->levels[0]; | 
 | 	level->index = -1; | 
 | 	level->dir = dir; | 
 |  | 
 | 	if (prefix && *prefix) { | 
 | 		iter->prefix = xstrdup(prefix); | 
 | 		level->prefix_state = PREFIX_WITHIN_DIR; | 
 | 	} else { | 
 | 		level->prefix_state = PREFIX_CONTAINS_DIR; | 
 | 	} | 
 |  | 
 | 	return ref_iterator; | 
 | } |