scope_table.c (5509B)
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 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 static inline int ht_match(const void *key, size_t len, fb_scope_t *scope) 68 { 69 const fb_ref_t *name = scope->name; 70 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 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 while (name && keyname) { 83 n1 = (size_t)name->ident->len; 84 n2 = (size_t)keyname->ident->len; 85 if (n1 != n2 || strncmp(name->ident->text, keyname->ident->text, n1)) { 86 return 0; 87 } 88 name = name->link; 89 keyname = keyname->link; 90 if (++count == 0) { 91 return name == 0; 92 } 93 } 94 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 while (name) { 102 s1 = name->ident->text; 103 n1 = (size_t)name->ident->len; 104 if (n1 > len) { 105 return 0; 106 } 107 for (i = 0; i < n1; ++i) { 108 if (s1[i] != s2[i]) { 109 return 0; 110 } 111 } 112 if (n1 == len) { 113 return name->link == 0; 114 } 115 if (s2[i] != '.') { 116 return 0; 117 } 118 len -= n1 + 1; 119 s2 += n1 + 1; 120 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 static inline size_t scope_hash(const void *key, size_t len) 143 { 144 size_t h = 0, i; 145 int count = (int)len; 146 147 if (count <= 0) { 148 const fb_ref_t *name = key; 149 150 while (name) { 151 h ^= ht_strn_hash_function(name->ident->text, (size_t)name->ident->len); 152 h = ht_int_hash_function((void *)h, 0); 153 name = name->link; 154 if (++count == 0) { 155 break; 156 } 157 } 158 return h; 159 } else { 160 const char *s = key; 161 for (;;) { 162 for (i = 0; i < len; ++i) { 163 if (s[i] == '.') { 164 break; 165 } 166 } 167 h ^= ht_strn_hash_function(s, i); 168 h = ht_int_hash_function((void *)h, 0); 169 if (i == len) { 170 break; 171 } 172 len -= i + 1; 173 s += i + 1; 174 } 175 return h; 176 } 177 }