mirror of
https://github.com/libgit2/libgit2.git
synced 2026-01-25 11:06:32 +00:00
Introduce `git_hashmap` and `git_hashset` functionality that is a port of `khash.h` to be more idiomatically libgit2. This gives us many of the benefits of khash that we had abstracted away: 1. Typesafety on the values, since we define the structs and functions 2. Ability to create hashes on the stack 3. Ability to new up hashmaps (or sets) without the libgit2 abstraction wrappers that we had been adding This uses the macros to define hashes (either the structure, or the functions, or both) which is very much in the spirit of khash, but the results are much more idiomatically libgit2.
228 lines
5.4 KiB
C
228 lines
5.4 KiB
C
#include "clar_libgit2.h"
|
|
#include "hashmap.h"
|
|
#include "hashmap_str.h"
|
|
|
|
GIT_HASHMAP_STR_SETUP(git_hashmap_test, char *);
|
|
|
|
static git_hashmap_test g_table;
|
|
|
|
void test_hashmap__initialize(void)
|
|
{
|
|
memset(&g_table, 0x0, sizeof(git_hashmap_test));
|
|
}
|
|
|
|
void test_hashmap__cleanup(void)
|
|
{
|
|
git_hashmap_test_dispose(&g_table);
|
|
}
|
|
|
|
void test_hashmap__0(void)
|
|
{
|
|
cl_assert(git_hashmap_test_size(&g_table) == 0);
|
|
}
|
|
|
|
static void insert_strings(git_hashmap_test *table, size_t count)
|
|
{
|
|
size_t i, j, over;
|
|
char *str;
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
str = git__malloc(10);
|
|
for (j = 0; j < 10; ++j)
|
|
str[j] = 'a' + (i % 26);
|
|
str[9] = '\0';
|
|
|
|
/* if > 26, then encode larger value in first letters */
|
|
for (j = 0, over = i / 26; over > 0; j++, over = over / 26)
|
|
str[j] = 'A' + (over % 26);
|
|
|
|
cl_git_pass(git_hashmap_test_put(table, str, str));
|
|
}
|
|
|
|
cl_assert_equal_i(git_hashmap_test_size(table), count);
|
|
}
|
|
|
|
void test_hashmap__inserted_strings_can_be_retrieved(void)
|
|
{
|
|
char *str;
|
|
git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT;
|
|
size_t idx = 0;
|
|
|
|
insert_strings(&g_table, 20);
|
|
|
|
cl_assert(git_hashmap_test_contains(&g_table, "aaaaaaaaa"));
|
|
cl_assert(git_hashmap_test_contains(&g_table, "ggggggggg"));
|
|
cl_assert(!git_hashmap_test_contains(&g_table, "aaaaaaaab"));
|
|
cl_assert(!git_hashmap_test_contains(&g_table, "abcdefghi"));
|
|
|
|
while (git_hashmap_test_iterate(&iter, NULL, &str, &g_table) == 0) {
|
|
idx++;
|
|
git__free(str);
|
|
}
|
|
|
|
cl_assert_equal_sz(20, idx);
|
|
}
|
|
|
|
void test_hashmap__deleted_entry_cannot_be_retrieved(void)
|
|
{
|
|
const char *key;
|
|
char *str;
|
|
git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT;
|
|
size_t idx = 0;
|
|
|
|
insert_strings(&g_table, 20);
|
|
|
|
cl_assert(git_hashmap_test_contains(&g_table, "bbbbbbbbb"));
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "bbbbbbbbb"));
|
|
cl_assert_equal_s(str, "bbbbbbbbb");
|
|
cl_git_pass(git_hashmap_test_remove(&g_table, "bbbbbbbbb"));
|
|
git__free(str);
|
|
|
|
cl_assert(!git_hashmap_test_contains(&g_table, "bbbbbbbbb"));
|
|
|
|
while (git_hashmap_test_iterate(&iter, &key, &str, &g_table) == 0) {
|
|
idx++;
|
|
git__free(str);
|
|
}
|
|
|
|
cl_assert_equal_sz(idx, 19);
|
|
}
|
|
|
|
void test_hashmap__inserting_many_keys_succeeds(void)
|
|
{
|
|
char *str;
|
|
git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT;
|
|
size_t idx = 0;
|
|
|
|
insert_strings(&g_table, 10000);
|
|
|
|
while (git_hashmap_test_iterate(&iter, NULL, &str, &g_table) == 0) {
|
|
idx++;
|
|
git__free(str);
|
|
}
|
|
|
|
cl_assert_equal_sz(idx, 10000);
|
|
}
|
|
|
|
void test_hashmap__get_succeeds_with_existing_entries(void)
|
|
{
|
|
const char *keys[] = { "foo", "bar", "gobble" };
|
|
char *values[] = { "oof", "rab", "elbbog" };
|
|
char *str;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(keys); i++)
|
|
cl_git_pass(git_hashmap_test_put(&g_table, keys[i], values[i]));
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "foo"));
|
|
cl_assert_equal_s(str, "oof");
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "bar"));
|
|
cl_assert_equal_s(str, "rab");
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "gobble"));
|
|
cl_assert_equal_s(str, "elbbog");
|
|
}
|
|
|
|
void test_hashmap__get_returns_notfound_on_nonexisting_key(void)
|
|
{
|
|
const char *keys[] = { "foo", "bar", "gobble" };
|
|
char *values[] = { "oof", "rab", "elbbog" };
|
|
char *str;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(keys); i++)
|
|
cl_git_pass(git_hashmap_test_put(&g_table, keys[i], values[i]));
|
|
|
|
cl_git_fail_with(GIT_ENOTFOUND, git_hashmap_test_get(&str, &g_table, "other"));
|
|
}
|
|
|
|
void test_hashmap__put_persists_key(void)
|
|
{
|
|
char *str;
|
|
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "foo", "oof"));
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "foo"));
|
|
cl_assert_equal_s(str, "oof");
|
|
}
|
|
|
|
void test_hashmap__put_persists_multpile_keys(void)
|
|
{
|
|
char *str;
|
|
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "foo", "oof"));
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "bar", "rab"));
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "foo"));
|
|
cl_assert_equal_s(str, "oof");
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "bar"));
|
|
cl_assert_equal_s(str, "rab");
|
|
}
|
|
|
|
void test_hashmap__put_updates_existing_key(void)
|
|
{
|
|
char *str;
|
|
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "foo", "oof"));
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "bar", "rab"));
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "gobble", "elbbog"));
|
|
cl_assert_equal_i(3, git_hashmap_test_size(&g_table));
|
|
|
|
cl_git_pass(git_hashmap_test_put(&g_table, "foo", "other"));
|
|
cl_assert_equal_i(git_hashmap_test_size(&g_table), 3);
|
|
|
|
cl_git_pass(git_hashmap_test_get(&str, &g_table, "foo"));
|
|
cl_assert_equal_s(str, "other");
|
|
}
|
|
|
|
void test_hashmap__iteration(void)
|
|
{
|
|
struct {
|
|
char *key;
|
|
char *value;
|
|
int seen;
|
|
} entries[] = {
|
|
{ "foo", "oof" },
|
|
{ "bar", "rab" },
|
|
{ "gobble", "elbbog" },
|
|
};
|
|
const char *key;
|
|
char *value;
|
|
uint32_t i, n;
|
|
git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(entries); i++)
|
|
cl_git_pass(git_hashmap_test_put(&g_table, entries[i].key, entries[i].value));
|
|
|
|
i = 0, n = 0;
|
|
while (git_hashmap_test_iterate(&iter, &key, &value, &g_table) == 0) {
|
|
size_t j;
|
|
|
|
for (j = 0; j < ARRAY_SIZE(entries); j++) {
|
|
if (strcmp(entries[j].key, key))
|
|
continue;
|
|
|
|
cl_assert_equal_i(entries[j].seen, 0);
|
|
cl_assert_equal_s(entries[j].value, value);
|
|
entries[j].seen++;
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(entries); i++)
|
|
cl_assert_equal_i(entries[i].seen, 1);
|
|
|
|
cl_assert_equal_i(n, ARRAY_SIZE(entries));
|
|
}
|
|
|
|
void test_hashmap__iterating_empty_map_stops_immediately(void)
|
|
{
|
|
git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT;
|
|
|
|
cl_git_fail_with(GIT_ITEROVER, git_hashmap_test_iterate(&iter, NULL, NULL, &g_table));
|
|
}
|