]>
Commit | Line | Data |
---|---|---|
9ce05555 A |
1 | /* |
2 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
22 | * | |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* CFSet.c | |
26 | Copyright 1998-2002, Apple, Inc. All rights reserved. | |
27 | Responsibility: Christopher Kane | |
28 | */ | |
29 | ||
30 | #include <CoreFoundation/CFSet.h> | |
31 | #include "CFInternal.h" | |
32 | ||
33 | const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; | |
34 | const CFSetCallBacks kCFCopyStringSetCallBacks = {0, (void *)CFStringCreateCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; | |
35 | static const CFSetCallBacks __kCFNullSetCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; | |
36 | ||
37 | ||
38 | static const uint32_t __CFSetCapacities[42] = { | |
39 | 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, | |
40 | 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, | |
41 | 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, | |
42 | 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U | |
43 | }; | |
44 | ||
45 | static const uint32_t __CFSetBuckets[42] = { // primes | |
46 | 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, | |
47 | 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, | |
48 | 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, | |
49 | 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U | |
50 | }; | |
51 | ||
52 | CF_INLINE CFIndex __CFSetRoundUpCapacity(CFIndex capacity) { | |
53 | CFIndex idx; | |
54 | for (idx = 0; idx < 42 && __CFSetCapacities[idx] < (uint32_t)capacity; idx++); | |
55 | if (42 <= idx) HALT; | |
56 | return __CFSetCapacities[idx]; | |
57 | } | |
58 | ||
59 | CF_INLINE CFIndex __CFSetNumBucketsForCapacity(CFIndex capacity) { | |
60 | CFIndex idx; | |
61 | for (idx = 0; idx < 42 && __CFSetCapacities[idx] < (uint32_t)capacity; idx++); | |
62 | if (42 <= idx) HALT; | |
63 | return __CFSetBuckets[idx]; | |
64 | } | |
65 | ||
66 | enum { /* Bits 1-0 */ | |
67 | __kCFSetImmutable = 0, /* unchangable and fixed capacity */ | |
68 | __kCFSetMutable = 1, /* changeable and variable capacity */ | |
69 | __kCFSetFixedMutable = 3 /* changeable and fixed capacity */ | |
70 | }; | |
71 | ||
72 | enum { /* Bits 3-2 */ | |
73 | __kCFSetHasNullCallBacks = 0, | |
74 | __kCFSetHasCFTypeCallBacks = 1, | |
75 | __kCFSetHasCustomCallBacks = 3 /* callbacks are at end of header */ | |
76 | }; | |
77 | ||
78 | struct __CFSetBucket { | |
79 | const void *_key; | |
80 | }; | |
81 | ||
82 | struct __CFSet { | |
83 | CFRuntimeBase _base; | |
84 | CFIndex _count; /* number of values */ | |
85 | CFIndex _capacity; /* maximum number of values */ | |
86 | CFIndex _bucketsUsed; /* number of slots used */ | |
87 | CFIndex _bucketsNum; /* number of slots */ | |
88 | const void *_emptyMarker; | |
89 | const void *_deletedMarker; | |
90 | void *_context; /* private */ | |
91 | struct __CFSetBucket *_buckets; /* can be NULL if not allocated yet */ | |
92 | }; | |
93 | ||
94 | CF_INLINE bool __CFSetBucketIsEmpty(CFSetRef set, const struct __CFSetBucket *bucket) { | |
95 | return (set->_emptyMarker == bucket->_key); | |
96 | } | |
97 | ||
98 | CF_INLINE bool __CFSetBucketIsDeleted(CFSetRef set, const struct __CFSetBucket *bucket) { | |
99 | return (set->_deletedMarker == bucket->_key); | |
100 | } | |
101 | ||
102 | CF_INLINE bool __CFSetBucketIsOccupied(CFSetRef set, const struct __CFSetBucket *bucket) { | |
103 | return (set->_emptyMarker != bucket->_key && set->_deletedMarker != bucket->_key); | |
104 | } | |
105 | ||
106 | /* Bits 1-0 of the base reserved bits are used for mutability variety */ | |
107 | /* Bits 3-2 of the base reserved bits are used for callback indicator bits */ | |
108 | ||
109 | CF_INLINE CFIndex __CFSetGetType(CFSetRef set) { | |
110 | return __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 1, 0); | |
111 | } | |
112 | ||
113 | CF_INLINE CFIndex __CFSetGetSizeOfType(CFIndex t) { | |
114 | CFIndex size = sizeof(struct __CFSet); | |
115 | if (__CFBitfieldGetValue(t, 3, 2) == __kCFSetHasCustomCallBacks) { | |
116 | size += sizeof(CFSetCallBacks); | |
117 | } | |
118 | return size; | |
119 | } | |
120 | ||
121 | CF_INLINE const CFSetCallBacks *__CFSetGetCallBacks(CFSetRef set) { | |
122 | CFSetCallBacks *result = NULL; | |
123 | switch (__CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { | |
124 | case __kCFSetHasNullCallBacks: | |
125 | return &__kCFNullSetCallBacks; | |
126 | case __kCFSetHasCFTypeCallBacks: | |
127 | return &kCFTypeSetCallBacks; | |
128 | case __kCFSetHasCustomCallBacks: | |
129 | break; | |
130 | } | |
131 | result = (CFSetCallBacks *)((uint8_t *)set + sizeof(struct __CFSet)); | |
132 | return result; | |
133 | } | |
134 | ||
135 | CF_INLINE bool __CFSetCallBacksMatchNull(const CFSetCallBacks *c) { | |
136 | return (NULL == c || | |
137 | (c->retain == __kCFNullSetCallBacks.retain && | |
138 | c->release == __kCFNullSetCallBacks.release && | |
139 | c->copyDescription == __kCFNullSetCallBacks.copyDescription && | |
140 | c->equal == __kCFNullSetCallBacks.equal && | |
141 | c->hash == __kCFNullSetCallBacks.hash)); | |
142 | } | |
143 | ||
144 | CF_INLINE bool __CFSetCallBacksMatchCFType(const CFSetCallBacks *c) { | |
145 | return (&kCFTypeSetCallBacks == c || | |
146 | (c->retain == kCFTypeSetCallBacks.retain && | |
147 | c->release == kCFTypeSetCallBacks.release && | |
148 | c->copyDescription == kCFTypeSetCallBacks.copyDescription && | |
149 | c->equal == kCFTypeSetCallBacks.equal && | |
150 | c->hash == kCFTypeSetCallBacks.hash)); | |
151 | } | |
152 | ||
153 | ||
154 | static void __CFSetFindBuckets1(CFSetRef set, const void *key, struct __CFSetBucket **match) { | |
155 | const CFSetCallBacks *cb = __CFSetGetCallBacks(set); | |
156 | struct __CFSetBucket *buckets = set->_buckets; | |
157 | CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, set->_context) : (CFHashCode)key; | |
158 | UInt32 start = keyHash % set->_bucketsNum; | |
159 | UInt32 probe = start; | |
160 | UInt32 probeskip = 1; | |
161 | *match = NULL; | |
162 | for (;;) { | |
163 | struct __CFSetBucket *currentBucket = buckets + probe; | |
164 | if (__CFSetBucketIsEmpty(set, currentBucket)) { | |
165 | return; | |
166 | } else if (__CFSetBucketIsDeleted(set, currentBucket)) { | |
167 | /* do nothing */ | |
168 | } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currentBucket->_key, key, set->_context))) { | |
169 | *match = currentBucket; | |
170 | return; | |
171 | } | |
172 | probe = (probe + probeskip) % set->_bucketsNum; | |
173 | if (start == probe) return; | |
174 | } | |
175 | } | |
176 | ||
177 | static void __CFSetFindBuckets2(CFSetRef set, const void *key, struct __CFSetBucket **match, struct __CFSetBucket **nomatch) { | |
178 | const CFSetCallBacks *cb = __CFSetGetCallBacks(set); | |
179 | struct __CFSetBucket *buckets = set->_buckets; | |
180 | CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, set->_context) : (CFHashCode)key; | |
181 | UInt32 start = keyHash % set->_bucketsNum; | |
182 | UInt32 probe = start; | |
183 | UInt32 probeskip = 1; | |
184 | *match = NULL; | |
185 | *nomatch = NULL; | |
186 | for (;;) { | |
187 | struct __CFSetBucket *currentBucket = buckets + probe; | |
188 | if (__CFSetBucketIsEmpty(set, currentBucket)) { | |
189 | if (!*nomatch) *nomatch = currentBucket; | |
190 | return; | |
191 | } else if (__CFSetBucketIsDeleted(set, currentBucket)) { | |
192 | if (!*nomatch) *nomatch = currentBucket; | |
193 | } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currentBucket->_key, key, set->_context)))) { | |
194 | *match = currentBucket; | |
195 | if (*nomatch) return; | |
196 | } | |
197 | probe = (probe + probeskip) % set->_bucketsNum; | |
198 | if (start == probe) return; | |
199 | } | |
200 | } | |
201 | ||
202 | static void __CFSetFindNewEmptyMarker(CFSetRef set) { | |
203 | struct __CFSetBucket *buckets; | |
204 | const void *newEmpty; | |
205 | bool hit; | |
206 | CFIndex idx, nbuckets; | |
207 | buckets = set->_buckets; | |
208 | nbuckets = set->_bucketsNum; | |
209 | newEmpty = set->_emptyMarker; | |
210 | do { | |
211 | (intptr_t)newEmpty -= 2; | |
212 | hit = false; | |
213 | for (idx = 0; idx < nbuckets; idx++) { | |
214 | if (newEmpty == buckets[idx]._key) { | |
215 | hit = true; | |
216 | break; | |
217 | } | |
218 | } | |
219 | } while (hit); | |
220 | for (idx = 0; idx < nbuckets; idx++) { | |
221 | if (set->_emptyMarker == buckets[idx]._key) { | |
222 | buckets[idx]._key = newEmpty; | |
223 | } | |
224 | } | |
225 | ((struct __CFSet *)set)->_emptyMarker = newEmpty; | |
226 | } | |
227 | ||
228 | static void __CFSetFindNewDeletedMarker(CFSetRef set) { | |
229 | struct __CFSetBucket *buckets; | |
230 | const void *newDeleted; | |
231 | bool hit; | |
232 | CFIndex idx, nbuckets; | |
233 | buckets = set->_buckets; | |
234 | nbuckets = set->_bucketsNum; | |
235 | newDeleted = set->_deletedMarker; | |
236 | do { | |
237 | (intptr_t)newDeleted += 2; | |
238 | hit = false; | |
239 | for (idx = 0; idx < nbuckets; idx++) { | |
240 | if (newDeleted == buckets[idx]._key) { | |
241 | hit = true; | |
242 | break; | |
243 | } | |
244 | } | |
245 | } while (hit); | |
246 | for (idx = 0; idx < nbuckets; idx++) { | |
247 | if (set->_deletedMarker == buckets[idx]._key) { | |
248 | buckets[idx]._key = newDeleted; | |
249 | } | |
250 | } | |
251 | ((struct __CFSet *)set)->_deletedMarker = newDeleted; | |
252 | } | |
253 | ||
254 | static bool __CFSetEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
255 | CFSetRef set1 = (CFSetRef)cf1; | |
256 | CFSetRef set2 = (CFSetRef)cf2; | |
257 | const CFSetCallBacks *cb1, *cb2; | |
258 | const struct __CFSetBucket *buckets; | |
259 | CFIndex idx, nbuckets; | |
260 | if (set1 == set2) return true; | |
261 | if (set1->_count != set2->_count) return false; | |
262 | cb1 = __CFSetGetCallBacks(set1); | |
263 | cb2 = __CFSetGetCallBacks(set2); | |
264 | if (cb1->equal != cb2->equal) return false; | |
265 | if (0 == set1->_count) return true; /* after function comparison! */ | |
266 | buckets = set1->_buckets; | |
267 | nbuckets = set1->_bucketsNum; | |
268 | for (idx = 0; idx < nbuckets; idx++) { | |
269 | if (__CFSetBucketIsOccupied(set1, &buckets[idx])) { | |
270 | if (1 != CFSetGetCountOfValue(set2, buckets[idx]._key)) { | |
271 | return false; | |
272 | } | |
273 | } | |
274 | } | |
275 | return true; | |
276 | } | |
277 | ||
278 | static CFHashCode __CFSetHash(CFTypeRef cf) { | |
279 | CFSetRef set = (CFSetRef)cf; | |
280 | return set->_count; | |
281 | } | |
282 | ||
283 | static CFStringRef __CFSetCopyDescription(CFTypeRef cf) { | |
284 | CFSetRef set = (CFSetRef)cf; | |
285 | const CFSetCallBacks *cb; | |
286 | const struct __CFSetBucket *buckets; | |
287 | CFIndex idx, nbuckets; | |
288 | CFMutableStringRef result; | |
289 | cb = __CFSetGetCallBacks(set); | |
290 | buckets = set->_buckets; | |
291 | nbuckets = set->_bucketsNum; | |
292 | result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
293 | CFStringAppendFormat(result, NULL, CFSTR("<CFSet %p [%p]>{count = %u, capacity = %u, values = (\n"), set, CFGetAllocator(set), set->_count, set->_capacity); | |
294 | for (idx = 0; idx < nbuckets; idx++) { | |
295 | if (__CFSetBucketIsOccupied(set, &buckets[idx])) { | |
296 | CFStringRef desc = NULL; | |
297 | if (NULL != cb->copyDescription) { | |
298 | desc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), buckets[idx]._key, set->_context); | |
299 | } | |
300 | if (NULL != desc) { | |
301 | CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, desc, NULL); | |
302 | CFRelease(desc); | |
303 | } else { | |
304 | CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, buckets[idx]._key, NULL); | |
305 | } | |
306 | } | |
307 | } | |
308 | CFStringAppend(result, CFSTR(")}")); | |
309 | return result; | |
310 | } | |
311 | ||
312 | static void __CFSetDeallocate(CFTypeRef cf) { | |
313 | CFMutableSetRef set = (CFMutableSetRef)cf; | |
314 | CFAllocatorRef allocator = __CFGetAllocator(set); | |
315 | if (__CFSetGetType(set) == __kCFSetImmutable) { | |
316 | __CFBitfieldSetValue(((CFRuntimeBase *)set)->_info, 1, 0, __kCFSetFixedMutable); | |
317 | } | |
318 | CFSetRemoveAllValues(set); | |
319 | if (__CFSetGetType(set) == __kCFSetMutable && set->_buckets) { | |
320 | CFAllocatorDeallocate(allocator, set->_buckets); | |
321 | } | |
322 | } | |
323 | ||
324 | static CFTypeID __kCFSetTypeID = _kCFRuntimeNotATypeID; | |
325 | ||
326 | static const CFRuntimeClass __CFSetClass = { | |
327 | 0, | |
328 | "CFSet", | |
329 | NULL, // init | |
330 | NULL, // copy | |
331 | __CFSetDeallocate, | |
332 | (void *)__CFSetEqual, | |
333 | __CFSetHash, | |
334 | NULL, // | |
335 | __CFSetCopyDescription | |
336 | }; | |
337 | ||
338 | __private_extern__ void __CFSetInitialize(void) { | |
339 | __kCFSetTypeID = _CFRuntimeRegisterClass(&__CFSetClass); | |
340 | } | |
341 | ||
342 | CFTypeID CFSetGetTypeID(void) { | |
343 | return __kCFSetTypeID; | |
344 | } | |
345 | ||
346 | static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFSetCallBacks *callBacks) { | |
347 | struct __CFSet *memory; | |
348 | UInt32 size; | |
349 | CFIndex idx; | |
350 | __CFBitfieldSetValue(flags, 31, 2, 0); | |
351 | if (__CFSetCallBacksMatchNull(callBacks)) { | |
352 | __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasNullCallBacks); | |
353 | } else if (__CFSetCallBacksMatchCFType(callBacks)) { | |
354 | __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCFTypeCallBacks); | |
355 | } else { | |
356 | __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCustomCallBacks); | |
357 | } | |
358 | size = __CFSetGetSizeOfType(flags) - sizeof(CFRuntimeBase); | |
359 | switch (__CFBitfieldGetValue(flags, 1, 0)) { | |
360 | case __kCFSetImmutable: | |
361 | case __kCFSetFixedMutable: | |
362 | size += __CFSetNumBucketsForCapacity(capacity) * sizeof(struct __CFSetBucket); | |
363 | break; | |
364 | case __kCFSetMutable: | |
365 | break; | |
366 | } | |
367 | memory = (struct __CFSet *)_CFRuntimeCreateInstance(allocator, __kCFSetTypeID, size, NULL); | |
368 | if (NULL == memory) { | |
369 | return NULL; | |
370 | } | |
371 | __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); | |
372 | memory->_count = 0; | |
373 | memory->_bucketsUsed = 0; | |
374 | memory->_emptyMarker = (const void *)0xa1b1c1d3; | |
375 | memory->_deletedMarker = (const void *)0xa1b1c1d5; | |
376 | memory->_context = NULL; | |
377 | switch (__CFBitfieldGetValue(flags, 1, 0)) { | |
378 | case __kCFSetImmutable: | |
379 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (immutable)"); | |
380 | memory->_capacity = capacity; /* Don't round up capacity */ | |
381 | memory->_bucketsNum = __CFSetNumBucketsForCapacity(memory->_capacity); | |
382 | memory->_buckets = (struct __CFSetBucket *)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); | |
383 | for (idx = memory->_bucketsNum; idx--;) { | |
384 | memory->_buckets[idx]._key = memory->_emptyMarker; | |
385 | } | |
386 | break; | |
387 | case __kCFSetFixedMutable: | |
388 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (mutable-fixed)"); | |
389 | memory->_capacity = capacity; /* Don't round up capacity */ | |
390 | memory->_bucketsNum = __CFSetNumBucketsForCapacity(memory->_capacity); | |
391 | memory->_buckets = (struct __CFSetBucket *)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); | |
392 | for (idx = memory->_bucketsNum; idx--;) { | |
393 | memory->_buckets[idx]._key = memory->_emptyMarker; | |
394 | } | |
395 | break; | |
396 | case __kCFSetMutable: | |
397 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (mutable-variable)"); | |
398 | memory->_capacity = __CFSetRoundUpCapacity(1); | |
399 | memory->_bucketsNum = 0; | |
400 | memory->_buckets = NULL; | |
401 | break; | |
402 | } | |
403 | if (__kCFSetHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { | |
404 | const CFSetCallBacks *cb = __CFSetGetCallBacks((CFSetRef)memory); | |
405 | *(CFSetCallBacks *)cb = *callBacks; | |
406 | FAULT_CALLBACK((void **)&(cb->retain)); | |
407 | FAULT_CALLBACK((void **)&(cb->release)); | |
408 | FAULT_CALLBACK((void **)&(cb->copyDescription)); | |
409 | FAULT_CALLBACK((void **)&(cb->equal)); | |
410 | FAULT_CALLBACK((void **)&(cb->hash)); | |
411 | } | |
412 | return (CFSetRef)memory; | |
413 | } | |
414 | ||
415 | CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFSetCallBacks *callBacks) { | |
416 | CFSetRef result; | |
417 | UInt32 flags; | |
418 | CFIndex idx; | |
419 | CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); | |
420 | result = __CFSetInit(allocator, __kCFSetImmutable, numValues, callBacks); | |
421 | flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); | |
422 | if (flags == __kCFSetImmutable) { | |
423 | __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFSetFixedMutable); | |
424 | } | |
425 | for (idx = 0; idx < numValues; idx++) { | |
426 | CFSetAddValue((CFMutableSetRef)result, values[idx]); | |
427 | } | |
428 | __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); | |
429 | return result; | |
430 | } | |
431 | ||
432 | CFMutableSetRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetCallBacks *callBacks) { | |
433 | CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); | |
434 | return (CFMutableSetRef)__CFSetInit(allocator, (0 == capacity) ? __kCFSetMutable : __kCFSetFixedMutable, capacity, callBacks); | |
435 | } | |
436 | ||
437 | CFSetRef CFSetCreateCopy(CFAllocatorRef allocator, CFSetRef set) { | |
438 | CFSetRef result; | |
439 | const CFSetCallBacks *cb; | |
440 | CFIndex numValues = CFSetGetCount(set); | |
441 | const void **list, *buffer[256]; | |
442 | list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); | |
443 | if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); | |
444 | CFSetGetValues(set, list); | |
445 | cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); | |
446 | result = CFSetCreate(allocator, list, numValues, cb); | |
447 | if (list != buffer) CFAllocatorDeallocate(allocator, list); | |
448 | return result; | |
449 | } | |
450 | ||
451 | CFMutableSetRef CFSetCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFSetRef set) { | |
452 | CFMutableSetRef result; | |
453 | const CFSetCallBacks *cb; | |
454 | CFIndex idx, numValues = CFSetGetCount(set); | |
455 | const void **list, *buffer[256]; | |
456 | CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable sets, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); | |
457 | list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); | |
458 | if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); | |
459 | CFSetGetValues(set, list); | |
460 | cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); | |
461 | result = CFSetCreateMutable(allocator, capacity, cb); | |
462 | if (0 == capacity) _CFSetSetCapacity(result, numValues); | |
463 | for (idx = 0; idx < numValues; idx++) { | |
464 | CFSetAddValue(result, list[idx]); | |
465 | } | |
466 | if (list != buffer) CFAllocatorDeallocate(allocator, list); | |
467 | return result; | |
468 | } | |
469 | ||
470 | void _CFSetSetContext(CFSetRef set, void *context) { | |
471 | ((struct __CFSet *)set)->_context = context; | |
472 | } | |
473 | ||
474 | CFIndex CFSetGetCount(CFSetRef set) { | |
475 | CF_OBJC_FUNCDISPATCH0(__kCFSetTypeID, CFIndex, set, "count"); | |
476 | __CFGenericValidateType(set, __kCFSetTypeID); | |
477 | return set->_count; | |
478 | } | |
479 | ||
480 | CFIndex CFSetGetCountOfValue(CFSetRef set, const void *value) { | |
481 | struct __CFSetBucket *match; | |
482 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, CFIndex, set, "countForObject:", value); | |
483 | __CFGenericValidateType(set, __kCFSetTypeID); | |
484 | if (0 == set->_count) return 0; | |
485 | __CFSetFindBuckets1(set, value, &match); | |
486 | return (match ? 1 : 0); | |
487 | } | |
488 | ||
489 | Boolean CFSetContainsValue(CFSetRef set, const void *value) { | |
490 | struct __CFSetBucket *match; | |
491 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, char, set, "containsObject:", value); | |
492 | __CFGenericValidateType(set, __kCFSetTypeID); | |
493 | if (0 == set->_count) return false; | |
494 | __CFSetFindBuckets1(set, value, &match); | |
495 | return (match ? true : false); | |
496 | } | |
497 | ||
498 | const void *CFSetGetValue(CFSetRef set, const void *value) { | |
499 | struct __CFSetBucket *match; | |
500 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, const void *, set, "member:", value); | |
501 | __CFGenericValidateType(set, __kCFSetTypeID); | |
502 | if (0 == set->_count) return NULL; | |
503 | __CFSetFindBuckets1(set, value, &match); | |
504 | return (match ? match->_key : NULL); | |
505 | } | |
506 | ||
507 | Boolean CFSetGetValueIfPresent(CFSetRef set, const void *candidate, const void **value) { | |
508 | struct __CFSetBucket *match; | |
509 | CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, char, set, "_getValue:forObj:", (void * *)value, candidate); | |
510 | __CFGenericValidateType(set, __kCFSetTypeID); | |
511 | if (0 == set->_count) return false; | |
512 | __CFSetFindBuckets1(set, candidate, &match); | |
513 | return (match ? ((value ? *value = match->_key : NULL), true) : false); | |
514 | } | |
515 | ||
516 | void CFSetGetValues(CFSetRef set, const void **values) { | |
517 | struct __CFSetBucket *buckets; | |
518 | CFIndex idx, cnt, nbuckets; | |
519 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "getObjects:", (void * *)values); | |
520 | __CFGenericValidateType(set, __kCFSetTypeID); | |
521 | buckets = set->_buckets; | |
522 | nbuckets = set->_bucketsNum; | |
523 | for (idx = 0; idx < nbuckets; idx++) { | |
524 | if (__CFSetBucketIsOccupied(set, &buckets[idx])) { | |
525 | for (cnt = 1; cnt--;) { | |
526 | if (values) *values++ = buckets[idx]._key; | |
527 | } | |
528 | } | |
529 | } | |
530 | } | |
531 | ||
532 | void CFSetApplyFunction(CFSetRef set, CFSetApplierFunction applier, void *context) { | |
533 | struct __CFSetBucket *buckets; | |
534 | CFIndex idx, cnt, nbuckets; | |
535 | FAULT_CALLBACK((void **)&(applier)); | |
536 | CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, void, set, "_applyValues:context:", applier, context); | |
537 | __CFGenericValidateType(set, __kCFSetTypeID); | |
538 | buckets = set->_buckets; | |
539 | nbuckets = set->_bucketsNum; | |
540 | for (idx = 0; idx < nbuckets; idx++) { | |
541 | if (__CFSetBucketIsOccupied(set, &buckets[idx])) { | |
542 | for (cnt = 1; cnt--;) { | |
543 | INVOKE_CALLBACK2(applier, buckets[idx]._key, context); | |
544 | } | |
545 | } | |
546 | } | |
547 | } | |
548 | ||
549 | static void __CFSetGrow(CFMutableSetRef set, CFIndex numNewValues) { | |
550 | struct __CFSetBucket *oldbuckets = set->_buckets; | |
551 | CFIndex idx, oldnbuckets = set->_bucketsNum; | |
552 | CFIndex oldCount = set->_count; | |
553 | set->_capacity = __CFSetRoundUpCapacity(oldCount + numNewValues); | |
554 | set->_bucketsNum = __CFSetNumBucketsForCapacity(set->_capacity); | |
555 | set->_buckets = CFAllocatorAllocate(__CFGetAllocator(set), set->_bucketsNum * sizeof(struct __CFSetBucket), 0); | |
556 | if (NULL == set->_buckets) HALT; | |
557 | if (__CFOASafe) __CFSetLastAllocationEventName(set->_buckets, "CFSet (store)"); | |
558 | for (idx = set->_bucketsNum; idx--;) { | |
559 | set->_buckets[idx]._key = set->_emptyMarker; | |
560 | } | |
561 | if (NULL == oldbuckets) return; | |
562 | for (idx = 0; idx < oldnbuckets; idx++) { | |
563 | if (__CFSetBucketIsOccupied(set, &oldbuckets[idx])) { | |
564 | struct __CFSetBucket *match, *nomatch; | |
565 | __CFSetFindBuckets2(set, oldbuckets[idx]._key, &match, &nomatch); | |
566 | CFAssert3(!match, __kCFLogAssertion, "%s(): two values (%p, %p) now hash to the same slot; mutable value changed while in table or hash value is not immutable", __PRETTY_FUNCTION__, oldbuckets[idx]._key, match->_key); | |
567 | nomatch->_key = oldbuckets[idx]._key; | |
568 | } | |
569 | } | |
570 | CFAssert1(set->_count == oldCount, __kCFLogAssertion, "%s(): set count differs after rehashing; error", __PRETTY_FUNCTION__); | |
571 | CFAllocatorDeallocate(__CFGetAllocator(set), oldbuckets); | |
572 | } | |
573 | ||
574 | // This function is for Foundation's benefit; no one else should use it. | |
575 | void _CFSetSetCapacity(CFMutableSetRef set, CFIndex cap) { | |
576 | if (CF_IS_OBJC(__kCFSetTypeID, set)) return; | |
577 | #if defined(DEBUG) | |
578 | __CFGenericValidateType(set, __kCFSetTypeID); | |
579 | CFAssert1(__CFSetGetType(set) != __kCFSetImmutable && __CFSetGetType(set) != __kCFSetFixedMutable, __kCFLogAssertion, "%s(): set is immutable or fixed-mutable", __PRETTY_FUNCTION__); | |
580 | CFAssert3(set->_count <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, set->_count); | |
581 | #endif | |
582 | __CFSetGrow(set, cap - set->_count); | |
583 | } | |
584 | ||
585 | // This function is for Foundation's benefit; no one else should use it. | |
586 | bool _CFSetIsMutable(CFSetRef set) { | |
587 | return (__CFSetGetType(set) != __kCFSetImmutable); | |
588 | } | |
589 | ||
590 | void CFSetAddValue(CFMutableSetRef set, const void *value) { | |
591 | struct __CFSetBucket *match, *nomatch; | |
592 | const CFSetCallBacks *cb; | |
593 | const void *newValue; | |
594 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "addObject:", value); | |
595 | __CFGenericValidateType(set, __kCFSetTypeID); | |
596 | switch (__CFSetGetType(set)) { | |
597 | case __kCFSetMutable: | |
598 | if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { | |
599 | __CFSetGrow(set, 1); | |
600 | } | |
601 | break; | |
602 | case __kCFSetFixedMutable: | |
603 | CFAssert3(set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); | |
604 | break; | |
605 | default: | |
606 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
607 | break; | |
608 | } | |
609 | __CFSetFindBuckets2(set, value, &match, &nomatch); | |
610 | if (match) { | |
611 | } else { | |
612 | cb = __CFSetGetCallBacks(set); | |
613 | if (cb->retain) { | |
614 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); | |
615 | } else { | |
616 | newValue = value; | |
617 | } | |
618 | if (set->_emptyMarker == newValue) { | |
619 | __CFSetFindNewEmptyMarker(set); | |
620 | } | |
621 | if (set->_deletedMarker == newValue) { | |
622 | __CFSetFindNewDeletedMarker(set); | |
623 | } | |
624 | nomatch->_key = newValue; | |
625 | set->_bucketsUsed++; | |
626 | set->_count++; | |
627 | } | |
628 | } | |
629 | ||
630 | __private_extern__ const void *__CFSetAddValueAndReturn(CFMutableSetRef set, const void *value) { | |
631 | struct __CFSetBucket *match, *nomatch; | |
632 | const CFSetCallBacks *cb; | |
633 | const void *newValue; | |
634 | // #warning not toll-free bridged, but internal | |
635 | __CFGenericValidateType(set, __kCFSetTypeID); | |
636 | switch (__CFSetGetType(set)) { | |
637 | case __kCFSetMutable: | |
638 | if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { | |
639 | __CFSetGrow(set, 1); | |
640 | } | |
641 | break; | |
642 | case __kCFSetFixedMutable: | |
643 | CFAssert3(set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); | |
644 | break; | |
645 | default: | |
646 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
647 | break; | |
648 | } | |
649 | __CFSetFindBuckets2(set, value, &match, &nomatch); | |
650 | if (match) { | |
651 | return match->_key; | |
652 | } else { | |
653 | cb = __CFSetGetCallBacks(set); | |
654 | if (cb->retain) { | |
655 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); | |
656 | } else { | |
657 | newValue = value; | |
658 | } | |
659 | if (set->_emptyMarker == newValue) { | |
660 | __CFSetFindNewEmptyMarker(set); | |
661 | } | |
662 | if (set->_deletedMarker == newValue) { | |
663 | __CFSetFindNewDeletedMarker(set); | |
664 | } | |
665 | nomatch->_key = newValue; | |
666 | set->_bucketsUsed++; | |
667 | set->_count++; | |
668 | return newValue; | |
669 | } | |
670 | } | |
671 | ||
672 | void CFSetReplaceValue(CFMutableSetRef set, const void *value) { | |
673 | struct __CFSetBucket *match; | |
674 | const CFSetCallBacks *cb; | |
675 | const void *newValue; | |
676 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_replaceObject:", value); | |
677 | __CFGenericValidateType(set, __kCFSetTypeID); | |
678 | switch (__CFSetGetType(set)) { | |
679 | case __kCFSetMutable: | |
680 | case __kCFSetFixedMutable: | |
681 | break; | |
682 | default: | |
683 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
684 | break; | |
685 | } | |
686 | if (0 == set->_count) return; | |
687 | __CFSetFindBuckets1(set, value, &match); | |
688 | if (!match) return; | |
689 | cb = __CFSetGetCallBacks(set); | |
690 | if (cb->retain) { | |
691 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); | |
692 | } else { | |
693 | newValue = value; | |
694 | } | |
695 | if (cb->release) { | |
696 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); | |
697 | match->_key = set->_deletedMarker; | |
698 | } | |
699 | if (set->_emptyMarker == newValue) { | |
700 | __CFSetFindNewEmptyMarker(set); | |
701 | } | |
702 | if (set->_deletedMarker == newValue) { | |
703 | __CFSetFindNewDeletedMarker(set); | |
704 | } | |
705 | match->_key = newValue; | |
706 | } | |
707 | ||
708 | void CFSetSetValue(CFMutableSetRef set, const void *value) { | |
709 | struct __CFSetBucket *match, *nomatch; | |
710 | const CFSetCallBacks *cb; | |
711 | const void *newValue; | |
712 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_setObject:", value); | |
713 | __CFGenericValidateType(set, __kCFSetTypeID); | |
714 | switch (__CFSetGetType(set)) { | |
715 | case __kCFSetMutable: | |
716 | if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { | |
717 | __CFSetGrow(set, 1); | |
718 | } | |
719 | break; | |
720 | case __kCFSetFixedMutable: | |
721 | break; | |
722 | default: | |
723 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
724 | break; | |
725 | } | |
726 | __CFSetFindBuckets2(set, value, &match, &nomatch); | |
727 | cb = __CFSetGetCallBacks(set); | |
728 | if (cb->retain) { | |
729 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); | |
730 | } else { | |
731 | newValue = value; | |
732 | } | |
733 | if (match) { | |
734 | if (cb->release) { | |
735 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); | |
736 | match->_key = set->_deletedMarker; | |
737 | } | |
738 | if (set->_emptyMarker == newValue) { | |
739 | __CFSetFindNewEmptyMarker(set); | |
740 | } | |
741 | if (set->_deletedMarker == newValue) { | |
742 | __CFSetFindNewDeletedMarker(set); | |
743 | } | |
744 | match->_key = newValue; | |
745 | } else { | |
746 | CFAssert3(__kCFSetFixedMutable != __CFSetGetType(set) || set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); | |
747 | if (set->_emptyMarker == newValue) { | |
748 | __CFSetFindNewEmptyMarker(set); | |
749 | } | |
750 | if (set->_deletedMarker == newValue) { | |
751 | __CFSetFindNewDeletedMarker(set); | |
752 | } | |
753 | nomatch->_key = newValue; | |
754 | set->_bucketsUsed++; | |
755 | set->_count++; | |
756 | } | |
757 | } | |
758 | ||
759 | void CFSetRemoveValue(CFMutableSetRef set, const void *value) { | |
760 | struct __CFSetBucket *match; | |
761 | const CFSetCallBacks *cb; | |
762 | CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "removeObject:", value); | |
763 | __CFGenericValidateType(set, __kCFSetTypeID); | |
764 | switch (__CFSetGetType(set)) { | |
765 | case __kCFSetMutable: | |
766 | case __kCFSetFixedMutable: | |
767 | break; | |
768 | default: | |
769 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
770 | break; | |
771 | } | |
772 | if (0 == set->_count) return; | |
773 | __CFSetFindBuckets1(set, value, &match); | |
774 | if (!match) return; | |
775 | set->_count--; | |
776 | if (1) { | |
777 | cb = __CFSetGetCallBacks(set); | |
778 | if (cb->release) { | |
779 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); | |
780 | } | |
781 | match->_key = set->_deletedMarker; | |
782 | set->_bucketsUsed--; | |
783 | } | |
784 | } | |
785 | ||
786 | void CFSetRemoveAllValues(CFMutableSetRef set) { | |
787 | struct __CFSetBucket *buckets; | |
788 | const CFSetCallBacks *cb; | |
789 | CFAllocatorRef allocator; | |
790 | CFIndex idx, nbuckets; | |
791 | CF_OBJC_FUNCDISPATCH0(__kCFSetTypeID, void, set, "removeAllObjects"); | |
792 | __CFGenericValidateType(set, __kCFSetTypeID); | |
793 | switch (__CFSetGetType(set)) { | |
794 | case __kCFSetMutable: | |
795 | case __kCFSetFixedMutable: | |
796 | break; | |
797 | default: | |
798 | CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); | |
799 | break; | |
800 | } | |
801 | if (0 == set->_count) return; | |
802 | buckets = set->_buckets; | |
803 | nbuckets = set->_bucketsNum; | |
804 | cb = __CFSetGetCallBacks(set); | |
805 | allocator = __CFGetAllocator(set); | |
806 | for (idx = 0; idx < nbuckets; idx++) { | |
807 | if (__CFSetBucketIsOccupied(set, &buckets[idx])) { | |
808 | if (cb->release) { | |
809 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, buckets[idx]._key, set->_context); | |
810 | } | |
811 | buckets[idx]._key = set->_emptyMarker; | |
812 | } | |
813 | } | |
814 | set->_bucketsUsed = 0; | |
815 | set->_count = 0; | |
816 | } | |
817 |