mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-25 22:12:55 +00:00
config: refcount the values map
This is mostly groundwork to let us re-use the map in the snapshots.
This commit is contained in:
parent
8c1f4ab4ab
commit
4b99b8f528
@ -87,8 +87,15 @@ struct reader {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
git_config_backend parent;
|
||||
git_atomic refcount;
|
||||
git_strmap *values;
|
||||
} refcounted_strmap;
|
||||
|
||||
typedef struct {
|
||||
git_config_backend parent;
|
||||
/* mutex to coordinate accessing the values */
|
||||
git_mutex values_mutex;
|
||||
refcounted_strmap *values;
|
||||
int readonly;
|
||||
} diskfile_header;
|
||||
|
||||
@ -139,18 +146,6 @@ static void cvar_free(cvar_t *var)
|
||||
git__free(var);
|
||||
}
|
||||
|
||||
static int cvar_length(cvar_t *var)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
while (var) {
|
||||
length++;
|
||||
var = var->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int git_config_file_normalize_section(char *start, char *end)
|
||||
{
|
||||
char *scan;
|
||||
@ -215,6 +210,58 @@ static void free_vars(git_strmap *values)
|
||||
git_strmap_free(values);
|
||||
}
|
||||
|
||||
static void refcounted_strmap_free(refcounted_strmap *map)
|
||||
{
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
if (git_atomic_dec(&map->refcount) != 0)
|
||||
return;
|
||||
|
||||
free_vars(map->values);
|
||||
git__free(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the current values map from the backend and increase its
|
||||
* refcount. This is its own function to make sure we use the mutex to
|
||||
* avoid the map pointer from changing under us.
|
||||
*/
|
||||
static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
|
||||
{
|
||||
refcounted_strmap *map;
|
||||
|
||||
git_mutex_lock(&h->values_mutex);
|
||||
|
||||
map = h->values;
|
||||
git_atomic_inc(&map->refcount);
|
||||
|
||||
git_mutex_unlock(&h->values_mutex);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static int refcounted_strmap_alloc(refcounted_strmap **out)
|
||||
{
|
||||
refcounted_strmap *map;
|
||||
int error;
|
||||
|
||||
map = git__calloc(1, sizeof(refcounted_strmap));
|
||||
if (!map) {
|
||||
giterr_set_oom();
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_atomic_set(&map->refcount, 1);
|
||||
if ((error = git_strmap_alloc(&map->values)) < 0) {
|
||||
git__free(map);
|
||||
return error;
|
||||
}
|
||||
|
||||
*out = map;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int config_open(git_config_backend *cfg, git_config_level_t level)
|
||||
{
|
||||
int res;
|
||||
@ -223,13 +270,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
|
||||
|
||||
b->level = level;
|
||||
|
||||
if ((res = git_strmap_alloc(&b->header.values)) < 0)
|
||||
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
|
||||
return res;
|
||||
|
||||
git_mutex_init(&b->header.values_mutex);
|
||||
git_array_init(b->readers);
|
||||
reader = git_array_alloc(b->readers);
|
||||
if (!reader) {
|
||||
git_strmap_free(b->header.values);
|
||||
refcounted_strmap_free(b->header.values);
|
||||
return -1;
|
||||
}
|
||||
memset(reader, 0, sizeof(struct reader));
|
||||
@ -245,8 +293,8 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
|
||||
if (res == GIT_ENOTFOUND)
|
||||
return 0;
|
||||
|
||||
if (res < 0 || (res = config_parse(b->header.values, b, reader, level, 0)) < 0) {
|
||||
free_vars(b->header.values);
|
||||
if (res < 0 || (res = config_parse(b->header.values->values, b, reader, level, 0)) < 0) {
|
||||
refcounted_strmap_free(b->header.values);
|
||||
b->header.values = NULL;
|
||||
}
|
||||
|
||||
@ -259,23 +307,29 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
|
||||
/* The meat of the refresh, as we want to use it in different places */
|
||||
static int config__refresh(git_config_backend *cfg)
|
||||
{
|
||||
git_strmap *values = NULL;
|
||||
refcounted_strmap *values = NULL, *tmp;
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
struct reader *reader = NULL;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_strmap_alloc(&values)) < 0)
|
||||
if ((error = refcounted_strmap_alloc(&values)) < 0)
|
||||
goto out;
|
||||
|
||||
reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
|
||||
|
||||
if ((error = config_parse(values, b, reader, b->level, 0)) < 0)
|
||||
if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
|
||||
goto out;
|
||||
|
||||
values = git__swap(b->header.values, values);
|
||||
git_mutex_lock(&b->header.values_mutex);
|
||||
|
||||
tmp = b->header.values;
|
||||
b->header.values = values;
|
||||
values = tmp;
|
||||
|
||||
git_mutex_unlock(&b->header.values_mutex);
|
||||
|
||||
out:
|
||||
free_vars(values);
|
||||
refcounted_strmap_free(values);
|
||||
git_buf_free(&reader->buffer);
|
||||
return error;
|
||||
}
|
||||
@ -321,7 +375,7 @@ static void backend_free(git_config_backend *_backend)
|
||||
git_array_clear(backend->readers);
|
||||
|
||||
git__free(backend->file_path);
|
||||
free_vars(backend->header.values);
|
||||
refcounted_strmap_free(backend->header.values);
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
@ -338,7 +392,7 @@ static int config_iterator_next(
|
||||
{
|
||||
git_config_file_iter *it = (git_config_file_iter *) iter;
|
||||
diskfile_header *h = (diskfile_header *) it->parent.backend;
|
||||
git_strmap *values = h->values;
|
||||
git_strmap *values = h->values->values;
|
||||
int err = 0;
|
||||
cvar_t * var;
|
||||
|
||||
@ -397,7 +451,8 @@ static int config_iterator_new(
|
||||
static int config_set(git_config_backend *cfg, const char *name, const char *value)
|
||||
{
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
git_strmap *values = b->header.values;
|
||||
refcounted_strmap *map;
|
||||
git_strmap *values;
|
||||
char *key, *esc_value = NULL;
|
||||
khiter_t pos;
|
||||
int rval, ret;
|
||||
@ -405,6 +460,9 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
|
||||
if ((rval = git_config__normalize_name(name, &key)) < 0)
|
||||
return rval;
|
||||
|
||||
map = refcounted_strmap_take(&b->header);
|
||||
values = map->values;
|
||||
|
||||
/*
|
||||
* Try to find it in the existing values and update it if it
|
||||
* only has one value.
|
||||
@ -414,17 +472,17 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
|
||||
cvar_t *existing = git_strmap_value_at(values, pos);
|
||||
|
||||
if (existing->next != NULL) {
|
||||
git__free(key);
|
||||
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* don't update if old and new values already match */
|
||||
if ((!existing->entry->value && !value) ||
|
||||
(existing->entry->value && value &&
|
||||
!strcmp(existing->entry->value, value))) {
|
||||
git__free(key);
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +499,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
|
||||
ret = config_refresh(cfg);
|
||||
|
||||
out:
|
||||
refcounted_strmap_free(map);
|
||||
git__free(esc_value);
|
||||
git__free(key);
|
||||
return ret;
|
||||
@ -452,6 +511,7 @@ out:
|
||||
static int config_get(git_config_backend *cfg, const char *key, const git_config_entry **out)
|
||||
{
|
||||
diskfile_header *h = (diskfile_header *)cfg;
|
||||
refcounted_strmap *map;
|
||||
git_strmap *values;
|
||||
khiter_t pos;
|
||||
cvar_t *var;
|
||||
@ -460,17 +520,22 @@ static int config_get(git_config_backend *cfg, const char *key, const git_config
|
||||
if (!h->readonly && ((error = config_refresh(cfg)) < 0))
|
||||
return error;
|
||||
|
||||
values = h->values;
|
||||
map = refcounted_strmap_take(h);
|
||||
values = map->values;
|
||||
|
||||
pos = git_strmap_lookup_index(values, key);
|
||||
|
||||
/* no error message; the config system will write one */
|
||||
if (!git_strmap_valid_index(values, pos))
|
||||
if (!git_strmap_valid_index(values, pos)) {
|
||||
refcounted_strmap_free(map);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
var = git_strmap_value_at(values, pos);
|
||||
while (var->next)
|
||||
var = var->next;
|
||||
|
||||
refcounted_strmap_free(map);
|
||||
*out = var->entry;
|
||||
return 0;
|
||||
}
|
||||
@ -479,7 +544,8 @@ static int config_set_multivar(
|
||||
git_config_backend *cfg, const char *name, const char *regexp, const char *value)
|
||||
{
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
git_strmap *values = b->header.values;
|
||||
refcounted_strmap *map;
|
||||
git_strmap *values;
|
||||
char *key;
|
||||
regex_t preg;
|
||||
int result;
|
||||
@ -490,20 +556,23 @@ static int config_set_multivar(
|
||||
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||
return result;
|
||||
|
||||
map = refcounted_strmap_take(&b->header);
|
||||
values = b->header.values->values;
|
||||
|
||||
pos = git_strmap_lookup_index(values, key);
|
||||
if (!git_strmap_valid_index(values, pos)) {
|
||||
/* If we don't have it, behave like a normal set */
|
||||
result = config_set(cfg, name, value);
|
||||
refcounted_strmap_free(map);
|
||||
git__free(key);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = regcomp(&preg, regexp, REG_EXTENDED);
|
||||
if (result < 0) {
|
||||
git__free(key);
|
||||
giterr_set_regex(&preg, result);
|
||||
regfree(&preg);
|
||||
return -1;
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If we do have it, set call config_write() and reload */
|
||||
@ -513,6 +582,7 @@ static int config_set_multivar(
|
||||
result = config_refresh(cfg);
|
||||
|
||||
out:
|
||||
refcounted_strmap_free(map);
|
||||
git__free(key);
|
||||
regfree(&preg);
|
||||
|
||||
@ -523,7 +593,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
|
||||
{
|
||||
cvar_t *var;
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
git_strmap *values = b->header.values;
|
||||
refcounted_strmap *map; git_strmap *values;
|
||||
char *key;
|
||||
int result;
|
||||
khiter_t pos;
|
||||
@ -531,15 +601,20 @@ static int config_delete(git_config_backend *cfg, const char *name)
|
||||
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||
return result;
|
||||
|
||||
map = refcounted_strmap_take(&b->header);
|
||||
values = b->header.values->values;
|
||||
|
||||
pos = git_strmap_lookup_index(values, key);
|
||||
git__free(key);
|
||||
|
||||
if (!git_strmap_valid_index(values, pos)) {
|
||||
refcounted_strmap_free(map);
|
||||
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
var = git_strmap_value_at(values, pos);
|
||||
refcounted_strmap_free(map);
|
||||
|
||||
if (var->next != NULL) {
|
||||
giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
|
||||
@ -555,7 +630,8 @@ static int config_delete(git_config_backend *cfg, const char *name)
|
||||
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
|
||||
{
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
git_strmap *values = b->header.values;
|
||||
refcounted_strmap *map;
|
||||
git_strmap *values;
|
||||
char *key;
|
||||
regex_t preg;
|
||||
int result;
|
||||
@ -564,14 +640,20 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
|
||||
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||
return result;
|
||||
|
||||
map = refcounted_strmap_take(&b->header);
|
||||
values = b->header.values->values;
|
||||
|
||||
pos = git_strmap_lookup_index(values, key);
|
||||
|
||||
if (!git_strmap_valid_index(values, pos)) {
|
||||
refcounted_strmap_free(map);
|
||||
git__free(key);
|
||||
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
refcounted_strmap_free(map);
|
||||
|
||||
result = regcomp(&preg, regexp, REG_EXTENDED);
|
||||
if (result < 0) {
|
||||
giterr_set_regex(&preg, result);
|
||||
@ -676,7 +758,7 @@ static void backend_readonly_free(git_config_backend *_backend)
|
||||
if (backend == NULL)
|
||||
return;
|
||||
|
||||
free_vars(backend->header.values);
|
||||
refcounted_strmap_free(backend->header.values);
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
@ -702,8 +784,8 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
|
||||
{
|
||||
diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
|
||||
diskfile_backend *src = b->snapshot_from;
|
||||
git_strmap *src_values = src->header.values;
|
||||
git_strmap *values;
|
||||
refcounted_strmap *src_map;
|
||||
git_strmap *src_values, *values;
|
||||
git_strmap_iter i;
|
||||
cvar_t *src_var;
|
||||
int error;
|
||||
@ -711,10 +793,12 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
|
||||
/* We're just copying data, don't care about the level */
|
||||
GIT_UNUSED(level);
|
||||
|
||||
if ((error = git_strmap_alloc(&b->header.values)) < 0)
|
||||
if ((error = refcounted_strmap_alloc(&b->header.values)) < 0)
|
||||
return error;
|
||||
|
||||
values = b->header.values;
|
||||
src_map = refcounted_strmap_take(&src->header);
|
||||
src_values = src->header.values->values;
|
||||
values = b->header.values->values;
|
||||
|
||||
i = git_strmap_begin(src_values);
|
||||
while ((error = git_strmap_next((void **) &src_var, &i, src_values)) == 0) {
|
||||
@ -725,8 +809,11 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
|
||||
var = git__calloc(1, sizeof(cvar_t));
|
||||
GITERR_CHECK_ALLOC(var);
|
||||
|
||||
if (config_entry_dup(&entry, src_var->entry) < 0)
|
||||
if (config_entry_dup(&entry, src_var->entry) < 0) {
|
||||
refcounted_strmap_free(b->header.values);
|
||||
refcounted_strmap_free(src_map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
var->entry = entry;
|
||||
|
||||
@ -738,6 +825,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
refcounted_strmap_free(src_map);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user