summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-04-25 15:24:05 -0700
committerRussell Belfer <rb@github.com>2012-04-25 15:24:05 -0700
commit3fc5c65d1a072fc727226cd66a1b096df4919da5 (patch)
tree3d0a6a9d43c715d77b2c0b151ed344253011631d
parentf50087c03b08230ba7d912e827a72e857128a7a8 (diff)
parentc2b670436f4cc8901811ae0348f3c56150d1ccd5 (diff)
downloadlibgit2-3fc5c65d1a072fc727226cd66a1b096df4919da5.tar.gz
Merge pull request #642 from arrbee/mem-pools
Memory pools and khash hashtables
-rw-r--r--src/attr.c105
-rw-r--r--src/attr.h9
-rw-r--r--src/attr_file.c66
-rw-r--r--src/attr_file.h15
-rw-r--r--src/blob.c4
-rw-r--r--src/buffer.c2
-rw-r--r--src/cache.c11
-rw-r--r--src/config.c1
-rw-r--r--src/config_cache.c1
-rw-r--r--src/config_file.c105
-rw-r--r--src/diff.c121
-rw-r--r--src/diff.h2
-rw-r--r--src/errors.c2
-rw-r--r--src/filter.c2
-rw-r--r--src/global.c6
-rw-r--r--src/hashtable.c267
-rw-r--r--src/hashtable.h95
-rw-r--r--src/ignore.c4
-rw-r--r--src/khash.h608
-rw-r--r--src/odb.c2
-rw-r--r--src/oidmap.h42
-rw-r--r--src/pool.c294
-rw-r--r--src/pool.h125
-rw-r--r--src/refs.c74
-rw-r--r--src/refs.h4
-rw-r--r--src/remote.c2
-rw-r--r--src/repository.c2
-rw-r--r--src/repository.h4
-rw-r--r--src/revwalk.c105
-rw-r--r--src/strmap.h54
-rw-r--r--src/submodule.c128
-rw-r--r--src/util.h17
-rw-r--r--tests-clar/attr/file.c10
-rw-r--r--tests-clar/attr/lookup.c13
-rw-r--r--tests-clar/core/pool.c85
-rw-r--r--tests-clar/core/strmap.c102
-rw-r--r--tests/t07-hashtable.c189
-rw-r--r--tests/test_main.c2
38 files changed, 1745 insertions, 935 deletions
diff --git a/src/attr.c b/src/attr.c
index c02289363..3e3a7e749 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -3,6 +3,8 @@
#include "config.h"
#include <ctype.h>
+GIT__USE_STRMAP;
+
static int collect_attr_files(
git_repository *repo, const char *path, git_vector *files);
@@ -124,14 +126,14 @@ int git_attr_foreach(
git_attr_file *file;
git_attr_rule *rule;
git_attr_assignment *assign;
- git_hashtable *seen = NULL;
+ git_strmap *seen = NULL;
if ((error = git_attr_path__init(
&path, pathname, git_repository_workdir(repo))) < 0 ||
(error = collect_attr_files(repo, pathname, &files)) < 0)
return error;
- seen = git_hashtable_alloc(8, git_hash__strhash_cb, git_hash__strcmp_cb);
+ seen = git_strmap_alloc();
GITERR_CHECK_ALLOC(seen);
git_vector_foreach(&files, i, file) {
@@ -140,10 +142,11 @@ int git_attr_foreach(
git_vector_foreach(&rule->assigns, k, assign) {
/* skip if higher priority assignment was already seen */
- if (git_hashtable_lookup(seen, assign->name))
+ if (git_strmap_exists(seen, assign->name))
continue;
- if (!(error = git_hashtable_insert(seen, assign->name, assign)))
+ git_strmap_insert(seen, assign->name, assign, error);
+ if (error >= 0)
error = callback(assign->name, assign->value, payload);
if (error != 0)
@@ -153,7 +156,7 @@ int git_attr_foreach(
}
cleanup:
- git_hashtable_free(seen);
+ git_strmap_free(seen);
git_vector_free(&files);
return error;
@@ -167,6 +170,7 @@ int git_attr_add_macro(
{
int error;
git_attr_rule *macro = NULL;
+ git_pool *pool;
if (git_attr_cache__init(repo) < 0)
return -1;
@@ -174,13 +178,15 @@ int git_attr_add_macro(
macro = git__calloc(1, sizeof(git_attr_rule));
GITERR_CHECK_ALLOC(macro);
- macro->match.pattern = git__strdup(name);
+ pool = &git_repository_attr_cache(repo)->pool;
+
+ macro->match.pattern = git_pool_strdup(pool, name);
GITERR_CHECK_ALLOC(macro->match.pattern);
macro->match.length = strlen(macro->match.pattern);
macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
- error = git_attr_assignment__parse(repo, &macro->assigns, &values);
+ error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
if (!error)
error = git_attr_cache__insert_macro(repo, macro);
@@ -194,10 +200,12 @@ int git_attr_add_macro(
bool git_attr_cache__is_cached(git_repository *repo, const char *path)
{
const char *cache_key = path;
+ git_strmap *files = git_repository_attr_cache(repo)->files;
+
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
cache_key += strlen(git_repository_workdir(repo));
- return (git_hashtable_lookup(
- git_repository_attr_cache(repo)->files, cache_key) != NULL);
+
+ return git_strmap_exists(files, cache_key);
}
int git_attr_cache__lookup_or_create_file(
@@ -210,9 +218,11 @@ int git_attr_cache__lookup_or_create_file(
int error;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
+ khiter_t pos;
- if ((file = git_hashtable_lookup(cache->files, key)) != NULL) {
- *file_ptr = file;
+ pos = git_strmap_lookup_index(cache->files, key);
+ if (git_strmap_valid_index(cache->files, pos)) {
+ *file_ptr = git_strmap_value_at(cache->files, pos);
return 0;
}
@@ -221,7 +231,7 @@ int git_attr_cache__lookup_or_create_file(
return 0;
}
- if (git_attr_file__new(&file) < 0)
+ if (git_attr_file__new(&file, &cache->pool) < 0)
return -1;
if (loader)
@@ -229,8 +239,11 @@ int git_attr_cache__lookup_or_create_file(
else
error = git_attr_file__set_path(repo, key, file);
- if (!error)
- error = git_hashtable_insert(cache->files, file->path, file);
+ if (!error) {
+ git_strmap_insert(cache->files, file->path, file, error);
+ if (error > 0)
+ error = 0;
+ }
if (error < 0) {
git_attr_file__free(file);
@@ -370,20 +383,20 @@ int git_attr_cache__init(git_repository *repo)
/* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
- cache->files = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->files)
- return -1;
+ cache->files = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->files);
}
/* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
- cache->macros = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->macros)
- return -1;
+ cache->macros = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->macros);
}
+ /* allocate string pool */
+ if (git_pool_init(&cache->pool, 1, 0) < 0)
+ return -1;
+
cache->initialized = 1;
/* insert default macros */
@@ -393,38 +406,62 @@ int git_attr_cache__init(git_repository *repo)
void git_attr_cache_flush(
git_repository *repo)
{
- git_hashtable *table;
+ git_attr_cache *cache;
if (!repo)
return;
- if ((table = git_repository_attr_cache(repo)->files) != NULL) {
+ cache = git_repository_attr_cache(repo);
+
+ if (cache->files != NULL) {
git_attr_file *file;
- GIT_HASHTABLE_FOREACH_VALUE(table, file, git_attr_file__free(file));
- git_hashtable_free(table);
+ git_strmap_foreach_value(cache->files, file, {
+ git_attr_file__free(file);
+ });
- git_repository_attr_cache(repo)->files = NULL;
+ git_strmap_free(cache->files);
}
- if ((table = git_repository_attr_cache(repo)->macros) != NULL) {
+ if (cache->macros != NULL) {
git_attr_rule *rule;
- GIT_HASHTABLE_FOREACH_VALUE(table, rule, git_attr_rule__free(rule));
- git_hashtable_free(table);
+ git_strmap_foreach_value(cache->macros, rule, {
+ git_attr_rule__free(rule);
+ });
- git_repository_attr_cache(repo)->macros = NULL;
+ git_strmap_free(cache->macros);
}
- git_repository_attr_cache(repo)->initialized = 0;
+ git_pool_clear(&cache->pool);
+
+ cache->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ int error;
+
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
return 0;
- return git_hashtable_insert(
- git_repository_attr_cache(repo)->macros, macro->match.pattern, macro);
+ git_strmap_insert(macros, macro->match.pattern, macro, error);
+ return (error < 0) ? -1 : 0;
+}
+
+git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name)
+{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ khiter_t pos;
+
+ pos = git_strmap_lookup_index(macros, name);
+
+ if (!git_strmap_valid_index(macros, pos))
+ return NULL;
+
+ return (git_attr_rule *)git_strmap_value_at(macros, pos);
}
+
diff --git a/src/attr.h b/src/attr.h
index 350c0ebad..43caf1b81 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -8,14 +8,16 @@
#define INCLUDE_attr_h__
#include "attr_file.h"
+#include "strmap.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct {
int initialized;
- git_hashtable *files; /* hash path to git_attr_file of rules */
- git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
+ git_pool pool;
+ git_strmap *files; /* hash path to git_attr_file of rules */
+ git_strmap *macros; /* hash name to vector<git_attr_assignment> */
const char *cfg_attr_file; /* cached value of core.attributesfile */
const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
@@ -25,6 +27,9 @@ extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
+extern git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name);
+
extern int git_attr_cache__lookup_or_create_file(
git_repository *repo,
const char *key,
diff --git a/src/attr_file.c b/src/attr_file.c
index b2edce90e..e34053fc3 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -9,21 +9,32 @@ const char *git_attr__false = "[internal]__FALSE__";
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
static void git_attr_rule__clear(git_attr_rule *rule);
-int git_attr_file__new(git_attr_file **attrs_ptr)
+int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool)
{
git_attr_file *attrs = NULL;
attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs);
- if (git_vector_init(&attrs->rules, 4, NULL) < 0) {
- git__free(attrs);
- attrs = NULL;
+ if (pool)
+ attrs->pool = pool;
+ else {
+ attrs->pool = git__calloc(1, sizeof(git_pool));
+ if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
+ goto fail;
+ attrs->pool_is_allocated = true;
}
+ if (git_vector_init(&attrs->rules, 4, NULL) < 0)
+ goto fail;
+
*attrs_ptr = attrs;
+ return 0;
- return attrs ? 0 : -1;
+fail:
+ git_attr_file__free(attrs);
+ attrs_ptr = NULL;
+ return -1;
}
int git_attr_file__set_path(
@@ -76,8 +87,10 @@ int git_attr_file__from_buffer(
}
/* parse the next "pattern attr attr attr" line */
- if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) &&
- !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
+ if (!(error = git_attr_fnmatch__parse(
+ &rule->match, attrs->pool, context, &scan)) &&
+ !(error = git_attr_assignment__parse(
+ repo, attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
@@ -141,12 +154,18 @@ void git_attr_file__free(git_attr_file *file)
git__free(file->path);
file->path = NULL;
+ if (file->pool_is_allocated) {
+ git_pool_clear(file->pool);
+ git__free(file->pool);
+ }
+ file->pool = NULL;
+
git__free(file);
}
-unsigned long git_attr_file__name_hash(const char *name)
+uint32_t git_attr_file__name_hash(const char *name)
{
- unsigned long h = 5381;
+ uint32_t h = 5381;
int c;
assert(name);
while ((c = (int)*name++) != 0)
@@ -293,6 +312,7 @@ int git_attr_path__init(
*/
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base)
{
@@ -358,7 +378,7 @@ int git_attr_fnmatch__parse(
/* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file
*/
- spec->pattern = git__malloc(sourcelen + spec->length + 1);
+ spec->pattern = git_pool_malloc(pool, sourcelen + spec->length + 1);
if (spec->pattern) {
memcpy(spec->pattern, source, sourcelen);
memcpy(spec->pattern + sourcelen, pattern, spec->length);
@@ -366,7 +386,7 @@ int git_attr_fnmatch__parse(
spec->pattern[spec->length] = '\0';
}
} else {
- spec->pattern = git__strndup(pattern, spec->length);
+ spec->pattern = git_pool_strndup(pool, pattern, spec->length);
}
if (!spec->pattern) {
@@ -405,14 +425,11 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
static void git_attr_assignment__free(git_attr_assignment *assign)
{
- git__free(assign->name);
+ /* name and value are stored in a git_pool associated with the
+ * git_attr_file, so they do not need to be freed here
+ */
assign->name = NULL;
-
- if (assign->is_allocated) {
- git__free((void *)assign->value);
- assign->value = NULL;
- }
-
+ assign->value = NULL;
git__free(assign);
}
@@ -428,6 +445,7 @@ static int merge_assignments(void **old_raw, void *new_raw)
int git_attr_assignment__parse(
git_repository *repo,
+ git_pool *pool,
git_vector *assigns,
const char **base)
{
@@ -454,7 +472,6 @@ int git_attr_assignment__parse(
assign->name_hash = 5381;
assign->value = git_attr__true;
- assign->is_allocated = 0;
/* look for magic name prefixes */
if (*scan == '-') {
@@ -482,7 +499,7 @@ int git_attr_assignment__parse(
}
/* allocate permanent storage for name */
- assign->name = git__strndup(name_start, scan - name_start);
+ assign->name = git_pool_strndup(pool, name_start, scan - name_start);
GITERR_CHECK_ALLOC(assign->name);
/* if there is an equals sign, find the value */
@@ -491,16 +508,15 @@ int git_attr_assignment__parse(
/* if we found a value, allocate permanent storage for it */
if (scan > value_start) {
- assign->value = git__strndup(value_start, scan - value_start);
+ assign->value = git_pool_strndup(pool, value_start, scan - value_start);
GITERR_CHECK_ALLOC(assign->value);
- assign->is_allocated = 1;
}
}
/* expand macros (if given a repo with a macro cache) */
if (repo != NULL && assign->value == git_attr__true) {
- git_attr_rule *macro = git_hashtable_lookup(
- git_repository_attr_cache(repo)->macros, assign->name);
+ git_attr_rule *macro =
+ git_attr_cache__lookup_macro(repo, assign->name);
if (macro != NULL) {
unsigned int i;
@@ -548,7 +564,7 @@ static void git_attr_rule__clear(git_attr_rule *rule)
git_vector_free(&rule->assigns);
}
- git__free(rule->match.pattern);
+ /* match.pattern is stored in a git_pool, so no need to free */
rule->match.pattern = NULL;
rule->match.length = 0;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index 294033d5e..677534158 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -9,7 +9,7 @@
#include "git2/attr.h"
#include "vector.h"
-#include "hashtable.h"
+#include "pool.h"
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
@@ -36,20 +36,21 @@ typedef struct {
typedef struct {
git_refcount unused;
const char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
} git_attr_name;
typedef struct {
git_refcount rc; /* for macros */
char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
const char *value;
- int is_allocated;
} git_attr_assignment;
typedef struct {
char *path; /* cache the path this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
+ git_pool *pool;
+ bool pool_is_allocated;
} git_attr_file;
typedef struct {
@@ -62,7 +63,7 @@ typedef struct {
* git_attr_file API
*/
-extern int git_attr_file__new(git_attr_file **attrs_ptr);
+extern int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool);
extern void git_attr_file__free(git_attr_file *file);
extern int git_attr_file__from_buffer(
@@ -84,7 +85,7 @@ extern int git_attr_file__lookup_one(
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
if (git_attr_rule__match((rule), (path)))
-extern unsigned long git_attr_file__name_hash(const char *name);
+extern uint32_t git_attr_file__name_hash(const char *name);
/*
@@ -93,6 +94,7 @@ extern unsigned long git_attr_file__name_hash(const char *name);
extern int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base);
@@ -114,6 +116,7 @@ extern int git_attr_path__init(
extern int git_attr_assignment__parse(
git_repository *repo, /* needed to expand macros */
+ git_pool *pool,
git_vector *assigns,
const char **scan);
diff --git a/src/blob.c b/src/blob.c
index f553de888..36571c70a 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -139,12 +139,12 @@ static int write_symlink(
read_len = p_readlink(path, link_data, link_size);
if (read_len != (ssize_t)link_size) {
giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
- free(link_data);
+ git__free(link_data);
return -1;
}
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
- free(link_data);
+ git__free(link_data);
return error;
}
diff --git a/src/buffer.c b/src/buffer.c
index c23803564..24a0abdbe 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -159,7 +159,7 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
va_end(arglist);
if (len < 0) {
- free(buf->ptr);
+ git__free(buf->ptr);
buf->ptr = &git_buf__oom;
return -1;
}
diff --git a/src/cache.c b/src/cache.c
index f445e906d..31da3c36e 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -9,21 +9,14 @@
#include "repository.h"
#include "commit.h"
#include "thread-utils.h"
+#include "util.h"
#include "cache.h"
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
if (size < 8)
size = 8;
-
- /* round up size to closest power of 2 */
- size--;
- size |= size >> 1;
- size |= size >> 2;
- size |= size >> 4;
- size |= size >> 8;
- size |= size >> 16;
- size++;
+ size = git__size_t_powerof2(size);
cache->size_mask = size - 1;
cache->lru_count = 0;
diff --git a/src/config.c b/src/config.c
index f5cfa9ec0..4c971924c 100644
--- a/src/config.c
+++ b/src/config.c
@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
-#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
diff --git a/src/config_cache.c b/src/config_cache.c
index 5e20847f5..3679a9646 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
-#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
diff --git a/src/config_file.c b/src/config_file.c
index 5cc15d457..7841ea00f 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -12,12 +12,14 @@
#include "buffer.h"
#include "git2/config.h"
#include "git2/types.h"
-
+#include "strmap.h"
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
+GIT__USE_STRMAP;
+
typedef struct cvar_t {
struct cvar_t *next;
char *key; /* TODO: we might be able to get rid of this */
@@ -70,7 +72,7 @@ typedef struct {
typedef struct {
git_config_file parent;
- git_hashtable *values;
+ git_strmap *values;
struct {
git_buf buffer;
@@ -130,22 +132,21 @@ static int normalize_name(const char *in, char **out)
return 0;
}
-static void free_vars(git_hashtable *values)
+static void free_vars(git_strmap *values)
{
cvar_t *var = NULL;
if (values == NULL)
return;
- GIT_HASHTABLE_FOREACH_VALUE(values, var,
- do {
- cvar_t *next = CVAR_LIST_NEXT(var);
- cvar_free(var);
- var = next;
- } while (var != NULL);
- )
+ git_strmap_foreach_value(values, var,
+ while (var != NULL) {
+ cvar_t *next = CVAR_LIST_NEXT(var);
+ cvar_free(var);
+ var = next;
+ });
- git_hashtable_free(values);
+ git_strmap_free(values);
}
static int config_open(git_config_file *cfg)
@@ -153,7 +154,7 @@ static int config_open(git_config_file *cfg)
int res;
diskfile_backend *b = (diskfile_backend *)cfg;
- b->values = git_hashtable_alloc (20, git_hash__strhash_cb, git_hash__strcmp_cb);
+ b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
git_buf_init(&b->reader.buffer, 0);
@@ -195,24 +196,25 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const
if (!b->values)
return 0;
- GIT_HASHTABLE_FOREACH(b->values, key, var,
+ git_strmap_foreach(b->values, key, var,
do {
if (fn(key, var->value, data) < 0)
break;
var = CVAR_LIST_NEXT(var);
} while (var != NULL);
- )
+ );
return 0;
}
static int config_set(git_config_file *cfg, const char *name, const char *value)
{
- cvar_t *var = NULL;
- cvar_t *existing = NULL, *old_value = NULL;
+ cvar_t *var = NULL, *old_var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ khiter_t pos;
+ int rval;
if (normalize_name(name, &key) < 0)
return -1;
@@ -221,8 +223,9 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
* Try to find it in the existing values and update it if it
* only has one value.
*/
- existing = git_hashtable_lookup(b->values, key);
- if (existing != NULL) {
+ pos = git_strmap_lookup_index(b->values, key);
+ if (git_strmap_valid_index(b->values, pos)) {
+ cvar_t *existing = git_strmap_value_at(b->values, pos);
char *tmp = NULL;
git__free(key);
@@ -255,10 +258,11 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
GITERR_CHECK_ALLOC(var->value);
}
- if (git_hashtable_insert2(b->values, key, var, (void **)&old_value) < 0)
+ git_strmap_insert2(b->values, key, var, old_var, rval);
+ if (rval < 0)
return -1;
-
- cvar_free(old_value);
+ if (old_var != NULL)
+ cvar_free(old_var);
if (config_write(b, key, NULL, value) < 0) {
cvar_free(var);
@@ -273,21 +277,22 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
*/
static int config_get(git_config_file *cfg, const char *name, const char **out)
{
- cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ khiter_t pos;
if (normalize_name(name, &key) < 0)
return -1;
- var = git_hashtable_lookup(b->values, key);
+ pos = git_strmap_lookup_index(b->values, key);
git__free(key);
/* no error message; the config system will write one */
- if (var == NULL)
+ if (!git_strmap_valid_index(b->values, pos))
return GIT_ENOTFOUND;
- *out = var->value;
+ *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
+
return 0;
}
@@ -301,16 +306,19 @@ static int config_get_multivar(
cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
+ khiter_t pos;
if (normalize_name(name, &key) < 0)
return -1;
- var = git_hashtable_lookup(b->values, key);
+ pos = git_strmap_lookup_index(b->values, key);
git__free(key);
- if (var == NULL)
+ if (!git_strmap_valid_index(b->values, pos))
return GIT_ENOTFOUND;
+ var = git_strmap_value_at(b->values, pos);
+
if (regex_str != NULL) {
regex_t regex;
int result;
@@ -350,7 +358,8 @@ static int config_get_multivar(
return 0;
}
-static int config_set_multivar(git_config_file *cfg, const char *name, const char *regexp, const char *value)
+static int config_set_multivar(
+ git_config_file *cfg, const char *name, const char *regexp, const char *value)
{
int replaced = 0;
cvar_t *var, *newvar;
@@ -358,19 +367,24 @@ static int config_set_multivar(git_config_file *cfg, const char *name, const cha
char *key;
regex_t preg;
int result;
+ khiter_t pos;
assert(regexp);
if (normalize_name(name, &key) < 0)
return -1;
- var = git_hashtable_lookup(b->values, key);
- if (var == NULL)
+ pos = git_strmap_lookup_index(b->values, key);
+ if (!git_strmap_valid_index(b->values, pos)) {
+ git__free(key);
return GIT_ENOTFOUND;
+ }
+
+ var = git_strmap_value_at(b->values, pos);
result = regcomp(&preg, regexp, REG_EXTENDED);
if (result < 0) {
- free(key);
+ git__free(key);
giterr_set_regex(&preg, result);
return -1;
}
@@ -380,7 +394,7 @@ static int config_set_multivar(git_config_file *cfg, const char *name, const cha
char *tmp = git__strdup(value);
GITERR_CHECK_ALLOC(tmp);
- free(var->value);
+ git__free(var->value);
var->value = tmp;
replaced = 1;
}
@@ -409,7 +423,7 @@ static int config_set_multivar(git_config_file *cfg, const char *name, const cha
result = config_write(b, key, &preg, value);
- free(key);
+ git__free(key);
regfree(&preg);
return result;
@@ -421,22 +435,26 @@ static int config_delete(git_config_file *cfg, const char *name)
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
int result;
+ khiter_t pos;
if (normalize_name(name, &key) < 0)
return -1;
- var = git_hashtable_lookup(b->values, key);
- free(key);
+ pos = git_strmap_lookup_index(b->values, key);
+ git__free(key);
- if (var == NULL)
+ if (!git_strmap_valid_index(b->values, pos))
return GIT_ENOTFOUND;
+ var = git_strmap_value_at(b->values, pos);
+
if (var->next != NULL) {
giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
return -1;
}
- git_hashtable_remove(b->values, var->key);
+ git_strmap_delete_at(b->values, pos);
+
result = config_write(b, var->key, NULL, NULL);
cvar_free(var);
@@ -843,6 +861,7 @@ static int config_parse(diskfile_backend *cfg_file)
cvar_t *var, *existing;
git_buf buf = GIT_BUF_INIT;
int result = 0;
+ khiter_t pos;
/* Initialize the reading position */
cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
@@ -895,10 +914,14 @@ static int config_parse(diskfile_backend *cfg_file)
var->value = var_value;
/* Add or append the new config option */
- existing = git_hashtable_lookup(cfg_file->values, var->key);
- if (existing == NULL) {
- result = git_hashtable_insert(cfg_file->values, var->key, var);
+ pos = git_strmap_lookup_index(cfg_file->values, var->key);
+ if (!git_strmap_valid_index(cfg_file->values, pos)) {
+ git_strmap_insert(cfg_file->values, var->key, var, result);
+ if (result < 0)
+ break;
+ result = 0;
} else {
+ existing = git_strmap_value_at(cfg_file->values, pos);
while (existing->next != NULL) {
existing = existing->next;
}
@@ -1275,7 +1298,7 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
char *proc_line = fixup_line(value_start, 0);
GITERR_CHECK_ALLOC(proc_line);
git_buf_puts(&multi_value, proc_line);
- free(proc_line);
+ git__free(proc_line);
if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
git__free(*var_name);
git__free(line);
diff --git a/src/diff.c b/src/diff.c
index c6a0088ec..7d2ad59aa 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -54,24 +54,6 @@ static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
return false;
}
-static void diff_delta__free(git_diff_delta *delta)
-{
- if (!delta)
- return;
-
- if (delta->new.flags & GIT_DIFF_FILE_FREE_PATH) {
- git__free((char *)delta->new.path);
- delta->new.path = NULL;
- }
-
- if (delta->old.flags & GIT_DIFF_FILE_FREE_PATH) {
- git__free((char *)delta->old.path);
- delta->old.path = NULL;
- }
-
- git__free(delta);
-}
-
static git_diff_delta *diff_delta__alloc(
git_diff_list *diff,
git_delta_t status,
@@ -81,12 +63,11 @@ static git_diff_delta *diff_delta__alloc(
if (!delta)
return NULL;
- delta->old.path = git__strdup(path);
+ delta->old.path = git_pool_strdup(&diff->pool, path);
if (delta->old.path == NULL) {
git__free(delta);
return NULL;
}
- delta->old.flags |= GIT_DIFF_FILE_FREE_PATH;
delta->new.path = delta->old.path;
if (diff->opts.flags & GIT_DIFF_REVERSE) {
@@ -101,7 +82,8 @@ static git_diff_delta *diff_delta__alloc(
return delta;
}
-static git_diff_delta *diff_delta__dup(const git_diff_delta *d)
+static git_diff_delta *diff_delta__dup(
+ const git_diff_delta *d, git_pool *pool)
{
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
if (!delta)
@@ -109,33 +91,29 @@ static git_diff_delta *diff_delta__dup(const git_diff_delta *d)
memcpy(delta, d, sizeof(git_diff_delta));
- delta->old.path = git__strdup(d->old.path);
- if (delta->old.path == NULL) {
- git__free(delta);
- return NULL;
- }
- delta->old.flags |= GIT_DIFF_FILE_FREE_PATH;
+ delta->old.path = git_pool_strdup(pool, d->old.path);
+ if (delta->old.path == NULL)
+ goto fail;
if (d->new.path != d->old.path) {
- delta->new.path = git__strdup(d->new.path);
- if (delta->new.path == NULL) {
- git__free(delta->old.path);
- git__free(delta);
- return NULL;
- }
- delta->new.flags |= GIT_DIFF_FILE_FREE_PATH;
+ delta->new.path = git_pool_strdup(pool, d->new.path);
+ if (delta->new.path == NULL)
+ goto fail;
} else {
delta->new.path = delta->old.path;
- delta->new.flags &= ~GIT_DIFF_FILE_FREE_PATH;
}
return delta;
+
+fail:
+ git__free(delta);
+ return NULL;
}
static git_diff_delta *diff_delta__merge_like_cgit(
- const git_diff_delta *a, const git_diff_delta *b)
+ const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
{
- git_diff_delta *dup = diff_delta__dup(a);
+ git_diff_delta *dup = diff_delta__dup(a, pool);
if (!dup)
return NULL;
@@ -146,9 +124,7 @@ static git_diff_delta *diff_delta__merge_like_cgit(
dup->new.mode = b->new.mode;
dup->new.size = b->new.size;
- dup->new.flags =
- (dup->new.flags & GIT_DIFF_FILE_FREE_PATH) |
- (b->new.flags & ~GIT_DIFF_FILE_FREE_PATH);
+ dup->new.flags = b->new.flags;
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
*
@@ -210,7 +186,7 @@ static int diff_delta__from_one(
delta->new.flags |= GIT_DIFF_FILE_VALID_OID;
if (git_vector_insert(&diff->deltas, delta) < 0) {
- diff_delta__free(delta);
+ git__free(delta);
return -1;
}
@@ -249,7 +225,7 @@ static int diff_delta__from_two(
delta->new.flags |= GIT_DIFF_FILE_VALID_OID;
if (git_vector_insert(&diff->deltas, delta) < 0) {
- diff_delta__free(delta);
+ git__free(delta);
return -1;
}
@@ -259,19 +235,15 @@ static int diff_delta__from_two(
#define DIFF_SRC_PREFIX_DEFAULT "a/"
#define DIFF_DST_PREFIX_DEFAULT "b/"
-static char *diff_strdup_prefix(const char *prefix)
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
{
size_t len = strlen(prefix);
- char *str = git__malloc(len + 2);
- if (str != NULL) {
- memcpy(str, prefix, len + 1);
- /* append '/' at end if needed */
- if (len > 0 && str[len - 1] != '/') {
- str[len] = '/';
- str[len + 1] = '\0';
- }
- }
- return str;
+
+ /* append '/' at end if needed */
+ if (len > 0 && prefix[len - 1] != '/')
+ return git_pool_strcat(pool, prefix, "/");
+ else
+ return git_pool_strndup(pool, prefix, len + 1);
}
static int diff_delta__cmp(const void *a, const void *b)
@@ -300,6 +272,10 @@ static git_diff_list *git_diff_list_alloc(
diff->repo = repo;
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+ git_pool_init(&diff->pool, 1, 0) < 0)
+ goto fail;
+
/* load config values that affect diff behavior */
if (git_repository_config__weakptr(&cfg, repo) < 0)
goto fail;
@@ -319,9 +295,9 @@ static git_diff_list *git_diff_list_alloc(
memcpy(&diff->opts, opts, sizeof(git_diff_options));
memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
- diff->opts.src_prefix = diff_strdup_prefix(
+ diff->opts.src_prefix = diff_strdup_prefix(&diff->pool,
opts->src_prefix ? opts->src_prefix : DIFF_SRC_PREFIX_DEFAULT);
- diff->opts.dst_prefix = diff_strdup_prefix(
+ diff->opts.dst_prefix = diff_strdup_prefix(&diff->pool,
opts->dst_prefix ? opts->dst_prefix : DIFF_DST_PREFIX_DEFAULT);
if (!diff->opts.src_prefix || !diff->opts.dst_prefix)
@@ -333,9 +309,6 @@ static git_diff_list *git_diff_list_alloc(
diff->opts.dst_prefix = swap;
}
- if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0)
- goto fail;
-
/* only copy pathspec if it is "interesting" so we can test
* diff->pathspec.length > 0 to know if it is worth calling
* fnmatch as we iterate.
@@ -349,11 +322,10 @@ static git_diff_list *git_diff_list_alloc(
for (i = 0; i < opts->pathspec.count; ++i) {
int ret;
const char *pattern = opts->pathspec.strings[i];
- git_attr_fnmatch *match =
- git__calloc(1, sizeof(git_attr_fnmatch));
+ git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
if (!match)
goto fail;
- ret = git_attr_fnmatch__parse(match, NULL, &pattern);
+ ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
git__free(match);
continue;
@@ -381,23 +353,18 @@ void git_diff_list_free(git_diff_list *diff)
return;
git_vector_foreach(&diff->deltas, i, delta) {
- diff_delta__free(delta);
+ git__free(delta);
diff->deltas.contents[i] = NULL;
}
git_vector_free(&diff->deltas);
git_vector_foreach(&diff->pathspec, i, match) {
- if (match != NULL) {
- git__free(match->pattern);
- match->pattern = NULL;
- git__free(match);
- diff->pathspec.contents[i] = NULL;
- }
+ git__free(match);
+ diff->pathspec.contents[i] = NULL;
}
git_vector_free(&diff->pathspec);
- git__free(diff->opts.src_prefix);
- git__free(diff->opts.dst_prefix);
+ git_pool_clear(&diff->pool);
git__free(diff);
}
@@ -709,6 +676,7 @@ int git_diff_merge(
const git_diff_list *from)
{
int error = 0;
+ git_pool onto_pool;
git_vector onto_new;
git_diff_delta *delta;
unsigned int i, j;
@@ -718,7 +686,8 @@ int git_diff_merge(
if (!from->deltas.length)
return 0;
- if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0)
+ if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
+ git_pool_init(&onto_pool, 1, 0) < 0)
return -1;
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
@@ -727,13 +696,13 @@ int git_diff_merge(
int cmp = !f ? -1 : !o ? 1 : strcmp(o->old.path, f->old.path);
if (cmp < 0) {
- delta = diff_delta__dup(o);
+ delta = diff_delta__dup(o, &onto_pool);
i++;
} else if (cmp > 0) {
- delta = diff_delta__dup(f);
+ delta = diff_delta__dup(f, &onto_pool);
j++;
} else {
- delta = diff_delta__merge_like_cgit(o, f);
+ delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
i++;
j++;
}
@@ -744,12 +713,14 @@ int git_diff_merge(
if (!error) {
git_vector_swap(&onto->deltas, &onto_new);
+ git_pool_swap(&onto->pool, &onto_pool);
onto->new_src = from->new_src;
}
git_vector_foreach(&onto_new, i, delta)
- diff_delta__free(delta);
+ git__free(delta);
git_vector_free(&onto_new);
+ git_pool_clear(&onto_pool);
return error;
}
diff --git a/src/diff.h b/src/diff.h
index 9da07c295..4de18beea 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -12,6 +12,7 @@
#include "buffer.h"
#include "iterator.h"
#include "repository.h"
+#include "pool.h"
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
@@ -26,6 +27,7 @@ struct git_diff_list {
git_diff_options opts;
git_vector pathspec;
git_vector deltas; /* vector of git_diff_file_delta */
+ git_pool pool;
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
diff --git a/src/errors.c b/src/errors.c
index aad6c4482..7a6bbd654 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -173,7 +173,7 @@ void giterr_set_str(int error_class, const char *string)
{
git_error *error = &GIT_GLOBAL->error_t;
- free(error->message);
+ git__free(error->message);
error->message = git__strdup(string);
error->klass = error_class;
diff --git a/src/filter.c b/src/filter.c
index f0ee1ad39..d2d113409 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -111,7 +111,7 @@ void git_filters_free(git_vector *filters)
if (filter->do_free != NULL)
filter->do_free(filter);
else
- free(filter);
+ git__free(filter);
}
git_vector_free(filters);
diff --git a/src/global.c b/src/global.c
index b10fabc61..368c6c664 100644
--- a/src/global.c
+++ b/src/global.c
@@ -62,7 +62,7 @@ git_global_st *git__global_state(void)
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
@@ -78,7 +78,7 @@ static int _tls_init = 0;
static void cb__free_status(void *st)
{
- free(st);
+ git__free(st);
}
void git_threads_init(void)
@@ -103,7 +103,7 @@ git_global_st *git__global_state(void)
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
diff --git a/src/hashtable.c b/src/hashtable.c
deleted file mode 100644
index 8e057d4b1..000000000
--- a/src/hashtable.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-
-#define MAX_LOOPS 5
-static const double max_load_factor = 0.65;
-
-static int resize_to(git_hashtable *self, size_t new_size);
-static int set_size(git_hashtable *self, size_t new_size);
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id);
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other);
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node);
-static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size);
-
-static int resize_to(git_hashtable *self, size_t new_size)
-{
- git_hashtable_node *old_nodes = self->nodes;
- size_t old_size = self->size;
-
- self->is_resizing = 1;
-
- do {
- self->size = new_size;
- self->size_mask = new_size - 1;
- self->key_count = 0;
- self->nodes = git__calloc(1, sizeof(git_hashtable_node) * self->size);
- GITERR_CHECK_ALLOC(self->nodes);
-
- if (insert_nodes(self, old_nodes, old_size) == 0)
- self->is_resizing = 0;
- else {
- new_size *= 2;
- git__free(self->nodes);
- }
- } while(self->is_resizing);
-
- git__free(old_nodes);
- return 0;
-}
-
-static int set_size(git_hashtable *self, size_t new_size)
-{
- self->nodes = git__realloc(self->nodes, new_size * sizeof(git_hashtable_node));
- GITERR_CHECK_ALLOC(self->nodes);
-
- if (new_size > self->size) {
- memset(&self->nodes[self->size], 0x0,
- (new_size - self->size) * sizeof(git_hashtable_node));
- }
-
- self->size = new_size;
- self->size_mask = new_size - 1;
- return 0;
-}
-
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id)
-{
- size_t pos = self->hash(key, hash_id) & self->size_mask;
- return git_hashtable_node_at(self->nodes, pos);
-}
-
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other)
-{
- git_hashtable_node tmp = *self;
- *self = *other;
- *other = tmp;
-}
-
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node)
-{
- int iteration, hash_id;
-
- for (iteration = 0; iteration < MAX_LOOPS; iteration++) {
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- git_hashtable_node *node;
- node = node_with_hash(self, new_node->key, hash_id);
- node_swap_with(new_node, node);
- if (new_node->key == 0x0){
- self->key_count++;
- return 0;
- }
- }
- }
-
- /* Failed to insert node. Hashtable is currently resizing */
- assert(!self->is_resizing);
-
- if (resize_to(self, self->size * 2) < 0)
- return -1;
-
- return git_hashtable_insert(self, new_node->key, new_node->value);
-}
-
-static int insert_nodes(
- git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size)
-{
- size_t i;
-
- for (i = 0; i < old_size; ++i) {
- git_hashtable_node *node = git_hashtable_node_at(old_nodes, i);
- if (node->key &&
- git_hashtable_insert(self, node->key, node->value) < 0)
- return -1;
- }
-
- return 0;
-}
-
-git_hashtable *git_hashtable_alloc(
- size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq)
-{
- git_hashtable *table;
-
- assert(hash && key_eq);
-
- if ((table = git__malloc(sizeof(*table))) == NULL)
- return NULL;
-
- memset(table, 0x0, sizeof(git_hashtable));
-
- if (min_size < 8)
- min_size = 8;
-
- /* round up size to closest power of 2 */
- min_size--;
- min_size |= min_size >> 1;
- min_size |= min_size >> 2;
- min_size |= min_size >> 4;
- min_size |= min_size >> 8;
- min_size |= min_size >> 16;
-
- table->hash = hash;
- table->key_equal = key_eq;
-
- set_size(table, min_size + 1);
-
- return table;
-}
-
-void git_hashtable_clear(git_hashtable *self)
-{
- assert(self);
-
- memset(self->nodes, 0x0, sizeof(git_hashtable_node) * self->size);
- self->key_count = 0;
-}
-
-void git_hashtable_free(git_hashtable *self)
-{
- assert(self);
-
- git__free(self->nodes);
- git__free(self);
-}
-
-
-int git_hashtable_insert2(
- git_hashtable *self, const void *key, void *value, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- *old_value = NULL;
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
-
- if (!node->key) {
- node->key = key;
- node->value = value;
- self->key_count++;
- return 0;
- }
-
- if (key == node->key || self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = key;
- node->value = value;
- return 0;
- }
- }
-
- /* no space in table; must do cuckoo dance */
- {
- git_hashtable_node x;
- x.key = key;
- x.value = value;
- return node_insert(self, &x);
- }
-}
-
-void *git_hashtable_lookup(git_hashtable *self, const void *key)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0)
- return node->value;
- }
-
- return NULL;
-}
-
-int git_hashtable_remove2(
- git_hashtable *self, const void *key, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = NULL;
- node->value = NULL;
- self->key_count--;
- return 0;
- }
- }
-
- return GIT_ENOTFOUND;
-}
-
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other)
-{
- if (resize_to(self, (self->size + other->size) * 2) < 0)
- return -1;
-
- return insert_nodes(self, other->nodes, other->key_count);
-}
-
-
-/**
- * Standard string
- */
-uint32_t git_hash__strhash_cb(const void *key, int hash_id)
-{
- static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
- 2147483647,
- 0x5d20bb23,
- 0x7daaab3c
- };
-
- size_t key_len = strlen((const char *)key);
-
- /* won't take hash of strings longer than 2^31 right now */
- assert(key_len == (size_t)((int)key_len));
-
- return git__hash(key, (int)key_len, hash_seeds[hash_id]);
-}
diff --git a/src/hashtable.h b/src/hashtable.h
deleted file mode 100644
index 0bab84543..000000000
--- a/src/hashtable.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_hashtable_h__
-#define INCLUDE_hashtable_h__
-
-#include "git2/common.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-#include "common.h"
-
-#define GIT_HASHTABLE_HASHES 3
-
-typedef uint32_t (*git_hash_ptr)(const void *, int hash_id);
-typedef int (*git_hash_keyeq_ptr)(const void *key_a, const void *key_b);
-
-struct git_hashtable_node {
- const void *key;
- void *value;
-};
-
-struct git_hashtable {
- struct git_hashtable_node *nodes;
-
- size_t size_mask;
- size_t size;
- size_t key_count;
-
- int is_resizing;
-
- git_hash_ptr hash;
- git_hash_keyeq_ptr key_equal;
-};
-
-typedef struct git_hashtable_node git_hashtable_node;
-typedef struct git_hashtable git_hashtable;
-
-git_hashtable *git_hashtable_alloc(size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq);
-void *git_hashtable_lookup(git_hashtable *h, const void *key);
-int git_hashtable_remove2(git_hashtable *table, const void *key, void **old_value);
-
-GIT_INLINE(int) git_hashtable_remove(git_hashtable *table, const void *key)
-{
- void *_unused;
- return git_hashtable_remove2(table, key, &_unused);
-}
-
-
-void git_hashtable_free(git_hashtable *h);
-void git_hashtable_clear(git_hashtable *h);
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other);
-
-int git_hashtable_insert2(git_hashtable *h, const void *key, void *value, void **old_value);
-
-GIT_INLINE(int) git_hashtable_insert(git_hashtable *h, const void *key, void *value)
-{
- void *_unused;
- return git_hashtable_insert2(h, key, value, &_unused);
-}
-
-#define git_hashtable_node_at(nodes, pos) ((git_hashtable_node *)(&nodes[pos]))
-
-#define GIT_HASHTABLE__FOREACH(self,block) { \
- size_t _c; \
- git_hashtable_node *_n = (self)->nodes; \
- for (_c = (self)->size; _c > 0; _c--, _n++) { \
- if (!_n->key) continue; block } }
-
-#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code)\
- GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;(pvalue)=_n->value;code;})
-
-#define GIT_HASHTABLE_FOREACH_KEY(self, pkey, code)\
- GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;code;})
-
-#define GIT_HASHTABLE_FOREACH_VALUE(self, pvalue, code)\
- GIT_HASHTABLE__FOREACH(self,{(pvalue)=_n->value;code;})
-
-#define GIT_HASHTABLE_FOREACH_DELETE() {\
- _node->key = NULL; _node->value = NULL; _self->key_count--;\
-}
-
-/*
- * If you want a hashtable with standard string keys, you can
- * just pass git_hash__strcmp_cb and git_hash__strhash_cb to
- * git_hashtable_alloc.
- */
-#define git_hash__strcmp_cb git__strcmp_cb
-extern uint32_t git_hash__strhash_cb(const void *key, int hash_id);
-
-#endif
diff --git a/src/ignore.c b/src/ignore.c
index 1827eda82..165754b4d 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -35,7 +35,9 @@ static int load_ignore_file(
GITERR_CHECK_ALLOC(match);
}
- if (!(error = git_attr_fnmatch__parse(match, context, &scan))) {
+ if (!(error = git_attr_fnmatch__parse(
+ match, ignores->pool, context, &scan)))
+ {
match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
scan = git__next_line(scan);
error = git_vector_insert(&ignores->rules, match);
diff --git a/src/khash.h b/src/khash.h
new file mode 100644
index 000000000..f9d239336
--- /dev/null
+++ b/src/khash.h
@@ -0,0 +1,608 @@
+/* The MIT License
+
+ Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/*
+ An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+ int ret, is_missing;
+ khiter_t k;
+ khash_t(32) *h = kh_init(32);
+ k = kh_put(32, h, 5, &ret);
+ kh_value(h, k) = 10;
+ k = kh_get(32, h, 10);
+ is_missing = (k == kh_end(h));
+ k = kh_get(32, h, 5);
+ kh_del(32, h, k);
+ for (k = kh_begin(h); k != kh_end(h); ++k)
+ if (kh_exist(h, k)) kh_value(h, k) = 1;
+ kh_destroy(32, h);
+ return 0;
+}
+*/
+
+/*
+ 2011-12-29 (0.2.7):
+
+ * Minor code clean up; no actual effect.
+
+ 2011-09-16 (0.2.6):
+
+ * The capacity is a power of 2. This seems to dramatically improve the
+ speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+ - http://code.google.com/p/ulib/
+ - http://nothings.org/computer/judy/
+
+ * Allow to optionally use linear probing which usually has better
+ performance for random input. Double hashing is still the default as it
+ is more robust to certain non-random input.
+
+ * Added Wang's integer hash function (not used by default). This hash
+ function is more robust to certain non-random input.
+
+ 2011-02-14 (0.2.5):
+
+ * Allow to declare global functions.
+
+ 2009-09-26 (0.2.4):
+
+ * Improve portability
+
+ 2008-09-19 (0.2.3):
+
+ * Corrected the example
+ * Improved interfaces
+
+ 2008-09-11 (0.2.2):
+
+ * Improved speed a little in kh_put()
+
+ 2008-09-10 (0.2.1):
+
+ * Added kh_clear()
+ * Fixed a compiling error
+
+ 2008-09-02 (0.2.0):
+
+ * Changed to token concatenation which increases flexibility.
+
+ 2008-08-31 (0.1.2):
+
+ * Fixed a bug in kh_get(), which has not been tested previously.
+
+ 2008-08-31 (0.1.1):
+
+ * Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+ @header
+
+ Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.6"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compipler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#ifdef KHASH_LINEAR
+#define __ac_inc(k, m) 1
+#else
+#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
+#endif
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+ typedef struct { \
+ khint_t n_buckets, size, n_occupied, upper_bound; \
+ khint32_t *flags; \
+ khkey_t *keys; \
+ khval_t *vals; \
+ } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
+ extern kh_##name##_t *kh_init_##name(void); \
+ extern void kh_destroy_##name(kh_##name##_t *h); \
+ extern void kh_clear_##name(kh_##name##_t *h); \
+ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+ extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ SCOPE kh_##name##_t *kh_init_##name(void) { \
+ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
+ } \
+ SCOPE void kh_destroy_##name(kh_##name##_t *h) \
+ { \
+ if (h) { \
+ kfree(h->keys); kfree(h->flags); \
+ kfree(h->vals); \
+ kfree(h); \
+ } \
+ } \
+ SCOPE void kh_clear_##name(kh_##name##_t *h) \
+ { \
+ if (h && h->flags) { \
+ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+ h->size = h->n_occupied = 0; \
+ } \
+ } \
+ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
+ { \
+ if (h->n_buckets) { \
+ khint_t inc, k, i, last, mask; \
+ mask = h->n_buckets - 1; \
+ k = __hash_func(key); i = k & mask; \
+ inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ i = (i + inc) & mask; \
+ if (i == last) return h->n_buckets; \
+ } \
+ return __ac_iseither(h->flags, i)? h->n_buckets : i; \
+ } else return 0; \
+ } \
+ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+ { /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+ khint32_t *new_flags = 0; \
+ khint_t j = 1; \
+ { \
+ kroundup32(new_n_buckets); \
+ if (new_n_buckets < 4) new_n_buckets = 4; \
+ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+ else { /* hash table size to be changed (shrink or expand); rehash */ \
+ new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (!new_flags) return -1; \
+ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (h->n_buckets < new_n_buckets) { /* expand */ \
+ khkey_t *new_keys = (khkey_t*)krealloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (!new_keys) return -1; \
+ h->keys = new_keys; \
+ if (kh_is_map) { \
+ khval_t *new_vals = (khval_t*)krealloc(h->vals, new_n_buckets * sizeof(khval_t)); \
+ if (!new_vals) return -1; \
+ h->vals = new_vals; \
+ } \
+ } /* otherwise shrink */ \
+ } \
+ } \
+ if (j) { /* rehashing is needed */ \
+ for (j = 0; j != h->n_buckets; ++j) { \
+ if (__ac_iseither(h->flags, j) == 0) { \
+ khkey_t key = h->keys[j]; \
+ khval_t val; \
+ khint_t new_mask; \
+ new_mask = new_n_buckets - 1; \
+ if (kh_is_map) val = h->vals[j]; \
+ __ac_set_isdel_true(h->flags, j); \
+ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+ khint_t inc, k, i; \
+ k = __hash_func(key); \
+ i = k & new_mask; \
+ inc = __ac_inc(k, new_mask); \
+ while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
+ __ac_set_isempty_false(new_flags, i); \
+ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+ } else { /* write the element and jump out of the loop */ \
+ h->keys[i] = key; \
+ if (kh_is_map) h->vals[i] = val; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+ h->keys = (khkey_t*)krealloc(h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (kh_is_map) h->vals = (khval_t*)krealloc(h->vals, new_n_buckets * sizeof(khval_t)); \
+ } \
+ kfree(h->flags); /* free the working space */ \
+ h->flags = new_flags; \
+ h->n_buckets = new_n_buckets; \
+ h->n_occupied = h->size; \
+ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+ } \
+ return 0; \
+ } \
+ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+ { \
+ khint_t x; \
+ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+ if (h->n_buckets > (h->size<<1)) { \
+ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+ { \
+ khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
+ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
+ else { \
+ inc = __ac_inc(k, mask); last = i; \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ if (__ac_isdel(h->flags, i)) site = i; \
+ i = (i + inc) & mask; \
+ if (i == last) { x = site; break; } \
+ } \
+ if (x == h->n_buckets) { \
+ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+ else x = i; \
+ } \
+ } \
+ } \
+ if (__ac_isempty(h->flags, x)) { /* not present at all */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; ++h->n_occupied; \
+ *ret = 1; \
+ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; \
+ *ret = 2; \
+ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+ return x; \
+ } \
+ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
+ { \
+ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
+ __ac_set_isdel_true(h->flags, x); \
+ --h->size; \
+ } \
+ }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+ @abstract Integer hash function
+ @param key The integer [khint32_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+ @abstract Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract 64-bit integer hash function
+ @param key The integer [khint64_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+ @abstract 64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract const char* hash function
+ @param s Pointer to a null terminated string
+ @return The hash value
+ */
+static inline khint_t __ac_X31_hash_string(const char *s)
+{
+ khint_t h = *s;
+ if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s;
+ return h;
+}
+/*! @function
+ @abstract Another interface to const char* hash function
+ @param key Pointer to a null terminated string [const char*]
+ @return The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+ @abstract Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static inline khint_t __ac_Wang_hash(khint_t key)
+{
+ key += ~(key << 15);
+ key ^= (key >> 10);
+ key += (key << 3);
+ key ^= (key >> 6);
+ key += ~(key << 11);
+ key ^= (key >> 16);
+ return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+ @abstract Type of the hash table.
+ @param name Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+ @abstract Initiate a hash table.
+ @param name Name of the hash table [symbol]
+ @return Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+ @abstract Destroy a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+ @abstract Reset a hash table without deallocating memory.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+ @abstract Resize a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param s New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+ @abstract Insert a key to the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @param r Extra return code: 0 if the key is present in the hash table;
+ 1 if the bucket is empty (never used); 2 if the element in
+ the bucket has been deleted [int*]
+ @return Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+ @abstract Retrieve a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+ @abstract Remove a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+ @abstract Test whether a bucket contains data.
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return 1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+ @abstract Get key given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+ @abstract Get value given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Value [type of values]
+ @discussion For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Get the start iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+ @abstract Get the end iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Get the number of elements in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+ @abstract Get the number of buckets in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Iterate over the entries in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param kvar Variable to which key will be assigned
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (kvar) = kh_key(h,__i); \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/*! @function
+ @abstract Iterate over the values in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/* More conenient interfaces */
+
+/*! @function
+ @abstract Instantiate a hash set containing integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name) \
+ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t) \
+ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name) \
+ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t) \
+ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name) \
+ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t) \
+ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/src/odb.c b/src/odb.c
index b615cc4f4..2538b8a77 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -169,7 +169,7 @@ int git_odb__hashlink(git_oid *out, const char *path)
}
result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
- free(link_data);
+ git__free(link_data);
} else {
int fd = git_futils_open_ro(path);
if (fd < 0)
diff --git a/src/oidmap.h b/src/oidmap.h
new file mode 100644
index 000000000..858268c92
--- /dev/null
+++ b/src/oidmap.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oidmap_h__
+#define INCLUDE_oidmap_h__
+
+#include "common.h"
+#include "git2/oid.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(oid, const git_oid *, void *);
+typedef khash_t(oid) git_oidmap;
+
+GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)
+{
+ int i;
+ khint_t h = 0;
+ for (i = 0; i < 20; ++i)
+ h = (h << 5) - h + oid->id[i];
+ return h;
+}
+
+GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b)
+{
+ return (memcmp(a->id, b->id, sizeof(a->id)) == 0);
+}
+
+#define GIT__USE_OIDMAP \
+ __KHASH_IMPL(oid, static inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal)
+
+#define git_oidmap_alloc() kh_init(oid)
+#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
+
+#endif
diff --git a/src/pool.c b/src/pool.c
new file mode 100644
index 000000000..8f5c7e75a
--- /dev/null
+++ b/src/pool.c
@@ -0,0 +1,294 @@
+#include "pool.h"
+#ifndef GIT_WIN32
+#include <unistd.h>
+#endif
+
+struct git_pool_page {
+ git_pool_page *next;
+ uint32_t size;
+ uint32_t avail;
+ char data[GIT_FLEX_ARRAY];
+};
+
+#define GIT_POOL_MIN_USABLE 4
+#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*)
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size);
+static void pool_insert_page(git_pool *pool, git_pool_page *page);
+
+int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page)
+{
+ assert(pool);
+
+ if (!item_size)
+ item_size = 1;
+ /* round up item_size for decent object alignment */
+ if (item_size > 4)
+ item_size = (item_size + 7) & ~7;
+ else if (item_size == 3)
+ item_size = 4;
+
+ if (!items_per_page)
+ items_per_page = git_pool__suggest_items_per_page(item_size);
+ if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
+ items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;
+
+ memset(pool, 0, sizeof(git_pool));
+ pool->item_size = item_size;
+ pool->page_size = item_size * items_per_page;
+
+ return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+ git_pool_page *scan, *next;
+
+ for (scan = pool->open; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->open = NULL;
+
+ for (scan = pool->full; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->full = NULL;
+
+ pool->free_list = NULL;
+
+ pool->items = 0;
+
+ pool->has_string_alloc = 0;
+ pool->has_multi_item_alloc = 0;
+ pool->has_large_page_alloc = 0;
+}
+
+void git_pool_swap(git_pool *a, git_pool *b)
+{
+ git_pool temp;
+
+ if (a == b)
+ return;
+
+ memcpy(&temp, a, sizeof(temp));
+ memcpy(a, b, sizeof(temp));
+ memcpy(b, &temp, sizeof(temp));
+}
+
+static void pool_insert_page(git_pool *pool, git_pool_page *page)
+{
+ git_pool_page *scan;
+
+ /* If there are no open pages or this page has the most open space,
+ * insert it at the beginning of the list. This is the common case.
+ */
+ if (pool->open == NULL || pool->open->avail < page->avail) {
+ page->next = pool->open;
+ pool->open = page;
+ return;
+ }
+
+ /* Otherwise insert into sorted position. */
+ for (scan = pool->open;
+ scan->next && scan->next->avail > page->avail;
+ scan = scan->next);
+ page->next = scan->next;
+ scan->next = page;
+}
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size)
+{
+ git_pool_page *page;
+ uint32_t alloc_size;
+
+ if (size <= pool->page_size)
+ alloc_size = pool->page_size;
+ else {
+ alloc_size = size;
+ pool->has_large_page_alloc = 1;
+ }
+
+ page = git__calloc(1, alloc_size + sizeof(git_pool_page));
+ if (!page)
+ return NULL;
+
+ page->size = alloc_size;
+ page->avail = alloc_size - size;
+
+ if (page->avail > 0)
+ pool_insert_page(pool, page);
+ else {
+ page->next = pool->full;
+ pool->full = page;
+ }
+
+ pool->items++;
+
+ return page->data;
+}
+
+GIT_INLINE(void) pool_remove_page(
+ git_pool *pool, git_pool_page *page, git_pool_page *prev)
+{
+ if (prev == NULL)
+ pool->open = page->next;
+ else
+ prev->next = page->next;
+}
+
+void *git_pool_malloc(git_pool *pool, uint32_t items)
+{
+ git_pool_page *scan = pool->open, *prev;
+ uint32_t size = items * pool->item_size;
+ void *ptr = NULL;
+
+ pool->has_string_alloc = 0;
+ if (items > 1)
+ pool->has_multi_item_alloc = 1;
+ else if (pool->free_list != NULL) {
+ ptr = pool->free_list;
+ pool->free_list = *((void **)pool->free_list);
+ return ptr;
+ }
+
+ /* just add a block if there is no open one to accomodate this */
+ if (size >= pool->page_size || !scan || scan->avail < size)
+ return pool_alloc_page(pool, size);
+
+ pool->items++;
+
+ /* find smallest block in free list with space */
+ for (scan = pool->open, prev = NULL;
+ scan->next && scan->next->avail >= size;
+ prev = scan, scan = scan->next);
+
+ /* allocate space from the block */
+ ptr = &scan->data[scan->size - scan->avail];
+ scan->avail -= size;
+
+ /* move to full list if there is almost no space left */
+ if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
+ pool_remove_page(pool, scan, prev);
+ scan->next = pool->full;
+ pool->full = scan;
+ }
+ /* reorder list if block is now smaller than the one after it */
+ else if (scan->next != NULL && scan->next->avail > scan->avail) {
+ pool_remove_page(pool, scan, prev);
+ pool_insert_page(pool, scan);
+ }
+
+ return ptr;
+}
+
+char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
+{
+ void *ptr = NULL;
+
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ if ((ptr = git_pool_malloc(pool, n + 1)) != NULL) {
+ memcpy(ptr, str, n);
+ *(((char *)ptr) + n) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+char *git_pool_strdup(git_pool *pool, const char *str)
+{
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ return git_pool_strndup(pool, str, strlen(str));
+}
+
+char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
+{
+ void *ptr;
+ size_t len_a, len_b;
+
+ assert(pool && a && b && pool->item_size == sizeof(char));
+
+ len_a = a ? strlen(a) : 0;
+ len_b = b ? strlen(b) : 0;
+
+ if ((ptr = git_pool_malloc(pool, len_a + len_b + 1)) != NULL) {
+ if (len_a)
+ memcpy(ptr, a, len_a);
+ if (len_b)
+ memcpy(((char *)ptr) + len_a, b, len_b);
+ *(((char *)ptr) + len_a + len_b) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+void git_pool_free(git_pool *pool, void *ptr)
+{
+ assert(pool && ptr && pool->item_size >= sizeof(void*));
+
+ *((void **)ptr) = pool->free_list;
+ pool->free_list = ptr;
+}
+
+uint32_t git_pool__open_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+uint32_t git_pool__full_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next)
+ if ( ((void *)scan->data) <= ptr &&
+ (((void *)scan->data) + scan->size) > ptr)
+ return true;
+ for (scan = pool->full; scan != NULL; scan = scan->next)
+ if ( ((void *)scan->data) <= ptr &&
+ (((void *)scan->data) + scan->size) > ptr)
+ return true;
+ return false;
+}
+
+uint32_t git_pool__system_page_size(void)
+{
+ static uint32_t size = 0;
+
+ if (!size) {
+#ifdef GIT_WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ size = (uint32_t)info.dwPageSize;
+#else
+ size = (uint32_t)sysconf(_SC_PAGE_SIZE);
+#endif
+
+ size -= 2 * sizeof(void *); /* allow space for malloc overhead */
+ }
+
+ return size;
+}
+
+uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
+{
+ uint32_t page_bytes =
+ git_pool__system_page_size() - sizeof(git_pool_page);
+ return page_bytes / item_size;
+}
+
diff --git a/src/pool.h b/src/pool.h
new file mode 100644
index 000000000..54a2861ed
--- /dev/null
+++ b/src/pool.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pool_h__
+#define INCLUDE_pool_h__
+
+#include "common.h"
+
+typedef struct git_pool_page git_pool_page;
+
+/**
+ * Chunked allocator.
+ *
+ * A `git_pool` can be used when you want to cheaply allocate
+ * multiple items of the same type and are willing to free them
+ * all together with a single call. The two most common cases
+ * are a set of fixed size items (such as lots of OIDs) or a
+ * bunch of strings.
+ *
+ * Internally, a `git_pool` allocates pages of memory and then
+ * deals out blocks from the trailing unused portion of each page.
+ * The pages guarantee that the number of actual allocations done
+ * will be much smaller than the number of items needed.
+ *
+ * For examples of how to set up a `git_pool` see `git_pool_init`.
+ */
+typedef struct {
+ git_pool_page *open; /* pages with space left */
+ git_pool_page *full; /* pages with no space left */
+ void *free_list; /* optional: list of freed blocks */
+ uint32_t item_size; /* size of single alloc unit in bytes */
+ uint32_t page_size; /* size of page in bytes */
+ uint32_t items;
+ unsigned has_string_alloc : 1; /* was the strdup function used */
+ unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */
+ unsigned has_large_page_alloc : 1; /* are any pages > page_size */
+} git_pool;
+
+#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }
+
+/**
+ * Initialize a pool.
+ *
+ * To allocation strings, use like this:
+ *
+ * git_pool_init(&string_pool, 1, 0);
+ * my_string = git_pool_strdup(&string_pool, your_string);
+ *
+ * To allocate items of fixed size, use like this:
+ *
+ * git_pool_init(&pool, sizeof(item), 0);
+ * my_item = git_pool_malloc(&pool, 1);
+ *
+ * Of course, you can use this in other ways, but those are the
+ * two most common patterns.
+ */
+extern int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page);
+
+/**
+ * Free all items in pool
+ */
+extern void git_pool_clear(git_pool *pool);
+
+/**
+ * Swap two pools with one another
+ */
+extern void git_pool_swap(git_pool *a, git_pool *b);
+
+/**
+ * Allocate space for one or more items from a pool.
+ */
+extern void *git_pool_malloc(git_pool *pool, uint32_t items);
+
+/**
+ * Allocate space and duplicate string data into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
+
+/**
+ * Allocate space and duplicate a string into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup(git_pool *pool, const char *str);
+
+/**
+ * Allocate space for the concatenation of two strings.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
+
+/**
+ * Push a block back onto the free list for the pool.
+ *
+ * This is allowed only if the item_size is >= sizeof(void*).
+ *
+ * In some cases, it is helpful to "release" an allocated block
+ * for reuse. Pools don't support a general purpose free, but
+ * they will keep a simple free blocks linked list provided the
+ * native block size is large enough to hold a void pointer
+ */
+extern void git_pool_free(git_pool *pool, void *ptr);
+
+/*
+ * Misc utilities
+ */
+
+extern uint32_t git_pool__open_pages(git_pool *pool);
+
+extern uint32_t git_pool__full_pages(git_pool *pool);
+
+extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
+
+extern uint32_t git_pool__system_page_size(void);
+
+extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);
+
+#endif
diff --git a/src/refs.c b/src/refs.c
index bea1f1724..7685d560c 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -15,6 +15,8 @@
#include <git2/tag.h>
#include <git2/object.h>
+GIT__USE_STRMAP;
+
#define DEFAULT_NESTING_LEVEL 5
#define MAX_NESTING_LEVEL 10
@@ -30,8 +32,6 @@ struct packref {
char name[GIT_FLEX_ARRAY];
};
-static const int default_table_size = 32;
-
static int reference_read(
git_buf *file_content,
time_t *mtime,
@@ -268,7 +268,7 @@ static int loose_lookup_to_packfile(
if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
git_buf_free(&ref_file);
- free(ref);
+ git__free(ref);
return -1;
}
@@ -423,9 +423,7 @@ static int packed_load(git_repository *repo)
/* First we make sure we have allocated the hash table */
if (ref_cache->packfile == NULL) {
- ref_cache->packfile = git_hashtable_alloc(
- default_table_size, git_hash__strhash_cb, git_hash__strcmp_cb);
-
+ ref_cache->packfile = git_strmap_alloc();
GITERR_CHECK_ALLOC(ref_cache->packfile);
}
@@ -440,7 +438,7 @@ static int packed_load(git_repository *repo)
* refresh the packed refs.
*/
if (result == GIT_ENOTFOUND) {
- git_hashtable_clear(ref_cache->packfile);
+ git_strmap_clear(ref_cache->packfile);
return 0;
}
@@ -454,7 +452,7 @@ static int packed_load(git_repository *repo)
* At this point, we want to refresh the packed refs. We already
* have the contents in our buffer.
*/
- git_hashtable_clear(ref_cache->packfile);
+ git_strmap_clear(ref_cache->packfile);
buffer_start = (const char *)packfile.ptr;
buffer_end = (const char *)(buffer_start) + packfile.size;
@@ -468,6 +466,7 @@ static int packed_load(git_repository *repo)
}
while (buffer_start < buffer_end) {
+ int err;
struct packref *ref = NULL;
if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
@@ -478,15 +477,16 @@ static int packed_load(git_repository *repo)
goto parse_failed;
}
- if (git_hashtable_insert(ref_cache->packfile, ref->name, ref) < 0)
- return -1;
+ git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
+ if (err < 0)
+ goto parse_failed;
}
git_buf_free(&packfile);
return 0;
parse_failed:
- git_hashtable_free(ref_cache->packfile);
+ git_strmap_free(ref_cache->packfile);
ref_cache->packfile = NULL;
git_buf_free(&packfile);
return -1;
@@ -512,7 +512,7 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
/* do not add twice a reference that exists already in the packfile */
if ((data->list_flags & GIT_REF_PACKED) != 0 &&
- git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
+ git_strmap_exists(data->repo->references.packfile, file_path))
return 0;
if (data->list_flags != GIT_REF_LISTALL) {
@@ -529,6 +529,7 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
void *old_ref = NULL;
struct packref *ref;
const char *file_path;
+ int err;
if (git_path_isdir(full_path->ptr) == true)
return git_path_direach(full_path, _dirent_loose_load, repository);
@@ -538,8 +539,9 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)
return -1;
- if (git_hashtable_insert2(repository->references.packfile,
- ref->name, ref, &old_ref) < 0) {
+ git_strmap_insert2(
+ repository->references.packfile, ref->name, ref, old_ref, err);
+ if (err < 0) {
git__free(ref);
return -1;
}
@@ -734,7 +736,8 @@ static int packed_write(git_repository *repo)
assert(repo && repo->references.packfile);
- total_refs = (unsigned int)repo->references.packfile->key_count;
+ total_refs =
+ (unsigned int)git_strmap_num_entries(repo->references.packfile);
if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
return -1;
@@ -743,10 +746,10 @@ static int packed_write(git_repository *repo)
{
struct packref *reference;
- GIT_HASHTABLE_FOREACH_VALUE(repo->references.packfile, reference,
- /* cannot fail: vector already has the right size */
+ /* cannot fail: vector already has the right size */
+ git_strmap_foreach_value(repo->references.packfile, reference, {
git_vector_insert(&packing_list, reference);
- );
+ });
}
/* sort the vector so the entries appear sorted on the packfile */
@@ -870,7 +873,8 @@ static int reference_exists(int *exists, git_repository *repo, const char *ref_n
return -1;
if (git_path_isfile(ref_path.ptr) == true ||
- git_hashtable_lookup(repo->references.packfile, ref_path.ptr) != NULL) {
+ git_strmap_exists(repo->references.packfile, ref_path.ptr))
+ {
*exists = 1;
} else {
*exists = 0;
@@ -936,6 +940,8 @@ static int reference_can_write(
static int packed_lookup(git_reference *ref)
{
struct packref *pack_ref = NULL;
+ git_strmap *packfile_refs;
+ khiter_t pos;
if (packed_load(ref->owner) < 0)
return -1;
@@ -952,12 +958,15 @@ static int packed_lookup(git_reference *ref)
}
/* Look up on the packfile */
- pack_ref = git_hashtable_lookup(ref->owner->references.packfile, ref->name);
- if (pack_ref == NULL) {
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);
return GIT_ENOTFOUND;
}
+ pack_ref = git_strmap_value_at(packfile_refs, pos);
+
ref->flags = GIT_REF_OID | GIT_REF_PACKED;
ref->mtime = ref->owner->references.packfile_time;
git_oid_cpy(&ref->target.oid, &pack_ref->oid);
@@ -1002,18 +1011,25 @@ static int reference_delete(git_reference *ref)
* We need to reload the packfile, remove the reference from the
* packing list, and repack */
if (ref->flags & GIT_REF_PACKED) {
+ git_strmap *packfile_refs;
struct packref *packref;
+ khiter_t pos;
+
/* load the existing packfile */
if (packed_load(ref->owner) < 0)
return -1;
- if (git_hashtable_remove2(ref->owner->references.packfile,
- ref->name, (void **) &packref) < 0) {
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
giterr_set(GITERR_REFERENCE,
"Reference %s stopped existing in the packfile", ref->name);
return -1;
}
+ packref = git_strmap_value_at(packfile_refs, pos);
+ git_strmap_delete_at(packfile_refs, pos);
+
git__free(packref);
if (packed_write(ref->owner) < 0)
return -1;
@@ -1467,14 +1483,15 @@ int git_reference_foreach(
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
const char *ref_name;
+ void *ref;
if (packed_load(repo) < 0)
return -1;
- GIT_HASHTABLE_FOREACH_KEY(repo->references.packfile, ref_name,
+ git_strmap_foreach(repo->references.packfile, ref_name, ref, {
if (callback(ref_name, payload) < 0)
return 0;
- );
+ });
}
/* now list the loose references, trying not to
@@ -1538,10 +1555,11 @@ void git_repository__refcache_free(git_refcache *refs)
if (refs->packfile) {
struct packref *reference;
- GIT_HASHTABLE_FOREACH_VALUE(
- refs->packfile, reference, git__free(reference));
+ git_strmap_foreach_value(refs->packfile, reference, {
+ git__free(reference);
+ });
- git_hashtable_free(refs->packfile);
+ git_strmap_free(refs->packfile);
}
}
diff --git a/src/refs.h b/src/refs.h
index e4a225ca3..369e91e1c 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -10,7 +10,7 @@
#include "common.h"
#include "git2/oid.h"
#include "git2/refs.h"
-#include "hashtable.h"
+#include "strmap.h"
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
@@ -46,7 +46,7 @@ struct git_reference {
};
typedef struct {
- git_hashtable *packfile;
+ git_strmap *packfile;
time_t packfile_time;
} git_refcache;
diff --git a/src/remote.c b/src/remote.c
index e1937df85..71cb62719 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -456,7 +456,7 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
size_t i;
char *elem;
git_vector_foreach(&list, i, elem) {
- free(elem);
+ git__free(elem);
}
git_vector_free(&list);
diff --git a/src/repository.c b/src/repository.c
index 88e3a182c..affc0c4c1 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -850,7 +850,7 @@ int git_repository_set_workdir(git_repository *repo, const char *workdir)
if (git_path_prettify_dir(&path, workdir, NULL) < 0)
return -1;
- free(repo->workdir);
+ git__free(repo->workdir);
repo->workdir = git_buf_detach(&path);
repo->is_bare = 0;
diff --git a/src/repository.h b/src/repository.h
index 178f29742..1ffac58f1 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -13,13 +13,13 @@
#include "git2/repository.h"
#include "git2/object.h"
-#include "hashtable.h"
#include "index.h"
#include "cache.h"
#include "refs.h"
#include "buffer.h"
#include "odb.h"
#include "attr.h"
+#include "strmap.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
@@ -83,7 +83,7 @@ struct git_repository {
git_cache objects;
git_refcache references;
git_attr_cache attrcache;
- git_hashtable *submodules;
+ git_strmap *submodules;
char *path_repository;
char *workdir;
diff --git a/src/revwalk.c b/src/revwalk.c
index 041dc1a1c..4f2f82798 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -8,14 +8,17 @@
#include "common.h"
#include "commit.h"
#include "odb.h"
-#include "hashtable.h"
#include "pqueue.h"
+#include "pool.h"
+#include "oidmap.h"
#include "git2/revwalk.h"
#include "git2/merge.h"
#include <regex.h>
+GIT__USE_OIDMAP;
+
#define PARENT1 (1 << 0)
#define PARENT2 (1 << 1)
#define RESULT (1 << 2)
@@ -45,7 +48,8 @@ struct git_revwalk {
git_repository *repo;
git_odb *odb;
- git_hashtable *commits;
+ git_oidmap *commits;
+ git_pool commit_pool;
commit_list *iterator_topo;
commit_list *iterator_rand;
@@ -55,9 +59,6 @@ struct git_revwalk {
int (*get_next)(commit_object **, git_revwalk *);
int (*enqueue)(git_revwalk *, commit_object *);
- git_vector memory_alloc;
- size_t chunk_size;
-
unsigned walking:1;
unsigned int sorting;
@@ -124,60 +125,36 @@ static commit_object *commit_list_pop(commit_list **stack)
return item;
}
-static uint32_t object_table_hash(const void *key, int hash_id)
-{
- uint32_t r;
- const git_oid *id = key;
-
- memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
- return r;
-}
-
-#define COMMITS_PER_CHUNK 128
-#define CHUNK_STEP 64
-#define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *))
-
-static int alloc_chunk(git_revwalk *walk)
-{
- void *chunk;
-
- chunk = git__calloc(COMMITS_PER_CHUNK, CHUNK_STEP);
- GITERR_CHECK_ALLOC(chunk);
-
- walk->chunk_size = 0;
- return git_vector_insert(&walk->memory_alloc, chunk);
-}
+#define PARENTS_PER_COMMIT 2
+#define COMMIT_ALLOC \
+ (sizeof(commit_object) + PARENTS_PER_COMMIT * sizeof(commit_object *))
static commit_object *alloc_commit(git_revwalk *walk)
{
- unsigned char *chunk;
-
- if (walk->chunk_size == COMMITS_PER_CHUNK)
- if (alloc_chunk(walk) < 0)
- return NULL;
-
- chunk = git_vector_get(&walk->memory_alloc, walk->memory_alloc.length - 1);
- chunk += (walk->chunk_size * CHUNK_STEP);
- walk->chunk_size++;
-
- return (commit_object *)chunk;
+ return (commit_object *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
}
-static commit_object **alloc_parents(commit_object *commit, size_t n_parents)
+static commit_object **alloc_parents(
+ git_revwalk *walk, commit_object *commit, size_t n_parents)
{
if (n_parents <= PARENTS_PER_COMMIT)
- return (commit_object **)((unsigned char *)commit + sizeof(commit_object));
+ return (commit_object **)((char *)commit + sizeof(commit_object));
- return git__malloc(n_parents * sizeof(commit_object *));
+ return (commit_object **)git_pool_malloc(
+ &walk->commit_pool, n_parents * sizeof(commit_object *));
}
static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
{
commit_object *commit;
+ khiter_t pos;
+ int ret;
- if ((commit = git_hashtable_lookup(walk->commits, oid)) != NULL)
- return commit;
+ /* lookup and reserve space if not already present */
+ pos = kh_get(oid, walk->commits, oid);
+ if (pos != kh_end(walk->commits))
+ return kh_value(walk->commits, pos);
commit = alloc_commit(walk);
if (commit == NULL)
@@ -185,10 +162,9 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
git_oid_cpy(&commit->oid, oid);
- if (git_hashtable_insert(walk->commits, &commit->oid, commit) < 0) {
- git__free(commit);
- return NULL;
- }
+ pos = kh_put(oid, walk->commits, &commit->oid, &ret);
+ assert(ret != 0);
+ kh_value(walk->commits, pos) = commit;
return commit;
}
@@ -212,7 +188,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
buffer += parent_len;
}
- commit->parents = alloc_parents(commit, parents);
+ commit->parents = alloc_parents(walk, commit, parents);
GITERR_CHECK_ALLOC(commit->parents);
buffer = parents_start;
@@ -757,15 +733,13 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
memset(walk, 0x0, sizeof(git_revwalk));
- walk->commits = git_hashtable_alloc(64,
- object_table_hash,
- (git_hash_keyeq_ptr)git_oid_cmp);
+ walk->commits = git_oidmap_alloc();
GITERR_CHECK_ALLOC(walk->commits);
if (git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp) < 0 ||
- git_vector_init(&walk->memory_alloc, 8, NULL) < 0 ||
git_vector_init(&walk->twos, 4, NULL) < 0 ||
- alloc_chunk(walk) < 0)
+ git_pool_init(&walk->commit_pool, 1,
+ git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
return -1;
walk->get_next = &revwalk_next_unsorted;
@@ -784,30 +758,15 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
void git_revwalk_free(git_revwalk *walk)
{
- unsigned int i;
- commit_object *commit;
-
if (walk == NULL)
return;
git_revwalk_reset(walk);
git_odb_free(walk->odb);
- /* if the parent has more than PARENTS_PER_COMMIT parents,
- * we had to allocate a separate array for those parents.
- * make sure it's being free'd */
- GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit, {
- if (commit->out_degree > PARENTS_PER_COMMIT)
- git__free(commit->parents);
- });
-
- git_hashtable_free(walk->commits);
+ git_oidmap_free(walk->commits);
+ git_pool_clear(&walk->commit_pool);
git_pqueue_free(&walk->iterator_time);
-
- for (i = 0; i < walk->memory_alloc.length; ++i)
- git__free(git_vector_get(&walk->memory_alloc, i));
-
- git_vector_free(&walk->memory_alloc);
git_vector_free(&walk->twos);
git__free(walk);
}
@@ -867,12 +826,12 @@ void git_revwalk_reset(git_revwalk *walk)
assert(walk);
- GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit,
+ kh_foreach_value(walk->commits, commit, {
commit->seen = 0;
commit->in_degree = 0;
commit->topo_delay = 0;
commit->uninteresting = 0;
- );
+ });
git_pqueue_clear(&walk->iterator_time);
commit_list_free(&walk->iterator_topo);
diff --git a/src/strmap.h b/src/strmap.h
new file mode 100644
index 000000000..55fbd7c6e
--- /dev/null
+++ b/src/strmap.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_strmap_h__
+#define INCLUDE_strmap_h__
+
+#include "common.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(str, const char *, void *);
+typedef khash_t(str) git_strmap;
+
+#define GIT__USE_STRMAP \
+ __KHASH_IMPL(str, static inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#define git_strmap_alloc() kh_init(str)
+#define git_strmap_free(h) kh_destroy(str, h), h = NULL
+#define git_strmap_clear(h) kh_clear(str, h)
+
+#define git_strmap_num_entries(h) kh_size(h)
+
+#define git_strmap_lookup_index(h, k) kh_get(str, h, k)
+#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+
+#define git_strmap_value_at(h, idx) kh_val(h, idx)
+#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
+#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
+
+#define git_strmap_insert(h, key, val, err) do { \
+ khiter_t __pos = kh_put(str, h, key, &err); \
+ if (err >= 0) kh_val(h, __pos) = val; \
+ } while (0)
+
+#define git_strmap_insert2(h, key, val, old, err) do { \
+ khiter_t __pos = kh_put(str, h, key, &err); \
+ if (err >= 0) { \
+ old = (err == 0) ? kh_val(h, __pos) : NULL; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_strmap_foreach kh_foreach
+#define git_strmap_foreach_value kh_foreach_value
+
+#endif
diff --git a/src/submodule.c b/src/submodule.c
index 907e43e88..1b5b59f45 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -12,7 +12,6 @@
#include "git2/index.h"
#include "git2/submodule.h"
#include "buffer.h"
-#include "hashtable.h"
#include "vector.h"
#include "posix.h"
#include "config_file.h"
@@ -32,41 +31,32 @@ static git_cvar_map _sm_ignore_map[] = {
{GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
};
-static uint32_t strhash_no_trailing_slash(const void *key, int hash_id)
+static inline khint_t str_hash_no_trailing_slash(const char *s)
{
- static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
- 0x01010101,
- 0x12345678,
- 0xFEDCBA98
- };
+ khint_t h;
- size_t key_len = key ? strlen((const char *)key) : 0;
- if (key_len > 0 && ((const char *)key)[key_len - 1] == '/')
- key_len--;
+ for (h = 0; *s; ++s)
+ if (s[1] || *s != '/')
+ h = (h << 5) - h + *s;
- return git__hash(key, (int)key_len, hash_seeds[hash_id]);
+ return h;
}
-static int strcmp_no_trailing_slash(const void *a, const void *b)
+static inline int str_equal_no_trailing_slash(const char *a, const char *b)
{
- const char *astr = (const char *)a;
- const char *bstr = (const char *)b;
- size_t alen = a ? strlen(astr) : 0;
- size_t blen = b ? strlen(bstr) : 0;
- int cmp;
+ size_t alen = a ? strlen(a) : 0;
+ size_t blen = b ? strlen(b) : 0;
- if (alen > 0 && astr[alen - 1] == '/')
+ if (alen && a[alen] == '/')
alen--;
- if (blen > 0 && bstr[blen - 1] == '/')
+ if (blen && b[blen] == '/')
blen--;
- cmp = strncmp(astr, bstr, min(alen, blen));
- if (cmp == 0)
- cmp = (alen < blen) ? -1 : (alen > blen) ? 1 : 0;
-
- return cmp;
+ return (alen == blen && strncmp(a, b, alen) == 0);
}
+__KHASH_IMPL(str, static inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+
static git_submodule *submodule_alloc(const char *name)
{
git_submodule *sm = git__calloc(1, sizeof(git_submodule));
@@ -99,13 +89,18 @@ static void submodule_release(git_submodule *sm, int decr)
}
static int submodule_from_entry(
- git_hashtable *smcfg, git_index_entry *entry)
+ git_strmap *smcfg, git_index_entry *entry)
{
git_submodule *sm;
void *old_sm;
+ khiter_t pos;
+ int error;
- sm = git_hashtable_lookup(smcfg, entry->path);
- if (!sm)
+ pos = git_strmap_lookup_index(smcfg, entry->path);
+
+ if (git_strmap_valid_index(smcfg, pos))
+ sm = git_strmap_value_at(smcfg, pos);
+ else
sm = submodule_alloc(entry->path);
git_oid_cpy(&sm->oid, &entry->oid);
@@ -120,7 +115,8 @@ static int submodule_from_entry(
goto fail;
}
- if (git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0)
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
goto fail;
sm->refcount++;
@@ -139,13 +135,15 @@ fail:
static int submodule_from_config(
const char *key, const char *value, void *data)
{
- git_hashtable *smcfg = data;
+ git_strmap *smcfg = data;
const char *namestart;
const char *property;
git_buf name = GIT_BUF_INIT;
git_submodule *sm;
void *old_sm = NULL;
bool is_path;
+ khiter_t pos;
+ int error;
if (git__prefixcmp(key, "submodule.") != 0)
return 0;
@@ -160,32 +158,40 @@ static int submodule_from_config(
if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
return -1;
- sm = git_hashtable_lookup(smcfg, name.ptr);
- if (!sm && is_path)
- sm = git_hashtable_lookup(smcfg, value);
- if (!sm)
+ pos = git_strmap_lookup_index(smcfg, name.ptr);
+ if (!git_strmap_valid_index(smcfg, pos) && is_path)
+ pos = git_strmap_lookup_index(smcfg, value);
+ if (!git_strmap_valid_index(smcfg, pos))
sm = submodule_alloc(name.ptr);
+ else
+ sm = git_strmap_value_at(smcfg, pos);
if (!sm)
goto fail;
if (strcmp(sm->name, name.ptr) != 0) {
assert(sm->path == sm->name);
sm->name = git_buf_detach(&name);
- if (git_hashtable_insert2(smcfg, sm->name, sm, &old_sm) < 0)
+
+ git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
+ if (error < 0)
goto fail;
sm->refcount++;
}
else if (is_path && strcmp(sm->path, value) != 0) {
assert(sm->path == sm->name);
- if ((sm->path = git__strdup(value)) == NULL ||
- git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0)
+ sm->path = git__strdup(value);
+ if (sm->path == NULL)
+ goto fail;
+
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
goto fail;
sm->refcount++;
}
git_buf_free(&name);
if (old_sm && ((git_submodule *)old_sm) != sm) {
- /* TODO: log entry about multiple submodules with same path */
+ /* TODO: log warning about multiple submodules with same path */
submodule_release(old_sm, 1);
}
@@ -241,7 +247,7 @@ static int load_submodule_config(git_repository *repo)
git_index *index;
unsigned int i, max_i;
git_oid gitmodules_oid;
- git_hashtable *smcfg;
+ git_strmap *smcfg;
struct git_config_file *mods = NULL;
if (repo->submodules)
@@ -251,8 +257,7 @@ static int load_submodule_config(git_repository *repo)
* under both its name and its path. These are usually the same, but
* that is not guaranteed.
*/
- smcfg = git_hashtable_alloc(
- 4, strhash_no_trailing_slash, strcmp_no_trailing_slash);
+ smcfg = git_strmap_alloc();
GITERR_CHECK_ALLOC(smcfg);
/* scan index for gitmodules (and .gitmodules entry) */
@@ -302,13 +307,13 @@ cleanup:
if (mods != NULL)
git_config_file_free(mods);
if (error)
- git_hashtable_free(smcfg);
+ git_strmap_free(smcfg);
return error;
}
void git_submodule_config_free(git_repository *repo)
{
- git_hashtable *smcfg = repo->submodules;
+ git_strmap *smcfg = repo->submodules;
git_submodule *sm;
repo->submodules = NULL;
@@ -316,8 +321,10 @@ void git_submodule_config_free(git_repository *repo)
if (smcfg == NULL)
return;
- GIT_HASHTABLE_FOREACH_VALUE(smcfg, sm, { submodule_release(sm,1); });
- git_hashtable_free(smcfg);
+ git_strmap_foreach_value(smcfg, sm, {
+ submodule_release(sm,1);
+ });
+ git_strmap_free(smcfg);
}
static int submodule_cmp(const void *a, const void *b)
@@ -338,19 +345,18 @@ int git_submodule_foreach(
if ((error = load_submodule_config(repo)) < 0)
return error;
- GIT_HASHTABLE_FOREACH_VALUE(
- repo->submodules, sm, {
- /* usually the following will not come into play */
- if (sm->refcount > 1) {
- if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
- continue;
- if ((error = git_vector_insert(&seen, sm)) < 0)
- break;
- }
-
- if ((error = callback(sm->name, payload)) < 0)
+ git_strmap_foreach_value(repo->submodules, sm, {
+ /* usually the following will not come into play */
+ if (sm->refcount > 1) {
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
+ continue;
+ if ((error = git_vector_insert(&seen, sm)) < 0)
break;
- });
+ }
+
+ if ((error = callback(sm->name, payload)) < 0)
+ break;
+ });
git_vector_free(&seen);
@@ -362,15 +368,17 @@ int git_submodule_lookup(
git_repository *repo,
const char *name) /* trailing slash is allowed */
{
- git_submodule *sm;
+ khiter_t pos;
if (load_submodule_config(repo) < 0)
return -1;
- sm = git_hashtable_lookup(repo->submodules, name);
+ pos = git_strmap_lookup_index(repo->submodules, name);
+ if (!git_strmap_valid_index(repo->submodules, pos))
+ return GIT_ENOTFOUND;
if (sm_ptr)
- *sm_ptr = sm;
+ *sm_ptr = git_strmap_value_at(repo->submodules, pos);
- return sm ? 0 : GIT_ENOTFOUND;
+ return 0;
}
diff --git a/src/util.h b/src/util.h
index afa3f7205..1fee9a70c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -179,4 +179,21 @@ GIT_INLINE(int) git__ishex(const char *str)
return 1;
}
+GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+
+ return v;
+}
+
+GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
+{
+ return git__size_t_bitmask(v) + 1;
+}
+
#endif /* INCLUDE_util_h__ */
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c
index 7fede5025..4e1010230 100644
--- a/tests-clar/attr/file.c
+++ b/tests-clar/attr/file.c
@@ -11,7 +11,7 @@ void test_attr_file__simple_read(void)
git_attr_assignment *assign;
git_attr_rule *rule;
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->path);
cl_assert(file->rules.length == 1);
@@ -27,7 +27,6 @@ void test_attr_file__simple_read(void)
cl_assert(assign != NULL);
cl_assert_equal_s("binary", assign->name);
cl_assert(GIT_ATTR_TRUE(assign->value));
- cl_assert(!assign->is_allocated);
git_attr_file__free(file);
}
@@ -38,7 +37,7 @@ void test_attr_file__match_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->path);
cl_assert(file->rules.length == 10);
@@ -56,7 +55,6 @@ void test_attr_file__match_variants(void)
cl_assert_equal_s("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
cl_assert(GIT_ATTR_TRUE(assign->value));
- cl_assert(!assign->is_allocated);
rule = get_rule(1);
cl_assert_equal_s("pat1", rule->match.pattern);
@@ -125,7 +123,7 @@ void test_attr_file__assign_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_assert_equal_s(cl_fixture("attr/attr2"), file->path);
cl_assert(file->rules.length == 11);
@@ -191,7 +189,7 @@ void test_attr_file__check_attr_examples(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_assert_equal_s(cl_fixture("attr/attr3"), file->path);
cl_assert(file->rules.length == 3);
diff --git a/tests-clar/attr/lookup.c b/tests-clar/attr/lookup.c
index 4ce80e947..accd617e6 100644
--- a/tests-clar/attr/lookup.c
+++ b/tests-clar/attr/lookup.c
@@ -9,7 +9,7 @@ void test_attr_lookup__simple(void)
git_attr_path path;
const char *value = NULL;
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->path);
cl_assert(file->rules.length == 1);
@@ -127,7 +127,7 @@ void test_attr_lookup__match_variants(void)
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->path);
cl_assert(file->rules.length == 10);
@@ -144,6 +144,7 @@ void test_attr_lookup__match_variants(void)
void test_attr_lookup__assign_variants(void)
{
git_attr_file *file;
+
struct attr_expected cases[] = {
/* pat0 -> simple assign */
{ "pat0", "simple", EXPECT_TRUE, NULL },
@@ -187,7 +188,7 @@ void test_attr_lookup__assign_variants(void)
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_assert(file->rules.length == 11);
@@ -199,6 +200,7 @@ void test_attr_lookup__assign_variants(void)
void test_attr_lookup__check_attr_examples(void)
{
git_attr_file *file;
+
struct attr_expected cases[] = {
{ "foo.java", "diff", EXPECT_STRING, "java" },
{ "foo.java", "crlf", EXPECT_FALSE, NULL },
@@ -222,7 +224,7 @@ void test_attr_lookup__check_attr_examples(void)
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_assert(file->rules.length == 3);
@@ -234,6 +236,7 @@ void test_attr_lookup__check_attr_examples(void)
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
+
struct attr_expected cases[] = {
{ "abc", "foo", EXPECT_TRUE, NULL },
{ "abc", "bar", EXPECT_TRUE, NULL },
@@ -247,7 +250,7 @@ void test_attr_lookup__from_buffer(void)
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
+ cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file));
cl_assert(file->rules.length == 3);
diff --git a/tests-clar/core/pool.c b/tests-clar/core/pool.c
new file mode 100644
index 000000000..5ed97366f
--- /dev/null
+++ b/tests-clar/core/pool.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+#include "git2/oid.h"
+
+void test_core_pool__0(void)
+{
+ int i;
+ git_pool p;
+ void *ptr;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 1; i < 10000; i *= 2) {
+ ptr = git_pool_malloc(&p, i);
+ cl_assert(ptr != NULL);
+ cl_assert(git_pool__ptr_in_pool(&p, ptr));
+ cl_assert(!git_pool__ptr_in_pool(&p, &i));
+ }
+
+ /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */
+ /* 2048 -> 1 block */
+ /* 4096 -> 1 block */
+ /* 8192 -> 1 block */
+
+ cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__1(void)
+{
+ int i;
+ git_pool p;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 505);
+
+ git_pool_clear(&p);
+
+ cl_git_pass(git_pool_init(&p, 1, 4100));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 492);
+
+ git_pool_clear(&p);
+}
+
+static char to_hex[] = "0123456789abcdef";
+
+void test_core_pool__2(void)
+{
+ git_pool p;
+ char oid_hex[GIT_OID_HEXSZ];
+ git_oid *oid;
+ int i, j;
+
+ memset(oid_hex, '0', sizeof(oid_hex));
+
+ cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100));
+
+ for (i = 1000; i < 10000; i++) {
+ oid = git_pool_malloc(&p, 1);
+ cl_assert(oid != NULL);
+
+ for (j = 0; j < 8; j++)
+ oid_hex[j] = to_hex[(i >> (4 * j)) & 0x0f];
+ cl_git_pass(git_oid_fromstr(oid, oid_hex));
+ }
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 0);
+ cl_assert(git_pool__full_pages(&p) == 90);
+
+ git_pool_clear(&p);
+}
diff --git a/tests-clar/core/strmap.c b/tests-clar/core/strmap.c
new file mode 100644
index 000000000..f34a4f89f
--- /dev/null
+++ b/tests-clar/core/strmap.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+#include "strmap.h"
+
+GIT__USE_STRMAP;
+
+void test_core_strmap__0(void)
+{
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+ cl_assert(git_strmap_num_entries(table) == 0);
+ git_strmap_free(table);
+}
+
+static void insert_strings(git_strmap *table, int count)
+{
+ int i, j, over, err;
+ char *str;
+
+ for (i = 0; i < count; ++i) {
+ str = 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);
+
+ git_strmap_insert(table, str, str, err);
+ cl_assert(err >= 0);
+ }
+
+ cl_assert((int)git_strmap_num_entries(table) == count);
+}
+
+void test_core_strmap__1(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 20);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__2(void)
+{
+ khiter_t pos;
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ cl_assert(git_strmap_exists(table, "bbbbbbbbb"));
+ pos = git_strmap_lookup_index(table, "bbbbbbbbb");
+ cl_assert(git_strmap_valid_index(table, pos));
+ cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb");
+ free(git_strmap_value_at(table, pos));
+ git_strmap_delete_at(table, pos);
+
+ cl_assert(!git_strmap_exists(table, "bbbbbbbbb"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 19);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__3(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 10000);
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 10000);
+
+ git_strmap_free(table);
+}
diff --git a/tests/t07-hashtable.c b/tests/t07-hashtable.c
deleted file mode 100644
index 4d45c7fc1..000000000
--- a/tests/t07-hashtable.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "hashtable.h"
-#include "hash.h"
-
-typedef struct _aux_object {
- int __bulk;
- git_oid id;
- int visited;
-} table_item;
-
-static uint32_t hash_func(const void *key, int hash_id)
-{
- uint32_t r;
- const git_oid *id = key;
-
- memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
- return r;
-}
-
-static int hash_cmpkey(const void *a, const void *b)
-{
- return git_oid_cmp(a, b);
-}
-
-BEGIN_TEST(table0, "create a new hashtable")
-
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(55, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
- must_be_true(table->size_mask + 1 == 64);
-
- git_hashtable_free(table);
-
-END_TEST
-
-BEGIN_TEST(table1, "fill the hashtable with random entries")
-
- const int objects_n = 32;
- int i;
-
- table_item *objects;
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n * 2, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- /* populate the hash table */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- /* make sure all the inserted objects can be found */
- for (i = 0; i < objects_n; ++i) {
- git_oid id;
- table_item *ob;
-
- git_hash_buf(&id, &i, sizeof(int));
- ob = (table_item *)git_hashtable_lookup(table, &id);
-
- must_be_true(ob != NULL);
- must_be_true(ob == &(objects[i]));
- }
-
- /* make sure we cannot find inexisting objects */
- for (i = 0; i < 50; ++i) {
- int hash_id;
- git_oid id;
-
- hash_id = (rand() % 50000) + objects_n;
- git_hash_buf(&id, &hash_id, sizeof(int));
- must_be_true(git_hashtable_lookup(table, &id) == NULL);
- }
-
- git_hashtable_free(table);
- git__free(objects);
-
-END_TEST
-
-
-BEGIN_TEST(table2, "make sure the table resizes automatically")
-
- const int objects_n = 64;
- int i;
- size_t old_size;
- table_item *objects;
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- old_size = table->size_mask + 1;
-
- /* populate the hash table -- should be automatically resized */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- must_be_true(table->size_mask > old_size);
-
- /* make sure all the inserted objects can be found */
- for (i = 0; i < objects_n; ++i) {
- git_oid id;
- table_item *ob;
-
- git_hash_buf(&id, &i, sizeof(int));
- ob = (table_item *)git_hashtable_lookup(table, &id);
-
- must_be_true(ob != NULL);
- must_be_true(ob == &(objects[i]));
- }
-
- git_hashtable_free(table);
- git__free(objects);
-
-END_TEST
-
-BEGIN_TEST(tableit0, "iterate through all the contents of the table")
-
- const int objects_n = 32;
- int i;
- table_item *objects, *ob;
-
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n * 2, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- /* populate the hash table */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- GIT_HASHTABLE_FOREACH_VALUE(table, ob, ob->visited = 1);
-
- /* make sure all nodes have been visited */
- for (i = 0; i < objects_n; ++i)
- must_be_true(objects[i].visited);
-
- git_hashtable_free(table);
- git__free(objects);
-END_TEST
-
-
-BEGIN_SUITE(hashtable)
- ADD_TEST(table0);
- ADD_TEST(table1);
- ADD_TEST(table2);
- ADD_TEST(tableit0);
-END_SUITE
-
diff --git a/tests/test_main.c b/tests/test_main.c
index 50256e97c..bc07f1ff1 100644
--- a/tests/test_main.c
+++ b/tests/test_main.c
@@ -37,7 +37,6 @@ DECLARE_SUITE(objwrite);
DECLARE_SUITE(commit);
DECLARE_SUITE(revwalk);
DECLARE_SUITE(index);
-DECLARE_SUITE(hashtable);
DECLARE_SUITE(tag);
DECLARE_SUITE(tree);
DECLARE_SUITE(refs);
@@ -53,7 +52,6 @@ static libgit2_suite suite_methods[]= {
SUITE_NAME(commit),
SUITE_NAME(revwalk),
SUITE_NAME(index),
- SUITE_NAME(hashtable),
SUITE_NAME(tag),
SUITE_NAME(tree),
SUITE_NAME(refs),