1
0

obs-hotkey-name-map.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /******************************************************************************
  2. Copyright (C) 2014 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include <string.h>
  15. #include <assert.h>
  16. #include <util/bmem.h>
  17. #include <util/c99defs.h>
  18. #include <util/darray.h>
  19. #include "obs-internal.h"
  20. struct obs_hotkey_name_map_edge;
  21. struct obs_hotkey_name_map_node;
  22. struct obs_hotkey_name_map;
  23. typedef struct obs_hotkey_name_map_edge obs_hotkey_name_map_edge_t;
  24. typedef struct obs_hotkey_name_map_node obs_hotkey_name_map_node_t;
  25. typedef struct obs_hotkey_name_map obs_hotkey_name_map_t;
  26. struct obs_hotkey_name_map_node {
  27. bool is_leaf;
  28. union {
  29. int val;
  30. DARRAY(obs_hotkey_name_map_edge_t) children;
  31. };
  32. };
  33. struct obs_hotkey_name_map {
  34. obs_hotkey_name_map_node_t root;
  35. obs_hotkey_name_map_node_t *leaves;
  36. size_t num_leaves;
  37. size_t next_leaf;
  38. };
  39. struct obs_hotkey_name_map_edge_prefix {
  40. uint8_t prefix_len;
  41. char *prefix;
  42. };
  43. #define NAME_MAP_COMPRESS_LENGTH \
  44. (sizeof(struct obs_hotkey_name_map_edge_prefix) - sizeof(uint8_t))
  45. struct obs_hotkey_name_map_edge {
  46. union {
  47. struct {
  48. uint8_t prefix_len;
  49. char *prefix;
  50. };
  51. struct {
  52. uint8_t compressed_len;
  53. char compressed_prefix[NAME_MAP_COMPRESS_LENGTH];
  54. };
  55. };
  56. struct obs_hotkey_name_map_node *node;
  57. };
  58. static inline obs_hotkey_name_map_node_t *new_node(void)
  59. {
  60. return bzalloc(sizeof(obs_hotkey_name_map_node_t));
  61. }
  62. static inline obs_hotkey_name_map_node_t *new_leaf(void)
  63. {
  64. obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
  65. obs_hotkey_name_map_node_t *node =
  66. &name_map->leaves[name_map->next_leaf];
  67. name_map->next_leaf += 1;
  68. node->is_leaf = true;
  69. return node;
  70. }
  71. static inline char *get_prefix(obs_hotkey_name_map_edge_t *e)
  72. {
  73. return e->prefix_len >= NAME_MAP_COMPRESS_LENGTH ? e->prefix
  74. : e->compressed_prefix;
  75. }
  76. static void set_prefix(obs_hotkey_name_map_edge_t *e, const char *prefix,
  77. size_t l)
  78. {
  79. assert(e->prefix_len == 0);
  80. e->compressed_len = (uint8_t)l;
  81. if (l < NAME_MAP_COMPRESS_LENGTH)
  82. strncpy(e->compressed_prefix, prefix, l);
  83. else
  84. e->prefix = bstrdup_n(prefix, l);
  85. }
  86. static obs_hotkey_name_map_edge_t *add_leaf(obs_hotkey_name_map_node_t *node,
  87. const char *key, size_t l, int v)
  88. {
  89. obs_hotkey_name_map_edge_t *e = da_push_back_new(node->children);
  90. set_prefix(e, key, l);
  91. e->node = new_leaf();
  92. e->node->val = v;
  93. return e;
  94. }
  95. static void shrink_prefix(obs_hotkey_name_map_edge_t *e, size_t l)
  96. {
  97. bool old_comp = e->prefix_len < NAME_MAP_COMPRESS_LENGTH;
  98. bool new_comp = l < NAME_MAP_COMPRESS_LENGTH;
  99. char *str = get_prefix(e);
  100. e->prefix_len = (uint8_t)l;
  101. if (get_prefix(e) != str)
  102. strncpy(get_prefix(e), str, l);
  103. else
  104. str[l] = 0;
  105. if (!old_comp && new_comp)
  106. bfree(str);
  107. }
  108. static void connect(obs_hotkey_name_map_edge_t *e,
  109. obs_hotkey_name_map_node_t *n)
  110. {
  111. e->node = n;
  112. }
  113. static void reduce_edge(obs_hotkey_name_map_edge_t *e, const char *key,
  114. size_t l, int v)
  115. {
  116. const char *str = get_prefix(e), *str_ = key;
  117. size_t common_length = 0;
  118. while (*str == *str_) {
  119. common_length += 1;
  120. str += 1;
  121. str_ += 1;
  122. }
  123. obs_hotkey_name_map_node_t *new_node_ = new_node();
  124. obs_hotkey_name_map_edge_t *tail =
  125. da_push_back_new(new_node_->children);
  126. connect(tail, e->node);
  127. set_prefix(tail, str, e->prefix_len - common_length);
  128. add_leaf(new_node_, str_, l - common_length, v);
  129. connect(e, new_node_);
  130. shrink_prefix(e, common_length);
  131. }
  132. enum obs_hotkey_name_map_edge_compare_result {
  133. RES_MATCHES,
  134. RES_NO_MATCH,
  135. RES_COMMON_PREFIX,
  136. RES_PREFIX_MATCHES,
  137. };
  138. static enum obs_hotkey_name_map_edge_compare_result
  139. compare_prefix(obs_hotkey_name_map_edge_t *edge, const char *key, size_t l)
  140. {
  141. uint8_t pref_len = edge->prefix_len;
  142. const char *str = get_prefix(edge);
  143. size_t i = 0;
  144. for (; i < l && i < pref_len; i++)
  145. if (str[i] != key[i])
  146. break;
  147. if (i != 0 && pref_len == i)
  148. return l == i ? RES_MATCHES : RES_PREFIX_MATCHES;
  149. if (i != 0)
  150. return pref_len == i ? RES_PREFIX_MATCHES : RES_COMMON_PREFIX;
  151. return RES_NO_MATCH;
  152. }
  153. static void insert(obs_hotkey_name_map_edge_t *edge,
  154. obs_hotkey_name_map_node_t *node, const char *key, size_t l,
  155. int v)
  156. {
  157. if (node->is_leaf && l > 0) {
  158. obs_hotkey_name_map_node_t *new_node_ = new_node();
  159. connect(edge, new_node_);
  160. obs_hotkey_name_map_edge_t *edge =
  161. da_push_back_new(new_node_->children);
  162. connect(edge, node);
  163. add_leaf(new_node_, key, l, v);
  164. return;
  165. }
  166. if (node->is_leaf && l == 0) {
  167. node->val = v;
  168. return;
  169. }
  170. for (size_t i = 0; i < node->children.num; i++) {
  171. obs_hotkey_name_map_edge_t *e = &node->children.array[i];
  172. switch (compare_prefix(e, key, l)) {
  173. case RES_NO_MATCH:
  174. continue;
  175. case RES_MATCHES:
  176. case RES_PREFIX_MATCHES:
  177. insert(e, e->node, key + e->prefix_len,
  178. l - e->prefix_len, v);
  179. return;
  180. case RES_COMMON_PREFIX:
  181. reduce_edge(e, key, l, v);
  182. return;
  183. }
  184. }
  185. add_leaf(node, key, l, v);
  186. }
  187. static void obs_hotkey_name_map_insert(obs_hotkey_name_map_t *trie,
  188. const char *key, int v)
  189. {
  190. if (!trie || !key)
  191. return;
  192. insert(NULL, &trie->root, key, strlen(key), v);
  193. }
  194. static bool obs_hotkey_name_map_lookup(obs_hotkey_name_map_t *trie,
  195. const char *key, int *v)
  196. {
  197. if (!trie || !key)
  198. return false;
  199. size_t len = strlen(key);
  200. obs_hotkey_name_map_node_t *n = &trie->root;
  201. size_t i = 0;
  202. for (; i < n->children.num;) {
  203. obs_hotkey_name_map_edge_t *e = &n->children.array[i];
  204. switch (compare_prefix(e, key, len)) {
  205. case RES_NO_MATCH:
  206. i++;
  207. continue;
  208. case RES_COMMON_PREFIX:
  209. return false;
  210. case RES_PREFIX_MATCHES:
  211. key += e->prefix_len;
  212. len -= e->prefix_len;
  213. n = e->node;
  214. i = 0;
  215. continue;
  216. case RES_MATCHES:
  217. n = e->node;
  218. if (!n->is_leaf) {
  219. for (size_t j = 0; j < n->children.num; j++) {
  220. if (n->children.array[j].prefix_len)
  221. continue;
  222. if (v)
  223. *v = n->children.array[j]
  224. .node->val;
  225. return true;
  226. }
  227. return false;
  228. }
  229. if (v)
  230. *v = n->val;
  231. return true;
  232. }
  233. }
  234. return false;
  235. }
  236. static void show_node(obs_hotkey_name_map_node_t *node, int in)
  237. {
  238. if (node->is_leaf) {
  239. printf(": % 3d\n", node->val);
  240. return;
  241. }
  242. printf("\n");
  243. for (int i = 0; i < in; i += 2)
  244. printf("| ");
  245. printf("%lu:\n", (unsigned long)node->children.num);
  246. for (size_t i = 0; i < node->children.num; i++) {
  247. for (int i = 0; i < in; i += 2)
  248. printf("| ");
  249. printf("\\ ");
  250. obs_hotkey_name_map_edge_t *e = &node->children.array[i];
  251. printf("%s", get_prefix(e));
  252. show_node(e->node, in + 2);
  253. }
  254. }
  255. void trie_print_size(obs_hotkey_name_map_t *trie)
  256. {
  257. show_node(&trie->root, 0);
  258. }
  259. static const char *obs_key_names[] = {
  260. #define OBS_HOTKEY(x) #x,
  261. #include "obs-hotkeys.h"
  262. #undef OBS_HOTKEY
  263. };
  264. const char *obs_key_to_name(obs_key_t key)
  265. {
  266. if (key >= OBS_KEY_LAST_VALUE) {
  267. blog(LOG_ERROR,
  268. "obs-hotkey.c: queried unknown key "
  269. "with code %d",
  270. (int)key);
  271. return "";
  272. }
  273. return obs_key_names[key];
  274. }
  275. static obs_key_t obs_key_from_name_fallback(const char *name)
  276. {
  277. #define OBS_HOTKEY(x) \
  278. if (strcmp(#x, name) == 0) \
  279. return x;
  280. #include "obs-hotkeys.h"
  281. #undef OBS_HOTKEY
  282. return OBS_KEY_NONE;
  283. }
  284. static void init_name_map(void)
  285. {
  286. obs->hotkeys.name_map = bzalloc(sizeof(struct obs_hotkey_name_map));
  287. obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
  288. #define OBS_HOTKEY(x) name_map->num_leaves += 1;
  289. #include "obs-hotkeys.h"
  290. #undef OBS_HOTKEY
  291. size_t size = sizeof(obs_hotkey_name_map_node_t) * name_map->num_leaves;
  292. name_map->leaves = bzalloc(size);
  293. #define OBS_HOTKEY(x) obs_hotkey_name_map_insert(name_map, #x, x);
  294. #include "obs-hotkeys.h"
  295. #undef OBS_HOTKEY
  296. }
  297. obs_key_t obs_key_from_name(const char *name)
  298. {
  299. if (!obs)
  300. return obs_key_from_name_fallback(name);
  301. if (pthread_once(&obs->hotkeys.name_map_init_token, init_name_map))
  302. return obs_key_from_name_fallback(name);
  303. int v = 0;
  304. if (obs_hotkey_name_map_lookup(obs->hotkeys.name_map, name, &v))
  305. return v;
  306. return OBS_KEY_NONE;
  307. }
  308. static void free_node(obs_hotkey_name_map_node_t *node, bool release);
  309. static void free_edge(obs_hotkey_name_map_edge_t *edge)
  310. {
  311. free_node(edge->node, true);
  312. if (edge->prefix_len < NAME_MAP_COMPRESS_LENGTH)
  313. return;
  314. bfree(get_prefix(edge));
  315. }
  316. static void free_node(obs_hotkey_name_map_node_t *node, bool release)
  317. {
  318. if (!node->is_leaf) {
  319. for (size_t i = 0; i < node->children.num; i++)
  320. free_edge(&node->children.array[i]);
  321. da_free(node->children);
  322. }
  323. if (release && !node->is_leaf)
  324. bfree(node);
  325. }
  326. void obs_hotkey_name_map_free(void)
  327. {
  328. if (!obs || !obs->hotkeys.name_map)
  329. return;
  330. free_node(&obs->hotkeys.name_map->root, false);
  331. bfree(obs->hotkeys.name_map->leaves);
  332. bfree(obs->hotkeys.name_map);
  333. }