xref: /inferno-os/libfreetype/fthash.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
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