· 6 years ago · Sep 09, 2019, 04:52 PM
1#ifndef GS_CONTAINERS_H
2#define GS_CONTAINERS_H
3
4#include "common/gs_types.h"
5#include "common/gs_util.h"
6
7/*===================================
8// Dynamic Array
9===================================*/
10
11/*
12 - HEAVILY lifted from Sean Barret's 'stretchy buffer' implementation
13
14 TODO(john): illustrate basic usage example
15*/
16
17typedef struct
18{
19 s32 size;
20 s32 capacity;
21} gs_dyn_array;
22
23#define gs_dyn_array_head( arr )\
24 ( ( gs_dyn_array* )( ( u8* )arr - sizeof( gs_dyn_array ) ) )
25
26#define gs_dyn_array_size( arr )\
27 gs_dyn_array_head( arr )->size
28
29#define gs_dyn_array_capacity( arr )\
30 gs_dyn_array_head( arr )->capacity
31
32#define gs_dyn_array_full( arr )\
33 ( ( gs_dyn_array_size( arr ) == gs_dyn_array_capacity( arr ) ) )
34
35// Should also be the grow implementation
36_inline void* gs_dyn_array_resize_impl( void* arr, usize sz, usize amount )
37{
38 usize capacity;
39
40 if ( arr ) {
41 capacity = amount;
42 } else {
43 capacity = 0;
44 }
45
46 // Create new gs_dyn_array with just the header information
47 gs_dyn_array* data = ( gs_dyn_array* )gs_realloc( arr ? gs_dyn_array_head( arr ) : 0, capacity * sz + sizeof( gs_dyn_array ) );
48 if ( data )
49 {
50 // Set size
51 if ( !arr )
52 {
53 data->size = 0;
54 }
55
56 data->capacity = capacity;
57
58 // Return actual data position in buffer
59 return ( ( s32* )data + 2 );
60 }
61
62 return NULL;
63}
64
65#define gs_dyn_array_grow( arr )\
66 gs_dyn_array_resize_impl( arr, sizeof( *( arr ) ), gs_dyn_array_capacity( arr ) ? gs_dyn_array_capacity( arr ) * 2 : 1 )
67
68#define gs_dyn_array_push( arr, val )\
69 do {\
70 if ( !( arr ) || ( ( arr ) && gs_dyn_array_full( arr ) ) ) {\
71 *( ( void ** )&( arr ) ) = gs_dyn_array_grow( arr ); \
72 }\
73 ( arr )[ gs_dyn_array_size( arr ) ] = ( val );\
74 gs_dyn_array_size( arr ) += 1;\
75 } while( 0 )
76
77#define gs_dyn_array_reserve( arr, amount )\
78 do {\
79 if ( ( !arr ) || amount > gs_dyn_array_capacity( arr ) ) {\
80 *( ( void ** )&( arr ) ) = gs_dyn_array_resize_impl( arr, sizeof( *arr ), amount );\
81 }\
82 } while( 0 )
83
84#define gs_dyn_array_empty( arr )\
85 ( arr && ( gs_dyn_array_size( arr ) == 0 ) )
86
87#define gs_dyn_array_pop( arr )\
88 do {\
89 if ( arr && !gs_dyn_array_empty( arr ) ) {\
90 gs_dyn_array_size( arr ) -= 1;\
91 }\
92 } while ( 0 )
93
94#define gs_dyn_array_swap( type, arr, idx_a, idx_b )\
95 do {\
96 type tmp = arr[ idx_a ];\
97 arr[ idx_a ] = arr[ idx_b ];\
98 arr[ idx_b ] = tmp;\
99 } while ( 0 )
100
101#define gs_dyn_array_swap_back( type, arr, idx_a )\
102 do {\
103 type tmp = arr[ idx_a ];\
104 arr[ idx_a ] = arr[ gs_dyn_array_size( arr ) - 1 ];\
105 arr[ gs_dyn_array_size( arr ) - 1 ] = tmp;\
106 } while ( 0 )
107
108#define gs_dyn_array_back( arr )\
109 ( arr + ( gs_dyn_array_size( arr ) ? gs_dyn_array_size( arr ) - 1 : 0 ) )
110
111#define gs_dyn_array_for( arr, type, iter_name )\
112 for ( type* iter_name = arr; iter_name != gs_dyn_array_back( arr ); ++iter_name )
113
114#define gs_dyn_array_new( type )\
115 ( type* )gs_dyn_array_resize_impl( NULL, sizeof( type ), 0 )
116
117#define gs_dyn_array_clear( arr )\
118 gs_dyn_array_size( arr ) = 0
119
120#define gs_dyn_array( type ) type*
121
122#define gs_dyn_array_free( arr )\
123 do {\
124 if ( arr ) {\
125 gs_free( gs_dyn_array_head( arr ) );\
126 arr = 0;\
127 }\
128 } while ( 0 )
129
130/*===================================
131// Hash Table
132===================================*/
133
134// TODO(john): Need to improve the hash table a lot ( round robin should help linear probing )
135
136/*
137 NOTE(john): Hash Function References: http://www.cse.yorku.ca/~oz/hash.html
138
139 Want to have syntax like this:
140 gs_hash_table table = gs_hash_table( u32, object* );
141 gs_hash_table_get( &table, key );
142 gs_hash_table_put( &table, key, val );
143 gs_hash_table_exists( &table, key );
144 gs_hash_table_size( &table );
145 gs_hash_table_clear( &table );
146
147 typedef struct
148 {
149 size_t key_type_size;
150 size_t val_type_size; // Not sure if this is necessary, but it might be
151 void* data_array;
152 hash_func_ptr;
153 hash_eq_func_ptr;
154 } gs_hash_table;
155
156// Will NOT work for const char*, however ( must define new type, I imagine )
157#define hash_table( key_type, val_type )\
158 { .key_type_size = sizeof( key_type ), .val_type_size = sizeof( val_type ) };
159
160 // Is this possible? ( I think so, if sizes are used )
161*/
162
163#define gs_hash_key_comp_str( a, b ) ( gs_string_compare_equal( a, b ) )
164
165#define gs_hash_key_comp_std_type( a, b ) ( a == b )
166
167#define gs_hash_table_valid_idx( idx )\
168 ( idx < ( UINT_MAX - 1 ) )
169
170typedef enum
171{
172 hash_table_entry_inactive = 0,
173 hash_table_entry_active = 1
174} gs_hash_table_entry_state;
175
176#define gs_hash_table_decl( key_type, val_type, _hash_func, _hash_comp_key_func, _invalid_key_func, _invalid_val_func )\
177 typedef struct\
178 {\
179 key_type key;\
180 val_type val;\
181 gs_hash_table_entry_state entry_state;\
182 } gs_ht_entry_##key_type##_##val_type;\
183\
184 typedef struct\
185 {\
186 gs_ht_entry_##key_type##_##val_type *data;\
187 u32 ( * hash_func )( key_type );\
188 u32 ( * hash_key_idx_func )( void*, key_type );\
189 val_type ( * hash_get_val_func )( void*, key_type );\
190 val_type* ( * hash_get_val_ptr_func )( void*, key_type );\
191 void ( * hash_remove_func )( void*, key_type );\
192 b8 ( * hash_comp_key_func )( key_type, key_type );\
193 void ( * hash_grow_func )( void*, u32 );\
194 } gs_ht_##key_type##_##val_type;\
195\
196 _inline u32 gs_ht_##key_type##_##val_type##_key_idx_func( void* tbl_ptr, key_type key )\
197 {\
198 gs_ht_##key_type##_##val_type* tbl = ( gs_ht_##key_type##_##val_type* )tbl_ptr;\
199 u32 size = gs_hash_table_size( ( *tbl ) );\
200 u32 capacity = gs_hash_table_capacity( ( *tbl ) );\
201 if ( !capacity ) return (UINT_MAX - 1);\
202 u32 idx = tbl->hash_func( key ) % capacity;\
203 for ( u32 i = idx, c = 0; c < capacity; ++c, i = ( ( i + 1 ) % capacity ) )\
204 {\
205 if ( _hash_comp_key_func( tbl->data[ i ].key, key ) && tbl->data[ i ].entry_state == hash_table_entry_active )\
206 {\
207 return i;\
208 }\
209 }\
210 return UINT_MAX - 1;\
211 }\
212 _inline void gs_ht_##key_type##_##val_type##_remove_func( void* tbl_ptr, key_type key )\
213 {\
214 gs_ht_##key_type##_##val_type* tbl = ( gs_ht_##key_type##_##val_type* )tbl_ptr;\
215 if ( gs_dyn_array_empty( tbl->data ) ) \
216 {\
217 return;\
218 }\
219 u32 size = gs_hash_table_size( ( *tbl ) );\
220 u32 capacity = gs_hash_table_capacity( ( *tbl ) );\
221 if ( !size || !capacity ) return;\
222 u32 idx = tbl->hash_func( key ) % capacity;\
223 gs_ht_entry_##key_type##_##val_type* data = tbl->data;\
224 for ( u32 i = idx, c = 0; c < capacity; ++c, i = ( ( i + 1 ) % capacity ) )\
225 {\
226 if ( _hash_comp_key_func( tbl->data[ i ].key, key ) && tbl->data[ i ].entry_state == hash_table_entry_active )\
227 {\
228 tbl->data[ i ].key = _invalid_key_func;\
229 tbl->data[ i ].entry_state = hash_table_entry_inactive;\
230 break;\
231 }\
232 }\
233 }\
234 _inline val_type gs_ht_##key_type##_##val_type##_get_func( void* tbl_ptr, key_type key )\
235 {\
236 val_type out = _invalid_val_func;\
237 gs_ht_##key_type##_##val_type* tbl = ( gs_ht_##key_type##_##val_type* )tbl_ptr;\
238 if ( gs_dyn_array_empty( tbl->data ) ) \
239 {\
240 return out;\
241 }\
242 u32 capacity = gs_hash_table_capacity( ( *tbl ) );\
243 u32 val = UINT_MAX - 1;\
244 u32 idx = tbl->hash_func( key ) % capacity;\
245 gs_ht_entry_##key_type##_##val_type* data = tbl->data;\
246 for ( u32 i = idx, c = 0; c < capacity; ++c, i = ( ( i + 1 ) % capacity ) )\
247 {\
248 if ( _hash_comp_key_func( data[ i ].key, key ) )\
249 {\
250 val = i;\
251 break;\
252 }\
253 }\
254 if ( gs_hash_table_valid_idx( val ) )\
255 {\
256 out = data[ val ].val;\
257 }\
258 return out;\
259 }\
260 _inline val_type* gs_ht_##key_type##_##val_type##_get_ptr_func( void* tbl_ptr, key_type key )\
261 {\
262 val_type* out = NULL;\
263 gs_ht_##key_type##_##val_type* tbl = ( gs_ht_##key_type##_##val_type* )tbl_ptr;\
264 if ( gs_dyn_array_empty( tbl->data ) ) \
265 {\
266 return out;\
267 }\
268 u32 capacity = gs_hash_table_capacity( ( *tbl ) );\
269 u32 val = UINT_MAX - 1;\
270 u32 idx = tbl->hash_func( key ) % capacity;\
271 gs_ht_entry_##key_type##_##val_type* data = tbl->data;\
272 for ( u32 i = idx, c = 0; c < capacity; ++c, i = ( ( i + 1 ) % capacity ) )\
273 {\
274 if ( _hash_comp_key_func( data[ i ].key, key ) )\
275 {\
276 val = i;\
277 break;\
278 }\
279 }\
280 if ( gs_hash_table_valid_idx( val ) )\
281 {\
282 out = &data[ val ].val;\
283 }\
284 return out;\
285 }\
286 _inline b8 gs_ht_##key_type##_##val_type##_comp_key_func( key_type k0, key_type k1 )\
287 {\
288 if ( _hash_comp_key_func( k0, k1 ) )\
289 {\
290 return true;\
291 }\
292 return false;\
293 }\
294\
295 _inline void gs_ht_##key_type##_##val_type##_grow_func( void* tbl_ptr, u32 new_sz );\
296\
297 _inline gs_ht_##key_type##_##val_type gs_ht_##key_type##_##val_type##_new()\
298 {\
299 gs_ht_##key_type##_##val_type ht;\
300 ht.data = gs_dyn_array_new( gs_ht_entry_##key_type##_##val_type );\
301 ht.hash_func = &_hash_func;\
302 ht.hash_key_idx_func = gs_ht_##key_type##_##val_type##_key_idx_func;\
303 ht.hash_get_val_func = gs_ht_##key_type##_##val_type##_get_func;\
304 ht.hash_get_val_ptr_func = gs_ht_##key_type##_##val_type##_get_ptr_func;\
305 ht.hash_comp_key_func = gs_ht_##key_type##_##val_type##_comp_key_func;\
306 ht.hash_grow_func = gs_ht_##key_type##_##val_type##_grow_func;\
307 ht.hash_remove_func = gs_ht_##key_type##_##val_type##_remove_func;\
308 gs_for_range_i( gs_dyn_array_capacity( ht.data ) ) {\
309 ht.data[ i ].key = _invalid_key_func;\
310 }\
311 return ht;\
312 }\
313\
314 _inline void gs_ht_##key_type##_##val_type##_grow_func( void* tbl_ptr, u32 new_sz )\
315 {\
316 gs_ht_##key_type##_##val_type* tbl = ( gs_ht_##key_type##_##val_type* )tbl_ptr;\
317 gs_ht_##key_type##_##val_type new_tbl = gs_ht_##key_type##_##val_type##_new();\
318 gs_dyn_array_reserve( new_tbl.data, new_sz );\
319 memset( new_tbl.data, 0, new_sz * sizeof( gs_ht_entry_##key_type##_##val_type ) );\
320 for ( u32 i = 0; i < gs_dyn_array_capacity( tbl->data ); ++i ) {\
321 if ( tbl->data[ i ].entry_state == hash_table_entry_active ) {\
322 gs_hash_table_insert( new_tbl, tbl->data[ i ].key, tbl->data[ i ].val );\
323 } else {\
324 new_tbl.data[ i ].key = _invalid_key_func;\
325 }\
326 }\
327 gs_dyn_array_free( tbl->data );\
328 tbl->data = new_tbl.data;\
329 }
330
331#define gs_hash_table_exists( tbl, k )\
332 gs_hash_table_valid_idx( tbl.hash_key_idx_func( &tbl, k ) )
333
334#define gs_hash_table_key_idx( tbl, k )\
335 tbl.hash_key_idx_func( &tbl, k )
336
337#define gs_hash_table_reserve( tbl, sz )\
338 __gs_hash_table_grow( tbl, sz )
339
340#define gs_hash_table_size( tbl )\
341 gs_dyn_array_size( tbl.data )
342
343#define gs_hash_table_empty( tbl )\
344 !gs_dyn_array_size( tbl.data )
345
346#define gs_hash_table_capacity( tbl )\
347 gs_dyn_array_capacity( tbl.data )
348
349#define gs_hash_table_load_factor( tbl )\
350 gs_hash_table_capacity( tbl ) ? (f32)gs_hash_table_size( tbl ) / (f32)gs_hash_table_capacity( tbl ) : 0.0f
351
352// Should this grow to be sizes equal to primes?
353#define __gs_hash_table_grow( tbl, sz )\
354 do {\
355 tbl.hash_grow_func( &tbl, sz );\
356 } while ( 0 )
357
358// Need to check load factor for this
359// If the load factor grows beyond max, then need to grow data array and rehash
360// If shrinks beyond min, then shrink data array and rehash
361// Need to handle collisions between keys ( preferrably robin hood hash with linear prob )
362// Need a way to determine whether or not a key exists
363// TODO(john): Fix bug with load factor ( for some reason, anything higher than 0.5 causes incorrect placement of values in slots )
364// TODO(john): Implement Robin Hood hashing for collision resolution
365#define gs_hash_table_insert( tbl, k, v )\
366 do {\
367 u32 capacity = gs_hash_table_capacity( tbl );\
368 f32 load_factor = gs_hash_table_load_factor( tbl );\
369 if ( load_factor >= 0.5f || !capacity )\
370 {\
371 __gs_hash_table_grow( tbl, capacity ? capacity * 2 : 1 );\
372 capacity = gs_hash_table_capacity( tbl );\
373 }\
374 u32 hash_idx = tbl.hash_func( k ) % gs_hash_table_capacity( tbl );\
375 u32 c = 0;\
376 while ( c < capacity && !tbl.hash_comp_key_func( tbl.data[ hash_idx ].key, k ) && tbl.data[ hash_idx ].entry_state == hash_table_entry_active ) {\
377 hash_idx = ( ( hash_idx + 1 ) % capacity );\
378 c++;\
379 }\
380 tbl.data[ hash_idx ].key = k;\
381 tbl.data[ hash_idx ].val = v;\
382 tbl.data[ hash_idx ].entry_state = hash_table_entry_active;\
383 gs_dyn_array_size( tbl.data )++;\
384 } while ( 0 )
385
386#define gs_hash_table_get( tbl, k )\
387 tbl.hash_get_val_func( ( void* )&( tbl ), ( k ) )
388
389#define gs_hash_table_get_ptr( tbl, k )\
390 tbl.hash_get_val_ptr_func( ( void* )&( tbl ), ( k ) )
391
392#define gs_hash_table( key_type, val_type )\
393 gs_ht_##key_type##_##val_type
394
395#define gs_hash_table_entry( key_type, val_type )\
396 gs_ht_entry_##key_type##_##val_type
397
398#define gs_hash_table_new( key_type, val_type )\
399 gs_ht_##key_type##_##val_type##_new()
400
401#define gs_hash_table_clear( tbl )\
402 gs_dyn_array_clear( tbl.data )
403
404#define gs_hash_table_remove( tbl, k )\
405 tbl.hash_remove_func( (void*)&(tbl), (k) )
406
407// Some typical hash tables
408
409// Hash table := key: u32, val: u32
410gs_hash_table_decl( u32, u32, gs_hash_u32, gs_hash_key_comp_std_type, (u32_max - 1), (u32_max - 1) );
411
412/*===================================
413// Slot Array
414===================================*/
415
416#define gs_slot_array_invalid_handle u32_max
417
418typedef struct
419gs_slot_array_base
420{
421 gs_dyn_array( u32 ) handle_indices;
422 gs_dyn_array( u32 ) reverse_indirection_indices;
423 gs_dyn_array( u32 ) index_free_list;
424} gs_slot_array_base;
425
426#define gs_slot_array_decl( T )\
427 typedef struct gs_sa_##T\
428 {\
429 gs_slot_array_base _base;\
430 gs_dyn_array( T ) data;\
431 u32 ( * insert_func )( struct gs_sa_##T*, T );\
432 void ( * remove_func )( struct gs_sa_##T*, u32 );\
433 } gs_sa_##T;\
434\
435 _force_inline u32\
436 gs_sa_##T##_insert_func( struct gs_sa_##T* s, T v )\
437 {\
438 u32 free_idx = gs_slot_array_find_next_available_index( ( gs_slot_array_base* )s );\
439 gs_dyn_array_push( s->data, v );\
440 gs_dyn_array_push( s->_base.reverse_indirection_indices, free_idx );\
441 s->_base.handle_indices[ free_idx ] = gs_dyn_array_size( s->data ) - 1;\
442 return free_idx;\
443 }\
444\
445 _force_inline void\
446 gs_sa_##T##_remove_func( struct gs_sa_##T* s, u32 handle )\
447 {\
448 if ( gs_slot_array_handle_valid( *(s), handle ) ) {\
449 u32 idx = s->_base.handle_indices[ handle ];\
450 s->_base.handle_indices[ handle ] = gs_slot_array_invalid_handle;\
451 if ( gs_dyn_array_size( s->data ) > 1 ) {\
452 gs_dyn_array_swap( T, s->data, idx, gs_dyn_array_size( s->data ) - 1 );\
453 gs_dyn_array_swap( u32, s->_base.reverse_indirection_indices, idx, gs_dyn_array_size( s->_base.reverse_indirection_indices ) - 1 );\
454 }\
455 if ( !gs_dyn_array_empty( s->data ) ) {\
456 s->_base.handle_indices[ s->_base.reverse_indirection_indices[ idx ] ] = idx;\
457 gs_dyn_array_pop( s->data );\
458 gs_dyn_array_pop( s->_base.reverse_indirection_indices );\
459 }\
460 gs_dyn_array_push( s->_base.index_free_list, handle );\
461 }\
462 }\
463\
464 _force_inline\
465 gs_sa_##T __gs_sa_##T##_new()\
466 {\
467 gs_sa_##T sa;\
468 sa.data = gs_dyn_array_new( T );\
469 sa._base.handle_indices = gs_dyn_array_new( u32 );\
470 sa._base.reverse_indirection_indices = gs_dyn_array_new( u32 );\
471 sa._base.index_free_list = gs_dyn_array_new( u32 );\
472 sa.insert_func = &gs_sa_##T##_insert_func;\
473 sa.remove_func = &gs_sa_##T##_remove_func;\
474 return sa;\
475 }
476
477_force_inline u32
478gs_slot_array_find_next_available_index( gs_slot_array_base* sa )
479{
480 if ( gs_dyn_array_empty( sa->index_free_list ) ) {
481 gs_dyn_array_push( sa->handle_indices, 0 );
482 return ( gs_dyn_array_size( sa->handle_indices ) - 1 );
483 }
484 u32 idx = sa->index_free_list[ 0 ];
485 if ( gs_dyn_array_size( sa->index_free_list ) > 1 ) {
486 u32 sz = gs_dyn_array_size( sa->index_free_list );
487 u32 tmp = sa->index_free_list[ sz - 1 ];
488 sa->index_free_list[ 0 ] = tmp;
489 gs_dyn_array_pop( sa->index_free_list );
490 } else {
491 gs_dyn_array_clear( sa->index_free_list );
492 }
493
494 return idx;
495}
496
497#define gs_slot_array_clear( s )\
498 do {\
499 gs_dyn_array_clear( s._base.handle_indices );\
500 gs_dyn_array_clear( s._base.index_free_list );\
501 gs_dyn_array_clear( s._base.reverse_indirection_indices );\
502 gs_dyn_array_clear( s.data );\
503 } while ( 0 )
504
505#define gs_slot_array_empty( s )\
506 gs_dyn_array_empty( (s) )
507
508#define gs_slot_array_size( s )\
509 gs_dyn_array_size( (s).data )
510
511#define gs_slot_array_handle_index( s, handle )\
512 s._base.handle_indices[ handle ]
513
514#define gs_slot_array_handle_size( s )\
515 gs_dyn_array_size( (s)._base.handle_indices )
516
517#define gs_slot_array_new( type )\
518 __gs_sa_##type##_new()
519
520#define gs_slot_array_insert( s, v )\
521 s.insert_func( &( s ), ( v ) )
522
523#define gs_slot_array_remove( s, handle )\
524 s.remove_func( &( s ), ( handle ) )
525
526#define gs_slot_array_handle_valid( s, handle )\
527 ( handle >= 0 && handle < gs_slot_array_handle_size( (s) ) && gs_slot_array_handle_index( (s), handle ) < gs_slot_array_size( (s) ) )
528
529#define gs_slot_array_get( s, handle )\
530 ( s.data[ s._base.handle_indices[ handle ] ] )
531
532#define gs_slot_array_get_unsafe( s, handle )\
533 ( &s.data[ s._base.handle_indices[ handle ] ] )
534
535#define gs_slot_array_valid( s )\
536 ( (s).data != NULL )
537
538#define gs_slot_array( T )\
539 gs_sa_##T
540
541/*===================================
542// Slot Map
543===================================*/
544
545// Similar to a slot array, but also holds a hash map that allows for lookups based on a unique identifier other than the slot array index id and
546// of desired type provided by user
547
548
549#endif