]> git.saurik.com Git - apple/cf.git/blob - CFBasicHashFindBucket.m
CF-1153.18.tar.gz
[apple/cf.git] / CFBasicHashFindBucket.m
1 /*
2 * Copyright (c) 2015 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 /* CFBasicHashFindBucket.m
25 Copyright (c) 2009-2014, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
27 */
28
29
30 #if !defined(FIND_BUCKET_NAME) || !defined(FIND_BUCKET_HASH_STYLE) || !defined(FIND_BUCKET_FOR_REHASH) || !defined(FIND_BUCKET_FOR_INDIRECT_KEY)
31 #error All of FIND_BUCKET_NAME, FIND_BUCKET_HASH_STYLE, FIND_BUCKET_FOR_REHASH, and FIND_BUCKET_FOR_INDIRECT_KEY must be defined before #including this file.
32 #endif
33
34
35 // During rehashing of a mutable CFBasicHash, we know that there are no
36 // deleted slots and the keys have already been uniqued. When rehashing,
37 // if key_hash is non-0, we use it as the hash code.
38 static
39 #if FIND_BUCKET_FOR_REHASH
40 CFIndex
41 #else
42 CFBasicHashBucket
43 #endif
44 FIND_BUCKET_NAME (CFConstBasicHashRef ht, uintptr_t stack_key
45 #if FIND_BUCKET_FOR_REHASH
46 , uintptr_t key_hash
47 #endif
48 ) {
49 uint8_t num_buckets_idx = ht->bits.num_buckets_idx;
50 uintptr_t num_buckets = __CFBasicHashTableSizes[num_buckets_idx];
51 #if FIND_BUCKET_FOR_REHASH
52 CFHashCode hash_code = key_hash ? key_hash : __CFBasicHashHashKey(ht, stack_key);
53 #else
54 CFHashCode hash_code = __CFBasicHashHashKey(ht, stack_key);
55 #endif
56
57 #if FIND_BUCKET_HASH_STYLE == 1 // __kCFBasicHashLinearHashingValue
58 // Linear probing, with c = 1
59 // probe[0] = h1(k)
60 // probe[i] = (h1(k) + i * c) mod num_buckets, i = 1 .. num_buckets - 1
61 // h1(k) = k mod num_buckets
62 #if defined(__arm__)
63 uintptr_t h1 = __CFBasicHashFold(hash_code, num_buckets_idx);
64 #else
65 uintptr_t h1 = hash_code % num_buckets;
66 #endif
67 #elif FIND_BUCKET_HASH_STYLE == 2 // __kCFBasicHashDoubleHashingValue
68 // Double hashing
69 // probe[0] = h1(k)
70 // probe[i] = (h1(k) + i * h2(k)) mod num_buckets, i = 1 .. num_buckets - 1
71 // h1(k) = k mod num_buckets
72 // h2(k) = floor(k / num_buckets) mod num_buckets
73 #if defined(__arm__)
74 uintptr_t h1 = __CFBasicHashFold(hash_code, num_buckets_idx);
75 uintptr_t h2 = __CFBasicHashFold(hash_code / num_buckets, num_buckets_idx);
76 #else
77 uintptr_t h1 = hash_code % num_buckets;
78 uintptr_t h2 = (hash_code / num_buckets) % num_buckets;
79 #endif
80 if (0 == h2) h2 = num_buckets - 1;
81 #elif FIND_BUCKET_HASH_STYLE == 3 // __kCFBasicHashExponentialHashingValue
82 // Improved exponential hashing
83 // probe[0] = h1(k)
84 // probe[i] = (h1(k) + pr(k)^i * h2(k)) mod num_buckets, i = 1 .. num_buckets - 1
85 // h1(k) = k mod num_buckets
86 // h2(k) = floor(k / num_buckets) mod num_buckets
87 // note: h2(k) has the effect of rotating the sequence if it is constant
88 // note: pr(k) is any primitive root of num_buckets, varying this gives different sequences
89 #if defined(__arm__)
90 uintptr_t h1 = __CFBasicHashFold(hash_code, num_buckets_idx);
91 uintptr_t h2 = __CFBasicHashFold(hash_code / num_buckets, num_buckets_idx);
92 #else
93 uintptr_t h1 = hash_code % num_buckets;
94 uintptr_t h2 = (hash_code / num_buckets) % num_buckets;
95 #endif
96 if (0 == h2) h2 = num_buckets - 1;
97 uintptr_t pr = __CFBasicHashPrimitiveRoots[num_buckets_idx];
98 #endif
99
100 COCOA_HASHTABLE_PROBING_START(ht, num_buckets);
101 CFBasicHashValue *keys = (ht->bits.keys_offset) ? __CFBasicHashGetKeys(ht) : __CFBasicHashGetValues(ht);
102 #if !FIND_BUCKET_FOR_REHASH
103 uintptr_t *hashes = (__CFBasicHashHasHashCache(ht)) ? __CFBasicHashGetHashes(ht) : NULL;
104 #endif
105 CFIndex deleted_idx = kCFNotFound;
106 uintptr_t probe = h1;
107 #if FIND_BUCKET_HASH_STYLE == 3 // __kCFBasicHashExponentialHashingValue
108 uintptr_t acc = pr;
109 #endif
110 for (CFIndex idx = 0; idx < num_buckets; idx++) {
111 uintptr_t curr_key = keys[probe].neutral;
112 if (curr_key == 0UL) {
113 COCOA_HASHTABLE_PROBE_EMPTY(ht, probe);
114 #if FIND_BUCKET_FOR_REHASH
115 CFIndex result = (kCFNotFound == deleted_idx) ? probe : deleted_idx;
116 #else
117 CFBasicHashBucket result;
118 result.idx = (kCFNotFound == deleted_idx) ? probe : deleted_idx;
119 result.count = 0;
120 #endif
121 COCOA_HASHTABLE_PROBING_END(ht, idx + 1);
122 return result;
123 #if !FIND_BUCKET_FOR_REHASH
124 } else if (curr_key == ~0UL) {
125 COCOA_HASHTABLE_PROBE_DELETED(ht, probe);
126 if (kCFNotFound == deleted_idx) {
127 deleted_idx = probe;
128 }
129 } else {
130 COCOA_HASHTABLE_PROBE_VALID(ht, probe);
131 if (__CFBasicHashSubABZero == curr_key) curr_key = 0UL;
132 if (__CFBasicHashSubABOne == curr_key) curr_key = ~0UL;
133 #if FIND_BUCKET_FOR_INDIRECT_KEY
134 // curr_key holds the value coming in here
135 curr_key = __CFBasicHashGetIndirectKey(ht, curr_key);
136 #endif
137 if (curr_key == stack_key || ((!hashes || hashes[probe] == hash_code) && __CFBasicHashTestEqualKey(ht, curr_key, stack_key))) {
138 COCOA_HASHTABLE_PROBING_END(ht, idx + 1);
139 #if FIND_BUCKET_FOR_REHASH
140 CFIndex result = probe;
141 #else
142 CFBasicHashBucket result;
143 result.idx = probe;
144 result.weak_value = __CFBasicHashGetValue(ht, probe);
145 result.weak_key = curr_key;
146 result.count = (ht->bits.counts_offset) ? __CFBasicHashGetSlotCount(ht, probe) : 1;
147 #endif
148 return result;
149 }
150 #endif
151 }
152
153 #if FIND_BUCKET_HASH_STYLE == 1 // __kCFBasicHashLinearHashingValue
154 probe += 1;
155 if (num_buckets <= probe) {
156 probe -= num_buckets;
157 }
158 #elif FIND_BUCKET_HASH_STYLE == 2 // __kCFBasicHashDoubleHashingValue
159 probe += h2;
160 if (num_buckets <= probe) {
161 probe -= num_buckets;
162 }
163 #elif FIND_BUCKET_HASH_STYLE == 3 // __kCFBasicHashExponentialHashingValue
164 probe = h1 + h2 * acc;
165 if (num_buckets <= probe) {
166 #if defined(__arm__)
167 probe = __CFBasicHashFold(probe, num_buckets_idx);
168 #else
169 probe = probe % num_buckets;
170 #endif
171 }
172 acc = acc * pr;
173 if (num_buckets <= acc) {
174 #if defined(__arm__)
175 acc = __CFBasicHashFold(acc, num_buckets_idx);
176 #else
177 acc = acc % num_buckets;
178 #endif
179 }
180 #endif
181
182 }
183 COCOA_HASHTABLE_PROBING_END(ht, num_buckets);
184 #if FIND_BUCKET_FOR_REHASH
185 CFIndex result = deleted_idx;
186 #else
187 CFBasicHashBucket result;
188 result.idx = deleted_idx;
189 result.count = 0;
190 #endif
191 return result; // all buckets full or deleted, return first deleted element which was found
192 }
193
194 #undef FIND_BUCKET_NAME
195 #undef FIND_BUCKET_HASH_STYLE
196 #undef FIND_BUCKET_FOR_REHASH
197 #undef FIND_BUCKET_FOR_INDIRECT_KEY
198