mirror of
https://github.com/libretro/RetroArch
synced 2025-04-01 13:20:43 +00:00
(config_file) Use hash map to optimise key/value lookups
This commit is contained in:
parent
ae21979b7c
commit
29d5963cad
libretro-common
@ -39,6 +39,7 @@
|
|||||||
#include <file/file_path.h>
|
#include <file/file_path.h>
|
||||||
#include <string/stdstring.h>
|
#include <string/stdstring.h>
|
||||||
#include <streams/file_stream.h>
|
#include <streams/file_stream.h>
|
||||||
|
#include <array/rhmap.h>
|
||||||
|
|
||||||
#define MAX_INCLUDE_DEPTH 16
|
#define MAX_INCLUDE_DEPTH 16
|
||||||
|
|
||||||
@ -48,6 +49,15 @@ struct config_include_list
|
|||||||
struct config_include_list *next;
|
struct config_include_list *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint32_t config_file_hash_string(const char *str)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
uint32_t hash = (uint32_t)0x811c9dc5;
|
||||||
|
while ((c = (unsigned char)*(str++)) != '\0')
|
||||||
|
hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)c);
|
||||||
|
return (hash ? hash : 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
static bool config_file_parse_line(config_file_t *conf,
|
static bool config_file_parse_line(config_file_t *conf,
|
||||||
struct config_entry_list *list, char *line, config_file_cb_t *cb);
|
struct config_entry_list *list, char *line, config_file_cb_t *cb);
|
||||||
@ -273,6 +283,8 @@ static char *config_file_extract_value(char *line, bool is_value)
|
|||||||
static void config_file_add_child_list(config_file_t *parent, config_file_t *child)
|
static void config_file_add_child_list(config_file_t *parent, config_file_t *child)
|
||||||
{
|
{
|
||||||
struct config_entry_list *list = child->entries;
|
struct config_entry_list *list = child->entries;
|
||||||
|
bool merge_hash_map = false;
|
||||||
|
|
||||||
if (parent->entries)
|
if (parent->entries)
|
||||||
{
|
{
|
||||||
struct config_entry_list *head = parent->entries;
|
struct config_entry_list *head = parent->entries;
|
||||||
@ -286,6 +298,8 @@ static void config_file_add_child_list(config_file_t *parent, config_file_t *chi
|
|||||||
list = list->next;
|
list = list->next;
|
||||||
}
|
}
|
||||||
head->next = child->entries;
|
head->next = child->entries;
|
||||||
|
|
||||||
|
merge_hash_map = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -298,8 +312,6 @@ static void config_file_add_child_list(config_file_t *parent, config_file_t *chi
|
|||||||
parent->entries = child->entries;
|
parent->entries = child->entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
child->entries = NULL;
|
|
||||||
|
|
||||||
/* Rebase tail. */
|
/* Rebase tail. */
|
||||||
if (parent->entries)
|
if (parent->entries)
|
||||||
{
|
{
|
||||||
@ -312,6 +324,44 @@ static void config_file_add_child_list(config_file_t *parent, config_file_t *chi
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
parent->tail = NULL;
|
parent->tail = NULL;
|
||||||
|
|
||||||
|
/* Update hash map */
|
||||||
|
if (merge_hash_map)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t cap;
|
||||||
|
|
||||||
|
/* We are merging two lists - if any child entry
|
||||||
|
* (key) is not present in the parent list, add it
|
||||||
|
* to the parent hash map */
|
||||||
|
for (i = 0, cap = RHMAP_CAP(child->entries_map); i != cap; i++)
|
||||||
|
{
|
||||||
|
uint32_t child_hash = RHMAP_KEY(child->entries_map, i);
|
||||||
|
|
||||||
|
if (!RHMAP_HAS(parent->entries_map, child_hash))
|
||||||
|
{
|
||||||
|
struct config_entry_list *entry =
|
||||||
|
RHMAP_GET(child->entries_map, child_hash);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
RHMAP_SET(parent->entries_map, child_hash, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Child entries map is no longer required,
|
||||||
|
* so free it now */
|
||||||
|
RHMAP_FREE(child->entries_map);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If parent list was originally empty,
|
||||||
|
* take map from child list */
|
||||||
|
RHMAP_FREE(parent->entries_map);
|
||||||
|
parent->entries_map = child->entries_map;
|
||||||
|
child->entries_map = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->entries = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void config_file_get_realpath(char *s, size_t len,
|
static void config_file_get_realpath(char *s, size_t len,
|
||||||
@ -426,8 +476,21 @@ static int config_file_load_internal(
|
|||||||
|
|
||||||
conf->tail = list;
|
conf->tail = list;
|
||||||
|
|
||||||
if (cb && list->key && list->value)
|
if (list->key)
|
||||||
cb->config_file_new_entry_cb(list->key, list->value) ;
|
{
|
||||||
|
/* Only add entry to the map if an entry
|
||||||
|
* with the specified value does not
|
||||||
|
* already exist */
|
||||||
|
uint32_t hash = config_file_hash_string(list->key);
|
||||||
|
|
||||||
|
if (!RHMAP_HAS(conf->entries_map, hash))
|
||||||
|
{
|
||||||
|
RHMAP_SET(conf->entries_map, hash, list);
|
||||||
|
|
||||||
|
if (cb && list->value)
|
||||||
|
cb->config_file_new_entry_cb(list->key, list->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
@ -628,6 +691,16 @@ static int config_file_from_string_internal(
|
|||||||
conf->entries = list;
|
conf->entries = list;
|
||||||
|
|
||||||
conf->tail = list;
|
conf->tail = list;
|
||||||
|
|
||||||
|
if (list->key)
|
||||||
|
{
|
||||||
|
/* Only add entry to the map if an entry
|
||||||
|
* with the specified value does not
|
||||||
|
* already exist */
|
||||||
|
uint32_t hash = config_file_hash_string(list->key);
|
||||||
|
if (!RHMAP_HAS(conf->entries_map, hash))
|
||||||
|
RHMAP_SET(conf->entries_map, hash, list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list != conf->tail)
|
if (list != conf->tail)
|
||||||
@ -705,6 +778,9 @@ bool config_file_deinitialize(config_file_t *conf)
|
|||||||
|
|
||||||
if (conf->path)
|
if (conf->path)
|
||||||
free(conf->path);
|
free(conf->path);
|
||||||
|
|
||||||
|
RHMAP_FREE(conf->entries_map);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,9 +794,22 @@ void config_file_free(config_file_t *conf)
|
|||||||
bool config_append_file(config_file_t *conf, const char *path)
|
bool config_append_file(config_file_t *conf, const char *path)
|
||||||
{
|
{
|
||||||
config_file_t *new_conf = config_file_new_from_path_to_string(path);
|
config_file_t *new_conf = config_file_new_from_path_to_string(path);
|
||||||
|
size_t i;
|
||||||
|
size_t cap;
|
||||||
|
|
||||||
if (!new_conf)
|
if (!new_conf)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* Update hash map */
|
||||||
|
for (i = 0, cap = RHMAP_CAP(new_conf->entries_map); i != cap; i++)
|
||||||
|
{
|
||||||
|
uint32_t new_hash = RHMAP_KEY(new_conf->entries_map, i);
|
||||||
|
struct config_entry_list *entry = RHMAP_GET(new_conf->entries_map, new_hash);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
RHMAP_SET(conf->entries_map, new_hash, entry);
|
||||||
|
}
|
||||||
|
|
||||||
if (new_conf->tail)
|
if (new_conf->tail)
|
||||||
{
|
{
|
||||||
new_conf->tail->next = conf->entries;
|
new_conf->tail->next = conf->entries;
|
||||||
@ -818,6 +907,7 @@ void config_file_initialize(struct config_file *conf)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
conf->path = NULL;
|
conf->path = NULL;
|
||||||
|
conf->entries_map = NULL;
|
||||||
conf->entries = NULL;
|
conf->entries = NULL;
|
||||||
conf->tail = NULL;
|
conf->tail = NULL;
|
||||||
conf->last = NULL;
|
conf->last = NULL;
|
||||||
@ -844,16 +934,18 @@ static struct config_entry_list *config_get_entry_internal(
|
|||||||
struct config_entry_list *entry = NULL;
|
struct config_entry_list *entry = NULL;
|
||||||
struct config_entry_list *previous = prev ? *prev : NULL;
|
struct config_entry_list *previous = prev ? *prev : NULL;
|
||||||
|
|
||||||
for (entry = conf->entries; entry; entry = entry->next)
|
entry = RHMAP_GET(conf->entries_map, config_file_hash_string(key));
|
||||||
{
|
|
||||||
if (string_is_equal(key, entry->key))
|
|
||||||
return entry;
|
|
||||||
|
|
||||||
previous = entry;
|
if (entry)
|
||||||
}
|
return entry;
|
||||||
|
|
||||||
if (prev)
|
if (prev)
|
||||||
|
{
|
||||||
|
for (entry = conf->entries; entry; entry = entry->next)
|
||||||
|
previous = entry;
|
||||||
|
|
||||||
*prev = previous;
|
*prev = previous;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -861,16 +953,9 @@ static struct config_entry_list *config_get_entry_internal(
|
|||||||
struct config_entry_list *config_get_entry(
|
struct config_entry_list *config_get_entry(
|
||||||
const config_file_t *conf, const char *key)
|
const config_file_t *conf, const char *key)
|
||||||
{
|
{
|
||||||
struct config_entry_list *entry = NULL;
|
return RHMAP_GET(conf->entries_map, config_file_hash_string(key));
|
||||||
for (entry = conf->entries; entry; entry = entry->next)
|
|
||||||
{
|
|
||||||
if (string_is_equal(key, entry->key))
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||||
{
|
{
|
||||||
const struct config_entry_list *entry = config_get_entry(conf, key);
|
const struct config_entry_list *entry = config_get_entry(conf, key);
|
||||||
@ -1141,6 +1226,8 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
|||||||
conf->entries = entry;
|
conf->entries = entry;
|
||||||
|
|
||||||
conf->last = entry;
|
conf->last = entry;
|
||||||
|
|
||||||
|
RHMAP_SET(conf->entries_map, config_file_hash_string(entry->key), entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void config_unset(config_file_t *conf, const char *key)
|
void config_unset(config_file_t *conf, const char *key)
|
||||||
@ -1157,6 +1244,8 @@ void config_unset(config_file_t *conf, const char *key)
|
|||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
(void)RHMAP_DEL(conf->entries_map, config_file_hash_string(entry->key));
|
||||||
|
|
||||||
if (entry->key)
|
if (entry->key)
|
||||||
free(entry->key);
|
free(entry->key);
|
||||||
|
|
||||||
@ -1376,16 +1465,7 @@ void config_file_dump(config_file_t *conf, FILE *file, bool sort)
|
|||||||
|
|
||||||
bool config_entry_exists(config_file_t *conf, const char *entry)
|
bool config_entry_exists(config_file_t *conf, const char *entry)
|
||||||
{
|
{
|
||||||
struct config_entry_list *list = conf->entries;
|
return (bool)RHMAP_HAS(conf->entries_map, config_file_hash_string(entry));
|
||||||
|
|
||||||
while (list)
|
|
||||||
{
|
|
||||||
if (string_is_equal(entry, list->key))
|
|
||||||
return true;
|
|
||||||
list = list->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool config_get_entry_list_head(config_file_t *conf,
|
bool config_get_entry_list_head(config_file_t *conf,
|
||||||
|
@ -55,6 +55,7 @@ struct config_file
|
|||||||
{
|
{
|
||||||
char *path;
|
char *path;
|
||||||
char *reference;
|
char *reference;
|
||||||
|
struct config_entry_list **entries_map;
|
||||||
struct config_entry_list *entries;
|
struct config_entry_list *entries;
|
||||||
struct config_entry_list *tail;
|
struct config_entry_list *tail;
|
||||||
struct config_entry_list *last;
|
struct config_entry_list *last;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user