1
0
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:
jdgleaver 2021-04-21 17:23:18 +01:00
parent ae21979b7c
commit 29d5963cad
2 changed files with 109 additions and 28 deletions
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;