Commit 5b229e20 by Vicent Martí

Merge pull request #1413 from arrbee/more-iterator-refactor

Further tree_iterator refactoring
parents e953c160 14bedad9
...@@ -330,6 +330,33 @@ static int get_workdir_sm_content( ...@@ -330,6 +330,33 @@ static int get_workdir_sm_content(
return 0; return 0;
} }
static int get_filtered(
git_map *map, git_file fd, git_diff_file *file, git_vector *filters)
{
int error;
git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
if ((error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) < 0)
return error;
if (!filters->length)
git_buf_swap(&filtered, &raw);
else
error = git_filters_apply(&filtered, &raw, filters);
if (!error) {
map->len = git_buf_len(&filtered);
map->data = git_buf_detach(&filtered);
file->flags |= GIT_DIFF_FLAG__FREE_DATA;
}
git_buf_free(&raw);
git_buf_free(&filtered);
return error;
}
static int get_workdir_content( static int get_workdir_content(
diff_context *ctxt, diff_context *ctxt,
git_diff_delta *delta, git_diff_delta *delta,
...@@ -381,8 +408,8 @@ static int get_workdir_content( ...@@ -381,8 +408,8 @@ static int get_workdir_content(
goto cleanup; goto cleanup;
} }
if (!file->size) if (!file->size && !(file->size = git_futils_filesize(fd)))
file->size = git_futils_filesize(fd); goto close_and_cleanup;
if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 || if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 ||
(delta->flags & GIT_DIFF_FLAG_BINARY) != 0) (delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
...@@ -394,26 +421,12 @@ static int get_workdir_content( ...@@ -394,26 +421,12 @@ static int get_workdir_content(
goto close_and_cleanup; goto close_and_cleanup;
if (error == 0) { /* note: git_filters_load returns filter count */ if (error == 0) { /* note: git_filters_load returns filter count */
if (!file->size)
goto close_and_cleanup;
error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size); error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size);
if (!error)
file->flags |= GIT_DIFF_FLAG__UNMAP_DATA; file->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
} else {
git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) &&
!(error = git_filters_apply(&filtered, &raw, &filters)))
{
map->len = git_buf_len(&filtered);
map->data = git_buf_detach(&filtered);
file->flags |= GIT_DIFF_FLAG__FREE_DATA;
}
git_buf_free(&raw);
git_buf_free(&filtered);
} }
if (error != 0)
error = get_filtered(map, fd, file, &filters);
close_and_cleanup: close_and_cleanup:
git_filters_free(&filters); git_filters_free(&filters);
......
...@@ -103,6 +103,7 @@ int giterr_set_regex(const regex_t *regex, int error_code) ...@@ -103,6 +103,7 @@ int giterr_set_regex(const regex_t *regex, int error_code)
void giterr_clear(void) void giterr_clear(void)
{ {
set_error(0, NULL);
GIT_GLOBAL->last_error = NULL; GIT_GLOBAL->last_error = NULL;
errno = 0; errno = 0;
......
...@@ -148,8 +148,7 @@ int git_iterator_for_nothing( ...@@ -148,8 +148,7 @@ int git_iterator_for_nothing(
const char *start, const char *start,
const char *end) const char *end)
{ {
empty_iterator *i = git__calloc(1, sizeof(empty_iterator)); empty_iterator *i;
GITERR_CHECK_ALLOC(i);
#define empty_iterator__current empty_iterator__noop #define empty_iterator__current empty_iterator__noop
#define empty_iterator__advance empty_iterator__noop #define empty_iterator__advance empty_iterator__noop
...@@ -165,15 +164,16 @@ int git_iterator_for_nothing( ...@@ -165,15 +164,16 @@ int git_iterator_for_nothing(
} }
typedef struct { typedef struct tree_iterator_entry tree_iterator_entry;
size_t parent_entry_index; /* index in parent entries array */ struct tree_iterator_entry {
size_t parent_tree_index; /* index in parent entry tree */ tree_iterator_entry *parent;
git_tree *tree; /* this tree if this is tree (only valid while current) */ const git_tree_entry *te;
} tree_iterator_entry; git_tree *tree;
};
typedef struct tree_iterator_frame tree_iterator_frame; typedef struct tree_iterator_frame tree_iterator_frame;
struct tree_iterator_frame { struct tree_iterator_frame {
tree_iterator_frame *parent, *child; tree_iterator_frame *up, *down;
size_t n_entries; /* items in this frame */ size_t n_entries; /* items in this frame */
size_t current; /* start of currently active range in frame */ size_t current; /* start of currently active range in frame */
...@@ -182,13 +182,14 @@ struct tree_iterator_frame { ...@@ -182,13 +182,14 @@ struct tree_iterator_frame {
const char *start; const char *start;
size_t startlen; size_t startlen;
tree_iterator_entry entries[GIT_FLEX_ARRAY]; tree_iterator_entry *entries[GIT_FLEX_ARRAY];
}; };
typedef struct { typedef struct {
git_iterator base; git_iterator base;
git_iterator_callbacks cb; git_iterator_callbacks cb;
tree_iterator_frame *head, *top; tree_iterator_frame *head, *root;
git_pool pool;
git_index_entry entry; git_index_entry entry;
git_buf path; git_buf path;
int path_ambiguities; int path_ambiguities;
...@@ -196,30 +197,6 @@ typedef struct { ...@@ -196,30 +197,6 @@ typedef struct {
int (*strncomp)(const char *a, const char *b, size_t sz); int (*strncomp)(const char *a, const char *b, size_t sz);
} tree_iterator; } tree_iterator;
static const git_tree_entry *tree_iterator__tree_entry(
tree_iterator_frame *tf, const tree_iterator_entry *entry)
{
git_tree *tree = tf->parent->entries[entry->parent_entry_index].tree;
if (!tree)
return NULL;
return git_tree_entry_byindex(tree, entry->parent_tree_index);
}
static const git_tree_entry *tree_iterator__tree_entry_by_index(
tree_iterator_frame *tf, size_t i)
{
git_tree *tree;
if (i >= tf->n_entries)
return NULL;
tree = tf->parent->entries[tf->entries[i].parent_entry_index].tree;
if (!tree)
return NULL;
return git_tree_entry_byindex(tree, tf->entries[i].parent_tree_index);
}
static char *tree_iterator__current_filename( static char *tree_iterator__current_filename(
tree_iterator *ti, const git_tree_entry *te) tree_iterator *ti, const git_tree_entry *te)
{ {
...@@ -238,90 +215,76 @@ static char *tree_iterator__current_filename( ...@@ -238,90 +215,76 @@ static char *tree_iterator__current_filename(
static void tree_iterator__rewrite_filename(tree_iterator *ti) static void tree_iterator__rewrite_filename(tree_iterator *ti)
{ {
tree_iterator_frame *scan = ti->head; tree_iterator_entry *scan = ti->head->entries[ti->head->current];
size_t current = scan->current;
ssize_t strpos = ti->path.size; ssize_t strpos = ti->path.size;
const git_tree_entry *te; const git_tree_entry *te;
if (strpos && ti->path.ptr[strpos - 1] == '/') if (strpos && ti->path.ptr[strpos - 1] == '/')
strpos--; strpos--;
while (scan && scan->parent) { for (; scan && (te = scan->te); scan = scan->parent) {
tree_iterator_entry *entry = &scan->entries[current];
if (!(te = tree_iterator__tree_entry(scan, entry)))
break;
strpos -= te->filename_len; strpos -= te->filename_len;
memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len); memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
strpos -= 1; /* separator */ strpos -= 1; /* separator */
current = entry->parent_entry_index;
scan = scan->parent;
} }
} }
static int tree_iterator__tree_entry_cmp( static int tree_iterator__te_cmp(
const git_tree_entry *a, const git_tree_entry *a,
const git_tree_entry *b, const git_tree_entry *b,
int (*strncomp)(const char *, const char *, size_t)) int (*compare)(const char *, const char *, size_t))
{ {
size_t common = min(a->filename_len, b->filename_len); return git_path_cmp(
int cmp = strncomp(a->filename, b->filename, common); a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
if (!cmp) { compare);
char a_next = a->filename[common], b_next = b->filename[common];
if (!a_next && a->attr == GIT_FILEMODE_TREE)
a_next = '/';
if (!b_next && b->attr == GIT_FILEMODE_TREE)
b_next = '/';
cmp = (int)a_next - (int)b_next;
}
return cmp;
} }
static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
{ {
const tree_iterator_entry *ae = a, *be = b; const tree_iterator_entry *ae = a, *be = b;
const git_tree_entry *ate = tree_iterator__tree_entry(p, ae); int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
const git_tree_entry *bte = tree_iterator__tree_entry(p, be);
int cmp = tree_iterator__tree_entry_cmp(ate, bte, git__strncasecmp);
/* stabilize sort order among equivalent names */
if (!cmp) { if (!cmp) {
cmp = (ae->parent_entry_index < be->parent_entry_index) ? -1 : /* stabilize sort order among equivalent names */
(ae->parent_entry_index > be->parent_entry_index) ? 1 : 0; if (!ae->parent->te || !be->parent->te)
if (!cmp) cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
cmp = (ae->parent_tree_index < be->parent_tree_index) ? -1 : else
(ae->parent_tree_index > be->parent_tree_index) ? 1 : 0; cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
} }
return cmp; return cmp;
} }
static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
{
const tree_iterator_frame *tf = key;
const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
return git_path_cmp(
tf->start, tf->startlen, false,
te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
((tree_iterator *)p)->strncomp);
}
static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
{ {
/* find next and load trees for current range */ int error;
int error = 0;
const git_tree_entry *te, *last = NULL; const git_tree_entry *te, *last = NULL;
tf->next = tf->current; tf->next = tf->current;
while (tf->next < tf->n_entries) { for (; tf->next < tf->n_entries; tf->next++, last = te) {
if (!(te = tree_iterator__tree_entry_by_index(tf, tf->next)) || te = tf->entries[tf->next]->te;
(last && tree_iterator__tree_entry_cmp(last, te, ti->strncomp)))
if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
break; break;
/* load trees for items in [current,next) range */
if (git_tree_entry__is_tree(te) && if (git_tree_entry__is_tree(te) &&
(error = git_tree_lookup( (error = git_tree_lookup(
&tf->entries[tf->next].tree, ti->base.repo, &te->oid)) < 0) &tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0)
break; return error;
tf->next++;
last = te;
} }
if (tf->next > tf->current + 1) if (tf->next > tf->current + 1)
...@@ -330,129 +293,139 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) ...@@ -330,129 +293,139 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
if (last && !tree_iterator__current_filename(ti, last)) if (last && !tree_iterator__current_filename(ti, last))
return -1; return -1;
return error; return 0;
} }
GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti) GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
{ {
return (ti->head->current < ti->head->n_entries && return (ti->head->current < ti->head->n_entries &&
ti->head->entries[ti->head->current].tree != NULL); ti->head->entries[ti->head->current]->tree != NULL);
} }
static int tree_iterator__push_frame(tree_iterator *ti) static int tree_iterator__push_frame(tree_iterator *ti)
{ {
int error = 0; int error = 0;
tree_iterator_frame *tf = ti->head, *new_tf = NULL; tree_iterator_frame *head = ti->head, *tf = NULL;
size_t i, n_entries = 0, sz = sizeof(tree_iterator_frame); size_t i, n_entries = 0;
const git_tree_entry *te;
/* if current item in head is not a tree, do nothing */ if (head->current >= head->n_entries || !head->entries[head->current]->tree)
if (tf->current >= tf->n_entries || !tf->entries[tf->current].tree)
return 0; return 0;
/* build frame - sum tree entries from parent range */ for (i = head->current; i < head->next; ++i)
for (i = tf->current; i < tf->next; ++i) n_entries += git_tree_entrycount(head->entries[i]->tree);
n_entries += git_tree_entrycount(tf->entries[i].tree);
sz += n_entries * sizeof(tree_iterator_entry); tf = git__calloc(sizeof(tree_iterator_frame) +
new_tf = git__calloc(sz, sizeof(char)); n_entries * sizeof(tree_iterator_entry *), 1);
GITERR_CHECK_ALLOC(new_tf); GITERR_CHECK_ALLOC(tf);
tf->n_entries = n_entries;
/* populate frame and entries */ tf->up = head;
new_tf->parent = tf; head->down = tf;
new_tf->n_entries = n_entries; ti->head = tf;
for (i = tf->current, n_entries = 0; i < tf->next; ++i) { for (i = head->current, n_entries = 0; i < head->next; ++i) {
git_tree *tree = tf->entries[i].tree; git_tree *tree = head->entries[i]->tree;
size_t j, max_j = git_tree_entrycount(tree); size_t j, max_j = git_tree_entrycount(tree);
for (j = 0; j < max_j; ++j) { for (j = 0; j < max_j; ++j) {
new_tf->entries[n_entries].parent_entry_index = i; tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
new_tf->entries[n_entries].parent_tree_index = j; GITERR_CHECK_ALLOC(entry);
n_entries++;
entry->parent = head->entries[i];
entry->te = git_tree_entry_byindex(tree, j);
entry->tree = NULL;
tf->entries[n_entries++] = entry;
} }
} }
/* if ignore_case, sort entries case insensitively */ /* if ignore_case, sort entries case insensitively */
if (iterator__ignore_case(ti)) if (iterator__ignore_case(ti))
git__qsort_r( git__tsort_r(
new_tf->entries, new_tf->n_entries, sizeof(tree_iterator_entry), (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
tree_iterator__entry_cmp, new_tf);
/* pick new_tf->current based on "start" (or start at zero) */
if (tf->startlen > 0) {
/* find first item >= start */
for (i = 0; i < new_tf->n_entries; ++i) {
if (!(te = tree_iterator__tree_entry_by_index(new_tf, i)))
break;
sz = min(tf->startlen, te->filename_len);
if (ti->strncomp(tf->start, te->filename, sz) <= 0 &&
(tf->startlen <= te->filename_len ||
tf->start[te->filename_len] == '/'))
break;
}
new_tf->current = i;
if ((new_tf->start = strchr(tf->start, '/')) != NULL) { /* pick tf->current based on "start" (or start at zero) */
new_tf->start++; if (head->startlen > 0) {
new_tf->startlen = strlen(new_tf->start); git__bsearch_r((void **)tf->entries, tf->n_entries, head,
tree_iterator__search_cmp, ti, &tf->current);
while (tf->current &&
!tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
tf->current--;
if ((tf->start = strchr(head->start, '/')) != NULL) {
tf->start++;
tf->startlen = strlen(tf->start);
} }
} }
ti->path_has_filename = false; ti->path_has_filename = false;
/* find next and load trees for current range */ if ((error = tree_iterator__set_next(ti, tf)) < 0)
if ((error = tree_iterator__set_next(ti, new_tf)) < 0)
return error; return error;
tf->child = new_tf; /* autoexpand as needed */
ti->head = new_tf;
if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
return tree_iterator__push_frame(ti); return tree_iterator__push_frame(ti);
return 0; return 0;
} }
GIT_INLINE(void) tree_iterator__free_tree(tree_iterator_entry *entry)
{
if (entry->tree) {
git_tree_free(entry->tree);
entry->tree = NULL;
}
}
static bool tree_iterator__move_to_next( static bool tree_iterator__move_to_next(
tree_iterator *ti, tree_iterator_frame *tf) tree_iterator *ti, tree_iterator_frame *tf)
{ {
if (tf->next > tf->current + 1) if (tf->next > tf->current + 1)
ti->path_ambiguities--; ti->path_ambiguities--;
if (!tf->up) { /* at root */
tf->current = tf->next;
return false;
}
for (; tf->current < tf->next; tf->current++) { for (; tf->current < tf->next; tf->current++) {
if (tf->parent) git_tree_free(tf->entries[tf->current]->tree);
tree_iterator__free_tree(&tf->entries[tf->current]); tf->entries[tf->current]->tree = NULL;
} }
return (tf->current < tf->n_entries); return (tf->current < tf->n_entries);
} }
static bool tree_iterator__pop_frame(tree_iterator *ti) static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
{ {
tree_iterator_frame *tf = ti->head; tree_iterator_frame *tf = ti->head;
if (!tf->parent) if (!tf->up)
return false; return false;
tree_iterator__move_to_next(ti, tf); ti->head = tf->up;
ti->head->down = NULL;
ti->head = tf->parent; tree_iterator__move_to_next(ti, tf);
ti->head->child = NULL;
git__free(tf);
if (!final) { /* if final, don't bother to clean up */
git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
git_buf_rtruncate_at_char(&ti->path, '/'); git_buf_rtruncate_at_char(&ti->path, '/');
}
git__free(tf);
return true; return true;
} }
static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
{
while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
if (!final) {
ti->head->current = to_end ? ti->head->n_entries : 0;
ti->path_ambiguities = 0;
git_buf_clear(&ti->path);
}
return 0;
}
static int tree_iterator__current( static int tree_iterator__current(
const git_index_entry **entry, git_iterator *self) const git_index_entry **entry, git_iterator *self)
{ {
...@@ -462,24 +435,21 @@ static int tree_iterator__current( ...@@ -462,24 +435,21 @@ static int tree_iterator__current(
iterator__clear_entry(entry); iterator__clear_entry(entry);
if (!(te = tree_iterator__tree_entry_by_index(tf, tf->current))) if (tf->current >= tf->n_entries)
return 0; return 0;
te = tf->entries[tf->current]->te;
ti->entry.mode = te->attr; ti->entry.mode = te->attr;
git_oid_cpy(&ti->entry.oid, &te->oid); git_oid_cpy(&ti->entry.oid, &te->oid);
ti->entry.path = tree_iterator__current_filename(ti, te); ti->entry.path = tree_iterator__current_filename(ti, te);
if (ti->entry.path == NULL) GITERR_CHECK_ALLOC(ti->entry.path);
return -1;
if (ti->path_ambiguities > 0) if (ti->path_ambiguities > 0)
tree_iterator__rewrite_filename(ti); tree_iterator__rewrite_filename(ti);
if (iterator__past_end(ti, ti->entry.path)) { if (iterator__past_end(ti, ti->entry.path))
while (tree_iterator__pop_frame(ti)) /* pop to top */; return tree_iterator__pop_all(ti, true, false);
ti->head->current = ti->head->n_entries;
return 0;
}
if (entry) if (entry)
*entry = &ti->entry; *entry = &ti->entry;
...@@ -524,7 +494,8 @@ static int tree_iterator__advance( ...@@ -524,7 +494,8 @@ static int tree_iterator__advance(
} }
/* scan forward and up, advancing in frame or popping frame when done */ /* scan forward and up, advancing in frame or popping frame when done */
while (!tree_iterator__move_to_next(ti, tf) && tree_iterator__pop_frame(ti)) while (!tree_iterator__move_to_next(ti, tf) &&
tree_iterator__pop_frame(ti, false))
tf = ti->head; tf = ti->head;
/* find next and load trees */ /* find next and load trees */
...@@ -549,15 +520,12 @@ static int tree_iterator__reset( ...@@ -549,15 +520,12 @@ static int tree_iterator__reset(
{ {
tree_iterator *ti = (tree_iterator *)self; tree_iterator *ti = (tree_iterator *)self;
while (tree_iterator__pop_frame(ti)) /* pop to top */; tree_iterator__pop_all(ti, false, false);
ti->top->current = 0;
if (iterator__reset_range(self, start, end) < 0) if (iterator__reset_range(self, start, end) < 0)
return -1; return -1;
git_buf_clear(&ti->path);
ti->path_ambiguities = 0;
return tree_iterator__push_frame(ti); /* re-expand top tree */ return tree_iterator__push_frame(ti); /* re-expand root tree */
} }
static int tree_iterator__at_end(git_iterator *self) static int tree_iterator__at_end(git_iterator *self)
...@@ -570,30 +538,29 @@ static void tree_iterator__free(git_iterator *self) ...@@ -570,30 +538,29 @@ static void tree_iterator__free(git_iterator *self)
{ {
tree_iterator *ti = (tree_iterator *)self; tree_iterator *ti = (tree_iterator *)self;
while (tree_iterator__pop_frame(ti)) /* pop to top */; tree_iterator__pop_all(ti, true, false);
if (ti->head) { git_tree_free(ti->head->entries[0]->tree);
tree_iterator__free_tree(&ti->head->entries[0]);
git__free(ti->head); git__free(ti->head);
} git_pool_clear(&ti->pool);
ti->head = ti->top = NULL;
git_buf_free(&ti->path); git_buf_free(&ti->path);
} }
static int tree_iterator__create_top_frame(tree_iterator *ti, git_tree *tree) static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
{ {
size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry); size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
tree_iterator_frame *top = git__calloc(sz, sizeof(char)); tree_iterator_frame *root = git__calloc(sz, sizeof(char));
GITERR_CHECK_ALLOC(top); GITERR_CHECK_ALLOC(root);
top->n_entries = 1; root->n_entries = 1;
top->next = 1; root->next = 1;
top->start = ti->base.start; root->start = ti->base.start;
top->startlen = top->start ? strlen(top->start) : 0; root->startlen = root->start ? strlen(root->start) : 0;
top->entries[0].tree = tree; root->entries[0] = git_pool_mallocz(&ti->pool, 1);
GITERR_CHECK_ALLOC(root->entries[0]);
root->entries[0]->tree = tree;
ti->head = ti->top = top; ti->head = ti->root = root;
return 0; return 0;
} }
...@@ -620,8 +587,9 @@ int git_iterator_for_tree( ...@@ -620,8 +587,9 @@ int git_iterator_for_tree(
goto fail; goto fail;
ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp; ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
if ((error = tree_iterator__create_top_frame(ti, tree)) < 0 || if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
(error = tree_iterator__push_frame(ti)) < 0) /* expand top right now */ (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
(error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
goto fail; goto fail;
*iter = (git_iterator *)ti; *iter = (git_iterator *)ti;
...@@ -878,7 +846,6 @@ typedef struct { ...@@ -878,7 +846,6 @@ typedef struct {
git_iterator base; git_iterator base;
git_iterator_callbacks cb; git_iterator_callbacks cb;
workdir_iterator_frame *stack; workdir_iterator_frame *stack;
int (*entrycmp)(const void *pfx, const void *item);
git_ignores ignores; git_ignores ignores;
git_index_entry entry; git_index_entry entry;
git_buf path; git_buf path;
...@@ -940,16 +907,11 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf) ...@@ -940,16 +907,11 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
static int workdir_iterator__update_entry(workdir_iterator *wi); static int workdir_iterator__update_entry(workdir_iterator *wi);
static int workdir_iterator__entry_cmp_case(const void *pfx, const void *item) static int workdir_iterator__entry_cmp(const void *i, const void *item)
{ {
const workdir_iterator *wi = (const workdir_iterator *)i;
const git_path_with_stat *ps = item; const git_path_with_stat *ps = item;
return git__prefixcmp((const char *)pfx, ps->path); return wi->base.prefixcomp(wi->base.start, ps->path);
}
static int workdir_iterator__entry_cmp_icase(const void *pfx, const void *item)
{
const git_path_with_stat *ps = item;
return git__prefixcmp_icase((const char *)pfx, ps->path);
} }
static void workdir_iterator__seek_frame_start( static void workdir_iterator__seek_frame_start(
...@@ -960,7 +922,7 @@ static void workdir_iterator__seek_frame_start( ...@@ -960,7 +922,7 @@ static void workdir_iterator__seek_frame_start(
if (wi->base.start) if (wi->base.start)
git_vector_bsearch2( git_vector_bsearch2(
&wf->index, &wf->entries, wi->entrycmp, wi->base.start); &wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
else else
wf->index = 0; wf->index = 0;
...@@ -1236,10 +1198,7 @@ int git_iterator_for_workdir( ...@@ -1236,10 +1198,7 @@ int git_iterator_for_workdir(
git__free(wi); git__free(wi);
return -1; return -1;
} }
wi->root_len = wi->path.size; wi->root_len = wi->path.size;
wi->entrycmp = iterator__ignore_case(wi) ?
workdir_iterator__entry_cmp_icase : workdir_iterator__entry_cmp_case;
if ((error = workdir_iterator__expand_dir(wi)) < 0) { if ((error = workdir_iterator__expand_dir(wi)) < 0) {
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
...@@ -1306,7 +1265,8 @@ int git_iterator_current_tree_entry( ...@@ -1306,7 +1265,8 @@ int git_iterator_current_tree_entry(
*tree_entry = NULL; *tree_entry = NULL;
else { else {
tree_iterator_frame *tf = ((tree_iterator *)iter)->head; tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
*tree_entry = tree_iterator__tree_entry_by_index(tf, tf->current); *tree_entry = (tf->current < tf->n_entries) ?
tf->entries[tf->current]->te : NULL;
} }
return 0; return 0;
...@@ -1327,12 +1287,10 @@ int git_iterator_current_parent_tree( ...@@ -1327,12 +1287,10 @@ int git_iterator_current_parent_tree(
if (iter->type != GIT_ITERATOR_TYPE_TREE) if (iter->type != GIT_ITERATOR_TYPE_TREE)
return 0; return 0;
tf = ti->top; for (tf = ti->root; *scan; ) {
if (!(tf = tf->down) ||
while (*scan) { tf->current >= tf->n_entries ||
/* get entry of this parent that child is currently on */ !(te = tf->entries[tf->current]->te) ||
if (!(tf = tf->child) ||
!(te = tree_iterator__tree_entry_by_index(tf, tf->current)) ||
ti->strncomp(scan, te->filename, te->filename_len) != 0) ti->strncomp(scan, te->filename, te->filename_len) != 0)
return 0; return 0;
...@@ -1341,7 +1299,7 @@ int git_iterator_current_parent_tree( ...@@ -1341,7 +1299,7 @@ int git_iterator_current_parent_tree(
scan++; scan++;
} }
*tree_ptr = tf->entries[tf->current].tree; *tree_ptr = tf->entries[tf->current]->tree;
return 0; return 0;
} }
......
...@@ -679,37 +679,14 @@ int git_path_apply_relative(git_buf *target, const char *relpath) ...@@ -679,37 +679,14 @@ int git_path_apply_relative(git_buf *target, const char *relpath)
int git_path_cmp( int git_path_cmp(
const char *name1, size_t len1, int isdir1, const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2) const char *name2, size_t len2, int isdir2,
int (*compare)(const char *, const char *, size_t))
{ {
unsigned char c1, c2; unsigned char c1, c2;
size_t len = len1 < len2 ? len1 : len2; size_t len = len1 < len2 ? len1 : len2;
int cmp; int cmp;
cmp = memcmp(name1, name2, len); cmp = compare(name1, name2, len);
if (cmp)
return cmp;
c1 = name1[len];
c2 = name2[len];
if (c1 == '\0' && isdir1)
c1 = '/';
if (c2 == '\0' && isdir2)
c2 = '/';
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
int git_path_icmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2)
{
unsigned char c1, c2;
size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = strncasecmp(name1, name2, len);
if (cmp) if (cmp)
return cmp; return cmp;
......
...@@ -265,12 +265,8 @@ extern int git_path_direach( ...@@ -265,12 +265,8 @@ extern int git_path_direach(
*/ */
extern int git_path_cmp( extern int git_path_cmp(
const char *name1, size_t len1, int isdir1, const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2); const char *name2, size_t len2, int isdir2,
int (*compare)(const char *, const char *, size_t));
/** Path sort function that is case insensitive */
extern int git_path_icmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2);
/** /**
* Invoke callback up path directory by directory until the ceiling is * Invoke callback up path directory by directory until the ceiling is
......
...@@ -10,6 +10,11 @@ struct git_pool_page { ...@@ -10,6 +10,11 @@ struct git_pool_page {
char data[GIT_FLEX_ARRAY]; char data[GIT_FLEX_ARRAY];
}; };
typedef struct git_pool_freelist_item git_pool_freelist_item;
struct git_pool_freelist_item {
git_pool_freelist_item *next;
};
#define GIT_POOL_MIN_USABLE 4 #define GIT_POOL_MIN_USABLE 4
#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*) #define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*)
...@@ -150,7 +155,7 @@ void *git_pool_malloc(git_pool *pool, uint32_t items) ...@@ -150,7 +155,7 @@ void *git_pool_malloc(git_pool *pool, uint32_t items)
pool->has_multi_item_alloc = 1; pool->has_multi_item_alloc = 1;
else if (pool->free_list != NULL) { else if (pool->free_list != NULL) {
ptr = pool->free_list; ptr = pool->free_list;
pool->free_list = *((void **)pool->free_list); pool->free_list = ((git_pool_freelist_item *)pool->free_list)->next;
return ptr; return ptr;
} }
...@@ -235,10 +240,31 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b) ...@@ -235,10 +240,31 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
void git_pool_free(git_pool *pool, void *ptr) void git_pool_free(git_pool *pool, void *ptr)
{ {
assert(pool && ptr && pool->item_size >= sizeof(void*)); git_pool_freelist_item *item = ptr;
assert(pool && pool->item_size >= sizeof(void*));
if (item) {
item->next = pool->free_list;
pool->free_list = item;
}
}
void git_pool_free_array(git_pool *pool, size_t count, void **ptrs)
{
git_pool_freelist_item **items = (git_pool_freelist_item **)ptrs;
size_t i;
assert(pool && ptrs && pool->item_size >= sizeof(void*));
if (!count)
return;
for (i = count - 1; i > 0; --i)
items[i]->next = items[i - 1];
*((void **)ptr) = pool->free_list; items[i]->next = pool->free_list;
pool->free_list = ptr; pool->free_list = items[count - 1];
} }
uint32_t git_pool__open_pages(git_pool *pool) uint32_t git_pool__open_pages(git_pool *pool)
......
...@@ -126,6 +126,13 @@ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); ...@@ -126,6 +126,13 @@ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
*/ */
extern void git_pool_free(git_pool *pool, void *ptr); extern void git_pool_free(git_pool *pool, void *ptr);
/**
* Push an array of pool allocated blocks efficiently onto the free list.
*
* This has the same constraints as `git_pool_free()` above.
*/
extern void git_pool_free_array(git_pool *pool, size_t count, void **ptrs);
/* /*
* Misc utilities * Misc utilities
*/ */
......
...@@ -55,23 +55,28 @@ static int valid_entry_name(const char *filename) ...@@ -55,23 +55,28 @@ static int valid_entry_name(const char *filename)
strcmp(filename, DOT_GIT) != 0)); strcmp(filename, DOT_GIT) != 0));
} }
int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2) static int entry_sort_cmp(const void *a, const void *b)
{ {
const git_tree_entry *e1 = (const git_tree_entry *)a;
const git_tree_entry *e2 = (const git_tree_entry *)b;
return git_path_cmp( return git_path_cmp(
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1), e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2)); e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
git__strncmp);
} }
int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2) int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
{ {
return git_path_icmp( return entry_sort_cmp(e1, e2);
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2));
} }
static int entry_sort_cmp(const void *a, const void *b) int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
{ {
return git_tree_entry_cmp((const git_tree_entry *)a, (const git_tree_entry *)b); return git_path_cmp(
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
git__strncasecmp);
} }
static git_tree_entry *alloc_entry(const char *filename) static git_tree_entry *alloc_entry(const char *filename)
......
...@@ -83,3 +83,53 @@ void test_core_pool__2(void) ...@@ -83,3 +83,53 @@ void test_core_pool__2(void)
git_pool_clear(&p); git_pool_clear(&p);
} }
void test_core_pool__free_list(void)
{
int i;
git_pool p;
void *ptr, *ptrs[50];
cl_git_pass(git_pool_init(&p, 100, 100));
for (i = 0; i < 10; ++i) {
ptr = git_pool_malloc(&p, 1);
cl_assert(ptr != NULL);
}
cl_assert_equal_i(10, (int)p.items);
for (i = 0; i < 50; ++i) {
ptrs[i] = git_pool_malloc(&p, 1);
cl_assert(ptrs[i] != NULL);
}
cl_assert_equal_i(60, (int)p.items);
git_pool_free(&p, ptr);
cl_assert_equal_i(60, (int)p.items);
git_pool_free_array(&p, 50, ptrs);
cl_assert_equal_i(60, (int)p.items);
for (i = 0; i < 50; ++i) {
ptrs[i] = git_pool_malloc(&p, 1);
cl_assert(ptrs[i] != NULL);
}
cl_assert_equal_i(60, (int)p.items);
for (i = 0; i < 111; ++i) {
ptr = git_pool_malloc(&p, 1);
cl_assert(ptr != NULL);
}
cl_assert_equal_i(170, (int)p.items);
git_pool_free_array(&p, 50, ptrs);
cl_assert_equal_i(170, (int)p.items);
for (i = 0; i < 50; ++i) {
ptrs[i] = git_pool_malloc(&p, 1);
cl_assert(ptrs[i] != NULL);
}
cl_assert_equal_i(170, (int)p.items);
git_pool_clear(&p);
}
...@@ -337,6 +337,8 @@ void test_repo_iterator__tree_icase(void) ...@@ -337,6 +337,8 @@ void test_repo_iterator__tree_icase(void)
&i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
expect_iterator_items(i, 1, NULL, 6, NULL); expect_iterator_items(i, 1, NULL, 6, NULL);
git_iterator_free(i); git_iterator_free(i);
git_tree_free(head);
} }
void test_repo_iterator__tree_more(void) void test_repo_iterator__tree_more(void)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment