2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/types.h>
26 #include "collections_utilities.h"
28 // =========== Required per-type definitions ===========
29 #define opaque_os_map_t IN_MAP(,_t)
30 #define os_map_hash IN_MAP(,_hash)
31 #define os_map_key_equals IN_MAP(,_key_equals)
33 // =========== Helpers, defined only once for all different types ===========
35 #ifndef _COLLETIONS_MAP_HELPERS_C
36 #define _COLLETIONS_MAP_HELPERS_C
39 #define _MAP_MAX_FILL_NUMER 4
40 #define _MAP_MAX_FILL_DENOM 5
41 #define _MAP_GROW_SHIFT_RATE 8
42 #define _MAP_MIN_FILL_DENOM 8
45 static inline uint32_t
46 map_next(uint32_t i
, uint32_t size
)
49 return i
>= size
? 0 : i
;
53 static inline uint32_t
54 map_prev(uint32_t i
, uint32_t size
)
56 return ((i
!= 0) ? i
: size
) - 1;
59 struct _os_map_internal_struct
{
64 uint16_t max_bucket_offset
;
65 // Keys are at data; values are at (data + count * sizeof(keys))
70 // =========== Helpers, defined per map ===========
72 #define _os_map_t IN_MAP(_,_t)
73 typedef struct _os_map_internal_struct _os_map_t
;
75 #define _os_map_data_segregated IN_MAP(_,_data_segregated)
76 struct _os_map_data_segregated
{
81 #define _os_map_data_ref_t IN_MAP(_,_data_ref_t)
82 typedef struct _os_map_data_segregated _os_map_data_ref_t
;
84 #define _alloc_data IN_MAP(_,_alloc_data)
87 _alloc_data(uint32_t size
)
89 assert(size
< UINT32_MAX
);
90 void *result
= calloc(size
, sizeof(os_map_key_t
) + sizeof(void *));
91 assert(result
!= NULL
);
96 #define _get_data_ref IN_MAP(_,_get_data_ref)
99 _get_data_ref(_os_map_t
*m
, _os_map_data_ref_t
*data
)
101 data
->keys
= (os_map_key_t
*)(m
->data
);
102 data
->vals
= (void *)(((char *)(m
->data
)) +
103 (m
->size
* sizeof(os_map_key_t
)));
107 #define _free_data IN_MAP(_,_free_data)
110 _free_data(_os_map_data_ref_t
*data
)
112 free((void *)data
->keys
);
115 #define _get_key(data, i) data.keys[i]
117 #define _get_val(data, i) data.vals[i]
119 #define _set_key(data, i, key) data.keys[i] = key
121 #define _set_val(data, i, val) data.vals[i] = val
124 #define _os_map_bucket IN_MAP(_,_probe_bucket)
126 static inline uint32_t
127 _os_map_bucket(_os_map_t
*m
, os_map_key_t key
)
129 return os_map_hash(key
) % m
->size
;
132 #define _os_map_bucket_offset IN_MAP(_,_bucket_offset)
134 static inline uint32_t
135 _os_map_bucket_offset(_os_map_t
*m
, os_map_key_t key
, uint32_t i
)
137 uint32_t bucket
= _os_map_bucket(m
, key
);
138 return (i
- bucket
) % m
->size
;
143 #define _get_next_offset IN_MAP(_,_verify_next_offset)
146 _get_next_offset(_os_map_t
*m
, _os_map_data_ref_t data
,
147 uint32_t index
, uint32_t size
)
149 uint32_t next_index
= map_next(index
, size
);
151 if (_get_val(data
, next_index
) == NULL
) {
155 return _os_map_bucket_offset(m
, _get_key(data
, next_index
),
159 #define _os_map_verify IN_MAP(_,_verify)
162 _os_map_verify(_os_map_t
*m
)
164 _os_map_data_ref_t data
;
165 _get_data_ref(m
, &data
);
167 int64_t current_bucket_offset
;
168 int64_t next_bucket_offset
;
169 if (_get_val(data
, 0) == NULL
) {
170 next_bucket_offset
= -1;
172 next_bucket_offset
= _os_map_bucket_offset(m
,
173 _get_key(data
, 0), 0);
176 uint32_t size
= m
->size
;
178 for (uint32_t index
= 0; index
< size
; index
++){
179 current_bucket_offset
= next_bucket_offset
;
180 next_bucket_offset
= _get_next_offset(m
, data
, index
, size
);
182 DEBUG_ASSERT(next_bucket_offset
<= current_bucket_offset
+ 1);
188 #define _os_map_verify(A)
192 #define _swap_if_needed IN_MAP(_,_swap_if_needed)
195 _swap_if_needed(_os_map_t
*m
, _os_map_data_ref_t data
, os_map_key_t
*key
,
196 void **val
, uint32_t *bucket_offset
, uint32_t i
)
198 if (*bucket_offset
!= 0) {
199 os_map_key_t loop_key
= _get_key(data
, i
);
201 // Doesn't support inserting twice
202 DEBUG_ASSERT(!os_map_key_equals(loop_key
, *key
));
204 uint32_t loop_bucket_offset
= _os_map_bucket_offset(m
,
206 if (*bucket_offset
> loop_bucket_offset
) {
207 if (*bucket_offset
> m
->max_bucket_offset
) {
208 assert(*bucket_offset
<= UINT16_MAX
);
209 DEBUG_ASSERT(*bucket_offset
==
210 m
->max_bucket_offset
+ 1);
211 m
->max_bucket_offset
= *bucket_offset
;
213 void *new_val
= _get_val(data
, i
);
214 _set_key(data
, i
, *key
);
215 _set_val(data
, i
, *val
);
218 *bucket_offset
= loop_bucket_offset
;
223 #define _os_map_insert_no_rehash IN_MAP(_,_insert_no_rehash)
226 _os_map_insert_no_rehash(_os_map_t
*m
, os_map_key_t key
, void *val
)
228 uint32_t size
= m
->size
, loop_limit
= m
->size
;
229 uint32_t i
= os_map_hash(key
) % size
;
230 _os_map_data_ref_t data
;
231 _get_data_ref(m
, &data
);
233 uint32_t bucket_offset
= 0;
235 DEBUG_ASSERT(bucket_offset
==
236 _os_map_bucket_offset(m
, key
, i
));
237 assert(loop_limit
-- != 0);
238 void *loop_val
= _get_val(data
, i
);
239 if (loop_val
== NULL
) {
243 _swap_if_needed(m
, data
, &key
, &val
, &bucket_offset
, i
);
246 i
= map_next(i
, size
);
249 DEBUG_ASSERT(_get_val(data
, i
) == NULL
);
251 if (bucket_offset
> m
->max_bucket_offset
) {
252 assert(bucket_offset
<= UINT16_MAX
);
253 DEBUG_ASSERT(bucket_offset
== m
->max_bucket_offset
+ 1);
254 m
->max_bucket_offset
= bucket_offset
;
256 _set_key(data
, i
, key
);
257 _set_val(data
, i
, val
);
261 #define _os_map_rehash IN_MAP(_,_rehash)
264 _os_map_rehash(_os_map_t
*m
, int direction
)
268 uint32_t old_size
= m
->size
;
270 _os_map_data_ref_t old_data
;
271 _get_data_ref(m
, &old_data
);
273 // Grow shift is used instead of simply doubling the size each time in
274 // order to increase the expected utilization, thus decreasing overall
275 // memory usage, at the cost of more frequent rehashing.
278 m
->size
+= (1 << m
->grow_shift
);
280 ((uint32_t)_MAP_GROW_SHIFT_RATE
<< m
->grow_shift
)) {
283 } else if (direction
< 0) {
284 if (m
->grow_shift
> MAP_MINSHIFT
) {
287 m
->size
= roundup(m
->size
/ 2, (1 << m
->grow_shift
));
291 m
->max_bucket_offset
= 0;
292 m
->data
= _alloc_data(m
->size
);
295 for (uint32_t i
= 0; i
< old_size
; i
++) {
296 if (_get_val(old_data
, i
) == NULL
) {
300 _os_map_insert_no_rehash(m
, _get_key(old_data
, i
),
301 _get_val(old_data
, i
));
303 _free_data(&old_data
);
309 #define _os_map_find_helper_empty_key IN_MAP(_,_find_helper_empty_key)
311 #define _os_map_find_helper IN_MAP(_,_find_helper)
314 _os_map_find_helper(_os_map_t
*m
, os_map_key_t key
, uint32_t *i
)
321 uint32_t size
= m
->size
, loop_limit
= m
->size
;
322 _os_map_data_ref_t data
;
323 _get_data_ref(m
, &data
);
325 *i
= _os_map_bucket(m
, key
);
327 uint32_t bucket_offset
= 0;
330 assert(loop_limit
-- != 0);
332 if (bucket_offset
> m
->max_bucket_offset
||
333 _get_val(data
, *i
) == NULL
) {
337 os_map_key_t loop_key
= _get_key(data
, *i
);
339 if (os_map_key_equals(key
, loop_key
)) {
340 return _get_val(data
, *i
);
342 *i
= map_next(*i
, size
);
347 #define _os_map_remove_entry IN_MAP(_,_remove_entry)
350 _os_map_remove_entry(_os_map_t
*m
, uint32_t current_index
)
352 _os_map_data_ref_t data
;
353 _get_data_ref(m
, &data
);
355 uint32_t size
= m
->size
;
357 uint32_t next_index
= map_next(current_index
, size
);
358 void *next_val
= _get_val(data
, next_index
);
359 os_map_key_t next_key
= _get_key(data
, next_index
);
360 while(next_val
!= NULL
&&
361 _os_map_bucket(m
, next_key
) != next_index
) {
362 _set_key(data
, current_index
, next_key
);
363 _set_val(data
, current_index
, next_val
);
365 DEBUG_ASSERT(_os_map_bucket_offset(m
,
366 next_key
, current_index
) <
367 _os_map_bucket_offset(m
, next_key
,
370 current_index
= next_index
;
371 next_index
= map_next(current_index
, size
);
372 next_val
= _get_val(data
, next_index
);
373 next_key
= _get_key(data
, next_index
);
376 _set_val(data
, current_index
, NULL
);
379 if ((m
->size
>= MAP_MINSIZE
* 2) &&
380 (m
->count
< m
->size
/ _MAP_MIN_FILL_DENOM
)) {
381 // if the map density drops below 12%, shrink it
382 _os_map_rehash(m
, -1);
386 // =========== Implementation ===========
389 #define os_map_init IN_MAP(,_init)
392 os_map_init(opaque_os_map_t
*m_raw
, os_map_config_t
*config
,
393 __unused
int struct_version
)
395 static_assert(sizeof(opaque_os_map_t
) == sizeof(_os_map_t
),
396 "Opaque string map incorrect size");
397 _os_map_t
*m
= (_os_map_t
*)m_raw
;
400 m
->size
= MAX(config
->initial_size
, MAP_MINSIZE
);
402 m
->size
= MAP_MINSIZE
;
406 m
->max_bucket_offset
= 0;
407 m
->data
= _alloc_data(m
->size
);
408 assert(m
->data
!= NULL
);
409 m
->grow_shift
= MAP_MINSHIFT
;
410 DEBUG_ASSERT_MAP_INVARIANTS(m
);
414 #define os_map_destroy IN_MAP(,_destroy)
417 os_map_destroy(opaque_os_map_t
*m_raw
)
419 _os_map_t
*m
= (_os_map_t
*)m_raw
;
425 #define os_map_insert IN_MAP(,_insert)
428 os_map_insert(opaque_os_map_t
*m_raw
, os_map_key_t key
, void *val
)
430 _os_map_t
*m
= (_os_map_str_t
*)m_raw
;
434 if (m
->count
>= _MAP_MAX_FILL_NUMER
* m
->size
/
435 _MAP_MAX_FILL_DENOM
) {
436 _os_map_rehash(m
, 1);
439 _os_map_insert_no_rehash(m
, key
, val
);
441 DEBUG_ASSERT_MAP_INVARIANTS(m
);
445 #define os_map_find IN_MAP(,_find)
448 os_map_find(opaque_os_map_t
*m_raw
, os_map_key_t key
)
450 _os_map_t
*m
= (_os_map_t
*)m_raw
;
452 return _os_map_find_helper(m
, key
, &i
);
456 #define os_map_delete IN_MAP(,_delete)
459 os_map_delete(opaque_os_map_t
*m_raw
, os_map_key_t key
)
461 _os_map_t
*m
= (_os_map_t
*)m_raw
;
464 void *val
= _os_map_find_helper(m
, key
, &i
);
469 _os_map_remove_entry(m
, i
);
474 #define os_map_payload_handler_t IN_MAP(,_payload_handler_t)
475 typedef bool (^os_map_payload_handler_t
) (os_map_key_t
, void *);
477 #define os_map_clear IN_MAP(,_clear)
480 os_map_clear(opaque_os_map_t
*m_raw
,
481 OS_NOESCAPE os_map_payload_handler_t handler
)
483 _os_map_t
*m
= (_os_map_t
*)m_raw
;
485 _os_map_data_ref_t oldData
;
486 _get_data_ref(m
, &oldData
);
487 uint32_t oldSize
= m
->size
;
490 m
->max_bucket_offset
= 0;
491 m
->size
= MAP_MINSIZE
;
492 m
->data
= _alloc_data(m
->size
);
493 m
->grow_shift
= MAP_MINSHIFT
;
494 DEBUG_ASSERT_MAP_INVARIANTS(m
);
496 if (handler
!= NULL
) {
497 for (uint32_t i
= 0; i
< oldSize
; i
++) {
498 void *val
= _get_val(oldData
, i
);
501 handler(_get_key(oldData
, i
), val
);
506 _free_data(&oldData
);
510 #define os_map_count IN_MAP(,_count)
513 os_map_count(opaque_os_map_t
*m_raw
)
515 _os_map_t
*m
= (_os_map_t
*)m_raw
;
520 #define os_map_foreach IN_MAP(,_foreach)
523 os_map_foreach(opaque_os_map_t
*m_raw
,
524 OS_NOESCAPE os_map_payload_handler_t handler
)
526 _os_map_t
*m
= (_os_map_t
*)m_raw
;
527 _os_map_data_ref_t data
;
528 _get_data_ref(m
, &data
);
530 for (uint32_t i
= 0; i
< m
->size
; i
++) {
531 void *val
= _get_val(data
, i
);
534 if (!handler(_get_key(data
, i
), val
)) break;
539 #ifdef MAP_SUPPORTS_ENTRY
541 #define os_map_entry IN_MAP(,_entry)
544 os_map_entry(opaque_os_map_t
*m_raw
, os_map_key_t key
)
546 _os_map_t
*m
= (_os_map_t
*)m_raw
;
547 _os_map_data_ref_t data
;
548 _get_data_ref(m
, &data
);
551 if (_os_map_find_helper(m
, key
, &i
) == NULL
) {
552 return (os_map_key_t
)NULL
;
554 return _get_key(data
, i
);