]> git.saurik.com Git - apple/libc.git/blob - collections/Source/collections_map.in.c
Libc-1439.40.11.tar.gz
[apple/libc.git] / collections / Source / collections_map.in.c
1 /*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <sys/types.h>
25
26 #include "collections_utilities.h"
27
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)
32
33 // =========== Helpers, defined only once for all different types ===========
34
35 #ifndef _COLLETIONS_MAP_HELPERS_C
36 #define _COLLETIONS_MAP_HELPERS_C
37
38
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
43
44 OS_ALWAYS_INLINE
45 static inline uint32_t
46 map_next(uint32_t i, uint32_t size)
47 {
48 i++;
49 return i >= size ? 0 : i;
50 }
51
52 OS_ALWAYS_INLINE
53 static inline uint32_t
54 map_prev(uint32_t i, uint32_t size)
55 {
56 return ((i != 0) ? i : size) - 1;
57 }
58
59 struct _os_map_internal_struct {
60 void *data;
61 uint32_t count;
62 uint32_t size;
63 uint16_t grow_shift;
64 uint16_t max_bucket_offset;
65 // Keys are at data; values are at (data + count * sizeof(keys))
66 };
67
68 #endif
69
70 // =========== Helpers, defined per map ===========
71
72 #define _os_map_t IN_MAP(_,_t)
73 typedef struct _os_map_internal_struct _os_map_t;
74
75 #define _os_map_data_segregated IN_MAP(_,_data_segregated)
76 struct _os_map_data_segregated {
77 os_map_key_t *keys;
78 void **vals;
79 };
80
81 #define _os_map_data_ref_t IN_MAP(_,_data_ref_t)
82 typedef struct _os_map_data_segregated _os_map_data_ref_t;
83
84 #define _alloc_data IN_MAP(_,_alloc_data)
85
86 static inline void *
87 _alloc_data(uint32_t size)
88 {
89 assert(size < UINT32_MAX);
90 void *result = calloc(size, sizeof(os_map_key_t) + sizeof(void *));
91 assert(result != NULL);
92 return result;
93 }
94
95
96 #define _get_data_ref IN_MAP(_,_get_data_ref)
97
98 static inline void
99 _get_data_ref(_os_map_t *m, _os_map_data_ref_t *data)
100 {
101 data->keys = (os_map_key_t *)(m->data);
102 data->vals = (void *)(((char *)(m->data)) +
103 (m->size * sizeof(os_map_key_t)));
104 }
105
106
107 #define _free_data IN_MAP(_,_free_data)
108
109 static inline void
110 _free_data(_os_map_data_ref_t *data)
111 {
112 free((void *)data->keys);
113 }
114
115 #define _get_key(data, i) data.keys[i]
116
117 #define _get_val(data, i) data.vals[i]
118
119 #define _set_key(data, i, key) data.keys[i] = key
120
121 #define _set_val(data, i, val) data.vals[i] = val
122
123
124 #define _os_map_bucket IN_MAP(_,_probe_bucket)
125
126 static inline uint32_t
127 _os_map_bucket(_os_map_t *m, os_map_key_t key)
128 {
129 return os_map_hash(key) % m->size;
130 }
131
132 #define _os_map_bucket_offset IN_MAP(_,_bucket_offset)
133
134 static inline uint32_t
135 _os_map_bucket_offset(_os_map_t *m, os_map_key_t key, uint32_t i)
136 {
137 uint32_t bucket = _os_map_bucket(m, key);
138 return (i - bucket) % m->size;
139 }
140
141 #ifdef DEBUG
142
143 #define _get_next_offset IN_MAP(_,_verify_next_offset)
144
145 inline int64_t
146 _get_next_offset(_os_map_t *m, _os_map_data_ref_t data,
147 uint32_t index, uint32_t size)
148 {
149 uint32_t next_index = map_next(index, size);
150
151 if (_get_val(data, next_index) == NULL) {
152 return -1;
153 }
154
155 return _os_map_bucket_offset(m, _get_key(data, next_index),
156 next_index);
157 }
158
159 #define _os_map_verify IN_MAP(_,_verify)
160
161 void
162 _os_map_verify(_os_map_t *m)
163 {
164 _os_map_data_ref_t data;
165 _get_data_ref(m, &data);
166
167 int64_t current_bucket_offset;
168 int64_t next_bucket_offset;
169 if (_get_val(data, 0) == NULL) {
170 next_bucket_offset = -1;
171 } else {
172 next_bucket_offset = _os_map_bucket_offset(m,
173 _get_key(data, 0), 0);
174 }
175
176 uint32_t size = m->size;
177
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);
181
182 DEBUG_ASSERT(next_bucket_offset <= current_bucket_offset + 1);
183 }
184
185 }
186 #else
187
188 #define _os_map_verify(A)
189
190 #endif
191
192 #define _swap_if_needed IN_MAP(_,_swap_if_needed)
193
194 static inline void
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)
197 {
198 if (*bucket_offset != 0) {
199 os_map_key_t loop_key = _get_key(data, i);
200
201 // Doesn't support inserting twice
202 DEBUG_ASSERT(!os_map_key_equals(loop_key, *key));
203
204 uint32_t loop_bucket_offset = _os_map_bucket_offset(m,
205 loop_key, i);
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;
212 }
213 void *new_val = _get_val(data, i);
214 _set_key(data, i, *key);
215 _set_val(data, i, *val);
216 *key = loop_key;
217 *val = new_val;
218 *bucket_offset = loop_bucket_offset;
219 }
220 }
221 }
222
223 #define _os_map_insert_no_rehash IN_MAP(_,_insert_no_rehash)
224
225 static void
226 _os_map_insert_no_rehash(_os_map_t *m, os_map_key_t key, void *val)
227 {
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);
232
233 uint32_t bucket_offset = 0;
234 for (;;) {
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) {
240 break;
241 }
242
243 _swap_if_needed(m, data, &key, &val, &bucket_offset, i);
244
245 bucket_offset++;
246 i = map_next(i, size);
247 }
248
249 DEBUG_ASSERT(_get_val(data, i) == NULL);
250
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;
255 }
256 _set_key(data, i, key);
257 _set_val(data, i, val);
258 m->count++;
259 }
260
261 #define _os_map_rehash IN_MAP(_,_rehash)
262
263 static void
264 _os_map_rehash(_os_map_t *m, int direction)
265 {
266 _os_map_verify(m);
267
268 uint32_t old_size = m->size;
269
270 _os_map_data_ref_t old_data;
271 _get_data_ref(m, &old_data);
272
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.
276
277 if (direction > 0) {
278 m->size += (1 << m->grow_shift);
279 if (m->size ==
280 ((uint32_t)_MAP_GROW_SHIFT_RATE << m->grow_shift)) {
281 m->grow_shift++;
282 }
283 } else if (direction < 0) {
284 if (m->grow_shift > MAP_MINSHIFT) {
285 m->grow_shift--;
286 }
287 m->size = roundup(m->size / 2, (1 << m->grow_shift));
288 }
289
290 m->count = 0;
291 m->max_bucket_offset = 0;
292 m->data = _alloc_data(m->size);
293 assert(m->data);
294
295 for (uint32_t i = 0; i < old_size; i++) {
296 if (_get_val(old_data, i) == NULL) {
297 continue;
298 }
299
300 _os_map_insert_no_rehash(m, _get_key(old_data, i),
301 _get_val(old_data, i));
302 }
303 _free_data(&old_data);
304
305 _os_map_verify(m);
306 }
307
308
309 #define _os_map_find_helper_empty_key IN_MAP(_,_find_helper_empty_key)
310
311 #define _os_map_find_helper IN_MAP(_,_find_helper)
312
313 static inline void *
314 _os_map_find_helper(_os_map_t *m, os_map_key_t key, uint32_t *i)
315 {
316 if (m->count == 0) {
317 return NULL;
318 }
319
320
321 uint32_t size = m->size, loop_limit = m->size;
322 _os_map_data_ref_t data;
323 _get_data_ref(m, &data);
324
325 *i = _os_map_bucket(m, key);
326
327 uint32_t bucket_offset = 0;
328
329 for (;;) {
330 assert(loop_limit-- != 0);
331
332 if (bucket_offset > m->max_bucket_offset ||
333 _get_val(data, *i) == NULL) {
334 return NULL;
335 }
336
337 os_map_key_t loop_key = _get_key(data, *i);
338
339 if (os_map_key_equals(key, loop_key)) {
340 return _get_val(data, *i);
341 }
342 *i = map_next(*i, size);
343 bucket_offset++;
344 }
345 }
346
347 #define _os_map_remove_entry IN_MAP(_,_remove_entry)
348
349 void
350 _os_map_remove_entry(_os_map_t *m, uint32_t current_index)
351 {
352 _os_map_data_ref_t data;
353 _get_data_ref(m, &data);
354
355 uint32_t size = m->size;
356
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);
364
365 DEBUG_ASSERT(_os_map_bucket_offset(m,
366 next_key, current_index) <
367 _os_map_bucket_offset(m, next_key,
368 next_index));
369
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);
374 }
375
376 _set_val(data, current_index, NULL);
377 m->count--;
378
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);
383 }
384 }
385
386 // =========== Implementation ===========
387
388
389 #define os_map_init IN_MAP(,_init)
390
391 void
392 os_map_init(opaque_os_map_t *m_raw, os_map_config_t *config,
393 __unused int struct_version)
394 {
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;
398
399 if (config) {
400 m->size = MAX(config->initial_size, MAP_MINSIZE);
401 } else {
402 m->size = MAP_MINSIZE;
403 }
404
405 m->count = 0;
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);
411 }
412
413
414 #define os_map_destroy IN_MAP(,_destroy)
415
416 void
417 os_map_destroy(opaque_os_map_t *m_raw)
418 {
419 _os_map_t *m = (_os_map_t *)m_raw;
420 free(m->data);
421 m->data = NULL;
422 m->size = 0;
423 }
424
425 #define os_map_insert IN_MAP(,_insert)
426
427 void
428 os_map_insert(opaque_os_map_t *m_raw, os_map_key_t key, void *val)
429 {
430 _os_map_t *m = (_os_map_str_t *)m_raw;
431
432 assert(val != NULL);
433
434 if (m->count >= _MAP_MAX_FILL_NUMER * m->size /
435 _MAP_MAX_FILL_DENOM) {
436 _os_map_rehash(m, 1);
437 }
438
439 _os_map_insert_no_rehash(m, key, val);
440
441 DEBUG_ASSERT_MAP_INVARIANTS(m);
442 }
443
444
445 #define os_map_find IN_MAP(,_find)
446
447 void *
448 os_map_find(opaque_os_map_t *m_raw, os_map_key_t key)
449 {
450 _os_map_t *m = (_os_map_t *)m_raw;
451 uint32_t i;
452 return _os_map_find_helper(m, key, &i);
453 }
454
455
456 #define os_map_delete IN_MAP(,_delete)
457
458 void *
459 os_map_delete(opaque_os_map_t *m_raw, os_map_key_t key)
460 {
461 _os_map_t *m = (_os_map_t *)m_raw;
462 uint32_t i;
463
464 void *val = _os_map_find_helper(m, key, &i);
465 if (val == NULL) {
466 return NULL;
467 }
468
469 _os_map_remove_entry(m, i);
470
471 return val;
472 }
473
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 *);
476
477 #define os_map_clear IN_MAP(,_clear)
478
479 void
480 os_map_clear(opaque_os_map_t *m_raw,
481 OS_NOESCAPE os_map_payload_handler_t handler)
482 {
483 _os_map_t *m = (_os_map_t *)m_raw;
484
485 _os_map_data_ref_t oldData;
486 _get_data_ref(m, &oldData);
487 uint32_t oldSize = m->size;
488
489 m->count = 0;
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);
495
496 if (handler != NULL) {
497 for (uint32_t i = 0; i < oldSize; i++) {
498 void *val = _get_val(oldData, i);
499
500 if (val != NULL) {
501 handler(_get_key(oldData, i), val);
502 }
503 }
504 }
505
506 _free_data(&oldData);
507 }
508
509
510 #define os_map_count IN_MAP(,_count)
511
512 size_t
513 os_map_count(opaque_os_map_t *m_raw)
514 {
515 _os_map_t *m = (_os_map_t *)m_raw;
516 return m->count;
517 }
518
519
520 #define os_map_foreach IN_MAP(,_foreach)
521
522 void
523 os_map_foreach(opaque_os_map_t *m_raw,
524 OS_NOESCAPE os_map_payload_handler_t handler)
525 {
526 _os_map_t *m = (_os_map_t *)m_raw;
527 _os_map_data_ref_t data;
528 _get_data_ref(m, &data);
529
530 for (uint32_t i = 0; i < m->size; i++) {
531 void *val = _get_val(data, i);
532
533 if (val != NULL) {
534 if (!handler(_get_key(data, i), val)) break;
535 }
536 }
537 }
538
539 #ifdef MAP_SUPPORTS_ENTRY
540
541 #define os_map_entry IN_MAP(,_entry)
542
543 os_map_key_t
544 os_map_entry(opaque_os_map_t *m_raw, os_map_key_t key)
545 {
546 _os_map_t *m = (_os_map_t *)m_raw;
547 _os_map_data_ref_t data;
548 _get_data_ref(m, &data);
549
550 uint32_t i;
551 if (_os_map_find_helper(m, key, &i) == NULL) {
552 return (os_map_key_t)NULL;
553 }
554 return _get_key(data, i);
555 }
556
557 #endif
558