Commit 0c468633 by Russell Belfer

Improved tree iterator internals

This updates the tree iterator internals to be more efficient.

The tree_iterator_entry objects are now kept as pointers that are
allocated from a git_pool, so that we may use git__tsort_r for
sorting (which is better than qsort, given that the tree is
likely mostly ordered already).

Those tree_iterator_entry objects now keep direct pointers to the
data they refer to instead of keeping indirect index values.  This
simplifies a lot of the data structure traversal code.

This also adds bsearch to find the start item position for range-
limited tree iterators, and is more explicit about using
git_path_cmp instead of reimplementing it.  The git_path_cmp
changed a bit to make it easier for tree_iterators to use it (but
it was barely being used previously, so not a big deal).

This adds a git_pool_free_array function that efficiently frees a
list of pool allocated pointers (which the tree_iterator keeps).
Also, added new tests for the git_pool free list functionality
that was not previously being tested (or used).
parent 6950dca4
......@@ -679,37 +679,14 @@ int git_path_apply_relative(git_buf *target, const char *relpath)
int git_path_cmp(
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;
size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = memcmp(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);
cmp = compare(name1, name2, len);
if (cmp)
return cmp;
......
......@@ -265,12 +265,8 @@ extern int git_path_direach(
*/
extern int git_path_cmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2);
/** 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);
const char *name2, size_t len2, int isdir2,
int (*compare)(const char *, const char *, size_t));
/**
* Invoke callback up path directory by directory until the ceiling is
......
......@@ -235,10 +235,28 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
void git_pool_free(git_pool *pool, void *ptr)
{
assert(pool && ptr && pool->item_size >= sizeof(void*));
assert(pool && pool->item_size >= sizeof(void*));
if (ptr) {
*((void **)ptr) = pool->free_list;
pool->free_list = ptr;
}
}
void git_pool_free_array(git_pool *pool, size_t count, void **ptrs)
{
size_t i;
assert(pool && ptrs && pool->item_size >= sizeof(void*));
if (!count)
return;
for (i = count - 1; i > 0; --i)
*((void **)ptrs[i]) = ptrs[i - 1];
*((void **)ptrs[0]) = pool->free_list;
pool->free_list = ptrs[count - 1];
}
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);
*/
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
*/
......
......@@ -55,23 +55,28 @@ static int valid_entry_name(const char *filename)
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(
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(
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2));
return entry_sort_cmp(e1, 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)
......
......@@ -83,3 +83,53 @@ void test_core_pool__2(void)
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);
}
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