Commit dadc0158 by Carlos Martín Nieto

config: use a singly-linked list instead of a hash table

Such a list preserves the order the variables were first read in which
will be useful later for merging different data-sets. Furthermore,
reading and writing out the same configuration should not reorganize
the variables, which could happen when iterating through all the items
in a hash table.

A hash table is overkill for this small a data-set anyway.

Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
parent d28830c2
...@@ -37,23 +37,37 @@ static int config_parse(git_config *cfg_file); ...@@ -37,23 +37,37 @@ static int config_parse(git_config *cfg_file);
static int parse_variable(git_config *cfg, const char *section_name, const char *line); static int parse_variable(git_config *cfg, const char *section_name, const char *line);
void git_config_free(git_config *cfg); void git_config_free(git_config *cfg);
static void cvar_free(git_cvar *var) static git_cvar *cvar_free(git_cvar *var)
{ {
git_cvar *next = var->next;
free(var->name); free(var->name);
free(var->value); free(var->value);
free(var); free(var);
return next;
}
static void cvar_list_free(git_cvar *start)
{
git_cvar *iter = start;
while ((iter = cvar_free(iter)) != NULL);
} }
uint32_t config_table_hash(const void *key, int hash_id) /*
* FIXME: Only the section name is case-insensitive
*/
static git_cvar *cvar_list_find(git_cvar *start, const char *name)
{ {
static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = { git_cvar *iter;
2147483647,
0x5d20bb23, CVAR_LIST_FOREACH (start, iter) {
0x7daaab3c if (!strcasecmp(name, iter->name))
}; return iter;
}
const char *var_name = (const char *)key;
return git__hash(key, strlen(var_name), hash_seeds[hash_id]); return NULL;
} }
int git_config_open(git_config **cfg_out, const char *path) int git_config_open(git_config **cfg_out, const char *path)
...@@ -75,20 +89,13 @@ int git_config_open(git_config **cfg_out, const char *path) ...@@ -75,20 +89,13 @@ int git_config_open(git_config **cfg_out, const char *path)
goto cleanup; goto cleanup;
} }
cfg->vars = git_hashtable_alloc(16, config_table_hash,
(git_hash_keyeq_ptr) strcmp);
if (cfg->vars == NULL){
error = GIT_ENOMEM;
goto cleanup;
}
error = gitfo_read_file(&cfg->reader.buffer, cfg->file_path); error = gitfo_read_file(&cfg->reader.buffer, cfg->file_path);
if(error < GIT_SUCCESS) if(error < GIT_SUCCESS)
goto cleanup; goto cleanup;
error = config_parse(cfg); error = config_parse(cfg);
if(error < GIT_SUCCESS) if(error < GIT_SUCCESS)
git_config_free(cfg); goto cleanup;
else else
*cfg_out = cfg; *cfg_out = cfg;
...@@ -96,7 +103,7 @@ int git_config_open(git_config **cfg_out, const char *path) ...@@ -96,7 +103,7 @@ int git_config_open(git_config **cfg_out, const char *path)
cleanup: cleanup:
if(cfg->vars) if(cfg->vars)
git_hashtable_free(cfg->vars); cvar_list_free(cfg->vars);
if(cfg->file_path) if(cfg->file_path)
free(cfg->file_path); free(cfg->file_path);
free(cfg); free(cfg);
...@@ -106,19 +113,11 @@ int git_config_open(git_config **cfg_out, const char *path) ...@@ -106,19 +113,11 @@ int git_config_open(git_config **cfg_out, const char *path)
void git_config_free(git_config *cfg) void git_config_free(git_config *cfg)
{ {
git_cvar *var;
const void *_unused;
if (cfg == NULL) if (cfg == NULL)
return; return;
free(cfg->file_path); free(cfg->file_path);
cvar_list_free(cfg->vars);
GIT_HASHTABLE_FOREACH(cfg->vars, _unused, var,
cvar_free(var);
);
git_hashtable_free(cfg->vars);
gitfo_free_buf(&cfg->reader.buffer); gitfo_free_buf(&cfg->reader.buffer);
free(cfg); free(cfg);
...@@ -132,12 +131,12 @@ int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *d ...@@ -132,12 +131,12 @@ int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *d
{ {
int ret = GIT_SUCCESS; int ret = GIT_SUCCESS;
git_cvar *var; git_cvar *var;
const void *_unused;
GIT_HASHTABLE_FOREACH(cfg->vars, _unused, var, CVAR_LIST_FOREACH(cfg->vars, var) {
ret = fn(var->name, data); ret = fn(var->name, data);
if(ret) break; if (ret)
); break;
}
return ret; return ret;
} }
...@@ -152,8 +151,28 @@ int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *d ...@@ -152,8 +151,28 @@ int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *d
static int config_set(git_config *cfg, const char *name, const char *value) static int config_set(git_config *cfg, const char *name, const char *value)
{ {
git_cvar *var = NULL; git_cvar *var = NULL;
git_cvar *existing = NULL;
int error = GIT_SUCCESS; int error = GIT_SUCCESS;
/*
* If it already exists, we just need to update its value.
*/
existing = cvar_list_find(cfg->vars, name);
if (existing != NULL) {
char *tmp = git__strdup(value);
if (tmp == NULL)
return GIT_ENOMEM;
free(var->value);
var->value = tmp;
return GIT_SUCCESS;
}
/*
* Otherwise, create it and stick it at the end of the queue.
*/
var = git__malloc(sizeof(git_cvar)); var = git__malloc(sizeof(git_cvar));
if(var == NULL){ if(var == NULL){
error = GIT_ENOMEM; error = GIT_ENOMEM;
...@@ -174,19 +193,29 @@ static int config_set(git_config *cfg, const char *name, const char *value) ...@@ -174,19 +193,29 @@ static int config_set(git_config *cfg, const char *name, const char *value)
goto out; goto out;
} }
error = git_hashtable_insert(cfg->vars, var->name, var); var->next = NULL;
if (cfg->vars_tail == NULL) {
cfg->vars = cfg->vars_tail = var;
}
else {
cfg->vars_tail->next = var;
cfg->vars_tail = var;
}
out:
if(error < GIT_SUCCESS) if(error < GIT_SUCCESS)
cvar_free(var); cvar_free(var);
out:
return error; return error;
} }
int git_config_set_int(git_config *cfg, const char *name, int value) int git_config_set_int(git_config *cfg, const char *name, int value)
{ {
char str_value[5]; /* Most numbers should fit in here */ char str_value[5]; /* Most numbers should fit in here */
int buf_len = sizeof(str_value), ret; int buf_len = sizeof(str_value), ret;
char *help_buf; char *help_buf = NULL;
if((ret = snprintf(str_value, buf_len, "%d", value)) >= buf_len - 1){ if((ret = snprintf(str_value, buf_len, "%d", value)) >= buf_len - 1){
/* The number is too large, we need to allocate more memory */ /* The number is too large, we need to allocate more memory */
...@@ -195,7 +224,12 @@ int git_config_set_int(git_config *cfg, const char *name, int value) ...@@ -195,7 +224,12 @@ int git_config_set_int(git_config *cfg, const char *name, int value)
snprintf(help_buf, buf_len, "%d", value); snprintf(help_buf, buf_len, "%d", value);
} }
return config_set(cfg, name, str_value); ret = config_set(cfg, name, str_value);
if (help_buf != NULL)
free(help_buf);
return ret;
} }
int git_config_set_bool(git_config *cfg, const char *name, int value) int git_config_set_bool(git_config *cfg, const char *name, int value)
...@@ -227,7 +261,8 @@ static int config_get(git_config *cfg, const char *name, const char **out) ...@@ -227,7 +261,8 @@ static int config_get(git_config *cfg, const char *name, const char **out)
git_cvar *var; git_cvar *var;
int error = GIT_SUCCESS; int error = GIT_SUCCESS;
var = git_hashtable_lookup(cfg->vars, name); var = cvar_list_find(cfg->vars, name);
if (var == NULL) if (var == NULL)
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
......
...@@ -13,12 +13,21 @@ struct git_config { ...@@ -13,12 +13,21 @@ struct git_config {
int eof; int eof;
} reader; } reader;
git_hashtable *vars; git_cvar *vars;
git_cvar *vars_tail;
}; };
struct git_cvar { struct git_cvar {
git_cvar *next;
char *name; char *name;
char *value; char *value;
}; };
/*
* If you're going to delete something inside this loop, it's such a
* hassle that you should use the for-loop directly.
*/
#define CVAR_LIST_FOREACH(start, iter) \
for ((iter) = (start); (iter) != NULL; (iter) = (iter)->next)
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment