Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/iterator.c')
-rw-r--r--src/iterator.c130
1 files changed, 102 insertions, 28 deletions
diff --git a/src/iterator.c b/src/iterator.c
index a9df39bb8..0b6a4fc15 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -31,6 +31,7 @@
(P)->base.end = end ? git__strdup(end) : NULL; \
if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
git__free(P); return -1; } \
+ (P)->base.prefixcomp = git__prefixcmp; \
} while (0)
static int iterator__reset_range(
@@ -75,6 +76,9 @@ static int iterator_update_ignore_case(
else if (ignore_case == 0)
iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
+ iter->prefixcomp = ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) ?
+ git__prefixcmp_icase : git__prefixcmp;
+
return error;
}
@@ -141,7 +145,10 @@ struct tree_iterator_frame {
tree_iterator_frame *next, *prev;
git_tree *tree;
char *start;
+ size_t startlen;
size_t index;
+ void **icase_map;
+ void *icase_data[GIT_FLEX_ARRAY];
};
typedef struct {
@@ -155,7 +162,13 @@ typedef struct {
GIT_INLINE(const git_tree_entry *)tree_iterator__tree_entry(tree_iterator *ti)
{
- return git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
+ tree_iterator_frame *tf = ti->stack;
+
+ if (tf->index >= git_tree_entrycount(tf->tree))
+ return NULL;
+
+ return git_tree_entry_byindex(
+ tf->tree, tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index);
}
static char *tree_iterator__current_filename(
@@ -174,7 +187,10 @@ static void tree_iterator__free_frame(tree_iterator_frame *tf)
{
if (!tf)
return;
+
git_tree_free(tf->tree);
+ tf->tree = NULL;
+
git__free(tf);
}
@@ -220,7 +236,7 @@ static int tree_iterator__current(
if (ti->entry.path == NULL)
return -1;
- if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
+ if (ti->base.end && ti->base.prefixcomp(ti->entry.path, ti->base.end) > 0)
return tree_iterator__to_end(ti);
if (entry)
@@ -234,10 +250,50 @@ static int tree_iterator__at_end(git_iterator *self)
return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
}
+static int tree_iterator__icase_map_cmp(const void *a, const void *b, void *data)
+{
+ git_tree *tree = data;
+ const git_tree_entry *te1 = git_tree_entry_byindex(tree, (size_t)a);
+ const git_tree_entry *te2 = git_tree_entry_byindex(tree, (size_t)b);
+ return te1 ? (te2 ? git_tree_entry_icmp(te1, te2) : 1) : -1;
+}
+
+static int tree_iterator__frame_start_icmp(const void *key, const void *element)
+{
+ const tree_iterator_frame *tf = (const tree_iterator_frame *)key;
+ const git_tree_entry *te = git_tree_entry_byindex(tf->tree, (size_t)element);
+
+ return memcmp(tf->start, te->filename, min(tf->startlen, te->filename_len));
+}
+
+static void tree_iterator__frame_seek_start(tree_iterator_frame *tf)
+{
+ if (!tf->start)
+ tf->index = 0;
+ else if (!tf->icase_map)
+ tf->index = git_tree__prefix_position(tf->tree, tf->start);
+ else {
+ if (!git__bsearch(
+ tf->icase_map, git_tree_entrycount(tf->tree),
+ tf, tree_iterator__frame_start_icmp, &tf->index))
+ {
+ while (tf->index > 0) {
+ /* move back while previous entry is still prefixed */
+ if (tree_iterator__frame_start_icmp(
+ tf, (const void *)(tf->index - 1)))
+ break;
+ tf->index--;
+ }
+ }
+ }
+}
+
static tree_iterator_frame *tree_iterator__alloc_frame(
- git_tree *tree, char *start)
+ tree_iterator *ti, git_tree *tree, char *start)
{
- tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
+ size_t i, max_i = git_tree_entrycount(tree);
+ tree_iterator_frame *tf =
+ git__calloc(1, sizeof(tree_iterator_frame) + max_i * sizeof(void *));
if (!tf)
return NULL;
@@ -245,9 +301,24 @@ static tree_iterator_frame *tree_iterator__alloc_frame(
if (start && *start) {
tf->start = start;
- tf->index = git_tree__prefix_position(tree, start);
+ tf->startlen = strlen(start);
}
+ if (!max_i)
+ return tf;
+
+ if ((ti->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
+ tf->icase_map = tf->icase_data;
+
+ for (i = 0; i < max_i; ++i)
+ tf->icase_map[i] = (void *)i;
+
+ git__tsort_r(
+ tf->icase_map, max_i, tree_iterator__icase_map_cmp, tf->tree);
+ }
+
+ tree_iterator__frame_seek_start(tf);
+
return tf;
}
@@ -265,7 +336,7 @@ static int tree_iterator__expand_tree(tree_iterator *ti)
/* check that we have not passed the range end */
if (ti->base.end != NULL &&
- git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
+ ti->base.prefixcomp(ti->path.ptr, ti->base.end) > 0)
return tree_iterator__to_end(ti);
if ((error = git_tree_lookup(&subtree, ti->base.repo, &te->oid)) < 0)
@@ -275,14 +346,13 @@ static int tree_iterator__expand_tree(tree_iterator *ti)
/* apply range start to new frame if relevant */
if (ti->stack->start &&
- git__prefixcmp(ti->stack->start, te->filename) == 0)
+ ti->base.prefixcomp(ti->stack->start, te->filename) == 0)
{
- size_t namelen = strlen(te->filename);
- if (ti->stack->start[namelen] == '/')
- relpath = ti->stack->start + namelen + 1;
+ if (ti->stack->start[te->filename_len] == '/')
+ relpath = ti->stack->start + te->filename_len + 1;
}
- if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
+ if ((tf = tree_iterator__alloc_frame(ti, subtree, relpath)) == NULL)
return -1;
tf->next = ti->stack;
@@ -311,8 +381,9 @@ static int tree_iterator__advance(
}
while (1) {
- te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
- if (te != NULL)
+ ++ti->stack->index;
+
+ if ((te = tree_iterator__tree_entry(ti)) != NULL)
break;
if (!tree_iterator__pop_frame(ti))
@@ -362,8 +433,8 @@ static int tree_iterator__reset(
if (iterator__reset_range(self, start, end) < 0)
return -1;
- ti->stack->index =
- git_tree__prefix_position(ti->stack->tree, ti->base.start);
+ /* reset start position */
+ tree_iterator__frame_seek_start(ti->stack);
git_buf_clear(&ti->path);
ti->path_has_filename = false;
@@ -394,10 +465,7 @@ int git_iterator_for_tree_range(
if ((error = iterator_update_ignore_case((git_iterator *)ti, flags)) < 0)
goto fail;
- /* TODO: implement icase support natively in tree iterators */
- ti->base.flags = (ti->base.flags & ~GIT_ITERATOR_IGNORE_CASE);
-
- ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start);
+ ti->stack = ti->tail = tree_iterator__alloc_frame(ti, tree, ti->base.start);
if ((error = tree_iterator__expand_tree(ti)) < 0)
goto fail;
@@ -447,7 +515,7 @@ static void index_iterator__skip_conflicts(
if (ie == NULL ||
(ii->base.end != NULL &&
- ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)) {
+ ii->base.prefixcomp(ie->path, ii->base.end) > 0)) {
ii->current = entrycount;
break;
}
@@ -513,8 +581,10 @@ int git_iterator_for_index_range(
ITERATOR_BASE_INIT(ii, index, INDEX);
ii->base.repo = git_index_owner(index);
- if (index->ignore_case)
+ if (index->ignore_case) {
ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
+ ii->base.prefixcomp = git__prefixcmp_icase;
+ }
ii->index = index;
GIT_REFCOUNT_INC(index);
@@ -772,8 +842,8 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
return -1;
- if (wi->base.end && ITERATOR_PREFIXCMP(
- wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
+ if (wi->base.end &&
+ wi->base.prefixcomp(wi->path.ptr + wi->root_len, wi->base.end) > 0)
return 0;
wi->entry.path = ps->path;
@@ -1064,10 +1134,14 @@ int git_iterator_current_parent_tree(
tree_iterator *ti = (tree_iterator *)iter;
tree_iterator_frame *tf;
const char *scan = parent_path;
+ int (*strncomp)(const char *a, const char *b, size_t sz);
if (iter->type != GIT_ITERATOR_TYPE_TREE || ti->stack == NULL)
goto notfound;
+ strncomp = ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) ?
+ git__strncasecmp : git__strncmp;
+
for (tf = ti->tail; tf != NULL; tf = tf->prev) {
const git_tree_entry *te;
@@ -1076,9 +1150,10 @@ int git_iterator_current_parent_tree(
return 0;
}
- te = git_tree_entry_byindex(tf->tree, tf->index);
+ te = git_tree_entry_byindex(tf->tree,
+ tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index);
- if (strncmp(scan, te->filename, te->filename_len) != 0)
+ if (strncomp(scan, te->filename, te->filename_len) != 0)
goto notfound;
scan += te->filename_len;
@@ -1129,8 +1204,7 @@ int git_iterator_advance_into_directory(
return entry ? git_iterator_current(iter, entry) : 0;
}
-int git_iterator_cmp(
- git_iterator *iter, const char *path_prefix)
+int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
{
const git_index_entry *entry;
@@ -1143,7 +1217,7 @@ int git_iterator_cmp(
if (!path_prefix)
return -1;
- return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix);
+ return iter->prefixcomp(entry->path, path_prefix);
}
int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path)