mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
core options: keys found in slots past 64 options
GET_VARIABLE and SET_CORE_OPTIONS_DISPLAY looked through every option for the key. Past 64 options the keys are put in slots by their hash when the options are made, and probed; up to 64 the walk in order is quicker and stays. A lookup among 400 options takes 87 cycles, not 239; among 200, 53 for 151.
This commit is contained in:
1 parent
a2cd920563
commit
730feb360e
3 files changed
+141
-39
No files matched your search
+87
-39
@@ -591,6 +591,69 @@ static uint32_t core_option_manager_hash_string(const char *str)
|
||||
return (hash ? hash : 1);
|
||||
}
|
||||
|
||||
/* Up to this many options are looked for in order, which is quicker
|
||||
* than probing the slots */
|
||||
#define CORE_OPTION_SCAN_MAX 64
|
||||
|
||||
/* The slots of every keyed option, the first of a key kept */
|
||||
static bool core_option_manager_index_keys(core_option_manager_t *opt)
|
||||
{
|
||||
size_t cap = 4, i;
|
||||
if (opt->size <= CORE_OPTION_SCAN_MAX)
|
||||
return true;
|
||||
while (cap < 2 * opt->size)
|
||||
cap <<= 1;
|
||||
if (!(opt->key_slots = (uint32_t*)calloc(cap, sizeof(uint32_t))))
|
||||
return false;
|
||||
opt->key_mask = cap - 1;
|
||||
for (i = 0; i < opt->size; i++)
|
||||
{
|
||||
const struct core_option *option = &opt->opts[i];
|
||||
size_t at = option->key_hash & opt->key_mask;
|
||||
if (!option->key)
|
||||
continue;
|
||||
for (;;)
|
||||
{
|
||||
uint32_t slot = opt->key_slots[at];
|
||||
if (!slot)
|
||||
{
|
||||
opt->key_slots[at] = (uint32_t)(i + 1);
|
||||
break;
|
||||
}
|
||||
if ( opt->opts[slot - 1].key_hash == option->key_hash
|
||||
&& string_is_equal(opt->opts[slot - 1].key, option->key))
|
||||
break;
|
||||
at = (at + 1) & opt->key_mask;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The index of the option of @key, or -1 */
|
||||
static INLINE size_t core_option_manager_find(
|
||||
const core_option_manager_t *opt, const char *key)
|
||||
{
|
||||
uint32_t hash = core_option_manager_hash_string(key);
|
||||
size_t at;
|
||||
if (!opt->key_slots)
|
||||
{
|
||||
for (at = 0; at < opt->size; at++)
|
||||
if ( opt->opts[at].key_hash == hash
|
||||
&& string_is_equal(key, opt->opts[at].key))
|
||||
return at;
|
||||
return (size_t)-1;
|
||||
}
|
||||
for (at = hash & opt->key_mask; ; at = (at + 1) & opt->key_mask)
|
||||
{
|
||||
uint32_t slot = opt->key_slots[at];
|
||||
if (!slot)
|
||||
return (size_t)-1;
|
||||
if ( opt->opts[slot - 1].key_hash == hash
|
||||
&& string_is_equal(key, opt->opts[slot - 1].key))
|
||||
return slot - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanitises a core option value label, handling the case
|
||||
* where an explicit label is not provided and performing
|
||||
* conversion of various true/false identifiers to
|
||||
@@ -829,6 +892,8 @@ core_option_manager_t *core_option_manager_new_vars(
|
||||
opt->opts = NULL;
|
||||
opt->size = 0;
|
||||
opt->val_hashes = NULL;
|
||||
opt->key_slots = NULL;
|
||||
opt->key_mask = 0;
|
||||
opt->option_map = nested_list_init();
|
||||
opt->updated = false;
|
||||
opt->log = true;
|
||||
@@ -904,6 +969,9 @@ core_option_manager_t *core_option_manager_new_vars(
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!core_option_manager_index_keys(opt))
|
||||
goto error;
|
||||
|
||||
if (config_src)
|
||||
config_file_free(config_src);
|
||||
|
||||
@@ -1135,6 +1203,8 @@ core_option_manager_t *core_option_manager_new(
|
||||
opt->opts = NULL;
|
||||
opt->size = 0;
|
||||
opt->val_hashes = NULL;
|
||||
opt->key_slots = NULL;
|
||||
opt->key_mask = 0;
|
||||
opt->option_map = nested_list_init();
|
||||
opt->updated = false;
|
||||
opt->log = true;
|
||||
@@ -1255,6 +1325,9 @@ core_option_manager_t *core_option_manager_new(
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!core_option_manager_index_keys(opt))
|
||||
goto error;
|
||||
|
||||
if (config_src)
|
||||
config_file_free(config_src);
|
||||
|
||||
@@ -1337,6 +1410,7 @@ void core_option_manager_free(core_option_manager_t *opt)
|
||||
free(opt->cats);
|
||||
free(opt->opts);
|
||||
free(opt->val_hashes);
|
||||
free(opt->key_slots);
|
||||
free(opt);
|
||||
}
|
||||
|
||||
@@ -1490,26 +1564,12 @@ bool core_option_manager_get_category_visible(core_option_manager_t *opt,
|
||||
bool core_option_manager_get_idx(core_option_manager_t *opt,
|
||||
const char *key, size_t *idx)
|
||||
{
|
||||
uint32_t key_hash;
|
||||
size_t i;
|
||||
if (!opt || (!key || !*key) || !idx)
|
||||
if (!opt || (!key || !*key) || !idx
|
||||
|| (i = core_option_manager_find(opt, key)) == (size_t)-1)
|
||||
return false;
|
||||
key_hash = core_option_manager_hash_string(key);
|
||||
|
||||
for (i = 0; i < opt->size; i++)
|
||||
{
|
||||
struct core_option *option = &opt->opts[i];
|
||||
|
||||
if ( (key_hash == option->key_hash)
|
||||
&& (option->key && *option->key)
|
||||
&& string_is_equal(key, option->key))
|
||||
{
|
||||
*idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
*idx = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1909,29 +1969,17 @@ void core_option_manager_set_default(core_option_manager_t *opt,
|
||||
void core_option_manager_set_display(core_option_manager_t *opt,
|
||||
const char *key, bool visible)
|
||||
{
|
||||
uint32_t key_hash;
|
||||
struct core_option *option;
|
||||
size_t i;
|
||||
if (!opt || (!key || !*key))
|
||||
if (!opt || (!key || !*key)
|
||||
|| (i = core_option_manager_find(opt, key)) == (size_t)-1)
|
||||
return;
|
||||
key_hash = core_option_manager_hash_string(key);
|
||||
|
||||
for (i = 0; i < opt->size; i++)
|
||||
{
|
||||
struct core_option *option = &opt->opts[i];
|
||||
|
||||
if ( (key_hash == option->key_hash)
|
||||
&& (option->key && *option->key)
|
||||
&& string_is_equal(key, option->key))
|
||||
{
|
||||
if (option->visible != visible && !opt->log)
|
||||
RARCH_DBG("[Core] Set display: %s = %s\n",
|
||||
option->key,
|
||||
visible ? "visible" : "hidden");
|
||||
|
||||
option->visible = visible;
|
||||
return;
|
||||
}
|
||||
}
|
||||
option = &opt->opts[i];
|
||||
if (option->visible != visible && !opt->log)
|
||||
RARCH_DBG("[Core] Set display: %s = %s\n",
|
||||
option->key,
|
||||
visible ? "visible" : "hidden");
|
||||
option->visible = visible;
|
||||
}
|
||||
|
||||
/**********************/
|
||||
|
||||
@@ -76,6 +76,11 @@ struct core_option_manager
|
||||
nested_list_t *option_map;
|
||||
/* Value hashes of every option, one allocation */
|
||||
uint32_t *val_hashes;
|
||||
/* Past CORE_OPTION_SCAN_MAX options, each option's index plus one at
|
||||
* its key hash, probed on; 0 is empty. key_mask + 1 slots, at least
|
||||
* twice the options */
|
||||
uint32_t *key_slots;
|
||||
size_t key_mask;
|
||||
|
||||
size_t cats_size;
|
||||
size_t size;
|
||||
|
||||
@@ -303,6 +303,53 @@ static void t_legacy_append_failures(void)
|
||||
fail_append(0);
|
||||
}
|
||||
|
||||
/* Keys found by their index, @n of them: few enough to be looked for in
|
||||
* order, or six hundred in their slots; every one at its own place, near
|
||||
* misses not at all, and the display of each set by its key */
|
||||
#define LOOKUP_N 600
|
||||
static void t_lookup(size_t n)
|
||||
{
|
||||
static struct retro_variable vars[LOOKUP_N + 1];
|
||||
static char keys[LOOKUP_N][16];
|
||||
static const char *misses[] = { "opt_6000", "opt_00", "opt_",
|
||||
"opt_0000x", "OPT_0001", "x" };
|
||||
core_option_manager_t *opt;
|
||||
size_t i, idx;
|
||||
unsigned ok = 0;
|
||||
|
||||
printf(" lookup: %u keys by index, misses, display\n", (unsigned)n);
|
||||
write_conf("");
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
snprintf(keys[i], sizeof(keys[i]), "opt_%04u", (unsigned)i);
|
||||
vars[i].key = keys[i];
|
||||
vars[i].value = "Option; on|off";
|
||||
}
|
||||
vars[n].key = NULL;
|
||||
vars[n].value = NULL;
|
||||
opt = core_option_manager_new_vars(CONF_PATH, NULL, vars);
|
||||
CHECK(opt != NULL, "manager not created");
|
||||
if (!opt)
|
||||
return;
|
||||
for (i = 0; i < n; i++)
|
||||
if (core_option_manager_get_idx(opt, keys[i], &idx) && idx == i)
|
||||
ok++;
|
||||
CHECK(ok == n, "a key not found at its index");
|
||||
for (i = 0; i < sizeof(misses) / sizeof(misses[0]); i++)
|
||||
CHECK(!core_option_manager_get_idx(opt, misses[i], &idx),
|
||||
"a key no option has found");
|
||||
CHECK(!core_option_manager_get_idx(opt, "", &idx), "the empty key found");
|
||||
for (i = 0; i < n; i += 7)
|
||||
core_option_manager_set_display(opt, keys[i], false);
|
||||
core_option_manager_set_display(opt, "opt_9999", false);
|
||||
ok = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
if (opt->opts[i].visible == (i % 7 != 0))
|
||||
ok++;
|
||||
CHECK(ok == n, "display set on the wrong option");
|
||||
core_option_manager_free(opt);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("core_options\n");
|
||||
@@ -310,6 +357,8 @@ int main(void)
|
||||
t_v2_append_failures();
|
||||
t_legacy();
|
||||
t_legacy_append_failures();
|
||||
t_lookup(30);
|
||||
t_lookup(LOOKUP_N);
|
||||
remove(CONF_PATH);
|
||||
|
||||
if (failures)
|
||||
|
||||
Reference in new issue
Block a user