polar-objc 5.44
An Objective-C runtime library
Loading...
Searching...
No Matches
polar-hash-table.c
1/* Internal API: hash table type for polar-objc.
2 Copyright (C) 2022-2025 Michael Malicoat <[email protected]>
3
4 This file is part of polar-objc.
5
6 polar-objc is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
8
9 polar-objc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11 details.
12
13 You should have received a copy of the GNU General Public License along with this program; see the file LICENSE. If
14 not, see <http://www.gnu.org/licenses/>.
15*/
16
17// polar_hash_table ****************************************************************************************************
18// Private =============================================================================================================
19
20// This variable is initialized in polar_hash_table_get_type()
21static polar_hash_table_class class_polar_hash_table;
22
23// This variable is initialized in _polar_hash_table_class_init()
24static polar_runtime_object_class *superclass_polar_hash_table = NULL;
25
26// ---------------------------------------------------------------------------------------------------------------------
27
28static inline void
29polar_hash_table_insert_node( polar_hash_table *self, polar_hash_table_node *node_new )
30{
31 assert( POLAR_IS_HASH_TABLE(self) );
32
33 POLAR_HASH_TABLE_GET_CLASS(self)->insert_node(self, node_new);
34}
35
36static inline BOOL
37polar_hash_table_must_grow( polar_hash_table *self )
38{
39 assert( POLAR_IS_HASH_TABLE(self) );
40
41 // Return YES if the table is filled to ~75% capacity or more; NO otherwise
42 return ( (self->count_entries + ((self->mask_buckets + 1) >> 2)) >= (self->mask_buckets + 1) );
43}
44
45static inline void
46polar_hash_table_maybe_free_buckets( polar_hash_table *self )
47{
48 assert( POLAR_IS_HASH_TABLE(self) );
49
50 if( self->count_entries > 0 )
51 ;
52
53 else
54 {
55 polar_runtime_object_free( (polar_runtime_object *)(self->array_buckets) );
56 self->array_buckets = NULL;
57 }
58}
59
60static inline polar_hash_table_node *
61polar_hash_table_get_bucket( polar_hash_table *self, intptr_t idx_bucket )
62{
63 assert( POLAR_IS_HASH_TABLE(self) );
64 assert( idx_bucket >= 0 );
65
66 if( (self->array_buckets != NULL) && (idx_bucket <= self->mask_buckets) )
67 return (polar_hash_table_node *)polar_paged_array_get_element(self->array_buckets, idx_bucket);
68
69 return NULL;
70}
71
72static inline polar_hash_table_node *
73polar_hash_table_swap_bucket( polar_hash_table *self, intptr_t idx_bucket, polar_hash_table_node *node_new )
74{
75 assert( idx_bucket >= 0 );
76
77 if( self->array_buckets != NULL )
78 return (polar_hash_table_node *)polar_paged_array_swap_element(self->array_buckets, idx_bucket, node_new);
79
80 else if ( (idx_bucket <= self->mask_buckets) && (node_new != NULL) )
81 {
82 self->array_buckets = polar_paged_array_new(NULL);
83 polar_paged_array_set_element(self->array_buckets, idx_bucket, node_new);
84 }
85
86 return NULL;
87}
88
89POLAR_FUNCTION_INTERNAL void
90polar_hash_table_set_bucket( polar_hash_table *self, intptr_t idx_bucket, polar_hash_table_node *node_new )
91{
92 polar_paged_array *array_buckets_new;
93
94 assert( POLAR_IS_HASH_TABLE(self) );
95 assert( idx_bucket >= 0 );
96
97 if( idx_bucket <= self->mask_buckets )
98 ;
99
100 else
101 return;
102
103 if( self->array_buckets != NULL )
104 ;
105
106 else if( POLAR_IS_HASH_TABLE_NODE(node_new) )
107 self->array_buckets = polar_paged_array_new(NULL);
108
109 else
110 return;
111
112 ( (polar_list_node *)node_new )->node_next = (polar_list_node *)
113 polar_paged_array_swap_element(self->array_buckets, idx_bucket, node_new);
114}
115
116POLAR_FUNCTION_INTERNAL void
117polar_hash_table_empty_bucket( polar_hash_table *self, intptr_t idx_bucket )
118{
119 polar_list_node *node_current;
120
121 assert( POLAR_IS_HASH_TABLE(self) );
122 assert( idx_bucket >= 0 );
123 assert( idx_bucket <= self->mask_buckets );
124
125 if( self->array_buckets != NULL )
126 {
127 node_current = (polar_list_node *)polar_paged_array_swap_element(self->array_buckets, idx_bucket, NULL);
128 self->count_entries -= polar_list_node_list_free( &node_current );
129 }
130}
131
132POLAR_FUNCTION_INTERNAL void
133polar_hash_table_grow( polar_hash_table *self )
134{
135 intptr_t count_buckets_new, mask_buckets_new;
136 polar_paged_array *array_buckets_new;
137 intptr_t i;
138 polar_hash_table_node *node_current, *node_next;
139 intptr_t idx_new;
140
141 assert( POLAR_IS_HASH_TABLE(self) );
142
143 if( self->array_buckets != NULL )
144 ;
145
146 else
147 return;
148
149 count_buckets_new = ( (self->mask_buckets + 1) << 1 );
150 mask_buckets_new = ( count_buckets_new - 1 );
151
152 array_buckets_new = polar_paged_array_new(NULL);
153
154 for( i = 0; i <= self->mask_buckets; i++ )
155 {
156 node_current = (polar_hash_table_node *)polar_paged_array_get_element(self->array_buckets, i);
157
158 while( node_current != NULL )
159 {
160 node_next = (polar_hash_table_node *)( POLAR_LIST_NODE(node_current)->node_next );
161
162 idx_new = ( polar_hash_table_node_get_hash_value(node_current) & mask_buckets_new );
163
164 ( (polar_list_node *)node_current )->node_next = (polar_list_node *)
165 polar_paged_array_swap_element(array_buckets_new, idx_new, node_current);
166
167 node_current = node_next;
168 }
169 }
170
171 polar_runtime_object_free( (polar_runtime_object *)(self->array_buckets) );
172
173 self->array_buckets = array_buckets_new;
174 self->mask_buckets = mask_buckets_new;
175}
176
177// ---------------------------------------------------------------------------------------------------------------------
178
179POLAR_FUNCTION_INTERNAL polar_hash_table *
180_polar_hash_table_init( polar_hash_table *self )
181{
182 // Chain up first
183 self = (polar_hash_table *)
184 POLAR_RUNTIME_OBJECT_CLASS(superclass_polar_hash_table)->init( (polar_runtime_object *)self );
185
186 if( self != NULL )
187 {
188 self->id_node_type = POLAR_TYPE_HASH_TABLE_NODE;
189 }
190
191 return self;
192}
193
194POLAR_FUNCTION_INTERNAL polar_runtime_object *
195_polar_hash_table_finalize( polar_hash_table *self )
196{
197 polar_hash_table_clear(self);
198
199 // Chain up
200 return POLAR_RUNTIME_OBJECT_CLASS(superclass_polar_hash_table)->finalize( (polar_runtime_object *)self );
201}
202
203POLAR_FUNCTION_INTERNAL void
204_polar_hash_table_insert_node( polar_hash_table *self, polar_hash_table_node *node_new )
205{
206 intptr_t idx_bucket;
207
208 assert( POLAR_IS_HASH_TABLE_NODE(node_new) );
209 assert( polar_hash_table_has_node(self, polar_hash_table_node_get_key(node_new)) == NO );
210
211 if( polar_hash_table_must_grow(self) == NO )
212 ;
213
214 else
215 polar_hash_table_grow(self);
216
217 idx_bucket = ( polar_hash_table_node_get_hash_value(node_new) & self->mask_buckets );
218 polar_hash_table_set_bucket(self, idx_bucket, node_new);
219
220 self->count_entries++;
221}
222
223POLAR_FUNCTION_INTERNAL void
224_polar_hash_table_class_init( polar_hash_table_class *self )
225{
226 // Acquire our superclass
227 superclass_polar_hash_table = POLAR_RUNTIME_OBJECT_CLASS(self)->class_superclass;
228
229 // Establish our virtual methods
230 ( (polar_runtime_object_class *)self )->init = (polar_func_runtime_object_lifecycle)_polar_hash_table_init;
231 ( (polar_runtime_object_class *)self )->finalize = (polar_func_runtime_object_lifecycle)_polar_hash_table_finalize;
232
233 self->insert_node = _polar_hash_table_insert_node;
234}
235
236static struct polar_internal_type_info pti_polar_hash_table =
237{
238 .name_type = "polar_hash_table",
239 .size_type_instance = sizeof(polar_hash_table),
240 .size_type_class = sizeof(polar_hash_table_class),
241 .type_class_init = (polar_func_runtime_object_class_lifecycle)_polar_hash_table_class_init
242};
243
244// Public ==============================================================================================================
245
246POLAR_FUNCTION_INTERNAL OBJC_FUNCTION_CONSTANT polar_internal_type
247polar_hash_table_get_type( void )
248{
249 static polar_internal_type pt_polar_hash_table = POLAR_INTERNAL_TYPE_INVALID;
250
251 if( pt_polar_hash_table != POLAR_INTERNAL_TYPE_INVALID )
252 return pt_polar_hash_table;
253
254 pt_polar_hash_table = polar_internal_type_init( POLAR_TYPE_RUNTIME_OBJECT, &class_polar_hash_table,
255 &pti_polar_hash_table );
256
257 return pt_polar_hash_table;
258}
259
260POLAR_FUNCTION_INTERNAL OBJC_RETURNS_VALID_POINTER polar_hash_table *
261polar_hash_table_new( intptr_t count_buckets_initial, polar_internal_type id_node_type )
262{
263 polar_hash_table *result;
264
265 assert( count_buckets_initial >= 2 );
266 assert( (count_buckets_initial & (count_buckets_initial - 1)) == 0 );
267 assert( polar_internal_type_is_a(id_node_type, POLAR_TYPE_HASH_TABLE_NODE) );
268
269 result = (polar_hash_table *)polar_runtime_object_new(POLAR_TYPE_HASH_TABLE);
270 result->mask_buckets = ( count_buckets_initial - 1 );
271 result->id_node_type = id_node_type;
272
273 return result;
274}
275
276POLAR_FUNCTION_INTERNAL OBJC_FUNCTION_HOTSPOT polar_hash_table_node *
277polar_hash_table_get_node( polar_hash_table *self, void *key_desired )
278{
279 polar_hash_table_node *result;
280 polar_hash_table_node *node_with_key_desired;
281 intptr_t idx_bucket;
282 char node_memory[512]; // Cheating here and not using alloca() or the heap
283
284 assert( POLAR_IS_HASH_TABLE(self) );
285
286 node_with_key_desired = (polar_hash_table_node *)
287 polar_runtime_object_preallocated_init( &node_memory[0], self->id_node_type, YES );
288
289 polar_hash_table_node_set_key(node_with_key_desired, key_desired);
290
291 idx_bucket = ( polar_hash_table_node_get_hash_value(node_with_key_desired) & self->mask_buckets );
292
293 result = polar_hash_table_get_bucket(self, idx_bucket);
294 while( result != NULL )
295 {
296 if( polar_hash_table_node_is_equal_to(result, node_with_key_desired) == NO )
297 result = (polar_hash_table_node *)( ((polar_list_node *)(result))->node_next );
298
299 else
300 break;
301 }
302
303 polar_runtime_object_preallocated_finalize(node_with_key_desired);
304
305 return result;
306}
307
308POLAR_FUNCTION_INTERNAL polar_hash_table_node *
309polar_hash_table_add_node( polar_hash_table *self, void *key_desired )
310{
311 polar_hash_table_node *result;
312
313 result = (polar_hash_table_node *)polar_runtime_object_new(self->id_node_type);
314 polar_hash_table_node_set_key(result, key_desired);
315
316 polar_hash_table_insert_node(self, result);
317 return result;
318}
319
320POLAR_FUNCTION_INTERNAL polar_hash_table_node *
321polar_hash_table_remove_node( polar_hash_table *self, void *key_desired )
322{
323 polar_hash_table_node *result, *node_with_key_desired, *node_previous;
324 intptr_t idx_bucket;
325
326 assert( POLAR_IS_HASH_TABLE(self) );
327
328 node_with_key_desired = (polar_hash_table_node *)alloca( polar_internal_type_get_instance_size(self->id_node_type) );
329 polar_runtime_object_preallocated_init(node_with_key_desired, self->id_node_type, YES);
330 polar_hash_table_node_set_key(node_with_key_desired, key_desired);
331
332 node_previous = NULL;
333 idx_bucket = ( polar_hash_table_node_get_hash_value(node_with_key_desired) & self->mask_buckets );
334
335 result = polar_hash_table_get_bucket(self, idx_bucket);
336 while( result != NULL )
337 {
338 if( polar_hash_table_node_is_equal_to(result, node_with_key_desired) == NO )
339 {
340 node_previous = result;
341 result = (polar_hash_table_node *)( POLAR_LIST_NODE(result)->node_next );
342 }
343
344 else
345 {
346 if( node_previous != NULL )
347 POLAR_LIST_NODE(node_previous)->node_next = POLAR_LIST_NODE(result)->node_next;
348
349 else
350 polar_paged_array_set_element(self->array_buckets, idx_bucket, ((polar_list_node *)result)->node_next);
351
352 POLAR_LIST_NODE(result)->node_next = NULL;
353 self->count_entries--;
354
355 polar_hash_table_maybe_free_buckets(self);
356 break;
357 }
358 }
359
360 polar_runtime_object_preallocated_finalize(node_with_key_desired);
361
362 return result;
363}
364
365POLAR_FUNCTION_INTERNAL void
366polar_hash_table_clear( polar_hash_table *self )
367{
368 intptr_t i;
369
370 assert( POLAR_IS_HASH_TABLE(self) );
371
372 if( self->count_entries > 0 )
373 {
374 for( i = 0; i <= self->mask_buckets; i++ )
375 polar_hash_table_empty_bucket(self, i);
376
377 self->count_entries = 0;
378 }
379
380 polar_hash_table_maybe_free_buckets(self);
381}
Definition polar-hash-table.h:34
Definition polar-hash-table-node.h:25
Definition polar-hash-table.h:25
Definition polar-internal-type.h:34
Definition polar-list-node.h:25
Definition polar-paged-array.h:25
Definition polar-runtime-object.h:36
Definition polar-runtime-object.h:31