]>
Commit | Line | Data |
---|---|---|
cf7d2af9 | 1 | /* |
d8b101a4 | 2 | * Copyright (c) 2014 Apple Inc. All rights reserved. |
cf7d2af9 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d7384798 | 5 | * |
cf7d2af9 A |
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. | |
d7384798 | 12 | * |
cf7d2af9 A |
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. | |
d7384798 | 20 | * |
cf7d2af9 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
f64f9b69 | 23 | |
cf7d2af9 | 24 | /* CFBasicHash.m |
d7384798 | 25 | Copyright (c) 2008-2014, Apple Inc. All rights reserved. |
cf7d2af9 A |
26 | Responsibility: Christopher Kane |
27 | */ | |
28 | ||
29 | #import "CFBasicHash.h" | |
30 | #import <CoreFoundation/CFRuntime.h> | |
31 | #import <CoreFoundation/CFSet.h> | |
32 | #import <Block.h> | |
cf7d2af9 | 33 | #import <math.h> |
a48904a4 A |
34 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
35 | #import <dispatch/dispatch.h> | |
36 | #endif | |
cf7d2af9 | 37 | |
856091c5 | 38 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
cf7d2af9 | 39 | #define __SetLastAllocationEventName(A, B) do { if (__CFOASafe && (A)) __CFSetLastAllocationEventName(A, B); } while (0) |
856091c5 A |
40 | #else |
41 | #define __SetLastAllocationEventName(A, B) do { } while (0) | |
cf7d2af9 A |
42 | #endif |
43 | ||
cf7d2af9 A |
44 | #define __AssignWithWriteBarrier(location, value) objc_assign_strongCast((id)value, (id *)location) |
45 | ||
46 | #define ENABLE_DTRACE_PROBES 0 | |
47 | #define ENABLE_MEMORY_COUNTERS 0 | |
48 | ||
8ca704e1 A |
49 | #if defined(DTRACE_PROBES_DISABLED) && DTRACE_PROBES_DISABLED |
50 | #undef ENABLE_DTRACE_PROBES | |
51 | #define ENABLE_DTRACE_PROBES 0 | |
52 | #endif | |
cf7d2af9 A |
53 | |
54 | /* | |
55 | // dtrace -h -s foo.d | |
8ca704e1 A |
56 | // Note: output then changed by casts of the arguments |
57 | // dtrace macros last generated 2010-09-08 on 10.7 prerelease (11A259) | |
cf7d2af9 A |
58 | |
59 | provider Cocoa_HashTable { | |
60 | probe hash_key(unsigned long table, unsigned long key, unsigned long hash); | |
61 | probe test_equal(unsigned long table, unsigned long key1, unsigned long key2); | |
62 | probe probing_start(unsigned long table, unsigned long num_buckets); | |
63 | probe probe_empty(unsigned long table, unsigned long idx); | |
64 | probe probe_deleted(unsigned long table, unsigned long idx); | |
65 | probe probe_valid(unsigned long table, unsigned long idx); | |
66 | probe probing_end(unsigned long table, unsigned long num_probes); | |
67 | probe rehash_start(unsigned long table, unsigned long num_buckets, unsigned long total_size); | |
68 | probe rehash_end(unsigned long table, unsigned long num_buckets, unsigned long total_size); | |
69 | }; | |
70 | ||
71 | #pragma D attributes Unstable/Unstable/Common provider Cocoa_HashTable provider | |
72 | #pragma D attributes Private/Private/Unknown provider Cocoa_HashTable module | |
73 | #pragma D attributes Private/Private/Unknown provider Cocoa_HashTable function | |
74 | #pragma D attributes Unstable/Unstable/Common provider Cocoa_HashTable name | |
75 | #pragma D attributes Unstable/Unstable/Common provider Cocoa_HashTable args | |
76 | */ | |
77 | ||
78 | #if ENABLE_DTRACE_PROBES | |
79 | ||
80 | #define COCOA_HASHTABLE_STABILITY "___dtrace_stability$Cocoa_HashTable$v1$4_4_5_1_1_0_1_1_0_4_4_5_4_4_5" | |
81 | ||
82 | #define COCOA_HASHTABLE_TYPEDEFS "___dtrace_typedefs$Cocoa_HashTable$v2" | |
83 | ||
84 | #define COCOA_HASHTABLE_REHASH_END(arg0, arg1, arg2) \ | |
85 | do { \ | |
86 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
87 | __dtrace_probe$Cocoa_HashTable$rehash_end$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1), (unsigned long)(arg2)); \ | |
88 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
89 | } while (0) | |
8ca704e1 A |
90 | #define COCOA_HASHTABLE_REHASH_END_ENABLED() \ |
91 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$rehash_end$v1(); \ | |
92 | __asm__ volatile(""); \ | |
93 | _r; }) | |
cf7d2af9 A |
94 | #define COCOA_HASHTABLE_REHASH_START(arg0, arg1, arg2) \ |
95 | do { \ | |
96 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
97 | __dtrace_probe$Cocoa_HashTable$rehash_start$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1), (unsigned long)(arg2)); \ | |
98 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
99 | } while (0) | |
8ca704e1 A |
100 | #define COCOA_HASHTABLE_REHASH_START_ENABLED() \ |
101 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$rehash_start$v1(); \ | |
102 | __asm__ volatile(""); \ | |
103 | _r; }) | |
cf7d2af9 A |
104 | #define COCOA_HASHTABLE_HASH_KEY(arg0, arg1, arg2) \ |
105 | do { \ | |
106 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
107 | __dtrace_probe$Cocoa_HashTable$hash_key$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1), (unsigned long)(arg2)); \ | |
108 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
109 | } while (0) | |
8ca704e1 A |
110 | #define COCOA_HASHTABLE_HASH_KEY_ENABLED() \ |
111 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$hash_key$v1(); \ | |
112 | __asm__ volatile(""); \ | |
113 | _r; }) | |
cf7d2af9 A |
114 | #define COCOA_HASHTABLE_PROBE_DELETED(arg0, arg1) \ |
115 | do { \ | |
116 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
117 | __dtrace_probe$Cocoa_HashTable$probe_deleted$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1)); \ | |
118 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
119 | } while (0) | |
8ca704e1 A |
120 | #define COCOA_HASHTABLE_PROBE_DELETED_ENABLED() \ |
121 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$probe_deleted$v1(); \ | |
122 | __asm__ volatile(""); \ | |
123 | _r; }) | |
cf7d2af9 A |
124 | #define COCOA_HASHTABLE_PROBE_EMPTY(arg0, arg1) \ |
125 | do { \ | |
126 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
127 | __dtrace_probe$Cocoa_HashTable$probe_empty$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1)); \ | |
128 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
129 | } while (0) | |
8ca704e1 A |
130 | #define COCOA_HASHTABLE_PROBE_EMPTY_ENABLED() \ |
131 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$probe_empty$v1(); \ | |
132 | __asm__ volatile(""); \ | |
133 | _r; }) | |
cf7d2af9 A |
134 | #define COCOA_HASHTABLE_PROBE_VALID(arg0, arg1) \ |
135 | do { \ | |
136 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
137 | __dtrace_probe$Cocoa_HashTable$probe_valid$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1)); \ | |
138 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
139 | } while (0) | |
8ca704e1 A |
140 | #define COCOA_HASHTABLE_PROBE_VALID_ENABLED() \ |
141 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$probe_valid$v1(); \ | |
142 | __asm__ volatile(""); \ | |
143 | _r; }) | |
cf7d2af9 A |
144 | #define COCOA_HASHTABLE_PROBING_END(arg0, arg1) \ |
145 | do { \ | |
146 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
147 | __dtrace_probe$Cocoa_HashTable$probing_end$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1)); \ | |
148 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
149 | } while (0) | |
8ca704e1 A |
150 | #define COCOA_HASHTABLE_PROBING_END_ENABLED() \ |
151 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$probing_end$v1(); \ | |
152 | __asm__ volatile(""); \ | |
153 | _r; }) | |
cf7d2af9 A |
154 | #define COCOA_HASHTABLE_PROBING_START(arg0, arg1) \ |
155 | do { \ | |
156 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
157 | __dtrace_probe$Cocoa_HashTable$probing_start$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1)); \ | |
158 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
159 | } while (0) | |
8ca704e1 A |
160 | #define COCOA_HASHTABLE_PROBING_START_ENABLED() \ |
161 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$probing_start$v1(); \ | |
162 | __asm__ volatile(""); \ | |
163 | _r; }) | |
cf7d2af9 A |
164 | #define COCOA_HASHTABLE_TEST_EQUAL(arg0, arg1, arg2) \ |
165 | do { \ | |
166 | __asm__ volatile(".reference " COCOA_HASHTABLE_TYPEDEFS); \ | |
167 | __dtrace_probe$Cocoa_HashTable$test_equal$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67((unsigned long)(arg0), (unsigned long)(arg1), (unsigned long)(arg2)); \ | |
168 | __asm__ volatile(".reference " COCOA_HASHTABLE_STABILITY); \ | |
169 | } while (0) | |
8ca704e1 A |
170 | #define COCOA_HASHTABLE_TEST_EQUAL_ENABLED() \ |
171 | ({ int _r = __dtrace_isenabled$Cocoa_HashTable$test_equal$v1(); \ | |
172 | __asm__ volatile(""); \ | |
173 | _r; }) | |
cf7d2af9 | 174 | |
cf7d2af9 A |
175 | extern void __dtrace_probe$Cocoa_HashTable$hash_key$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long, unsigned long); |
176 | extern int __dtrace_isenabled$Cocoa_HashTable$hash_key$v1(void); | |
177 | extern void __dtrace_probe$Cocoa_HashTable$probe_deleted$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long); | |
178 | extern int __dtrace_isenabled$Cocoa_HashTable$probe_deleted$v1(void); | |
179 | extern void __dtrace_probe$Cocoa_HashTable$probe_empty$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long); | |
180 | extern int __dtrace_isenabled$Cocoa_HashTable$probe_empty$v1(void); | |
181 | extern void __dtrace_probe$Cocoa_HashTable$probe_valid$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long); | |
182 | extern int __dtrace_isenabled$Cocoa_HashTable$probe_valid$v1(void); | |
183 | extern void __dtrace_probe$Cocoa_HashTable$probing_end$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long); | |
184 | extern int __dtrace_isenabled$Cocoa_HashTable$probing_end$v1(void); | |
185 | extern void __dtrace_probe$Cocoa_HashTable$probing_start$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long); | |
186 | extern int __dtrace_isenabled$Cocoa_HashTable$probing_start$v1(void); | |
8ca704e1 A |
187 | extern void __dtrace_probe$Cocoa_HashTable$rehash_end$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long, unsigned long); |
188 | extern int __dtrace_isenabled$Cocoa_HashTable$rehash_end$v1(void); | |
189 | extern void __dtrace_probe$Cocoa_HashTable$rehash_start$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long, unsigned long); | |
190 | extern int __dtrace_isenabled$Cocoa_HashTable$rehash_start$v1(void); | |
cf7d2af9 A |
191 | extern void __dtrace_probe$Cocoa_HashTable$test_equal$v1$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67(unsigned long, unsigned long, unsigned long); |
192 | extern int __dtrace_isenabled$Cocoa_HashTable$test_equal$v1(void); | |
193 | ||
194 | #else | |
195 | ||
196 | #define COCOA_HASHTABLE_REHASH_END(arg0, arg1, arg2) do {} while (0) | |
197 | #define COCOA_HASHTABLE_REHASH_END_ENABLED() 0 | |
198 | #define COCOA_HASHTABLE_REHASH_START(arg0, arg1, arg2) do {} while (0) | |
199 | #define COCOA_HASHTABLE_REHASH_START_ENABLED() 0 | |
200 | #define COCOA_HASHTABLE_HASH_KEY(arg0, arg1, arg2) do {} while (0) | |
201 | #define COCOA_HASHTABLE_HASH_KEY_ENABLED() 0 | |
202 | #define COCOA_HASHTABLE_PROBE_DELETED(arg0, arg1) do {} while (0) | |
203 | #define COCOA_HASHTABLE_PROBE_DELETED_ENABLED() 0 | |
204 | #define COCOA_HASHTABLE_PROBE_EMPTY(arg0, arg1) do {} while (0) | |
205 | #define COCOA_HASHTABLE_PROBE_EMPTY_ENABLED() 0 | |
206 | #define COCOA_HASHTABLE_PROBE_VALID(arg0, arg1) do {} while (0) | |
207 | #define COCOA_HASHTABLE_PROBE_VALID_ENABLED() 0 | |
208 | #define COCOA_HASHTABLE_PROBING_END(arg0, arg1) do {} while (0) | |
209 | #define COCOA_HASHTABLE_PROBING_END_ENABLED() 0 | |
210 | #define COCOA_HASHTABLE_PROBING_START(arg0, arg1) do {} while (0) | |
211 | #define COCOA_HASHTABLE_PROBING_START_ENABLED() 0 | |
212 | #define COCOA_HASHTABLE_TEST_EQUAL(arg0, arg1, arg2) do {} while (0) | |
213 | #define COCOA_HASHTABLE_TEST_EQUAL_ENABLED() 0 | |
214 | ||
215 | #endif | |
216 | ||
217 | ||
218 | #if !defined(__LP64__) | |
219 | #define __LP64__ 0 | |
220 | #endif | |
221 | ||
8ca704e1 A |
222 | // Prime numbers. Values above 100 have been adjusted up so that the |
223 | // malloced block size will be just below a multiple of 512; values | |
224 | // above 1200 have been adjusted up to just below a multiple of 4096. | |
225 | static const uintptr_t __CFBasicHashTableSizes[64] = { | |
226 | 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723, | |
227 | 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607, | |
228 | 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119, | |
229 | 6221311, 10066421, 16287743, 26354171, 42641881, 68996069, | |
230 | 111638519, 180634607, 292272623, 472907251, | |
cf7d2af9 | 231 | #if __LP64__ |
8ca704e1 A |
232 | 765180413UL, 1238087663UL, 2003267557UL, 3241355263UL, 5244622819UL, |
233 | #if 0 | |
234 | 8485977589UL, 13730600407UL, 22216578047UL, 35947178479UL, | |
235 | 58163756537UL, 94110934997UL, 152274691561UL, 246385626107UL, | |
236 | 398660317687UL, 645045943807UL, 1043706260983UL, 1688752204787UL, | |
237 | 2732458465769UL, 4421210670577UL, 7153669136377UL, | |
238 | 11574879807461UL, 18728548943849UL, 30303428750843UL | |
239 | #endif | |
240 | #endif | |
241 | }; | |
242 | ||
243 | static const uintptr_t __CFBasicHashTableCapacities[64] = { | |
244 | 0, 3, 6, 11, 19, 32, 52, 85, 118, 155, 237, 390, 672, 1065, | |
245 | 1732, 2795, 4543, 7391, 12019, 19302, 31324, 50629, 81956, | |
246 | 132580, 214215, 346784, 561026, 907847, 1468567, 2376414, | |
247 | 3844982, 6221390, 10066379, 16287773, 26354132, 42641916, | |
248 | 68996399, 111638327, 180634415, 292272755, | |
249 | #if __LP64__ | |
250 | 472907503UL, 765180257UL, 1238087439UL, 2003267722UL, 3241355160UL, | |
251 | #if 0 | |
252 | 5244622578UL, 8485977737UL, 13730600347UL, 22216578100UL, | |
253 | 35947178453UL, 58163756541UL, 94110935011UL, 152274691274UL, | |
254 | 246385626296UL, 398660317578UL, 645045943559UL, 1043706261135UL, | |
255 | 1688752204693UL, 2732458465840UL, 4421210670552UL, | |
256 | 7153669136706UL, 11574879807265UL, 18728548943682UL | |
257 | #endif | |
258 | #endif | |
259 | }; | |
260 | ||
261 | // Primitive roots for the primes above | |
262 | static const uintptr_t __CFBasicHashPrimitiveRoots[64] = { | |
263 | 0, 2, 3, 2, 5, 6, 7, 3, 19, 6, 5, 3, 3, 3, | |
264 | 2, 5, 6, 3, 3, 6, 2, 3, 3, | |
265 | 3, 5, 10, 3, 3, 22, 3, | |
266 | 3, 3, 5, 2, 22, 2, | |
267 | 11, 5, 5, 2, | |
268 | #if __LP64__ | |
269 | 3, 10, 2, 3, 10, | |
270 | 2, 3, 5, 3, | |
271 | 3, 2, 7, 2, | |
272 | 3, 3, 3, 2, | |
273 | 3, 5, 5, | |
274 | 2, 3, 2 | |
cf7d2af9 A |
275 | #endif |
276 | }; | |
277 | ||
8ca704e1 A |
278 | CF_INLINE void *__CFBasicHashAllocateMemory(CFConstBasicHashRef ht, CFIndex count, CFIndex elem_size, Boolean strong, Boolean compactable) { |
279 | CFAllocatorRef allocator = CFGetAllocator(ht); | |
280 | void *new_mem = NULL; | |
281 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
282 | new_mem = auto_zone_allocate_object(objc_collectableZone(), count * elem_size, strong ? (compactable ? AUTO_POINTERS_ONLY : AUTO_MEMORY_SCANNED) : AUTO_UNSCANNED, false, false); | |
283 | } else { | |
284 | new_mem = CFAllocatorAllocate(allocator, count * elem_size, 0); | |
285 | } | |
856091c5 A |
286 | return new_mem; |
287 | } | |
288 | ||
289 | CF_INLINE void *__CFBasicHashAllocateMemory2(CFAllocatorRef allocator, CFIndex count, CFIndex elem_size, Boolean strong, Boolean compactable) { | |
290 | void *new_mem = NULL; | |
291 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
292 | new_mem = auto_zone_allocate_object(objc_collectableZone(), count * elem_size, strong ? (compactable ? AUTO_POINTERS_ONLY : AUTO_MEMORY_SCANNED) : AUTO_UNSCANNED, false, false); | |
293 | } else { | |
294 | new_mem = CFAllocatorAllocate(allocator, count * elem_size, 0); | |
295 | } | |
8ca704e1 A |
296 | return new_mem; |
297 | } | |
298 | ||
299 | #define __CFBasicHashSubABZero 0xa7baadb1 | |
300 | #define __CFBasicHashSubABOne 0xa5baadb9 | |
301 | ||
cf7d2af9 | 302 | typedef union { |
8ca704e1 | 303 | uintptr_t neutral; |
cf7d2af9 | 304 | id strong; |
8ca704e1 | 305 | id weak; |
cf7d2af9 A |
306 | } CFBasicHashValue; |
307 | ||
308 | struct __CFBasicHash { | |
309 | CFRuntimeBase base; | |
a48904a4 A |
310 | struct { // 192 bits |
311 | uint16_t mutations; | |
8ca704e1 | 312 | uint8_t hash_style:2; |
8ca704e1 A |
313 | uint8_t keys_offset:1; |
314 | uint8_t counts_offset:2; | |
315 | uint8_t counts_width:2; | |
316 | uint8_t hashes_offset:2; | |
317 | uint8_t strong_values:1; | |
318 | uint8_t strong_keys:1; | |
319 | uint8_t weak_values:1; | |
320 | uint8_t weak_keys:1; | |
321 | uint8_t int_values:1; | |
322 | uint8_t int_keys:1; | |
323 | uint8_t indirect_keys:1; | |
a48904a4 A |
324 | uint32_t used_buckets; /* number of used buckets */ |
325 | uint64_t deleted:16; | |
326 | uint64_t num_buckets_idx:8; /* index to number of buckets */ | |
327 | uint64_t __kret:10; | |
328 | uint64_t __vret:10; | |
329 | uint64_t __krel:10; | |
330 | uint64_t __vrel:10; | |
331 | uint64_t __:1; | |
332 | uint64_t null_rc:1; | |
333 | uint64_t fast_grow:1; | |
334 | uint64_t finalized:1; | |
335 | uint64_t __kdes:10; | |
336 | uint64_t __vdes:10; | |
337 | uint64_t __kequ:10; | |
338 | uint64_t __vequ:10; | |
339 | uint64_t __khas:10; | |
340 | uint64_t __kget:10; | |
cf7d2af9 | 341 | } bits; |
cf7d2af9 A |
342 | void *pointers[1]; |
343 | }; | |
344 | ||
a48904a4 A |
345 | static void *CFBasicHashCallBackPtrs[(1UL << 10)]; |
346 | static int32_t CFBasicHashCallBackPtrsCount = 0; | |
cf7d2af9 | 347 | |
a48904a4 A |
348 | static int32_t CFBasicHashGetPtrIndex(void *ptr) { |
349 | static dispatch_once_t once; | |
350 | dispatch_once(&once, ^{ | |
351 | CFBasicHashCallBackPtrs[0] = NULL; | |
352 | CFBasicHashCallBackPtrs[1] = (void *)CFCopyDescription; | |
353 | CFBasicHashCallBackPtrs[2] = (void *)__CFTypeCollectionRelease; | |
354 | CFBasicHashCallBackPtrs[3] = (void *)__CFTypeCollectionRetain; | |
355 | CFBasicHashCallBackPtrs[4] = (void *)CFEqual; | |
356 | CFBasicHashCallBackPtrs[5] = (void *)CFHash; | |
357 | CFBasicHashCallBackPtrs[6] = (void *)__CFStringCollectionCopy; | |
358 | CFBasicHashCallBackPtrs[7] = NULL; | |
359 | CFBasicHashCallBackPtrsCount = 8; | |
360 | }); | |
361 | ||
362 | // The uniquing here is done locklessly for best performance, and in | |
363 | // a way that will keep multiple threads from stomping each other's | |
364 | // newly registered values, but may result in multiple slots | |
365 | // containing the same pointer value. | |
366 | ||
367 | int32_t idx; | |
368 | for (idx = 0; idx < CFBasicHashCallBackPtrsCount; idx++) { | |
369 | if (CFBasicHashCallBackPtrs[idx] == ptr) return idx; | |
370 | } | |
371 | ||
372 | if (1000 < CFBasicHashCallBackPtrsCount) HALT; | |
373 | idx = OSAtomicIncrement32(&CFBasicHashCallBackPtrsCount); // returns new value | |
374 | CFBasicHashCallBackPtrs[idx - 1] = ptr; | |
375 | return idx - 1; | |
cf7d2af9 A |
376 | } |
377 | ||
a48904a4 | 378 | CF_PRIVATE Boolean CFBasicHashHasStrongValues(CFConstBasicHashRef ht) { |
856091c5 | 379 | #if DEPLOYMENT_TARGET_MACOSX |
a48904a4 | 380 | return ht->bits.strong_values ? true : false; |
856091c5 A |
381 | #else |
382 | return false; | |
8ca704e1 | 383 | #endif |
cf7d2af9 A |
384 | } |
385 | ||
a48904a4 | 386 | CF_PRIVATE Boolean CFBasicHashHasStrongKeys(CFConstBasicHashRef ht) { |
856091c5 | 387 | #if DEPLOYMENT_TARGET_MACOSX |
a48904a4 | 388 | return ht->bits.strong_keys ? true : false; |
856091c5 A |
389 | #else |
390 | return false; | |
8ca704e1 | 391 | #endif |
cf7d2af9 A |
392 | } |
393 | ||
8ca704e1 | 394 | CF_INLINE Boolean __CFBasicHashHasHashCache(CFConstBasicHashRef ht) { |
856091c5 | 395 | #if DEPLOYMENT_TARGET_MACOSX |
8ca704e1 | 396 | return ht->bits.hashes_offset ? true : false; |
856091c5 A |
397 | #else |
398 | return false; | |
8ca704e1 | 399 | #endif |
cf7d2af9 A |
400 | } |
401 | ||
8ca704e1 | 402 | CF_INLINE uintptr_t __CFBasicHashImportValue(CFConstBasicHashRef ht, uintptr_t stack_value) { |
a48904a4 A |
403 | uintptr_t (*func)(CFAllocatorRef, uintptr_t) = (uintptr_t (*)(CFAllocatorRef, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__vret]; |
404 | if (!func || ht->bits.null_rc) return stack_value; | |
405 | CFAllocatorRef alloc = CFGetAllocator(ht); | |
406 | return func(alloc, stack_value); | |
cf7d2af9 A |
407 | } |
408 | ||
8ca704e1 | 409 | CF_INLINE uintptr_t __CFBasicHashImportKey(CFConstBasicHashRef ht, uintptr_t stack_key) { |
a48904a4 A |
410 | uintptr_t (*func)(CFAllocatorRef, uintptr_t) = (uintptr_t (*)(CFAllocatorRef, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__kret]; |
411 | if (!func || ht->bits.null_rc) return stack_key; | |
412 | CFAllocatorRef alloc = CFGetAllocator(ht); | |
413 | return func(alloc, stack_key); | |
cf7d2af9 A |
414 | } |
415 | ||
8ca704e1 | 416 | CF_INLINE void __CFBasicHashEjectValue(CFConstBasicHashRef ht, uintptr_t stack_value) { |
a48904a4 A |
417 | void (*func)(CFAllocatorRef, uintptr_t) = (void (*)(CFAllocatorRef, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__vrel]; |
418 | if (!func || ht->bits.null_rc) return; | |
419 | CFAllocatorRef alloc = CFGetAllocator(ht); | |
420 | func(alloc, stack_value); | |
cf7d2af9 A |
421 | } |
422 | ||
8ca704e1 | 423 | CF_INLINE void __CFBasicHashEjectKey(CFConstBasicHashRef ht, uintptr_t stack_key) { |
a48904a4 A |
424 | void (*func)(CFAllocatorRef, uintptr_t) = (void (*)(CFAllocatorRef, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__krel]; |
425 | if (!func || ht->bits.null_rc) return; | |
426 | CFAllocatorRef alloc = CFGetAllocator(ht); | |
427 | func(alloc, stack_key); | |
428 | } | |
429 | ||
430 | CF_INLINE CFStringRef __CFBasicHashDescValue(CFConstBasicHashRef ht, uintptr_t stack_value) { | |
431 | CFStringRef (*func)(uintptr_t) = (CFStringRef (*)(uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__vdes]; | |
432 | if (!func) return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%p>"), (void *)stack_value); | |
433 | return func(stack_value); | |
434 | } | |
435 | ||
436 | CF_INLINE CFStringRef __CFBasicHashDescKey(CFConstBasicHashRef ht, uintptr_t stack_key) { | |
437 | CFStringRef (*func)(uintptr_t) = (CFStringRef (*)(uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__kdes]; | |
438 | if (!func) return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%p>"), (void *)stack_key); | |
439 | return func(stack_key); | |
cf7d2af9 A |
440 | } |
441 | ||
8ca704e1 | 442 | CF_INLINE Boolean __CFBasicHashTestEqualValue(CFConstBasicHashRef ht, uintptr_t stack_value_a, uintptr_t stack_value_b) { |
a48904a4 A |
443 | Boolean (*func)(uintptr_t, uintptr_t) = (Boolean (*)(uintptr_t, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__vequ]; |
444 | if (!func) return (stack_value_a == stack_value_b); | |
445 | return func(stack_value_a, stack_value_b); | |
cf7d2af9 A |
446 | } |
447 | ||
8ca704e1 A |
448 | CF_INLINE Boolean __CFBasicHashTestEqualKey(CFConstBasicHashRef ht, uintptr_t in_coll_key, uintptr_t stack_key) { |
449 | COCOA_HASHTABLE_TEST_EQUAL(ht, in_coll_key, stack_key); | |
a48904a4 A |
450 | Boolean (*func)(uintptr_t, uintptr_t) = (Boolean (*)(uintptr_t, uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__kequ]; |
451 | if (!func) return (in_coll_key == stack_key); | |
452 | return func(in_coll_key, stack_key); | |
cf7d2af9 A |
453 | } |
454 | ||
8ca704e1 | 455 | CF_INLINE CFHashCode __CFBasicHashHashKey(CFConstBasicHashRef ht, uintptr_t stack_key) { |
a48904a4 A |
456 | CFHashCode (*func)(uintptr_t) = (CFHashCode (*)(uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__khas]; |
457 | CFHashCode hash_code = func ? func(stack_key) : stack_key; | |
8ca704e1 A |
458 | COCOA_HASHTABLE_HASH_KEY(ht, stack_key, hash_code); |
459 | return hash_code; | |
cf7d2af9 A |
460 | } |
461 | ||
a48904a4 A |
462 | CF_INLINE uintptr_t __CFBasicHashGetIndirectKey(CFConstBasicHashRef ht, uintptr_t coll_key) { |
463 | uintptr_t (*func)(uintptr_t) = (uintptr_t (*)(uintptr_t))CFBasicHashCallBackPtrs[ht->bits.__kget]; | |
464 | if (!func) return coll_key; | |
465 | return func(coll_key); | |
466 | } | |
467 | ||
8ca704e1 A |
468 | CF_INLINE CFBasicHashValue *__CFBasicHashGetValues(CFConstBasicHashRef ht) { |
469 | return (CFBasicHashValue *)ht->pointers[0]; | |
cf7d2af9 A |
470 | } |
471 | ||
8ca704e1 A |
472 | CF_INLINE void __CFBasicHashSetValues(CFBasicHashRef ht, CFBasicHashValue *ptr) { |
473 | __AssignWithWriteBarrier(&ht->pointers[0], ptr); | |
cf7d2af9 A |
474 | } |
475 | ||
8ca704e1 A |
476 | CF_INLINE CFBasicHashValue *__CFBasicHashGetKeys(CFConstBasicHashRef ht) { |
477 | return (CFBasicHashValue *)ht->pointers[ht->bits.keys_offset]; | |
cf7d2af9 A |
478 | } |
479 | ||
8ca704e1 A |
480 | CF_INLINE void __CFBasicHashSetKeys(CFBasicHashRef ht, CFBasicHashValue *ptr) { |
481 | __AssignWithWriteBarrier(&ht->pointers[ht->bits.keys_offset], ptr); | |
cf7d2af9 A |
482 | } |
483 | ||
8ca704e1 A |
484 | CF_INLINE void *__CFBasicHashGetCounts(CFConstBasicHashRef ht) { |
485 | return (void *)ht->pointers[ht->bits.counts_offset]; | |
cf7d2af9 A |
486 | } |
487 | ||
8ca704e1 A |
488 | CF_INLINE void __CFBasicHashSetCounts(CFBasicHashRef ht, void *ptr) { |
489 | __AssignWithWriteBarrier(&ht->pointers[ht->bits.counts_offset], ptr); | |
cf7d2af9 A |
490 | } |
491 | ||
8ca704e1 A |
492 | CF_INLINE uintptr_t __CFBasicHashGetValue(CFConstBasicHashRef ht, CFIndex idx) { |
493 | uintptr_t val = __CFBasicHashGetValues(ht)[idx].neutral; | |
494 | if (__CFBasicHashSubABZero == val) return 0UL; | |
495 | if (__CFBasicHashSubABOne == val) return ~0UL; | |
496 | return val; | |
cf7d2af9 A |
497 | } |
498 | ||
8ca704e1 A |
499 | CF_INLINE void __CFBasicHashSetValue(CFBasicHashRef ht, CFIndex idx, uintptr_t stack_value, Boolean ignoreOld, Boolean literal) { |
500 | CFBasicHashValue *valuep = &(__CFBasicHashGetValues(ht)[idx]); | |
501 | uintptr_t old_value = ignoreOld ? 0 : valuep->neutral; | |
502 | if (!literal) { | |
503 | if (0UL == stack_value) stack_value = __CFBasicHashSubABZero; | |
504 | if (~0UL == stack_value) stack_value = __CFBasicHashSubABOne; | |
505 | } | |
506 | if (CFBasicHashHasStrongValues(ht)) valuep->strong = (id)stack_value; else valuep->neutral = stack_value; | |
507 | if (!ignoreOld) { | |
508 | if (!(old_value == 0UL || old_value == ~0UL)) { | |
509 | if (__CFBasicHashSubABZero == old_value) old_value = 0UL; | |
510 | if (__CFBasicHashSubABOne == old_value) old_value = ~0UL; | |
511 | __CFBasicHashEjectValue(ht, old_value); | |
512 | } | |
513 | } | |
cf7d2af9 A |
514 | } |
515 | ||
8ca704e1 A |
516 | CF_INLINE uintptr_t __CFBasicHashGetKey(CFConstBasicHashRef ht, CFIndex idx) { |
517 | if (ht->bits.keys_offset) { | |
518 | uintptr_t key = __CFBasicHashGetKeys(ht)[idx].neutral; | |
519 | if (__CFBasicHashSubABZero == key) return 0UL; | |
520 | if (__CFBasicHashSubABOne == key) return ~0UL; | |
521 | return key; | |
522 | } | |
523 | if (ht->bits.indirect_keys) { | |
524 | uintptr_t stack_value = __CFBasicHashGetValue(ht, idx); | |
a48904a4 | 525 | return __CFBasicHashGetIndirectKey(ht, stack_value); |
8ca704e1 A |
526 | } |
527 | return __CFBasicHashGetValue(ht, idx); | |
cf7d2af9 A |
528 | } |
529 | ||
8ca704e1 A |
530 | CF_INLINE void __CFBasicHashSetKey(CFBasicHashRef ht, CFIndex idx, uintptr_t stack_key, Boolean ignoreOld, Boolean literal) { |
531 | if (0 == ht->bits.keys_offset) HALT; | |
532 | CFBasicHashValue *keyp = &(__CFBasicHashGetKeys(ht)[idx]); | |
533 | uintptr_t old_key = ignoreOld ? 0 : keyp->neutral; | |
534 | if (!literal) { | |
535 | if (0UL == stack_key) stack_key = __CFBasicHashSubABZero; | |
536 | if (~0UL == stack_key) stack_key = __CFBasicHashSubABOne; | |
537 | } | |
538 | if (CFBasicHashHasStrongKeys(ht)) keyp->strong = (id)stack_key; else keyp->neutral = stack_key; | |
539 | if (!ignoreOld) { | |
540 | if (!(old_key == 0UL || old_key == ~0UL)) { | |
541 | if (__CFBasicHashSubABZero == old_key) old_key = 0UL; | |
542 | if (__CFBasicHashSubABOne == old_key) old_key = ~0UL; | |
543 | __CFBasicHashEjectKey(ht, old_key); | |
544 | } | |
545 | } | |
cf7d2af9 A |
546 | } |
547 | ||
8ca704e1 A |
548 | CF_INLINE uintptr_t __CFBasicHashIsEmptyOrDeleted(CFConstBasicHashRef ht, CFIndex idx) { |
549 | uintptr_t stack_value = __CFBasicHashGetValues(ht)[idx].neutral; | |
550 | return (0UL == stack_value || ~0UL == stack_value); | |
cf7d2af9 A |
551 | } |
552 | ||
8ca704e1 A |
553 | CF_INLINE uintptr_t __CFBasicHashIsDeleted(CFConstBasicHashRef ht, CFIndex idx) { |
554 | uintptr_t stack_value = __CFBasicHashGetValues(ht)[idx].neutral; | |
555 | return (~0UL == stack_value); | |
cf7d2af9 A |
556 | } |
557 | ||
8ca704e1 A |
558 | CF_INLINE uintptr_t __CFBasicHashGetSlotCount(CFConstBasicHashRef ht, CFIndex idx) { |
559 | void *counts = __CFBasicHashGetCounts(ht); | |
560 | switch (ht->bits.counts_width) { | |
561 | case 0: return ((uint8_t *)counts)[idx]; | |
562 | case 1: return ((uint16_t *)counts)[idx]; | |
563 | case 2: return ((uint32_t *)counts)[idx]; | |
564 | case 3: return ((uint64_t *)counts)[idx]; | |
565 | } | |
566 | return 0; | |
cf7d2af9 A |
567 | } |
568 | ||
8ca704e1 A |
569 | CF_INLINE void __CFBasicHashBumpCounts(CFBasicHashRef ht) { |
570 | void *counts = __CFBasicHashGetCounts(ht); | |
571 | CFAllocatorRef allocator = CFGetAllocator(ht); | |
572 | switch (ht->bits.counts_width) { | |
573 | case 0: { | |
574 | uint8_t *counts08 = (uint8_t *)counts; | |
575 | ht->bits.counts_width = 1; | |
576 | CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
577 | uint16_t *counts16 = (uint16_t *)__CFBasicHashAllocateMemory(ht, num_buckets, 2, false, false); | |
856091c5 | 578 | if (!counts16) HALT; |
8ca704e1 A |
579 | __SetLastAllocationEventName(counts16, "CFBasicHash (count-store)"); |
580 | for (CFIndex idx2 = 0; idx2 < num_buckets; idx2++) { | |
581 | counts16[idx2] = counts08[idx2]; | |
582 | } | |
583 | __CFBasicHashSetCounts(ht, counts16); | |
584 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
585 | CFAllocatorDeallocate(allocator, counts08); | |
586 | } | |
587 | break; | |
588 | } | |
589 | case 1: { | |
590 | uint16_t *counts16 = (uint16_t *)counts; | |
591 | ht->bits.counts_width = 2; | |
592 | CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
593 | uint32_t *counts32 = (uint32_t *)__CFBasicHashAllocateMemory(ht, num_buckets, 4, false, false); | |
856091c5 | 594 | if (!counts32) HALT; |
8ca704e1 A |
595 | __SetLastAllocationEventName(counts32, "CFBasicHash (count-store)"); |
596 | for (CFIndex idx2 = 0; idx2 < num_buckets; idx2++) { | |
597 | counts32[idx2] = counts16[idx2]; | |
598 | } | |
599 | __CFBasicHashSetCounts(ht, counts32); | |
600 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
601 | CFAllocatorDeallocate(allocator, counts16); | |
602 | } | |
603 | break; | |
604 | } | |
605 | case 2: { | |
606 | uint32_t *counts32 = (uint32_t *)counts; | |
607 | ht->bits.counts_width = 3; | |
608 | CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
609 | uint64_t *counts64 = (uint64_t *)__CFBasicHashAllocateMemory(ht, num_buckets, 8, false, false); | |
856091c5 | 610 | if (!counts64) HALT; |
8ca704e1 A |
611 | __SetLastAllocationEventName(counts64, "CFBasicHash (count-store)"); |
612 | for (CFIndex idx2 = 0; idx2 < num_buckets; idx2++) { | |
613 | counts64[idx2] = counts32[idx2]; | |
614 | } | |
615 | __CFBasicHashSetCounts(ht, counts64); | |
616 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
617 | CFAllocatorDeallocate(allocator, counts32); | |
618 | } | |
619 | break; | |
620 | } | |
621 | case 3: { | |
622 | HALT; | |
623 | break; | |
624 | } | |
625 | } | |
cf7d2af9 A |
626 | } |
627 | ||
8ca704e1 A |
628 | static void __CFBasicHashIncSlotCount(CFBasicHashRef ht, CFIndex idx) { |
629 | void *counts = __CFBasicHashGetCounts(ht); | |
630 | switch (ht->bits.counts_width) { | |
631 | case 0: { | |
632 | uint8_t *counts08 = (uint8_t *)counts; | |
633 | uint8_t val = counts08[idx]; | |
634 | if (val < INT8_MAX) { | |
635 | counts08[idx] = val + 1; | |
636 | return; | |
637 | } | |
638 | __CFBasicHashBumpCounts(ht); | |
639 | __CFBasicHashIncSlotCount(ht, idx); | |
640 | break; | |
641 | } | |
642 | case 1: { | |
643 | uint16_t *counts16 = (uint16_t *)counts; | |
644 | uint16_t val = counts16[idx]; | |
645 | if (val < INT16_MAX) { | |
646 | counts16[idx] = val + 1; | |
647 | return; | |
648 | } | |
649 | __CFBasicHashBumpCounts(ht); | |
650 | __CFBasicHashIncSlotCount(ht, idx); | |
651 | break; | |
652 | } | |
653 | case 2: { | |
654 | uint32_t *counts32 = (uint32_t *)counts; | |
655 | uint32_t val = counts32[idx]; | |
656 | if (val < INT32_MAX) { | |
657 | counts32[idx] = val + 1; | |
658 | return; | |
659 | } | |
660 | __CFBasicHashBumpCounts(ht); | |
661 | __CFBasicHashIncSlotCount(ht, idx); | |
662 | break; | |
663 | } | |
664 | case 3: { | |
665 | uint64_t *counts64 = (uint64_t *)counts; | |
666 | uint64_t val = counts64[idx]; | |
667 | if (val < INT64_MAX) { | |
668 | counts64[idx] = val + 1; | |
669 | return; | |
670 | } | |
671 | __CFBasicHashBumpCounts(ht); | |
672 | __CFBasicHashIncSlotCount(ht, idx); | |
673 | break; | |
674 | } | |
675 | } | |
cf7d2af9 A |
676 | } |
677 | ||
8ca704e1 A |
678 | CF_INLINE void __CFBasicHashDecSlotCount(CFBasicHashRef ht, CFIndex idx) { |
679 | void *counts = __CFBasicHashGetCounts(ht); | |
680 | switch (ht->bits.counts_width) { | |
681 | case 0: ((uint8_t *)counts)[idx]--; return; | |
682 | case 1: ((uint16_t *)counts)[idx]--; return; | |
683 | case 2: ((uint32_t *)counts)[idx]--; return; | |
684 | case 3: ((uint64_t *)counts)[idx]--; return; | |
cf7d2af9 | 685 | } |
cf7d2af9 A |
686 | } |
687 | ||
8ca704e1 A |
688 | CF_INLINE uintptr_t *__CFBasicHashGetHashes(CFConstBasicHashRef ht) { |
689 | return (uintptr_t *)ht->pointers[ht->bits.hashes_offset]; | |
690 | } | |
cf7d2af9 | 691 | |
8ca704e1 A |
692 | CF_INLINE void __CFBasicHashSetHashes(CFBasicHashRef ht, uintptr_t *ptr) { |
693 | __AssignWithWriteBarrier(&ht->pointers[ht->bits.hashes_offset], ptr); | |
694 | } | |
cf7d2af9 | 695 | |
cf7d2af9 A |
696 | |
697 | // to expose the load factor, expose this function to customization | |
8ca704e1 | 698 | CF_INLINE CFIndex __CFBasicHashGetCapacityForNumBuckets(CFConstBasicHashRef ht, CFIndex num_buckets_idx) { |
cf7d2af9 | 699 | return __CFBasicHashTableCapacities[num_buckets_idx]; |
cf7d2af9 A |
700 | } |
701 | ||
8ca704e1 | 702 | CF_INLINE CFIndex __CFBasicHashGetNumBucketsIndexForCapacity(CFConstBasicHashRef ht, CFIndex capacity) { |
cf7d2af9 A |
703 | for (CFIndex idx = 0; idx < 64; idx++) { |
704 | if (capacity <= __CFBasicHashGetCapacityForNumBuckets(ht, idx)) return idx; | |
705 | } | |
706 | HALT; | |
707 | return 0; | |
708 | } | |
709 | ||
a48904a4 | 710 | CF_PRIVATE CFIndex CFBasicHashGetNumBuckets(CFConstBasicHashRef ht) { |
cf7d2af9 A |
711 | return __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; |
712 | } | |
713 | ||
a48904a4 | 714 | CF_PRIVATE CFIndex CFBasicHashGetCapacity(CFConstBasicHashRef ht) { |
cf7d2af9 A |
715 | return __CFBasicHashGetCapacityForNumBuckets(ht, ht->bits.num_buckets_idx); |
716 | } | |
717 | ||
718 | // In returned struct, .count is zero if the bucket is empty or deleted, | |
719 | // and the .weak_key field indicates which. .idx is either the index of | |
720 | // the found bucket or the index of the bucket which should be filled by | |
721 | // an add operation. For a set or multiset, the .weak_key and .weak_value | |
722 | // are the same. | |
a48904a4 | 723 | CF_PRIVATE CFBasicHashBucket CFBasicHashGetBucket(CFConstBasicHashRef ht, CFIndex idx) { |
cf7d2af9 A |
724 | CFBasicHashBucket result; |
725 | result.idx = idx; | |
8ca704e1 A |
726 | if (__CFBasicHashIsEmptyOrDeleted(ht, idx)) { |
727 | result.count = 0; | |
728 | result.weak_value = 0; | |
729 | result.weak_key = 0; | |
730 | } else { | |
731 | result.count = (ht->bits.counts_offset) ? __CFBasicHashGetSlotCount(ht, idx) : 1; | |
732 | result.weak_value = __CFBasicHashGetValue(ht, idx); | |
733 | result.weak_key = __CFBasicHashGetKey(ht, idx); | |
734 | } | |
cf7d2af9 A |
735 | return result; |
736 | } | |
737 | ||
8ca704e1 A |
738 | #if defined(__arm__) |
739 | static uintptr_t __CFBasicHashFold(uintptr_t dividend, uint8_t idx) { | |
740 | switch (idx) { | |
741 | case 1: return dividend % 3; | |
742 | case 2: return dividend % 7; | |
743 | case 3: return dividend % 13; | |
744 | case 4: return dividend % 23; | |
745 | case 5: return dividend % 41; | |
746 | case 6: return dividend % 71; | |
747 | case 7: return dividend % 127; | |
748 | case 8: return dividend % 191; | |
749 | case 9: return dividend % 251; | |
750 | case 10: return dividend % 383; | |
751 | case 11: return dividend % 631; | |
752 | case 12: return dividend % 1087; | |
753 | case 13: return dividend % 1723; | |
754 | case 14: return dividend % 2803; | |
755 | case 15: return dividend % 4523; | |
756 | case 16: return dividend % 7351; | |
757 | case 17: return dividend % 11959; | |
758 | case 18: return dividend % 19447; | |
759 | case 19: return dividend % 31231; | |
760 | case 20: return dividend % 50683; | |
761 | case 21: return dividend % 81919; | |
762 | case 22: return dividend % 132607; | |
763 | case 23: return dividend % 214519; | |
764 | case 24: return dividend % 346607; | |
765 | case 25: return dividend % 561109; | |
766 | case 26: return dividend % 907759; | |
767 | case 27: return dividend % 1468927; | |
768 | case 28: return dividend % 2376191; | |
769 | case 29: return dividend % 3845119; | |
770 | case 30: return dividend % 6221311; | |
771 | case 31: return dividend % 10066421; | |
772 | case 32: return dividend % 16287743; | |
773 | case 33: return dividend % 26354171; | |
774 | case 34: return dividend % 42641881; | |
775 | case 35: return dividend % 68996069; | |
776 | case 36: return dividend % 111638519; | |
777 | case 37: return dividend % 180634607; | |
778 | case 38: return dividend % 292272623; | |
779 | case 39: return dividend % 472907251; | |
cf7d2af9 | 780 | } |
8ca704e1 A |
781 | HALT; |
782 | return ~0; | |
783 | } | |
784 | #endif | |
785 | ||
786 | ||
787 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Linear | |
788 | #define FIND_BUCKET_HASH_STYLE 1 | |
789 | #define FIND_BUCKET_FOR_REHASH 0 | |
790 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
791 | #include "CFBasicHashFindBucket.m" | |
792 | ||
793 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Linear_NoCollision | |
794 | #define FIND_BUCKET_HASH_STYLE 1 | |
795 | #define FIND_BUCKET_FOR_REHASH 1 | |
796 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
797 | #include "CFBasicHashFindBucket.m" | |
798 | ||
799 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Linear_Indirect | |
800 | #define FIND_BUCKET_HASH_STYLE 1 | |
801 | #define FIND_BUCKET_FOR_REHASH 0 | |
802 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
803 | #include "CFBasicHashFindBucket.m" | |
804 | ||
805 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Linear_Indirect_NoCollision | |
806 | #define FIND_BUCKET_HASH_STYLE 1 | |
807 | #define FIND_BUCKET_FOR_REHASH 1 | |
808 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
809 | #include "CFBasicHashFindBucket.m" | |
810 | ||
811 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Double | |
812 | #define FIND_BUCKET_HASH_STYLE 2 | |
813 | #define FIND_BUCKET_FOR_REHASH 0 | |
814 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
815 | #include "CFBasicHashFindBucket.m" | |
816 | ||
817 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Double_NoCollision | |
818 | #define FIND_BUCKET_HASH_STYLE 2 | |
819 | #define FIND_BUCKET_FOR_REHASH 1 | |
820 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
821 | #include "CFBasicHashFindBucket.m" | |
822 | ||
823 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Double_Indirect | |
824 | #define FIND_BUCKET_HASH_STYLE 2 | |
825 | #define FIND_BUCKET_FOR_REHASH 0 | |
826 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
827 | #include "CFBasicHashFindBucket.m" | |
828 | ||
829 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Double_Indirect_NoCollision | |
830 | #define FIND_BUCKET_HASH_STYLE 2 | |
831 | #define FIND_BUCKET_FOR_REHASH 1 | |
832 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
833 | #include "CFBasicHashFindBucket.m" | |
834 | ||
835 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Exponential | |
836 | #define FIND_BUCKET_HASH_STYLE 3 | |
837 | #define FIND_BUCKET_FOR_REHASH 0 | |
838 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
839 | #include "CFBasicHashFindBucket.m" | |
840 | ||
841 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Exponential_NoCollision | |
842 | #define FIND_BUCKET_HASH_STYLE 3 | |
843 | #define FIND_BUCKET_FOR_REHASH 1 | |
844 | #define FIND_BUCKET_FOR_INDIRECT_KEY 0 | |
845 | #include "CFBasicHashFindBucket.m" | |
846 | ||
847 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Exponential_Indirect | |
848 | #define FIND_BUCKET_HASH_STYLE 3 | |
849 | #define FIND_BUCKET_FOR_REHASH 0 | |
850 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
851 | #include "CFBasicHashFindBucket.m" | |
852 | ||
853 | #define FIND_BUCKET_NAME ___CFBasicHashFindBucket_Exponential_Indirect_NoCollision | |
854 | #define FIND_BUCKET_HASH_STYLE 3 | |
855 | #define FIND_BUCKET_FOR_REHASH 1 | |
856 | #define FIND_BUCKET_FOR_INDIRECT_KEY 1 | |
857 | #include "CFBasicHashFindBucket.m" | |
858 | ||
859 | ||
860 | CF_INLINE CFBasicHashBucket __CFBasicHashFindBucket(CFConstBasicHashRef ht, uintptr_t stack_key) { | |
861 | if (0 == ht->bits.num_buckets_idx) { | |
862 | CFBasicHashBucket result = {kCFNotFound, 0UL, 0UL, 0}; | |
863 | return result; | |
cf7d2af9 | 864 | } |
8ca704e1 A |
865 | if (ht->bits.indirect_keys) { |
866 | switch (ht->bits.hash_style) { | |
867 | case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear_Indirect(ht, stack_key); | |
868 | case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double_Indirect(ht, stack_key); | |
869 | case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential_Indirect(ht, stack_key); | |
870 | } | |
871 | } else { | |
872 | switch (ht->bits.hash_style) { | |
873 | case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear(ht, stack_key); | |
874 | case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double(ht, stack_key); | |
875 | case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential(ht, stack_key); | |
cf7d2af9 | 876 | } |
cf7d2af9 | 877 | } |
8ca704e1 A |
878 | HALT; |
879 | CFBasicHashBucket result = {kCFNotFound, 0UL, 0UL, 0}; | |
880 | return result; | |
cf7d2af9 A |
881 | } |
882 | ||
8ca704e1 A |
883 | CF_INLINE CFIndex __CFBasicHashFindBucket_NoCollision(CFConstBasicHashRef ht, uintptr_t stack_key, uintptr_t key_hash) { |
884 | if (0 == ht->bits.num_buckets_idx) { | |
885 | return kCFNotFound; | |
886 | } | |
887 | if (ht->bits.indirect_keys) { | |
888 | switch (ht->bits.hash_style) { | |
889 | case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear_Indirect_NoCollision(ht, stack_key, key_hash); | |
890 | case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double_Indirect_NoCollision(ht, stack_key, key_hash); | |
891 | case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential_Indirect_NoCollision(ht, stack_key, key_hash); | |
892 | } | |
893 | } else { | |
894 | switch (ht->bits.hash_style) { | |
895 | case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear_NoCollision(ht, stack_key, key_hash); | |
896 | case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double_NoCollision(ht, stack_key, key_hash); | |
897 | case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential_NoCollision(ht, stack_key, key_hash); | |
898 | } | |
cf7d2af9 A |
899 | } |
900 | HALT; | |
8ca704e1 | 901 | return kCFNotFound; |
cf7d2af9 A |
902 | } |
903 | ||
a48904a4 | 904 | CF_PRIVATE CFBasicHashBucket CFBasicHashFindBucket(CFConstBasicHashRef ht, uintptr_t stack_key) { |
8ca704e1 A |
905 | if (__CFBasicHashSubABZero == stack_key || __CFBasicHashSubABOne == stack_key) { |
906 | CFBasicHashBucket result = {kCFNotFound, 0UL, 0UL, 0}; | |
cf7d2af9 A |
907 | return result; |
908 | } | |
8ca704e1 | 909 | return __CFBasicHashFindBucket(ht, stack_key); |
cf7d2af9 A |
910 | } |
911 | ||
a48904a4 A |
912 | CF_PRIVATE void CFBasicHashSuppressRC(CFBasicHashRef ht) { |
913 | ht->bits.null_rc = 1; | |
cf7d2af9 A |
914 | } |
915 | ||
a48904a4 A |
916 | CF_PRIVATE void CFBasicHashUnsuppressRC(CFBasicHashRef ht) { |
917 | ht->bits.null_rc = 0; | |
8ca704e1 A |
918 | } |
919 | ||
a48904a4 | 920 | CF_PRIVATE CFOptionFlags CFBasicHashGetFlags(CFConstBasicHashRef ht) { |
cf7d2af9 | 921 | CFOptionFlags flags = (ht->bits.hash_style << 13); |
8ca704e1 A |
922 | if (CFBasicHashHasStrongValues(ht)) flags |= kCFBasicHashStrongValues; |
923 | if (CFBasicHashHasStrongKeys(ht)) flags |= kCFBasicHashStrongKeys; | |
cf7d2af9 | 924 | if (ht->bits.fast_grow) flags |= kCFBasicHashAggressiveGrowth; |
cf7d2af9 | 925 | if (ht->bits.keys_offset) flags |= kCFBasicHashHasKeys; |
cf7d2af9 | 926 | if (ht->bits.counts_offset) flags |= kCFBasicHashHasCounts; |
8ca704e1 | 927 | if (__CFBasicHashHasHashCache(ht)) flags |= kCFBasicHashHasHashCache; |
cf7d2af9 A |
928 | return flags; |
929 | } | |
930 | ||
a48904a4 | 931 | CF_PRIVATE CFIndex CFBasicHashGetCount(CFConstBasicHashRef ht) { |
8ca704e1 | 932 | if (ht->bits.counts_offset) { |
cf7d2af9 A |
933 | CFIndex total = 0L; |
934 | CFIndex cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
cf7d2af9 | 935 | for (CFIndex idx = 0; idx < cnt; idx++) { |
8ca704e1 | 936 | total += __CFBasicHashGetSlotCount(ht, idx); |
cf7d2af9 A |
937 | } |
938 | return total; | |
939 | } | |
940 | return (CFIndex)ht->bits.used_buckets; | |
941 | } | |
942 | ||
a48904a4 | 943 | CF_PRIVATE CFIndex CFBasicHashGetCountOfKey(CFConstBasicHashRef ht, uintptr_t stack_key) { |
8ca704e1 A |
944 | if (__CFBasicHashSubABZero == stack_key || __CFBasicHashSubABOne == stack_key) { |
945 | return 0L; | |
946 | } | |
cf7d2af9 A |
947 | if (0L == ht->bits.used_buckets) { |
948 | return 0L; | |
949 | } | |
950 | return __CFBasicHashFindBucket(ht, stack_key).count; | |
951 | } | |
952 | ||
a48904a4 | 953 | CF_PRIVATE CFIndex CFBasicHashGetCountOfValue(CFConstBasicHashRef ht, uintptr_t stack_value) { |
8ca704e1 A |
954 | if (__CFBasicHashSubABZero == stack_value) { |
955 | return 0L; | |
956 | } | |
cf7d2af9 A |
957 | if (0L == ht->bits.used_buckets) { |
958 | return 0L; | |
959 | } | |
8ca704e1 | 960 | if (!(ht->bits.keys_offset)) { |
cf7d2af9 A |
961 | return __CFBasicHashFindBucket(ht, stack_value).count; |
962 | } | |
963 | __block CFIndex total = 0L; | |
964 | CFBasicHashApply(ht, ^(CFBasicHashBucket bkt) { | |
965 | if ((stack_value == bkt.weak_value) || __CFBasicHashTestEqualValue(ht, bkt.weak_value, stack_value)) total += bkt.count; | |
966 | return (Boolean)true; | |
967 | }); | |
968 | return total; | |
969 | } | |
970 | ||
a48904a4 | 971 | CF_PRIVATE Boolean CFBasicHashesAreEqual(CFConstBasicHashRef ht1, CFConstBasicHashRef ht2) { |
cf7d2af9 A |
972 | CFIndex cnt1 = CFBasicHashGetCount(ht1); |
973 | if (cnt1 != CFBasicHashGetCount(ht2)) return false; | |
974 | if (0 == cnt1) return true; | |
975 | __block Boolean equal = true; | |
976 | CFBasicHashApply(ht1, ^(CFBasicHashBucket bkt1) { | |
977 | CFBasicHashBucket bkt2 = __CFBasicHashFindBucket(ht2, bkt1.weak_key); | |
978 | if (bkt1.count != bkt2.count) { | |
979 | equal = false; | |
980 | return (Boolean)false; | |
981 | } | |
8ca704e1 | 982 | if ((ht1->bits.keys_offset) && (bkt1.weak_value != bkt2.weak_value) && !__CFBasicHashTestEqualValue(ht1, bkt1.weak_value, bkt2.weak_value)) { |
cf7d2af9 A |
983 | equal = false; |
984 | return (Boolean)false; | |
985 | } | |
986 | return (Boolean)true; | |
987 | }); | |
988 | return equal; | |
989 | } | |
990 | ||
a48904a4 | 991 | CF_PRIVATE void CFBasicHashApply(CFConstBasicHashRef ht, Boolean (^block)(CFBasicHashBucket)) { |
cf7d2af9 A |
992 | CFIndex used = (CFIndex)ht->bits.used_buckets, cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; |
993 | for (CFIndex idx = 0; 0 < used && idx < cnt; idx++) { | |
994 | CFBasicHashBucket bkt = CFBasicHashGetBucket(ht, idx); | |
995 | if (0 < bkt.count) { | |
996 | if (!block(bkt)) { | |
997 | return; | |
998 | } | |
999 | used--; | |
1000 | } | |
1001 | } | |
1002 | } | |
1003 | ||
a48904a4 | 1004 | CF_PRIVATE void CFBasicHashApplyIndexed(CFConstBasicHashRef ht, CFRange range, Boolean (^block)(CFBasicHashBucket)) { |
8ca704e1 A |
1005 | if (range.length < 0) HALT; |
1006 | if (range.length == 0) return; | |
1007 | CFIndex cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1008 | if (cnt < range.location + range.length) HALT; | |
1009 | for (CFIndex idx = 0; idx < range.length; idx++) { | |
1010 | CFBasicHashBucket bkt = CFBasicHashGetBucket(ht, range.location + idx); | |
1011 | if (0 < bkt.count) { | |
1012 | if (!block(bkt)) { | |
1013 | return; | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | } | |
1018 | ||
a48904a4 | 1019 | CF_PRIVATE void CFBasicHashGetElements(CFConstBasicHashRef ht, CFIndex bufferslen, uintptr_t *weak_values, uintptr_t *weak_keys) { |
cf7d2af9 | 1020 | CFIndex used = (CFIndex)ht->bits.used_buckets, cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; |
cf7d2af9 A |
1021 | CFIndex offset = 0; |
1022 | for (CFIndex idx = 0; 0 < used && idx < cnt && offset < bufferslen; idx++) { | |
8ca704e1 A |
1023 | CFBasicHashBucket bkt = CFBasicHashGetBucket(ht, idx); |
1024 | if (0 < bkt.count) { | |
cf7d2af9 | 1025 | used--; |
8ca704e1 A |
1026 | for (CFIndex cnt = bkt.count; cnt-- && offset < bufferslen;) { |
1027 | if (weak_values) { weak_values[offset] = bkt.weak_value; } | |
1028 | if (weak_keys) { weak_keys[offset] = bkt.weak_key; } | |
cf7d2af9 A |
1029 | offset++; |
1030 | } | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | ||
a48904a4 | 1035 | CF_PRIVATE unsigned long __CFBasicHashFastEnumeration(CFConstBasicHashRef ht, struct __objcFastEnumerationStateEquivalent2 *state, void *stackbuffer, unsigned long count) { |
cf7d2af9 A |
1036 | /* copy as many as count items over */ |
1037 | if (0 == state->state) { /* first time */ | |
a48904a4 | 1038 | state->mutationsPtr = (unsigned long *)&ht->bits; |
cf7d2af9 A |
1039 | } |
1040 | state->itemsPtr = (unsigned long *)stackbuffer; | |
1041 | CFIndex cntx = 0; | |
1042 | CFIndex used = (CFIndex)ht->bits.used_buckets, cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1043 | for (CFIndex idx = (CFIndex)state->state; 0 < used && idx < cnt && cntx < (CFIndex)count; idx++) { | |
1044 | CFBasicHashBucket bkt = CFBasicHashGetBucket(ht, idx); | |
1045 | if (0 < bkt.count) { | |
1046 | state->itemsPtr[cntx++] = (unsigned long)bkt.weak_key; | |
1047 | used--; | |
1048 | } | |
1049 | state->state++; | |
1050 | } | |
1051 | return cntx; | |
1052 | } | |
1053 | ||
1054 | #if ENABLE_MEMORY_COUNTERS | |
1055 | static volatile int64_t __CFBasicHashTotalCount = 0ULL; | |
1056 | static volatile int64_t __CFBasicHashTotalSize = 0ULL; | |
1057 | static volatile int64_t __CFBasicHashPeakCount = 0ULL; | |
1058 | static volatile int64_t __CFBasicHashPeakSize = 0ULL; | |
1059 | static volatile int32_t __CFBasicHashSizes[64] = {0}; | |
1060 | #endif | |
1061 | ||
1062 | static void __CFBasicHashDrain(CFBasicHashRef ht, Boolean forFinalization) { | |
1063 | #if ENABLE_MEMORY_COUNTERS | |
1064 | OSAtomicAdd64Barrier(-1 * (int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1065 | #endif | |
1066 | ||
cf7d2af9 A |
1067 | CFIndex old_num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; |
1068 | ||
1069 | CFAllocatorRef allocator = CFGetAllocator(ht); | |
1070 | Boolean nullify = (!forFinalization || !CF_IS_COLLECTABLE_ALLOCATOR(allocator)); | |
1071 | ||
8ca704e1 A |
1072 | CFBasicHashValue *old_values = NULL, *old_keys = NULL; |
1073 | void *old_counts = NULL; | |
1074 | uintptr_t *old_hashes = NULL; | |
cf7d2af9 A |
1075 | |
1076 | old_values = __CFBasicHashGetValues(ht); | |
1077 | if (nullify) __CFBasicHashSetValues(ht, NULL); | |
8ca704e1 | 1078 | if (ht->bits.keys_offset) { |
cf7d2af9 A |
1079 | old_keys = __CFBasicHashGetKeys(ht); |
1080 | if (nullify) __CFBasicHashSetKeys(ht, NULL); | |
1081 | } | |
8ca704e1 | 1082 | if (ht->bits.counts_offset) { |
cf7d2af9 A |
1083 | old_counts = __CFBasicHashGetCounts(ht); |
1084 | if (nullify) __CFBasicHashSetCounts(ht, NULL); | |
1085 | } | |
8ca704e1 | 1086 | if (__CFBasicHashHasHashCache(ht)) { |
cf7d2af9 A |
1087 | old_hashes = __CFBasicHashGetHashes(ht); |
1088 | if (nullify) __CFBasicHashSetHashes(ht, NULL); | |
1089 | } | |
1090 | ||
1091 | if (nullify) { | |
1092 | ht->bits.mutations++; | |
1093 | ht->bits.num_buckets_idx = 0; | |
1094 | ht->bits.used_buckets = 0; | |
cf7d2af9 A |
1095 | ht->bits.deleted = 0; |
1096 | } | |
1097 | ||
cf7d2af9 | 1098 | for (CFIndex idx = 0; idx < old_num_buckets; idx++) { |
8ca704e1 A |
1099 | uintptr_t stack_value = old_values[idx].neutral; |
1100 | if (stack_value != 0UL && stack_value != ~0UL) { | |
1101 | uintptr_t old_value = stack_value; | |
1102 | if (__CFBasicHashSubABZero == old_value) old_value = 0UL; | |
1103 | if (__CFBasicHashSubABOne == old_value) old_value = ~0UL; | |
1104 | __CFBasicHashEjectValue(ht, old_value); | |
cf7d2af9 | 1105 | if (old_keys) { |
8ca704e1 A |
1106 | uintptr_t old_key = old_keys[idx].neutral; |
1107 | if (__CFBasicHashSubABZero == old_key) old_key = 0UL; | |
1108 | if (__CFBasicHashSubABOne == old_key) old_key = ~0UL; | |
1109 | __CFBasicHashEjectKey(ht, old_key); | |
cf7d2af9 A |
1110 | } |
1111 | } | |
1112 | } | |
cf7d2af9 | 1113 | |
cf7d2af9 A |
1114 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
1115 | CFAllocatorDeallocate(allocator, old_values); | |
cf7d2af9 | 1116 | CFAllocatorDeallocate(allocator, old_keys); |
cf7d2af9 | 1117 | CFAllocatorDeallocate(allocator, old_counts); |
cf7d2af9 A |
1118 | CFAllocatorDeallocate(allocator, old_hashes); |
1119 | } | |
1120 | ||
1121 | #if ENABLE_MEMORY_COUNTERS | |
1122 | int64_t size_now = OSAtomicAdd64Barrier((int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1123 | while (__CFBasicHashPeakSize < size_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakSize, size_now, & __CFBasicHashPeakSize)); | |
1124 | #endif | |
1125 | } | |
1126 | ||
1127 | static void __CFBasicHashRehash(CFBasicHashRef ht, CFIndex newItemCount) { | |
1128 | #if ENABLE_MEMORY_COUNTERS | |
1129 | OSAtomicAdd64Barrier(-1 * (int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1130 | OSAtomicAdd32Barrier(-1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1131 | #endif | |
1132 | ||
1133 | if (COCOA_HASHTABLE_REHASH_START_ENABLED()) COCOA_HASHTABLE_REHASH_START(ht, CFBasicHashGetNumBuckets(ht), CFBasicHashGetSize(ht, true)); | |
1134 | ||
1135 | CFIndex new_num_buckets_idx = ht->bits.num_buckets_idx; | |
1136 | if (0 != newItemCount) { | |
1137 | if (newItemCount < 0) newItemCount = 0; | |
1138 | CFIndex new_capacity_req = ht->bits.used_buckets + newItemCount; | |
1139 | new_num_buckets_idx = __CFBasicHashGetNumBucketsIndexForCapacity(ht, new_capacity_req); | |
1140 | if (1 == newItemCount && ht->bits.fast_grow) { | |
1141 | new_num_buckets_idx++; | |
1142 | } | |
1143 | } | |
1144 | ||
1145 | CFIndex new_num_buckets = __CFBasicHashTableSizes[new_num_buckets_idx]; | |
1146 | CFIndex old_num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1147 | ||
8ca704e1 A |
1148 | CFBasicHashValue *new_values = NULL, *new_keys = NULL; |
1149 | void *new_counts = NULL; | |
1150 | uintptr_t *new_hashes = NULL; | |
cf7d2af9 A |
1151 | |
1152 | if (0 < new_num_buckets) { | |
a48904a4 | 1153 | new_values = (CFBasicHashValue *)__CFBasicHashAllocateMemory(ht, new_num_buckets, sizeof(CFBasicHashValue), CFBasicHashHasStrongValues(ht), 0); |
856091c5 | 1154 | if (!new_values) HALT; |
cf7d2af9 | 1155 | __SetLastAllocationEventName(new_values, "CFBasicHash (value-store)"); |
8ca704e1 A |
1156 | memset(new_values, 0, new_num_buckets * sizeof(CFBasicHashValue)); |
1157 | if (ht->bits.keys_offset) { | |
a48904a4 | 1158 | new_keys = (CFBasicHashValue *)__CFBasicHashAllocateMemory(ht, new_num_buckets, sizeof(CFBasicHashValue), CFBasicHashHasStrongKeys(ht), 0); |
856091c5 | 1159 | if (!new_keys) HALT; |
cf7d2af9 | 1160 | __SetLastAllocationEventName(new_keys, "CFBasicHash (key-store)"); |
8ca704e1 | 1161 | memset(new_keys, 0, new_num_buckets * sizeof(CFBasicHashValue)); |
cf7d2af9 | 1162 | } |
8ca704e1 A |
1163 | if (ht->bits.counts_offset) { |
1164 | new_counts = (uintptr_t *)__CFBasicHashAllocateMemory(ht, new_num_buckets, (1 << ht->bits.counts_width), false, false); | |
856091c5 | 1165 | if (!new_counts) HALT; |
cf7d2af9 | 1166 | __SetLastAllocationEventName(new_counts, "CFBasicHash (count-store)"); |
8ca704e1 | 1167 | memset(new_counts, 0, new_num_buckets * (1 << ht->bits.counts_width)); |
cf7d2af9 | 1168 | } |
8ca704e1 A |
1169 | if (__CFBasicHashHasHashCache(ht)) { |
1170 | new_hashes = (uintptr_t *)__CFBasicHashAllocateMemory(ht, new_num_buckets, sizeof(uintptr_t), false, false); | |
856091c5 | 1171 | if (!new_hashes) HALT; |
cf7d2af9 A |
1172 | __SetLastAllocationEventName(new_hashes, "CFBasicHash (hash-store)"); |
1173 | memset(new_hashes, 0, new_num_buckets * sizeof(uintptr_t)); | |
1174 | } | |
1175 | } | |
1176 | ||
cf7d2af9 A |
1177 | ht->bits.num_buckets_idx = new_num_buckets_idx; |
1178 | ht->bits.deleted = 0; | |
1179 | ||
8ca704e1 A |
1180 | CFBasicHashValue *old_values = NULL, *old_keys = NULL; |
1181 | void *old_counts = NULL; | |
1182 | uintptr_t *old_hashes = NULL; | |
cf7d2af9 A |
1183 | |
1184 | old_values = __CFBasicHashGetValues(ht); | |
1185 | __CFBasicHashSetValues(ht, new_values); | |
8ca704e1 | 1186 | if (ht->bits.keys_offset) { |
cf7d2af9 A |
1187 | old_keys = __CFBasicHashGetKeys(ht); |
1188 | __CFBasicHashSetKeys(ht, new_keys); | |
1189 | } | |
8ca704e1 | 1190 | if (ht->bits.counts_offset) { |
cf7d2af9 A |
1191 | old_counts = __CFBasicHashGetCounts(ht); |
1192 | __CFBasicHashSetCounts(ht, new_counts); | |
1193 | } | |
8ca704e1 | 1194 | if (__CFBasicHashHasHashCache(ht)) { |
cf7d2af9 A |
1195 | old_hashes = __CFBasicHashGetHashes(ht); |
1196 | __CFBasicHashSetHashes(ht, new_hashes); | |
1197 | } | |
1198 | ||
1199 | if (0 < old_num_buckets) { | |
cf7d2af9 | 1200 | for (CFIndex idx = 0; idx < old_num_buckets; idx++) { |
8ca704e1 A |
1201 | uintptr_t stack_value = old_values[idx].neutral; |
1202 | if (stack_value != 0UL && stack_value != ~0UL) { | |
1203 | if (__CFBasicHashSubABZero == stack_value) stack_value = 0UL; | |
1204 | if (__CFBasicHashSubABOne == stack_value) stack_value = ~0UL; | |
1205 | uintptr_t stack_key = stack_value; | |
1206 | if (ht->bits.keys_offset) { | |
1207 | stack_key = old_keys[idx].neutral; | |
1208 | if (__CFBasicHashSubABZero == stack_key) stack_key = 0UL; | |
1209 | if (__CFBasicHashSubABOne == stack_key) stack_key = ~0UL; | |
cf7d2af9 | 1210 | } |
8ca704e1 | 1211 | if (ht->bits.indirect_keys) { |
a48904a4 | 1212 | stack_key = __CFBasicHashGetIndirectKey(ht, stack_value); |
cf7d2af9 | 1213 | } |
8ca704e1 A |
1214 | CFIndex bkt_idx = __CFBasicHashFindBucket_NoCollision(ht, stack_key, old_hashes ? old_hashes[idx] : 0UL); |
1215 | __CFBasicHashSetValue(ht, bkt_idx, stack_value, false, false); | |
1216 | if (old_keys) { | |
1217 | __CFBasicHashSetKey(ht, bkt_idx, stack_key, false, false); | |
cf7d2af9 A |
1218 | } |
1219 | if (old_counts) { | |
8ca704e1 A |
1220 | switch (ht->bits.counts_width) { |
1221 | case 0: ((uint8_t *)new_counts)[bkt_idx] = ((uint8_t *)old_counts)[idx]; break; | |
1222 | case 1: ((uint16_t *)new_counts)[bkt_idx] = ((uint16_t *)old_counts)[idx]; break; | |
1223 | case 2: ((uint32_t *)new_counts)[bkt_idx] = ((uint32_t *)old_counts)[idx]; break; | |
1224 | case 3: ((uint64_t *)new_counts)[bkt_idx] = ((uint64_t *)old_counts)[idx]; break; | |
1225 | } | |
cf7d2af9 A |
1226 | } |
1227 | if (old_hashes) { | |
8ca704e1 | 1228 | new_hashes[bkt_idx] = old_hashes[idx]; |
cf7d2af9 A |
1229 | } |
1230 | } | |
1231 | } | |
1232 | } | |
1233 | ||
1234 | CFAllocatorRef allocator = CFGetAllocator(ht); | |
1235 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
1236 | CFAllocatorDeallocate(allocator, old_values); | |
cf7d2af9 | 1237 | CFAllocatorDeallocate(allocator, old_keys); |
cf7d2af9 | 1238 | CFAllocatorDeallocate(allocator, old_counts); |
cf7d2af9 A |
1239 | CFAllocatorDeallocate(allocator, old_hashes); |
1240 | } | |
1241 | ||
1242 | if (COCOA_HASHTABLE_REHASH_END_ENABLED()) COCOA_HASHTABLE_REHASH_END(ht, CFBasicHashGetNumBuckets(ht), CFBasicHashGetSize(ht, true)); | |
1243 | ||
1244 | #if ENABLE_MEMORY_COUNTERS | |
1245 | int64_t size_now = OSAtomicAdd64Barrier((int64_t) CFBasicHashGetSize(ht, true), &__CFBasicHashTotalSize); | |
1246 | while (__CFBasicHashPeakSize < size_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakSize, size_now, & __CFBasicHashPeakSize)); | |
1247 | OSAtomicAdd32Barrier(1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1248 | #endif | |
1249 | } | |
1250 | ||
a48904a4 | 1251 | CF_PRIVATE void CFBasicHashSetCapacity(CFBasicHashRef ht, CFIndex capacity) { |
cf7d2af9 A |
1252 | if (!CFBasicHashIsMutable(ht)) HALT; |
1253 | if (ht->bits.used_buckets < capacity) { | |
1254 | ht->bits.mutations++; | |
1255 | __CFBasicHashRehash(ht, capacity - ht->bits.used_buckets); | |
1256 | } | |
1257 | } | |
1258 | ||
8ca704e1 | 1259 | static void __CFBasicHashAddValue(CFBasicHashRef ht, CFIndex bkt_idx, uintptr_t stack_key, uintptr_t stack_value) { |
cf7d2af9 | 1260 | ht->bits.mutations++; |
cf7d2af9 A |
1261 | if (CFBasicHashGetCapacity(ht) < ht->bits.used_buckets + 1) { |
1262 | __CFBasicHashRehash(ht, 1); | |
8ca704e1 A |
1263 | bkt_idx = __CFBasicHashFindBucket_NoCollision(ht, stack_key, 0); |
1264 | } else if (__CFBasicHashIsDeleted(ht, bkt_idx)) { | |
cf7d2af9 A |
1265 | ht->bits.deleted--; |
1266 | } | |
8ca704e1 A |
1267 | uintptr_t key_hash = 0; |
1268 | if (__CFBasicHashHasHashCache(ht)) { | |
1269 | key_hash = __CFBasicHashHashKey(ht, stack_key); | |
cf7d2af9 | 1270 | } |
8ca704e1 A |
1271 | stack_value = __CFBasicHashImportValue(ht, stack_value); |
1272 | if (ht->bits.keys_offset) { | |
1273 | stack_key = __CFBasicHashImportKey(ht, stack_key); | |
cf7d2af9 | 1274 | } |
8ca704e1 A |
1275 | __CFBasicHashSetValue(ht, bkt_idx, stack_value, false, false); |
1276 | if (ht->bits.keys_offset) { | |
1277 | __CFBasicHashSetKey(ht, bkt_idx, stack_key, false, false); | |
cf7d2af9 | 1278 | } |
8ca704e1 A |
1279 | if (ht->bits.counts_offset) { |
1280 | __CFBasicHashIncSlotCount(ht, bkt_idx); | |
cf7d2af9 | 1281 | } |
8ca704e1 A |
1282 | if (__CFBasicHashHasHashCache(ht)) { |
1283 | __CFBasicHashGetHashes(ht)[bkt_idx] = key_hash; | |
cf7d2af9 A |
1284 | } |
1285 | ht->bits.used_buckets++; | |
1286 | } | |
1287 | ||
8ca704e1 | 1288 | static void __CFBasicHashReplaceValue(CFBasicHashRef ht, CFIndex bkt_idx, uintptr_t stack_key, uintptr_t stack_value) { |
cf7d2af9 A |
1289 | ht->bits.mutations++; |
1290 | stack_value = __CFBasicHashImportValue(ht, stack_value); | |
8ca704e1 | 1291 | if (ht->bits.keys_offset) { |
cf7d2af9 | 1292 | stack_key = __CFBasicHashImportKey(ht, stack_key); |
cf7d2af9 | 1293 | } |
8ca704e1 A |
1294 | __CFBasicHashSetValue(ht, bkt_idx, stack_value, false, false); |
1295 | if (ht->bits.keys_offset) { | |
1296 | __CFBasicHashSetKey(ht, bkt_idx, stack_key, false, false); | |
cf7d2af9 A |
1297 | } |
1298 | } | |
1299 | ||
8ca704e1 | 1300 | static void __CFBasicHashRemoveValue(CFBasicHashRef ht, CFIndex bkt_idx) { |
cf7d2af9 | 1301 | ht->bits.mutations++; |
8ca704e1 A |
1302 | __CFBasicHashSetValue(ht, bkt_idx, ~0UL, false, true); |
1303 | if (ht->bits.keys_offset) { | |
1304 | __CFBasicHashSetKey(ht, bkt_idx, ~0UL, false, true); | |
1305 | } | |
1306 | if (ht->bits.counts_offset) { | |
1307 | __CFBasicHashDecSlotCount(ht, bkt_idx); | |
1308 | } | |
1309 | if (__CFBasicHashHasHashCache(ht)) { | |
1310 | __CFBasicHashGetHashes(ht)[bkt_idx] = 0; | |
cf7d2af9 A |
1311 | } |
1312 | ht->bits.used_buckets--; | |
1313 | ht->bits.deleted++; | |
1314 | Boolean do_shrink = false; | |
1315 | if (ht->bits.fast_grow) { // == slow shrink | |
1316 | do_shrink = (5 < ht->bits.num_buckets_idx && ht->bits.used_buckets < __CFBasicHashGetCapacityForNumBuckets(ht, ht->bits.num_buckets_idx - 5)); | |
1317 | } else { | |
1318 | do_shrink = (2 < ht->bits.num_buckets_idx && ht->bits.used_buckets < __CFBasicHashGetCapacityForNumBuckets(ht, ht->bits.num_buckets_idx - 2)); | |
1319 | } | |
1320 | if (do_shrink) { | |
1321 | __CFBasicHashRehash(ht, -1); | |
1322 | return; | |
1323 | } | |
1324 | do_shrink = (0 == ht->bits.deleted); // .deleted roll-over | |
1325 | CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1326 | do_shrink = do_shrink || ((20 <= num_buckets) && (num_buckets / 4 <= ht->bits.deleted)); | |
1327 | if (do_shrink) { | |
1328 | __CFBasicHashRehash(ht, 0); | |
1329 | } | |
1330 | } | |
1331 | ||
a48904a4 | 1332 | CF_PRIVATE Boolean CFBasicHashAddValue(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t stack_value) { |
cf7d2af9 | 1333 | if (!CFBasicHashIsMutable(ht)) HALT; |
8ca704e1 A |
1334 | if (__CFBasicHashSubABZero == stack_key) HALT; |
1335 | if (__CFBasicHashSubABOne == stack_key) HALT; | |
1336 | if (__CFBasicHashSubABZero == stack_value) HALT; | |
1337 | if (__CFBasicHashSubABOne == stack_value) HALT; | |
cf7d2af9 A |
1338 | CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key); |
1339 | if (0 < bkt.count) { | |
1340 | ht->bits.mutations++; | |
8ca704e1 A |
1341 | if (ht->bits.counts_offset && bkt.count < LONG_MAX) { // if not yet as large as a CFIndex can be... otherwise clamp and do nothing |
1342 | __CFBasicHashIncSlotCount(ht, bkt.idx); | |
1343 | return true; | |
cf7d2af9 A |
1344 | } |
1345 | } else { | |
8ca704e1 A |
1346 | __CFBasicHashAddValue(ht, bkt.idx, stack_key, stack_value); |
1347 | return true; | |
cf7d2af9 | 1348 | } |
8ca704e1 | 1349 | return false; |
cf7d2af9 A |
1350 | } |
1351 | ||
a48904a4 | 1352 | CF_PRIVATE void CFBasicHashReplaceValue(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t stack_value) { |
cf7d2af9 | 1353 | if (!CFBasicHashIsMutable(ht)) HALT; |
8ca704e1 A |
1354 | if (__CFBasicHashSubABZero == stack_key) HALT; |
1355 | if (__CFBasicHashSubABOne == stack_key) HALT; | |
1356 | if (__CFBasicHashSubABZero == stack_value) HALT; | |
1357 | if (__CFBasicHashSubABOne == stack_value) HALT; | |
cf7d2af9 A |
1358 | CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key); |
1359 | if (0 < bkt.count) { | |
8ca704e1 | 1360 | __CFBasicHashReplaceValue(ht, bkt.idx, stack_key, stack_value); |
cf7d2af9 A |
1361 | } |
1362 | } | |
1363 | ||
a48904a4 | 1364 | CF_PRIVATE void CFBasicHashSetValue(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t stack_value) { |
cf7d2af9 | 1365 | if (!CFBasicHashIsMutable(ht)) HALT; |
8ca704e1 A |
1366 | if (__CFBasicHashSubABZero == stack_key) HALT; |
1367 | if (__CFBasicHashSubABOne == stack_key) HALT; | |
1368 | if (__CFBasicHashSubABZero == stack_value) HALT; | |
1369 | if (__CFBasicHashSubABOne == stack_value) HALT; | |
cf7d2af9 A |
1370 | CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key); |
1371 | if (0 < bkt.count) { | |
8ca704e1 | 1372 | __CFBasicHashReplaceValue(ht, bkt.idx, stack_key, stack_value); |
cf7d2af9 | 1373 | } else { |
8ca704e1 | 1374 | __CFBasicHashAddValue(ht, bkt.idx, stack_key, stack_value); |
cf7d2af9 A |
1375 | } |
1376 | } | |
1377 | ||
a48904a4 | 1378 | CF_PRIVATE CFIndex CFBasicHashRemoveValue(CFBasicHashRef ht, uintptr_t stack_key) { |
cf7d2af9 | 1379 | if (!CFBasicHashIsMutable(ht)) HALT; |
8ca704e1 | 1380 | if (__CFBasicHashSubABZero == stack_key || __CFBasicHashSubABOne == stack_key) return 0; |
cf7d2af9 A |
1381 | CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key); |
1382 | if (1 < bkt.count) { | |
1383 | ht->bits.mutations++; | |
8ca704e1 A |
1384 | if (ht->bits.counts_offset && bkt.count < LONG_MAX) { // if not as large as a CFIndex can be... otherwise clamp and do nothing |
1385 | __CFBasicHashDecSlotCount(ht, bkt.idx); | |
cf7d2af9 A |
1386 | } |
1387 | } else if (0 < bkt.count) { | |
8ca704e1 A |
1388 | __CFBasicHashRemoveValue(ht, bkt.idx); |
1389 | } | |
1390 | return bkt.count; | |
1391 | } | |
1392 | ||
a48904a4 | 1393 | CF_PRIVATE CFIndex CFBasicHashRemoveValueAtIndex(CFBasicHashRef ht, CFIndex idx) { |
8ca704e1 A |
1394 | if (!CFBasicHashIsMutable(ht)) HALT; |
1395 | CFBasicHashBucket bkt = CFBasicHashGetBucket(ht, idx); | |
1396 | if (1 < bkt.count) { | |
1397 | ht->bits.mutations++; | |
1398 | if (ht->bits.counts_offset && bkt.count < LONG_MAX) { // if not as large as a CFIndex can be... otherwise clamp and do nothing | |
1399 | __CFBasicHashDecSlotCount(ht, bkt.idx); | |
1400 | } | |
1401 | } else if (0 < bkt.count) { | |
1402 | __CFBasicHashRemoveValue(ht, bkt.idx); | |
cf7d2af9 A |
1403 | } |
1404 | return bkt.count; | |
1405 | } | |
1406 | ||
a48904a4 | 1407 | CF_PRIVATE void CFBasicHashRemoveAllValues(CFBasicHashRef ht) { |
cf7d2af9 A |
1408 | if (!CFBasicHashIsMutable(ht)) HALT; |
1409 | if (0 == ht->bits.num_buckets_idx) return; | |
1410 | __CFBasicHashDrain(ht, false); | |
1411 | } | |
1412 | ||
a48904a4 | 1413 | CF_PRIVATE Boolean CFBasicHashAddIntValueAndInc(CFBasicHashRef ht, uintptr_t stack_key, uintptr_t int_value) { |
8ca704e1 A |
1414 | if (!CFBasicHashIsMutable(ht)) HALT; |
1415 | if (__CFBasicHashSubABZero == stack_key) HALT; | |
1416 | if (__CFBasicHashSubABOne == stack_key) HALT; | |
1417 | if (__CFBasicHashSubABZero == int_value) HALT; | |
1418 | if (__CFBasicHashSubABOne == int_value) HALT; | |
1419 | CFBasicHashBucket bkt = __CFBasicHashFindBucket(ht, stack_key); | |
1420 | if (0 < bkt.count) { | |
1421 | ht->bits.mutations++; | |
1422 | } else { | |
1423 | // must rehash before renumbering | |
1424 | if (CFBasicHashGetCapacity(ht) < ht->bits.used_buckets + 1) { | |
1425 | __CFBasicHashRehash(ht, 1); | |
1426 | bkt.idx = __CFBasicHashFindBucket_NoCollision(ht, stack_key, 0); | |
1427 | } | |
1428 | CFIndex cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1429 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
1430 | if (!__CFBasicHashIsEmptyOrDeleted(ht, idx)) { | |
1431 | uintptr_t stack_value = __CFBasicHashGetValue(ht, idx); | |
1432 | if (int_value <= stack_value) { | |
1433 | stack_value++; | |
1434 | __CFBasicHashSetValue(ht, idx, stack_value, true, false); | |
1435 | ht->bits.mutations++; | |
1436 | } | |
1437 | } | |
1438 | } | |
1439 | __CFBasicHashAddValue(ht, bkt.idx, stack_key, int_value); | |
1440 | return true; | |
1441 | } | |
1442 | return false; | |
1443 | } | |
1444 | ||
a48904a4 | 1445 | CF_PRIVATE void CFBasicHashRemoveIntValueAndDec(CFBasicHashRef ht, uintptr_t int_value) { |
8ca704e1 A |
1446 | if (!CFBasicHashIsMutable(ht)) HALT; |
1447 | if (__CFBasicHashSubABZero == int_value) HALT; | |
1448 | if (__CFBasicHashSubABOne == int_value) HALT; | |
1449 | uintptr_t bkt_idx = ~0UL; | |
1450 | CFIndex cnt = (CFIndex)__CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1451 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
1452 | if (!__CFBasicHashIsEmptyOrDeleted(ht, idx)) { | |
1453 | uintptr_t stack_value = __CFBasicHashGetValue(ht, idx); | |
1454 | if (int_value == stack_value) { | |
1455 | bkt_idx = idx; | |
1456 | } | |
1457 | if (int_value < stack_value) { | |
1458 | stack_value--; | |
1459 | __CFBasicHashSetValue(ht, idx, stack_value, true, false); | |
1460 | ht->bits.mutations++; | |
1461 | } | |
1462 | } | |
1463 | } | |
1464 | __CFBasicHashRemoveValue(ht, bkt_idx); | |
1465 | } | |
1466 | ||
a48904a4 | 1467 | CF_PRIVATE size_t CFBasicHashGetSize(CFConstBasicHashRef ht, Boolean total) { |
cf7d2af9 | 1468 | size_t size = sizeof(struct __CFBasicHash); |
8ca704e1 A |
1469 | if (ht->bits.keys_offset) size += sizeof(CFBasicHashValue *); |
1470 | if (ht->bits.counts_offset) size += sizeof(void *); | |
1471 | if (__CFBasicHashHasHashCache(ht)) size += sizeof(uintptr_t *); | |
cf7d2af9 A |
1472 | if (total) { |
1473 | CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; | |
1474 | if (0 < num_buckets) { | |
1475 | size += malloc_size(__CFBasicHashGetValues(ht)); | |
8ca704e1 A |
1476 | if (ht->bits.keys_offset) size += malloc_size(__CFBasicHashGetKeys(ht)); |
1477 | if (ht->bits.counts_offset) size += malloc_size(__CFBasicHashGetCounts(ht)); | |
1478 | if (__CFBasicHashHasHashCache(ht)) size += malloc_size(__CFBasicHashGetHashes(ht)); | |
cf7d2af9 A |
1479 | } |
1480 | } | |
1481 | return size; | |
1482 | } | |
1483 | ||
a48904a4 | 1484 | CF_PRIVATE CFStringRef CFBasicHashCopyDescription(CFConstBasicHashRef ht, Boolean detailed, CFStringRef prefix, CFStringRef entryPrefix, Boolean describeElements) { |
cf7d2af9 | 1485 | CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
8ca704e1 | 1486 | CFStringAppendFormat(result, NULL, CFSTR("%@{type = %s %s%s, count = %ld,\n"), prefix, (CFBasicHashIsMutable(ht) ? "mutable" : "immutable"), ((ht->bits.counts_offset) ? "multi" : ""), ((ht->bits.keys_offset) ? "dict" : "set"), CFBasicHashGetCount(ht)); |
cf7d2af9 A |
1487 | if (detailed) { |
1488 | const char *cb_type = "custom"; | |
8ca704e1 | 1489 | CFStringAppendFormat(result, NULL, CFSTR("%@hash cache = %s, strong values = %s, strong keys = %s, cb = %s,\n"), prefix, (__CFBasicHashHasHashCache(ht) ? "yes" : "no"), (CFBasicHashHasStrongValues(ht) ? "yes" : "no"), (CFBasicHashHasStrongKeys(ht) ? "yes" : "no"), cb_type); |
a48904a4 | 1490 | CFStringAppendFormat(result, NULL, CFSTR("%@num bucket index = %d, num buckets = %ld, capacity = %ld, num buckets used = %u,\n"), prefix, ht->bits.num_buckets_idx, CFBasicHashGetNumBuckets(ht), (long)CFBasicHashGetCapacity(ht), ht->bits.used_buckets); |
8ca704e1 | 1491 | CFStringAppendFormat(result, NULL, CFSTR("%@counts width = %d, finalized = %s,\n"), prefix,((ht->bits.counts_offset) ? (1 << ht->bits.counts_width) : 0), (ht->bits.finalized ? "yes" : "no")); |
a48904a4 | 1492 | CFStringAppendFormat(result, NULL, CFSTR("%@num mutations = %ld, num deleted = %ld, size = %ld, total size = %ld,\n"), prefix, (long)ht->bits.mutations, (long)ht->bits.deleted, CFBasicHashGetSize(ht, false), CFBasicHashGetSize(ht, true)); |
8ca704e1 | 1493 | CFStringAppendFormat(result, NULL, CFSTR("%@values ptr = %p, keys ptr = %p, counts ptr = %p, hashes ptr = %p,\n"), prefix, __CFBasicHashGetValues(ht), ((ht->bits.keys_offset) ? __CFBasicHashGetKeys(ht) : NULL), ((ht->bits.counts_offset) ? __CFBasicHashGetCounts(ht) : NULL), (__CFBasicHashHasHashCache(ht) ? __CFBasicHashGetHashes(ht) : NULL)); |
cf7d2af9 A |
1494 | } |
1495 | CFStringAppendFormat(result, NULL, CFSTR("%@entries =>\n"), prefix); | |
1496 | CFBasicHashApply(ht, ^(CFBasicHashBucket bkt) { | |
1497 | CFStringRef vDesc = NULL, kDesc = NULL; | |
8ca704e1 A |
1498 | if (!describeElements) { |
1499 | vDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%p>"), (void *)bkt.weak_value); | |
1500 | if (ht->bits.keys_offset) { | |
1501 | kDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%p>"), (void *)bkt.weak_key); | |
1502 | } | |
1503 | } else { | |
a48904a4 | 1504 | vDesc = __CFBasicHashDescValue(ht, bkt.weak_value); |
8ca704e1 | 1505 | if (ht->bits.keys_offset) { |
a48904a4 | 1506 | kDesc = __CFBasicHashDescKey(ht, bkt.weak_key); |
8ca704e1 | 1507 | } |
cf7d2af9 | 1508 | } |
8ca704e1 | 1509 | if (ht->bits.keys_offset && ht->bits.counts_offset) { |
cf7d2af9 | 1510 | CFStringAppendFormat(result, NULL, CFSTR("%@%ld : %@ = %@ (%ld)\n"), entryPrefix, bkt.idx, kDesc, vDesc, bkt.count); |
8ca704e1 | 1511 | } else if (ht->bits.keys_offset) { |
cf7d2af9 | 1512 | CFStringAppendFormat(result, NULL, CFSTR("%@%ld : %@ = %@\n"), entryPrefix, bkt.idx, kDesc, vDesc); |
8ca704e1 | 1513 | } else if (ht->bits.counts_offset) { |
cf7d2af9 A |
1514 | CFStringAppendFormat(result, NULL, CFSTR("%@%ld : %@ (%ld)\n"), entryPrefix, bkt.idx, vDesc, bkt.count); |
1515 | } else { | |
1516 | CFStringAppendFormat(result, NULL, CFSTR("%@%ld : %@\n"), entryPrefix, bkt.idx, vDesc); | |
1517 | } | |
1518 | if (kDesc) CFRelease(kDesc); | |
1519 | if (vDesc) CFRelease(vDesc); | |
1520 | return (Boolean)true; | |
1521 | }); | |
1522 | CFStringAppendFormat(result, NULL, CFSTR("%@}\n"), prefix); | |
1523 | return result; | |
1524 | } | |
1525 | ||
a48904a4 | 1526 | CF_PRIVATE void CFBasicHashShow(CFConstBasicHashRef ht) { |
cf7d2af9 A |
1527 | CFStringRef str = CFBasicHashCopyDescription(ht, true, CFSTR(""), CFSTR("\t"), false); |
1528 | CFShow(str); | |
1529 | CFRelease(str); | |
1530 | } | |
1531 | ||
a48904a4 | 1532 | CF_PRIVATE Boolean __CFBasicHashEqual(CFTypeRef cf1, CFTypeRef cf2) { |
cf7d2af9 A |
1533 | CFBasicHashRef ht1 = (CFBasicHashRef)cf1; |
1534 | CFBasicHashRef ht2 = (CFBasicHashRef)cf2; | |
1535 | //#warning this used to require that the key and value equal callbacks were pointer identical | |
1536 | return CFBasicHashesAreEqual(ht1, ht2); | |
1537 | } | |
1538 | ||
a48904a4 | 1539 | CF_PRIVATE CFHashCode __CFBasicHashHash(CFTypeRef cf) { |
cf7d2af9 A |
1540 | CFBasicHashRef ht = (CFBasicHashRef)cf; |
1541 | return CFBasicHashGetCount(ht); | |
1542 | } | |
1543 | ||
a48904a4 | 1544 | CF_PRIVATE CFStringRef __CFBasicHashCopyDescription(CFTypeRef cf) { |
cf7d2af9 A |
1545 | CFBasicHashRef ht = (CFBasicHashRef)cf; |
1546 | CFStringRef desc = CFBasicHashCopyDescription(ht, false, CFSTR(""), CFSTR("\t"), true); | |
1547 | CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFBasicHash %p [%p]>%@"), cf, CFGetAllocator(cf), desc); | |
1548 | CFRelease(desc); | |
1549 | return result; | |
1550 | } | |
1551 | ||
a48904a4 | 1552 | CF_PRIVATE void __CFBasicHashDeallocate(CFTypeRef cf) { |
cf7d2af9 A |
1553 | CFBasicHashRef ht = (CFBasicHashRef)cf; |
1554 | if (ht->bits.finalized) HALT; | |
1555 | ht->bits.finalized = 1; | |
1556 | __CFBasicHashDrain(ht, true); | |
1557 | #if ENABLE_MEMORY_COUNTERS | |
1558 | OSAtomicAdd64Barrier(-1, &__CFBasicHashTotalCount); | |
1559 | OSAtomicAdd32Barrier(-1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1560 | #endif | |
1561 | } | |
1562 | ||
1563 | static CFTypeID __kCFBasicHashTypeID = _kCFRuntimeNotATypeID; | |
1564 | ||
1565 | static const CFRuntimeClass __CFBasicHashClass = { | |
1566 | _kCFRuntimeScannedObject, | |
1567 | "CFBasicHash", | |
1568 | NULL, // init | |
1569 | NULL, // copy | |
1570 | __CFBasicHashDeallocate, | |
1571 | __CFBasicHashEqual, | |
1572 | __CFBasicHashHash, | |
1573 | NULL, // | |
1574 | __CFBasicHashCopyDescription | |
1575 | }; | |
1576 | ||
a48904a4 | 1577 | CF_PRIVATE CFTypeID CFBasicHashGetTypeID(void) { |
d7384798 A |
1578 | static dispatch_once_t initOnce; |
1579 | dispatch_once(&initOnce, ^{ __kCFBasicHashTypeID = _CFRuntimeRegisterClass(&__CFBasicHashClass); }); | |
cf7d2af9 A |
1580 | return __kCFBasicHashTypeID; |
1581 | } | |
1582 | ||
a48904a4 | 1583 | CF_PRIVATE CFBasicHashRef CFBasicHashCreate(CFAllocatorRef allocator, CFOptionFlags flags, const CFBasicHashCallbacks *cb) { |
cf7d2af9 | 1584 | size_t size = sizeof(struct __CFBasicHash) - sizeof(CFRuntimeBase); |
cf7d2af9 | 1585 | if (flags & kCFBasicHashHasKeys) size += sizeof(CFBasicHashValue *); // keys |
8ca704e1 | 1586 | if (flags & kCFBasicHashHasCounts) size += sizeof(void *); // counts |
cf7d2af9 A |
1587 | if (flags & kCFBasicHashHasHashCache) size += sizeof(uintptr_t *); // hashes |
1588 | CFBasicHashRef ht = (CFBasicHashRef)_CFRuntimeCreateInstance(allocator, CFBasicHashGetTypeID(), size, NULL); | |
856091c5 | 1589 | if (NULL == ht) return NULL; |
cf7d2af9 A |
1590 | |
1591 | ht->bits.finalized = 0; | |
cf7d2af9 A |
1592 | ht->bits.hash_style = (flags >> 13) & 0x3; |
1593 | ht->bits.fast_grow = (flags & kCFBasicHashAggressiveGrowth) ? 1 : 0; | |
8ca704e1 A |
1594 | ht->bits.counts_width = 0; |
1595 | ht->bits.strong_values = (flags & kCFBasicHashStrongValues) ? 1 : 0; | |
1596 | ht->bits.strong_keys = (flags & kCFBasicHashStrongKeys) ? 1 : 0; | |
1597 | ht->bits.weak_values = (flags & kCFBasicHashWeakValues) ? 1 : 0; | |
1598 | ht->bits.weak_keys = (flags & kCFBasicHashWeakKeys) ? 1 : 0; | |
1599 | ht->bits.int_values = (flags & kCFBasicHashIntegerValues) ? 1 : 0; | |
1600 | ht->bits.int_keys = (flags & kCFBasicHashIntegerKeys) ? 1 : 0; | |
1601 | ht->bits.indirect_keys = (flags & kCFBasicHashIndirectKeys) ? 1 : 0; | |
cf7d2af9 A |
1602 | ht->bits.num_buckets_idx = 0; |
1603 | ht->bits.used_buckets = 0; | |
cf7d2af9 A |
1604 | ht->bits.deleted = 0; |
1605 | ht->bits.mutations = 1; | |
1606 | ||
8ca704e1 A |
1607 | if (ht->bits.strong_values && ht->bits.weak_values) HALT; |
1608 | if (ht->bits.strong_values && ht->bits.int_values) HALT; | |
1609 | if (ht->bits.strong_keys && ht->bits.weak_keys) HALT; | |
1610 | if (ht->bits.strong_keys && ht->bits.int_keys) HALT; | |
1611 | if (ht->bits.weak_values && ht->bits.int_values) HALT; | |
1612 | if (ht->bits.weak_keys && ht->bits.int_keys) HALT; | |
1613 | if (ht->bits.indirect_keys && ht->bits.strong_keys) HALT; | |
1614 | if (ht->bits.indirect_keys && ht->bits.weak_keys) HALT; | |
1615 | if (ht->bits.indirect_keys && ht->bits.int_keys) HALT; | |
1616 | ||
cf7d2af9 | 1617 | uint64_t offset = 1; |
cf7d2af9 | 1618 | ht->bits.keys_offset = (flags & kCFBasicHashHasKeys) ? offset++ : 0; |
cf7d2af9 | 1619 | ht->bits.counts_offset = (flags & kCFBasicHashHasCounts) ? offset++ : 0; |
cf7d2af9 A |
1620 | ht->bits.hashes_offset = (flags & kCFBasicHashHasHashCache) ? offset++ : 0; |
1621 | ||
856091c5 | 1622 | #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI |
8ca704e1 A |
1623 | ht->bits.hashes_offset = 0; |
1624 | ht->bits.strong_values = 0; | |
1625 | ht->bits.strong_keys = 0; | |
1626 | ht->bits.weak_values = 0; | |
1627 | ht->bits.weak_keys = 0; | |
1628 | #endif | |
1629 | ||
a48904a4 A |
1630 | ht->bits.__kret = CFBasicHashGetPtrIndex((void *)cb->retainKey); |
1631 | ht->bits.__vret = CFBasicHashGetPtrIndex((void *)cb->retainValue); | |
1632 | ht->bits.__krel = CFBasicHashGetPtrIndex((void *)cb->releaseKey); | |
1633 | ht->bits.__vrel = CFBasicHashGetPtrIndex((void *)cb->releaseValue); | |
1634 | ht->bits.__kdes = CFBasicHashGetPtrIndex((void *)cb->copyKeyDescription); | |
1635 | ht->bits.__vdes = CFBasicHashGetPtrIndex((void *)cb->copyValueDescription); | |
1636 | ht->bits.__kequ = CFBasicHashGetPtrIndex((void *)cb->equateKeys); | |
1637 | ht->bits.__vequ = CFBasicHashGetPtrIndex((void *)cb->equateValues); | |
1638 | ht->bits.__khas = CFBasicHashGetPtrIndex((void *)cb->hashKey); | |
1639 | ht->bits.__kget = CFBasicHashGetPtrIndex((void *)cb->getIndirectKey); | |
1640 | ||
cf7d2af9 A |
1641 | for (CFIndex idx = 0; idx < offset; idx++) { |
1642 | ht->pointers[idx] = NULL; | |
1643 | } | |
1644 | ||
1645 | #if ENABLE_MEMORY_COUNTERS | |
1646 | int64_t size_now = OSAtomicAdd64Barrier((int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1647 | while (__CFBasicHashPeakSize < size_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakSize, size_now, & __CFBasicHashPeakSize)); | |
1648 | int64_t count_now = OSAtomicAdd64Barrier(1, & __CFBasicHashTotalCount); | |
1649 | while (__CFBasicHashPeakCount < count_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakCount, count_now, & __CFBasicHashPeakCount)); | |
1650 | OSAtomicAdd32Barrier(1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1651 | #endif | |
1652 | ||
1653 | return ht; | |
1654 | } | |
1655 | ||
a48904a4 | 1656 | CF_PRIVATE CFBasicHashRef CFBasicHashCreateCopy(CFAllocatorRef allocator, CFConstBasicHashRef src_ht) { |
cf7d2af9 | 1657 | size_t size = CFBasicHashGetSize(src_ht, false) - sizeof(CFRuntimeBase); |
856091c5 A |
1658 | CFIndex new_num_buckets = __CFBasicHashTableSizes[src_ht->bits.num_buckets_idx]; |
1659 | CFBasicHashValue *new_values = NULL, *new_keys = NULL; | |
1660 | void *new_counts = NULL; | |
1661 | uintptr_t *new_hashes = NULL; | |
1662 | ||
1663 | if (0 < new_num_buckets) { | |
1664 | Boolean strongValues = CFBasicHashHasStrongValues(src_ht) && !(kCFUseCollectableAllocator && !CF_IS_COLLECTABLE_ALLOCATOR(allocator)); | |
1665 | Boolean strongKeys = CFBasicHashHasStrongKeys(src_ht) && !(kCFUseCollectableAllocator && !CF_IS_COLLECTABLE_ALLOCATOR(allocator)); | |
a48904a4 | 1666 | new_values = (CFBasicHashValue *)__CFBasicHashAllocateMemory2(allocator, new_num_buckets, sizeof(CFBasicHashValue), strongValues, 0); |
856091c5 A |
1667 | if (!new_values) return NULL; // in this unusual circumstance, leak previously allocated blocks for now |
1668 | __SetLastAllocationEventName(new_values, "CFBasicHash (value-store)"); | |
1669 | if (src_ht->bits.keys_offset) { | |
1670 | new_keys = (CFBasicHashValue *)__CFBasicHashAllocateMemory2(allocator, new_num_buckets, sizeof(CFBasicHashValue), strongKeys, false); | |
1671 | if (!new_keys) return NULL; // in this unusual circumstance, leak previously allocated blocks for now | |
1672 | __SetLastAllocationEventName(new_keys, "CFBasicHash (key-store)"); | |
1673 | } | |
1674 | if (src_ht->bits.counts_offset) { | |
1675 | new_counts = (uintptr_t *)__CFBasicHashAllocateMemory2(allocator, new_num_buckets, (1 << src_ht->bits.counts_width), false, false); | |
1676 | if (!new_counts) return NULL; // in this unusual circumstance, leak previously allocated blocks for now | |
1677 | __SetLastAllocationEventName(new_counts, "CFBasicHash (count-store)"); | |
1678 | } | |
1679 | if (__CFBasicHashHasHashCache(src_ht)) { | |
1680 | new_hashes = (uintptr_t *)__CFBasicHashAllocateMemory2(allocator, new_num_buckets, sizeof(uintptr_t), false, false); | |
1681 | if (!new_hashes) return NULL; // in this unusual circumstance, leak previously allocated blocks for now | |
1682 | __SetLastAllocationEventName(new_hashes, "CFBasicHash (hash-store)"); | |
1683 | } | |
1684 | } | |
1685 | ||
cf7d2af9 | 1686 | CFBasicHashRef ht = (CFBasicHashRef)_CFRuntimeCreateInstance(allocator, CFBasicHashGetTypeID(), size, NULL); |
856091c5 | 1687 | if (NULL == ht) return NULL; // in this unusual circumstance, leak previously allocated blocks for now |
cf7d2af9 A |
1688 | |
1689 | memmove((uint8_t *)ht + sizeof(CFRuntimeBase), (uint8_t *)src_ht + sizeof(CFRuntimeBase), sizeof(ht->bits)); | |
1690 | if (kCFUseCollectableAllocator && !CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
1691 | ht->bits.strong_values = 0; | |
cf7d2af9 | 1692 | ht->bits.strong_keys = 0; |
8ca704e1 A |
1693 | ht->bits.weak_values = 0; |
1694 | ht->bits.weak_keys = 0; | |
cf7d2af9 A |
1695 | } |
1696 | ht->bits.finalized = 0; | |
1697 | ht->bits.mutations = 1; | |
cf7d2af9 | 1698 | |
cf7d2af9 A |
1699 | if (0 == new_num_buckets) { |
1700 | #if ENABLE_MEMORY_COUNTERS | |
1701 | int64_t size_now = OSAtomicAdd64Barrier((int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1702 | while (__CFBasicHashPeakSize < size_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakSize, size_now, & __CFBasicHashPeakSize)); | |
1703 | int64_t count_now = OSAtomicAdd64Barrier(1, & __CFBasicHashTotalCount); | |
1704 | while (__CFBasicHashPeakCount < count_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakCount, count_now, & __CFBasicHashPeakCount)); | |
1705 | OSAtomicAdd32Barrier(1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1706 | #endif | |
1707 | return ht; | |
1708 | } | |
1709 | ||
8ca704e1 A |
1710 | CFBasicHashValue *old_values = NULL, *old_keys = NULL; |
1711 | void *old_counts = NULL; | |
1712 | uintptr_t *old_hashes = NULL; | |
1713 | ||
cf7d2af9 | 1714 | old_values = __CFBasicHashGetValues(src_ht); |
8ca704e1 | 1715 | if (src_ht->bits.keys_offset) { |
cf7d2af9 A |
1716 | old_keys = __CFBasicHashGetKeys(src_ht); |
1717 | } | |
8ca704e1 | 1718 | if (src_ht->bits.counts_offset) { |
cf7d2af9 A |
1719 | old_counts = __CFBasicHashGetCounts(src_ht); |
1720 | } | |
8ca704e1 | 1721 | if (__CFBasicHashHasHashCache(src_ht)) { |
cf7d2af9 A |
1722 | old_hashes = __CFBasicHashGetHashes(src_ht); |
1723 | } | |
1724 | ||
cf7d2af9 | 1725 | __CFBasicHashSetValues(ht, new_values); |
cf7d2af9 A |
1726 | if (new_keys) { |
1727 | __CFBasicHashSetKeys(ht, new_keys); | |
1728 | } | |
cf7d2af9 A |
1729 | if (new_counts) { |
1730 | __CFBasicHashSetCounts(ht, new_counts); | |
1731 | } | |
cf7d2af9 A |
1732 | if (new_hashes) { |
1733 | __CFBasicHashSetHashes(ht, new_hashes); | |
1734 | } | |
1735 | ||
8ca704e1 A |
1736 | for (CFIndex idx = 0; idx < new_num_buckets; idx++) { |
1737 | uintptr_t stack_value = old_values[idx].neutral; | |
1738 | if (stack_value != 0UL && stack_value != ~0UL) { | |
1739 | uintptr_t old_value = stack_value; | |
1740 | if (__CFBasicHashSubABZero == old_value) old_value = 0UL; | |
1741 | if (__CFBasicHashSubABOne == old_value) old_value = ~0UL; | |
1742 | __CFBasicHashSetValue(ht, idx, __CFBasicHashImportValue(ht, old_value), true, false); | |
1743 | if (new_keys) { | |
1744 | uintptr_t old_key = old_keys[idx].neutral; | |
1745 | if (__CFBasicHashSubABZero == old_key) old_key = 0UL; | |
1746 | if (__CFBasicHashSubABOne == old_key) old_key = ~0UL; | |
1747 | __CFBasicHashSetKey(ht, idx, __CFBasicHashImportKey(ht, old_key), true, false); | |
1748 | } | |
1749 | } else { | |
1750 | __CFBasicHashSetValue(ht, idx, stack_value, true, true); | |
1751 | if (new_keys) { | |
1752 | __CFBasicHashSetKey(ht, idx, stack_value, true, true); | |
1753 | } | |
1754 | } | |
1755 | } | |
1756 | if (new_counts) memmove(new_counts, old_counts, new_num_buckets * (1 << ht->bits.counts_width)); | |
1757 | if (new_hashes) memmove(new_hashes, old_hashes, new_num_buckets * sizeof(uintptr_t)); | |
1758 | ||
cf7d2af9 A |
1759 | #if ENABLE_MEMORY_COUNTERS |
1760 | int64_t size_now = OSAtomicAdd64Barrier((int64_t) CFBasicHashGetSize(ht, true), & __CFBasicHashTotalSize); | |
1761 | while (__CFBasicHashPeakSize < size_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakSize, size_now, & __CFBasicHashPeakSize)); | |
1762 | int64_t count_now = OSAtomicAdd64Barrier(1, & __CFBasicHashTotalCount); | |
1763 | while (__CFBasicHashPeakCount < count_now && !OSAtomicCompareAndSwap64Barrier(__CFBasicHashPeakCount, count_now, & __CFBasicHashPeakCount)); | |
1764 | OSAtomicAdd32Barrier(1, &__CFBasicHashSizes[ht->bits.num_buckets_idx]); | |
1765 | #endif | |
1766 | ||
1767 | return ht; | |
1768 | } | |
1769 | ||
cf7d2af9 | 1770 |