1 #include <ft2build.h> 2 #include FT_TYPES_H 3 #include FT_INTERNAL_HASH_H 4 #include FT_INTERNAL_MEMORY_H 5 #include FT_INTERNAL_DEBUG_H 6 7 #define FT_HASH_MAX_LOAD 2 8 #define FT_HASH_MIN_LOAD 1 9 #define FT_HASH_SUB_LOAD (FT_HASH_MAX_LOAD-FT_HASH_MIN_LOAD) 10 11 /* this one _must_ be a power of 2 !! */ 12 #define FT_HASH_INITIAL_SIZE 8 13 14 15 FT_BASE_DEF( void ) 16 ft_hash_done( FT_Hash table, 17 FT_Hash_ForeachFunc node_func, 18 const FT_Pointer node_data ) 19 { 20 if ( table ) 21 { 22 FT_Memory memory = table->memory; 23 24 if ( node_func ) 25 ft_hash_foreach( table, node_func, node_data ); 26 27 FT_FREE( table->buckets ); 28 table->p = 0; 29 table->mask = 0; 30 table->slack = 0; 31 32 table->node_equal = NULL; 33 } 34 } 35 36 37 FT_BASE_DEF( FT_UInt ) 38 ft_hash_get_size( FT_Hash table ) 39 { 40 FT_UInt result = 0; 41 42 if ( table ) 43 result = (table->p + table->mask + 1)*FT_HASH_MAX_LOAD - table->slack; 44 45 return result; 46 } 47 48 49 50 FT_BASE_DEF( FT_Error ) 51 ft_hash_init( FT_Hash table, 52 FT_Hash_EqualFunc equal, 53 FT_Memory memory ) 54 { 55 FT_Error error; 56 57 table->memory = memory; 58 table->p = 0; 59 table->mask = FT_HASH_INITIAL_SIZE-1; 60 table->slack = FT_HASH_INITIAL_SIZE*FT_HASH_MAX_LOAD; 61 table->node_equal = equal; 62 63 (void)FT_NEW_ARRAY( table->buckets, FT_HASH_INITIAL_SIZE*2 ); 64 65 return error; 66 } 67 68 69 70 FT_BASE_DEF( void ) 71 ft_hash_foreach( FT_Hash table, 72 FT_Hash_ForeachFunc foreach_func, 73 const FT_Pointer foreach_data ) 74 { 75 FT_UInt count = table->p + table->mask + 1; 76 FT_HashNode* pnode = table->buckets; 77 FT_HashNode node, next; 78 79 for ( ; count > 0; count--, pnode++ ) 80 { 81 node = *pnode; 82 while ( node ) 83 { 84 next = node->link; 85 foreach_func( node, foreach_data ); 86 node = next; 87 } 88 } 89 } 90 91 92 93 FT_BASE_DEF( FT_HashLookup ) 94 ft_hash_lookup( FT_Hash table, 95 FT_HashNode keynode ) 96 { 97 FT_UInt index; 98 FT_UInt32 hash = keynode->hash; 99 FT_HashNode node, *pnode; 100 101 index = (FT_UInt)(hash & table->mask); 102 if ( index < table->p ) 103 index = (FT_UInt)(hash & (2*table->mask+1)); 104 105 pnode = &table->buckets[index]; 106 for (;;) 107 { 108 node = *pnode; 109 if ( node == NULL ) 110 break; 111 112 if ( node->hash == hash && table->node_equal( node, keynode ) ) 113 break; 114 115 pnode = &node->link; 116 } 117 118 return pnode; 119 } 120 121 122 123 124 FT_BASE_DEF( FT_Error ) 125 ft_hash_add( FT_Hash table, 126 FT_HashLookup lookup, 127 FT_HashNode new_node ) 128 { 129 FT_Error error = 0; 130 131 /* add it to the hash table */ 132 new_node->link = *lookup; 133 *lookup = new_node; 134 135 if ( --table->slack < 0 ) 136 { 137 FT_UInt p = table->p; 138 FT_UInt mask = table->mask; 139 FT_HashNode new_list, node, *pnode; 140 141 /* split a single bucket */ 142 new_list = NULL; 143 pnode = table->buckets + p; 144 for (;;) 145 { 146 node = *pnode; 147 if ( node == NULL ) 148 break; 149 150 if ( node->hash & mask ) 151 { 152 *pnode = node->link; 153 node->link = new_list; 154 new_list = node; 155 } 156 else 157 pnode = &node->link; 158 } 159 160 table->buckets[ p + mask + 1 ] = new_list; 161 162 table->slack += FT_HASH_MAX_LOAD; 163 164 if ( p >= mask ) 165 { 166 FT_Memory memory = table->memory; 167 168 169 if (FT_RENEW_ARRAY( table->buckets, (mask+1)*2, (mask+1)*4 )) 170 goto Exit; 171 172 table->mask = 2*mask + 1; 173 table->p = 0; 174 } 175 else 176 table->p = p + 1; 177 } 178 Exit: 179 return error; 180 } 181 182 183 184 FT_BASE_DEF( FT_Error ) 185 ft_hash_remove( FT_Hash table, 186 FT_HashLookup lookup ) 187 { 188 FT_HashNode node; 189 FT_UInt num_buckets; 190 FT_Error error = 0; 191 192 FT_ASSERT( pnode != NULL && node != NULL ); 193 194 node = *lookup; 195 *lookup = node->link; 196 node->link = NULL; 197 198 num_buckets = ( table->p + table->mask + 1) ; 199 200 if ( ++ table->slack > (FT_Long)num_buckets*FT_HASH_SUB_LOAD ) 201 { 202 FT_UInt p = table->p; 203 FT_UInt mask = table->mask; 204 FT_UInt old_index = p + mask; 205 FT_HashNode* pnode; 206 FT_HashNode* pold; 207 208 if ( old_index < FT_HASH_INITIAL_SIZE ) 209 goto Exit; 210 211 if ( p == 0 ) 212 { 213 FT_Memory memory = table->memory; 214 215 table->mask >>= 1; 216 p = table->mask; 217 218 if ( FT_RENEW_ARRAY( table->buckets, (mask+1)*2, (mask+1) ) ) 219 { 220 /* this should never happen normally, but who knows :-) */ 221 /* we need to re-inject the node in the hash table before */ 222 /* returning there, since it's safer */ 223 pnode = table->buckets; 224 node->link = *pnode; 225 *pnode = node; 226 227 goto Exit; 228 } 229 } 230 else 231 p--; 232 233 pnode = table->buckets + p; 234 while ( *pnode ) 235 pnode = &(*pnode)->link; 236 237 pold = table->buckets + old_index; 238 *pnode = *pold; 239 *pold = NULL; 240 241 table->slack -= FT_HASH_MAX_LOAD; 242 table->p = p; 243 } 244 Exit: 245 return error; 246 } 247