|
@@ -20,305 +20,55 @@
|
|
|
|
|
|
#include <util/bmem.h>
|
|
|
#include <util/c99defs.h>
|
|
|
-#include <util/darray.h>
|
|
|
|
|
|
#include "obs-internal.h"
|
|
|
|
|
|
-struct obs_hotkey_name_map_edge;
|
|
|
-struct obs_hotkey_name_map_node;
|
|
|
struct obs_hotkey_name_map;
|
|
|
|
|
|
-typedef struct obs_hotkey_name_map_edge obs_hotkey_name_map_edge_t;
|
|
|
-typedef struct obs_hotkey_name_map_node obs_hotkey_name_map_node_t;
|
|
|
-typedef struct obs_hotkey_name_map obs_hotkey_name_map_t;
|
|
|
+typedef struct obs_hotkey_name_map_item obs_hotkey_name_map_item_t;
|
|
|
|
|
|
-struct obs_hotkey_name_map_node {
|
|
|
- bool is_leaf;
|
|
|
- union {
|
|
|
- int val;
|
|
|
- DARRAY(obs_hotkey_name_map_edge_t) children;
|
|
|
- };
|
|
|
+struct obs_hotkey_name_map_item {
|
|
|
+ char *key;
|
|
|
+ int val;
|
|
|
+ UT_hash_handle hh;
|
|
|
};
|
|
|
|
|
|
-struct obs_hotkey_name_map {
|
|
|
- obs_hotkey_name_map_node_t root;
|
|
|
- obs_hotkey_name_map_node_t *leaves;
|
|
|
- size_t num_leaves;
|
|
|
- size_t next_leaf;
|
|
|
-};
|
|
|
-
|
|
|
-struct obs_hotkey_name_map_edge_prefix {
|
|
|
- uint8_t prefix_len;
|
|
|
- char *prefix;
|
|
|
-};
|
|
|
-
|
|
|
-#define NAME_MAP_COMPRESS_LENGTH \
|
|
|
- (sizeof(struct obs_hotkey_name_map_edge_prefix) - sizeof(uint8_t))
|
|
|
-
|
|
|
-struct obs_hotkey_name_map_edge {
|
|
|
- union {
|
|
|
- struct {
|
|
|
- uint8_t prefix_len;
|
|
|
- char *prefix;
|
|
|
- };
|
|
|
- struct {
|
|
|
- uint8_t compressed_len;
|
|
|
- char compressed_prefix[NAME_MAP_COMPRESS_LENGTH];
|
|
|
- };
|
|
|
- };
|
|
|
- struct obs_hotkey_name_map_node *node;
|
|
|
-};
|
|
|
-
|
|
|
-static inline obs_hotkey_name_map_node_t *new_node(void)
|
|
|
-{
|
|
|
- return bzalloc(sizeof(obs_hotkey_name_map_node_t));
|
|
|
-}
|
|
|
-
|
|
|
-static inline obs_hotkey_name_map_node_t *new_leaf(void)
|
|
|
-{
|
|
|
- obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
|
|
|
- obs_hotkey_name_map_node_t *node =
|
|
|
- &name_map->leaves[name_map->next_leaf];
|
|
|
- name_map->next_leaf += 1;
|
|
|
-
|
|
|
- node->is_leaf = true;
|
|
|
- return node;
|
|
|
-}
|
|
|
-
|
|
|
-static inline char *get_prefix(obs_hotkey_name_map_edge_t *e)
|
|
|
-{
|
|
|
- return e->prefix_len >= NAME_MAP_COMPRESS_LENGTH ? e->prefix
|
|
|
- : e->compressed_prefix;
|
|
|
-}
|
|
|
-
|
|
|
-static void set_prefix(obs_hotkey_name_map_edge_t *e, const char *prefix,
|
|
|
- size_t l)
|
|
|
-{
|
|
|
- assert(e->prefix_len == 0);
|
|
|
-
|
|
|
- e->compressed_len = (uint8_t)l;
|
|
|
- if (l < NAME_MAP_COMPRESS_LENGTH)
|
|
|
- strncpy(e->compressed_prefix, prefix, l);
|
|
|
- else
|
|
|
- e->prefix = bstrdup_n(prefix, l);
|
|
|
-}
|
|
|
-
|
|
|
-static obs_hotkey_name_map_edge_t *add_leaf(obs_hotkey_name_map_node_t *node,
|
|
|
- const char *key, size_t l, int v)
|
|
|
-{
|
|
|
- obs_hotkey_name_map_edge_t *e = da_push_back_new(node->children);
|
|
|
-
|
|
|
- set_prefix(e, key, l);
|
|
|
-
|
|
|
- e->node = new_leaf();
|
|
|
- e->node->val = v;
|
|
|
-
|
|
|
- return e;
|
|
|
-}
|
|
|
-
|
|
|
-static void shrink_prefix(obs_hotkey_name_map_edge_t *e, size_t l)
|
|
|
-{
|
|
|
- bool old_comp = e->prefix_len < NAME_MAP_COMPRESS_LENGTH;
|
|
|
- bool new_comp = l < NAME_MAP_COMPRESS_LENGTH;
|
|
|
-
|
|
|
- char *str = get_prefix(e);
|
|
|
-
|
|
|
- e->prefix_len = (uint8_t)l;
|
|
|
- if (get_prefix(e) != str)
|
|
|
- strncpy(get_prefix(e), str, l);
|
|
|
- else
|
|
|
- str[l] = 0;
|
|
|
-
|
|
|
- if (!old_comp && new_comp)
|
|
|
- bfree(str);
|
|
|
-}
|
|
|
-
|
|
|
-static void connect(obs_hotkey_name_map_edge_t *e,
|
|
|
- obs_hotkey_name_map_node_t *n)
|
|
|
-{
|
|
|
- e->node = n;
|
|
|
-}
|
|
|
-
|
|
|
-static void reduce_edge(obs_hotkey_name_map_edge_t *e, const char *key,
|
|
|
- size_t l, int v)
|
|
|
-{
|
|
|
- const char *str = get_prefix(e), *str_ = key;
|
|
|
- size_t common_length = 0;
|
|
|
- while (*str == *str_) {
|
|
|
- common_length += 1;
|
|
|
- str += 1;
|
|
|
- str_ += 1;
|
|
|
- }
|
|
|
-
|
|
|
- obs_hotkey_name_map_node_t *new_node_ = new_node();
|
|
|
- obs_hotkey_name_map_edge_t *tail =
|
|
|
- da_push_back_new(new_node_->children);
|
|
|
-
|
|
|
- connect(tail, e->node);
|
|
|
- set_prefix(tail, str, e->prefix_len - common_length);
|
|
|
-
|
|
|
- add_leaf(new_node_, str_, l - common_length, v);
|
|
|
-
|
|
|
- connect(e, new_node_);
|
|
|
- shrink_prefix(e, common_length);
|
|
|
-}
|
|
|
-
|
|
|
-enum obs_hotkey_name_map_edge_compare_result {
|
|
|
- RES_MATCHES,
|
|
|
- RES_NO_MATCH,
|
|
|
- RES_COMMON_PREFIX,
|
|
|
- RES_PREFIX_MATCHES,
|
|
|
-};
|
|
|
-
|
|
|
-static enum obs_hotkey_name_map_edge_compare_result
|
|
|
-compare_prefix(obs_hotkey_name_map_edge_t *edge, const char *key, size_t l)
|
|
|
-{
|
|
|
- uint8_t pref_len = edge->prefix_len;
|
|
|
- const char *str = get_prefix(edge);
|
|
|
- size_t i = 0;
|
|
|
-
|
|
|
- for (; i < l && i < pref_len; i++)
|
|
|
- if (str[i] != key[i])
|
|
|
- break;
|
|
|
-
|
|
|
- if (i != 0 && pref_len == i)
|
|
|
- return l == i ? RES_MATCHES : RES_PREFIX_MATCHES;
|
|
|
- if (i != 0)
|
|
|
- return pref_len == i ? RES_PREFIX_MATCHES : RES_COMMON_PREFIX;
|
|
|
- return RES_NO_MATCH;
|
|
|
-}
|
|
|
-
|
|
|
-static void insert(obs_hotkey_name_map_edge_t *edge,
|
|
|
- obs_hotkey_name_map_node_t *node, const char *key, size_t l,
|
|
|
- int v)
|
|
|
+static void obs_hotkey_name_map_insert(obs_hotkey_name_map_item_t **hmap,
|
|
|
+ const char *key, int v)
|
|
|
{
|
|
|
- if (node->is_leaf && l > 0) {
|
|
|
- obs_hotkey_name_map_node_t *new_node_ = new_node();
|
|
|
- connect(edge, new_node_);
|
|
|
-
|
|
|
- obs_hotkey_name_map_edge_t *edge =
|
|
|
- da_push_back_new(new_node_->children);
|
|
|
- connect(edge, node);
|
|
|
- add_leaf(new_node_, key, l, v);
|
|
|
+ if (!hmap || !key)
|
|
|
return;
|
|
|
- }
|
|
|
|
|
|
- if (node->is_leaf && l == 0) {
|
|
|
- node->val = v;
|
|
|
+ obs_hotkey_name_map_item_t *t;
|
|
|
+ HASH_FIND_STR(*hmap, key, t);
|
|
|
+ if (t)
|
|
|
return;
|
|
|
- }
|
|
|
-
|
|
|
- for (size_t i = 0; i < node->children.num; i++) {
|
|
|
- obs_hotkey_name_map_edge_t *e = &node->children.array[i];
|
|
|
-
|
|
|
- switch (compare_prefix(e, key, l)) {
|
|
|
- case RES_NO_MATCH:
|
|
|
- continue;
|
|
|
-
|
|
|
- case RES_MATCHES:
|
|
|
- case RES_PREFIX_MATCHES:
|
|
|
- insert(e, e->node, key + e->prefix_len,
|
|
|
- l - e->prefix_len, v);
|
|
|
- return;
|
|
|
|
|
|
- case RES_COMMON_PREFIX:
|
|
|
- reduce_edge(e, key, l, v);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- add_leaf(node, key, l, v);
|
|
|
-}
|
|
|
-
|
|
|
-static void obs_hotkey_name_map_insert(obs_hotkey_name_map_t *trie,
|
|
|
- const char *key, int v)
|
|
|
-{
|
|
|
- if (!trie || !key)
|
|
|
- return;
|
|
|
+ t = bzalloc(sizeof(obs_hotkey_name_map_item_t));
|
|
|
+ t->key = bstrdup(key);
|
|
|
+ t->val = v;
|
|
|
|
|
|
- insert(NULL, &trie->root, key, strlen(key), v);
|
|
|
+ HASH_ADD_STR(*hmap, key, t);
|
|
|
}
|
|
|
|
|
|
-static bool obs_hotkey_name_map_lookup(obs_hotkey_name_map_t *trie,
|
|
|
+static bool obs_hotkey_name_map_lookup(obs_hotkey_name_map_item_t *hmap,
|
|
|
const char *key, int *v)
|
|
|
{
|
|
|
- if (!trie || !key)
|
|
|
+ if (!hmap || !key)
|
|
|
return false;
|
|
|
|
|
|
- size_t len = strlen(key);
|
|
|
- obs_hotkey_name_map_node_t *n = &trie->root;
|
|
|
-
|
|
|
- size_t i = 0;
|
|
|
- for (; i < n->children.num;) {
|
|
|
- obs_hotkey_name_map_edge_t *e = &n->children.array[i];
|
|
|
-
|
|
|
- switch (compare_prefix(e, key, len)) {
|
|
|
- case RES_NO_MATCH:
|
|
|
- i++;
|
|
|
- continue;
|
|
|
-
|
|
|
- case RES_COMMON_PREFIX:
|
|
|
- return false;
|
|
|
+ obs_hotkey_name_map_item_t *elem;
|
|
|
|
|
|
- case RES_PREFIX_MATCHES:
|
|
|
- key += e->prefix_len;
|
|
|
- len -= e->prefix_len;
|
|
|
- n = e->node;
|
|
|
- i = 0;
|
|
|
- continue;
|
|
|
+ HASH_FIND_STR(hmap, key, elem);
|
|
|
|
|
|
- case RES_MATCHES:
|
|
|
- n = e->node;
|
|
|
- if (!n->is_leaf) {
|
|
|
- for (size_t j = 0; j < n->children.num; j++) {
|
|
|
- if (n->children.array[j].prefix_len)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (v)
|
|
|
- *v = n->children.array[j]
|
|
|
- .node->val;
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (v)
|
|
|
- *v = n->val;
|
|
|
- return true;
|
|
|
- }
|
|
|
+ if (elem) {
|
|
|
+ *v = elem->val;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-static void show_node(obs_hotkey_name_map_node_t *node, int in)
|
|
|
-{
|
|
|
- if (node->is_leaf) {
|
|
|
- printf(": % 3d\n", node->val);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- printf("\n");
|
|
|
- for (int i = 0; i < in; i += 2)
|
|
|
- printf("| ");
|
|
|
- printf("%lu:\n", (unsigned long)node->children.num);
|
|
|
-
|
|
|
- for (size_t i = 0; i < node->children.num; i++) {
|
|
|
- for (int i = 0; i < in; i += 2)
|
|
|
- printf("| ");
|
|
|
- printf("\\ ");
|
|
|
-
|
|
|
- obs_hotkey_name_map_edge_t *e = &node->children.array[i];
|
|
|
- printf("%s", get_prefix(e));
|
|
|
- show_node(e->node, in + 2);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void trie_print_size(obs_hotkey_name_map_t *trie)
|
|
|
-{
|
|
|
- show_node(&trie->root, 0);
|
|
|
-}
|
|
|
-
|
|
|
static const char *obs_key_names[] = {
|
|
|
#define OBS_HOTKEY(x) #x,
|
|
|
#include "obs-hotkeys.h"
|
|
@@ -350,17 +100,7 @@ static obs_key_t obs_key_from_name_fallback(const char *name)
|
|
|
|
|
|
static void init_name_map(void)
|
|
|
{
|
|
|
- obs->hotkeys.name_map = bzalloc(sizeof(struct obs_hotkey_name_map));
|
|
|
- obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
|
|
|
-
|
|
|
-#define OBS_HOTKEY(x) name_map->num_leaves += 1;
|
|
|
-#include "obs-hotkeys.h"
|
|
|
-#undef OBS_HOTKEY
|
|
|
-
|
|
|
- size_t size = sizeof(obs_hotkey_name_map_node_t) * name_map->num_leaves;
|
|
|
- name_map->leaves = bzalloc(size);
|
|
|
-
|
|
|
-#define OBS_HOTKEY(x) obs_hotkey_name_map_insert(name_map, #x, x);
|
|
|
+#define OBS_HOTKEY(x) obs_hotkey_name_map_insert(&obs->hotkeys.name_map, #x, x);
|
|
|
#include "obs-hotkeys.h"
|
|
|
#undef OBS_HOTKEY
|
|
|
}
|
|
@@ -380,37 +120,17 @@ obs_key_t obs_key_from_name(const char *name)
|
|
|
return OBS_KEY_NONE;
|
|
|
}
|
|
|
|
|
|
-static void free_node(obs_hotkey_name_map_node_t *node, bool release);
|
|
|
-
|
|
|
-static void free_edge(obs_hotkey_name_map_edge_t *edge)
|
|
|
-{
|
|
|
- free_node(edge->node, true);
|
|
|
-
|
|
|
- if (edge->prefix_len < NAME_MAP_COMPRESS_LENGTH)
|
|
|
- return;
|
|
|
-
|
|
|
- bfree(get_prefix(edge));
|
|
|
-}
|
|
|
-
|
|
|
-static void free_node(obs_hotkey_name_map_node_t *node, bool release)
|
|
|
-{
|
|
|
- if (!node->is_leaf) {
|
|
|
- for (size_t i = 0; i < node->children.num; i++)
|
|
|
- free_edge(&node->children.array[i]);
|
|
|
-
|
|
|
- da_free(node->children);
|
|
|
- }
|
|
|
-
|
|
|
- if (release && !node->is_leaf)
|
|
|
- bfree(node);
|
|
|
-}
|
|
|
-
|
|
|
void obs_hotkey_name_map_free(void)
|
|
|
{
|
|
|
if (!obs || !obs->hotkeys.name_map)
|
|
|
return;
|
|
|
|
|
|
- free_node(&obs->hotkeys.name_map->root, false);
|
|
|
- bfree(obs->hotkeys.name_map->leaves);
|
|
|
- bfree(obs->hotkeys.name_map);
|
|
|
+ obs_hotkey_name_map_item_t *root = obs->hotkeys.name_map;
|
|
|
+ obs_hotkey_name_map_item_t *n, *tmp;
|
|
|
+
|
|
|
+ HASH_ITER (hh, root, n, tmp) {
|
|
|
+ HASH_DEL(root, n);
|
|
|
+ bfree(n->key);
|
|
|
+ bfree(n);
|
|
|
+ }
|
|
|
}
|