Branch data Line data Source code
1 : : /* Note: only one hash table can be implemented a single file. */
2 : :
3 : :
4 : : /*
5 : : * The generic hash table is designed to make the key length optional
6 : : * and we do not need it because our key is a terminated token list.
7 : : *
8 : : * The token list avoids having to allocated a new string and the
9 : : * associated issues of memory management. In most cases the search key
10 : : * is also a similar token list.
11 : : *
12 : : * However, on occasion we need to look up an unparsed string of dot
13 : : * separated scopes (nested_flatbuffer attributes). This is not
14 : : * trivially possible without reverting to allocating the strings.
15 : : * We could parse the attribute into tokens but it is also non-trivial
16 : : * because the token buffer breaks pointers when reallocating and
17 : : * the parse output is considered read-only at this point.
18 : : *
19 : : * We can however, use a trick to overcome this because the hash table
20 : : * does not enforce that the search key has same representation as the
21 : : * stored key. We can use the key length to switch between key types.
22 : : *
23 : : * When the key is paresed to a token list:
24 : : *
25 : : * enemy: MyGame . Example.Monster
26 : : *
27 : : * the spaces in dots may be ignored by the parser.
28 : : * Spaces must be handled explicitly or disallowed when the key is
29 : : * parsed as an attribute string (only the quoted content):
30 : : *
31 : : * (nested_flatbuffer:"MyGame.Example.Monster")
32 : : *
33 : : * vs
34 : : *
35 : : * (nested_flatbuffer:"MyGame . Example.Monster")
36 : : *
37 : : * Googles flatc allows spaces in the token stream where dots are
38 : : * operators, but not in attribute strings which are supposed to
39 : : * be unique so we follow that convention.
40 : : *
41 : : * On both key representations, preprocessing must strip the trailing
42 : : * symbol stored within the scope before lookup - minding that this
43 : : * lookup only finds the scope itself. For token lists this can be
44 : : * done by either zero terminating the list early, or by issuing
45 : : * a negative length (after cast to int) of elements to consider. For
46 : : * string keys the key length should be made to the length to be
47 : : * considered.
48 : : *
49 : : * If the scope string is zero length, a null key should be issued
50 : : * with zero length. This is indistinguishly from a null length token
51 : : * list - both indicating a global scope - null thus being a valid key.
52 : : *
53 : : * Note: it is important to not use a non-null zero length string
54 : : * as key.
55 : : */
56 : :
57 : : #include "../symbols.h"
58 : :
59 : : static inline size_t scope_hash(const void *s, size_t len);
60 : : #define HT_HASH_FUNCTION scope_hash
61 : :
62 : : #include "hash/hash_table_def.h"
63 [ + - ]: 724 : DEFINE_HASH_TABLE(fb_scope_table)
64 : : #include "hash/hash_table_impl.h"
65 : :
66 : : /* Null is a valid key used for root scopes. */
67 : 169 : static inline int ht_match(const void *key, size_t len, fb_scope_t *scope)
68 : : {
69 : 169 : const fb_ref_t *name = scope->name;
70 : 169 : int count = (int)len;
71 : : size_t n1, n2, i;
72 : :
73 : : /* Note: `name` may be null here - this is the global scope name. */
74 [ + + ]: 169 : if (count <= 0) {
75 : : const fb_ref_t *keyname = key;
76 : : /*
77 : : * If count is negative, this is the token count of the key
78 : : * which may have suffix to be ignored, otherwise the key is the
79 : : * full list.
80 : : */
81 : : /* `key` is a ref list (a list of tokens). */
82 [ + + ]: 234 : while (name && keyname) {
83 : 145 : n1 = name->ident->len;
84 : 145 : n2 = keyname->ident->len;
85 [ + - ][ + - ]: 145 : if (n1 != n2 || strncmp(name->ident->text, keyname->ident->text, n1)) {
86 : : return 0;
87 : : }
88 : 145 : name = name->link;
89 : 145 : keyname = keyname->link;
90 [ + + ]: 145 : if (++count == 0) {
91 : 247 : return name == 0;
92 : : }
93 : : }
94 [ + + ]: 89 : if (name || keyname) {
95 : : return 0;
96 : : }
97 : : return 1;
98 : : } else {
99 : : /* `key` is a dotted string. */
100 : : const char *s1, *s2 = key;
101 [ + - ]: 2 : while (name) {
102 : 2 : s1 = name->ident->text;
103 : 2 : n1 = name->ident->len;
104 [ + - ]: 2 : if (n1 > len) {
105 : : return 0;
106 : : }
107 [ + + ]: 13 : for (i = 0; i < n1; ++i) {
108 [ + - ]: 11 : if (s1[i] != s2[i]) {
109 : : return 0;
110 : : }
111 : : }
112 [ + + ]: 2 : if (n1 == len) {
113 : 1 : return name->link == 0;
114 : : }
115 [ + - ]: 1 : if (s2[i] != '.') {
116 : : return 0;
117 : : }
118 : 1 : len -= n1 + 1;
119 : 1 : s2 += n1 + 1;
120 : 1 : name = name->link;
121 : : }
122 : : return 0;
123 : : }
124 : : }
125 : :
126 : : static inline const void *ht_key(fb_scope_t *scope)
127 : : {
128 : : return scope->name;
129 : : }
130 : :
131 : : static inline size_t ht_key_len(fb_scope_t *scope)
132 : : {
133 : : (void)scope;
134 : : /*
135 : : * Must be zero because the result is passed to ht_match
136 : : * when comparing two stored items for hash conflicts.
137 : : * Only external lookup keys can be non-zero.
138 : : */
139 : : return 0;
140 : : }
141 : :
142 : 249 : static inline size_t scope_hash(const void *key, size_t len)
143 : : {
144 : : size_t h = 0, i;
145 : 249 : int count = (int)len;
146 : :
147 [ + + ]: 249 : if (count <= 0) {
148 : : const fb_ref_t *name = key;
149 : :
150 [ + + ]: 427 : while (name) {
151 : 258 : h ^= ht_strn_hash_function(name->ident->text, name->ident->len);
152 : : h = ht_int_hash_function((void *)h, 0);
153 : 258 : name = name->link;
154 [ + + ]: 506 : if (++count == 0) {
155 : : break;
156 : : }
157 : : }
158 : : return h;
159 : : } else {
160 : : const char *s = key;
161 : : for (;;) {
162 [ + + ]: 13 : for (i = 0; i < len; ++i) {
163 [ + + ]: 13 : if (s[i] == '.') {
164 : : break;
165 : : }
166 : : }
167 : 2 : h ^= ht_strn_hash_function(s, i);
168 : : h = ht_int_hash_function((void *)h, 0);
169 [ + + ]: 2 : if (i == len) {
170 : : break;
171 : : }
172 : 1 : len -= i + 1;
173 : 1 : s += i + 1;
174 : : }
175 : : return h;
176 : : }
177 : : }
|