xref: /inferno-os/libfreetype/fthash.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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 )
ft_hash_done(FT_Hash table,FT_Hash_ForeachFunc node_func,const FT_Pointer node_data)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 )
ft_hash_get_size(FT_Hash table)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 )
ft_hash_init(FT_Hash table,FT_Hash_EqualFunc equal,FT_Memory memory)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 )
ft_hash_foreach(FT_Hash table,FT_Hash_ForeachFunc foreach_func,const FT_Pointer foreach_data)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 )
ft_hash_lookup(FT_Hash table,FT_HashNode keynode)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 )
ft_hash_add(FT_Hash table,FT_HashLookup lookup,FT_HashNode new_node)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 )
ft_hash_remove(FT_Hash table,FT_HashLookup lookup)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