mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-07 08:20:14 +00:00
Merge pull request #642 from arrbee/mem-pools
Memory pools and khash hashtables
This commit is contained in:
commit
3fc5c65d1a
105
src/attr.c
105
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, ¯o->assigns, &values);
|
||||
error = git_attr_assignment__parse(repo, pool, ¯o->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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
*attrs_ptr = attrs;
|
||||
if (git_vector_init(&attrs->rules, 4, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
return attrs ? 0 : -1;
|
||||
*attrs_ptr = attrs;
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
11
src/cache.c
11
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;
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "hashtable.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "vector.h"
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "hashtable.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "vector.h"
|
||||
|
@ -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);
|
||||
|
121
src/diff.c
121
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
267
src/hashtable.c
267
src/hashtable.c
@ -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]);
|
||||
}
|
@ -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
|
@ -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);
|
||||
|
608
src/khash.h
Normal file
608
src/khash.h
Normal file
@ -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 */
|
@ -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)
|
||||
|
42
src/oidmap.h
Normal file
42
src/oidmap.h
Normal file
@ -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
|
294
src/pool.c
Normal file
294
src/pool.c
Normal file
@ -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;
|
||||
}
|
||||
|
125
src/pool.h
Normal file
125
src/pool.h
Normal file
@ -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
|
74
src/refs.c
74
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
105
src/revwalk.c
105
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);
|
||||
|
54
src/strmap.h
Normal file
54
src/strmap.h
Normal file
@ -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
|
128
src/submodule.c
128
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;
|
||||
}
|
||||
|
17
src/util.h
17
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__ */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
85
tests-clar/core/pool.c
Normal file
85
tests-clar/core/pool.c
Normal file
@ -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);
|
||||
}
|
102
tests-clar/core/strmap.c
Normal file
102
tests-clar/core/strmap.c
Normal file
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user