]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
d8925383 | 2 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
9ce05555 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. | |
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 | /* CFBag.c | |
24 | Copyright 1998-2002, Apple, Inc. All rights reserved. | |
25 | Responsibility: Christopher Kane | |
26 | */ | |
27 | ||
28 | #include <CoreFoundation/CFBag.h> | |
29 | #include "CFInternal.h" | |
30 | ||
31 | const CFBagCallBacks kCFTypeBagCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; | |
32 | const CFBagCallBacks kCFCopyStringBagCallBacks = {0, (void *)CFStringCreateCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; | |
33 | static const CFBagCallBacks __kCFNullBagCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; | |
34 | ||
35 | ||
36 | static const uint32_t __CFBagCapacities[42] = { | |
37 | 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, | |
38 | 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, | |
39 | 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, | |
40 | 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U | |
41 | }; | |
42 | ||
43 | static const uint32_t __CFBagBuckets[42] = { // primes | |
44 | 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, | |
45 | 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, | |
46 | 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, | |
47 | 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U | |
48 | }; | |
49 | ||
50 | CF_INLINE CFIndex __CFBagRoundUpCapacity(CFIndex capacity) { | |
51 | CFIndex idx; | |
52 | for (idx = 0; idx < 42 && __CFBagCapacities[idx] < (uint32_t)capacity; idx++); | |
53 | if (42 <= idx) HALT; | |
54 | return __CFBagCapacities[idx]; | |
55 | } | |
56 | ||
57 | CF_INLINE CFIndex __CFBagNumBucketsForCapacity(CFIndex capacity) { | |
58 | CFIndex idx; | |
59 | for (idx = 0; idx < 42 && __CFBagCapacities[idx] < (uint32_t)capacity; idx++); | |
60 | if (42 <= idx) HALT; | |
61 | return __CFBagBuckets[idx]; | |
62 | } | |
63 | ||
64 | enum { /* Bits 1-0 */ | |
65 | __kCFBagImmutable = 0, /* unchangable and fixed capacity */ | |
66 | __kCFBagMutable = 1, /* changeable and variable capacity */ | |
67 | __kCFBagFixedMutable = 3 /* changeable and fixed capacity */ | |
68 | }; | |
69 | ||
70 | enum { /* Bits 3-2 */ | |
71 | __kCFBagHasNullCallBacks = 0, | |
72 | __kCFBagHasCFTypeCallBacks = 1, | |
73 | __kCFBagHasCustomCallBacks = 3 /* callbacks are at end of header */ | |
74 | }; | |
75 | ||
d8925383 A |
76 | enum { /* Bit 4 */ |
77 | __kCFCollectionIsWeak = 0, | |
78 | __kCFCollectionIsStrong = 1, | |
79 | }; | |
80 | ||
81 | ||
9ce05555 A |
82 | struct __CFBagBucket { |
83 | const void *_key; | |
84 | CFIndex _count; | |
85 | }; | |
86 | ||
87 | struct __CFBag { | |
88 | CFRuntimeBase _base; | |
89 | CFIndex _count; /* number of values */ | |
90 | CFIndex _capacity; /* maximum number of values */ | |
91 | CFIndex _bucketsUsed; /* number of slots used */ | |
92 | CFIndex _bucketsNum; /* number of slots */ | |
93 | const void *_emptyMarker; | |
94 | const void *_deletedMarker; | |
95 | void *_context; /* private */ | |
96 | struct __CFBagBucket *_buckets; /* can be NULL if not allocated yet */ | |
97 | }; | |
98 | ||
99 | CF_INLINE bool __CFBagBucketIsEmpty(CFBagRef bag, const struct __CFBagBucket *bucket) { | |
100 | return (bag->_emptyMarker == bucket->_key); | |
101 | } | |
102 | ||
103 | CF_INLINE bool __CFBagBucketIsDeleted(CFBagRef bag, const struct __CFBagBucket *bucket) { | |
104 | return (bag->_deletedMarker == bucket->_key); | |
105 | } | |
106 | ||
107 | CF_INLINE bool __CFBagBucketIsOccupied(CFBagRef bag, const struct __CFBagBucket *bucket) { | |
108 | return (bag->_emptyMarker != bucket->_key && bag->_deletedMarker != bucket->_key); | |
109 | } | |
110 | ||
111 | /* Bits 1-0 of the base reserved bits are used for mutability variety */ | |
112 | /* Bits 3-2 of the base reserved bits are used for callback indicator bits */ | |
d8925383 A |
113 | /* Bits 4-5 are used by GC */ |
114 | ||
115 | static bool isStrongMemory(CFTypeRef collection) { | |
116 | return ! __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 4, 4); | |
117 | } | |
118 | ||
119 | static bool needsRestore(CFTypeRef collection) { | |
120 | return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 5, 5); | |
121 | } | |
122 | ||
9ce05555 A |
123 | |
124 | CF_INLINE CFIndex __CFBagGetType(CFBagRef bag) { | |
125 | return __CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 1, 0); | |
126 | } | |
127 | ||
128 | CF_INLINE CFIndex __CFBagGetSizeOfType(CFIndex t) { | |
129 | CFIndex size = sizeof(struct __CFBag); | |
130 | if (__CFBitfieldGetValue(t, 3, 2) == __kCFBagHasCustomCallBacks) { | |
131 | size += sizeof(CFBagCallBacks); | |
132 | } | |
133 | return size; | |
134 | } | |
135 | ||
136 | CF_INLINE const CFBagCallBacks *__CFBagGetCallBacks(CFBagRef bag) { | |
137 | CFBagCallBacks *result = NULL; | |
138 | switch (__CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 3, 2)) { | |
139 | case __kCFBagHasNullCallBacks: | |
140 | return &__kCFNullBagCallBacks; | |
141 | case __kCFBagHasCFTypeCallBacks: | |
142 | return &kCFTypeBagCallBacks; | |
143 | case __kCFBagHasCustomCallBacks: | |
144 | break; | |
145 | } | |
146 | result = (CFBagCallBacks *)((uint8_t *)bag + sizeof(struct __CFBag)); | |
147 | return result; | |
148 | } | |
149 | ||
150 | CF_INLINE bool __CFBagCallBacksMatchNull(const CFBagCallBacks *c) { | |
151 | return (NULL == c || | |
152 | (c->retain == __kCFNullBagCallBacks.retain && | |
153 | c->release == __kCFNullBagCallBacks.release && | |
154 | c->copyDescription == __kCFNullBagCallBacks.copyDescription && | |
155 | c->equal == __kCFNullBagCallBacks.equal && | |
156 | c->hash == __kCFNullBagCallBacks.hash)); | |
157 | } | |
158 | ||
159 | CF_INLINE bool __CFBagCallBacksMatchCFType(const CFBagCallBacks *c) { | |
160 | return (&kCFTypeBagCallBacks == c || | |
161 | (c->retain == kCFTypeBagCallBacks.retain && | |
162 | c->release == kCFTypeBagCallBacks.release && | |
163 | c->copyDescription == kCFTypeBagCallBacks.copyDescription && | |
164 | c->equal == kCFTypeBagCallBacks.equal && | |
165 | c->hash == kCFTypeBagCallBacks.hash)); | |
166 | } | |
167 | ||
168 | ||
169 | static void __CFBagFindBuckets1(CFBagRef bag, const void *key, struct __CFBagBucket **match) { | |
170 | const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); | |
171 | struct __CFBagBucket *buckets = bag->_buckets; | |
172 | CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, bag->_context) : (CFHashCode)key; | |
173 | UInt32 start = keyHash % bag->_bucketsNum; | |
174 | UInt32 probe = start; | |
175 | UInt32 probeskip = 1; | |
176 | *match = NULL; | |
177 | for (;;) { | |
178 | struct __CFBagBucket *currentBucket = buckets + probe; | |
179 | if (__CFBagBucketIsEmpty(bag, currentBucket)) { | |
180 | return; | |
181 | } else if (__CFBagBucketIsDeleted(bag, currentBucket)) { | |
182 | /* do nothing */ | |
d8925383 | 183 | } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context))) { |
9ce05555 A |
184 | *match = currentBucket; |
185 | return; | |
186 | } | |
187 | probe = (probe + probeskip) % bag->_bucketsNum; | |
188 | if (start == probe) return; | |
189 | } | |
190 | } | |
191 | ||
192 | static void __CFBagFindBuckets2(CFBagRef bag, const void *key, struct __CFBagBucket **match, struct __CFBagBucket **nomatch) { | |
193 | const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); | |
194 | struct __CFBagBucket *buckets = bag->_buckets; | |
195 | CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, bag->_context) : (CFHashCode)key; | |
196 | UInt32 start = keyHash % bag->_bucketsNum; | |
197 | UInt32 probe = start; | |
198 | UInt32 probeskip = 1; | |
199 | *match = NULL; | |
200 | *nomatch = NULL; | |
201 | for (;;) { | |
202 | struct __CFBagBucket *currentBucket = buckets + probe; | |
203 | if (__CFBagBucketIsEmpty(bag, currentBucket)) { | |
204 | if (!*nomatch) *nomatch = currentBucket; | |
205 | return; | |
206 | } else if (__CFBagBucketIsDeleted(bag, currentBucket)) { | |
207 | if (!*nomatch) *nomatch = currentBucket; | |
d8925383 | 208 | } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context)))) { |
9ce05555 | 209 | *match = currentBucket; |
d8925383 | 210 | return; |
9ce05555 A |
211 | } |
212 | probe = (probe + probeskip) % bag->_bucketsNum; | |
213 | if (start == probe) return; | |
214 | } | |
215 | } | |
216 | ||
217 | static void __CFBagFindNewEmptyMarker(CFBagRef bag) { | |
218 | struct __CFBagBucket *buckets; | |
219 | const void *newEmpty; | |
220 | bool hit; | |
221 | CFIndex idx, nbuckets; | |
222 | buckets = bag->_buckets; | |
223 | nbuckets = bag->_bucketsNum; | |
224 | newEmpty = bag->_emptyMarker; | |
225 | do { | |
226 | (intptr_t)newEmpty -= 2; | |
227 | hit = false; | |
228 | for (idx = 0; idx < nbuckets; idx++) { | |
229 | if (newEmpty == buckets[idx]._key) { | |
230 | hit = true; | |
231 | break; | |
232 | } | |
233 | } | |
234 | } while (hit); | |
235 | for (idx = 0; idx < nbuckets; idx++) { | |
236 | if (bag->_emptyMarker == buckets[idx]._key) { | |
237 | buckets[idx]._key = newEmpty; | |
238 | } | |
239 | } | |
240 | ((struct __CFBag *)bag)->_emptyMarker = newEmpty; | |
241 | } | |
242 | ||
243 | static void __CFBagFindNewDeletedMarker(CFBagRef bag) { | |
244 | struct __CFBagBucket *buckets; | |
245 | const void *newDeleted; | |
246 | bool hit; | |
247 | CFIndex idx, nbuckets; | |
248 | buckets = bag->_buckets; | |
249 | nbuckets = bag->_bucketsNum; | |
250 | newDeleted = bag->_deletedMarker; | |
251 | do { | |
252 | (intptr_t)newDeleted += 2; | |
253 | hit = false; | |
254 | for (idx = 0; idx < nbuckets; idx++) { | |
255 | if (newDeleted == buckets[idx]._key) { | |
256 | hit = true; | |
257 | break; | |
258 | } | |
259 | } | |
260 | } while (hit); | |
261 | for (idx = 0; idx < nbuckets; idx++) { | |
262 | if (bag->_deletedMarker == buckets[idx]._key) { | |
263 | buckets[idx]._key = newDeleted; | |
264 | } | |
265 | } | |
266 | ((struct __CFBag *)bag)->_deletedMarker = newDeleted; | |
267 | } | |
268 | ||
269 | static bool __CFBagEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
270 | CFBagRef bag1 = (CFBagRef)cf1; | |
271 | CFBagRef bag2 = (CFBagRef)cf2; | |
272 | const CFBagCallBacks *cb1, *cb2; | |
273 | const struct __CFBagBucket *buckets; | |
274 | CFIndex idx, nbuckets; | |
275 | if (bag1 == bag2) return true; | |
276 | if (bag1->_count != bag2->_count) return false; | |
277 | cb1 = __CFBagGetCallBacks(bag1); | |
278 | cb2 = __CFBagGetCallBacks(bag2); | |
279 | if (cb1->equal != cb2->equal) return false; | |
280 | if (0 == bag1->_count) return true; /* after function comparison! */ | |
281 | buckets = bag1->_buckets; | |
282 | nbuckets = bag1->_bucketsNum; | |
283 | for (idx = 0; idx < nbuckets; idx++) { | |
284 | if (__CFBagBucketIsOccupied(bag1, &buckets[idx])) { | |
285 | if (buckets[idx]._count != CFBagGetCountOfValue(bag2, buckets[idx]._key)) { | |
286 | return false; | |
287 | } | |
288 | } | |
289 | } | |
290 | return true; | |
291 | } | |
292 | ||
293 | static CFHashCode __CFBagHash(CFTypeRef cf) { | |
294 | CFBagRef bag = (CFBagRef)cf; | |
295 | return bag->_count; | |
296 | } | |
297 | ||
298 | static CFStringRef __CFBagCopyDescription(CFTypeRef cf) { | |
299 | CFBagRef bag = (CFBagRef)cf; | |
300 | const CFBagCallBacks *cb; | |
301 | const struct __CFBagBucket *buckets; | |
302 | CFIndex idx, nbuckets; | |
303 | CFMutableStringRef result; | |
304 | cb = __CFBagGetCallBacks(bag); | |
305 | buckets = bag->_buckets; | |
306 | nbuckets = bag->_bucketsNum; | |
307 | result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
308 | CFStringAppendFormat(result, NULL, CFSTR("<CFBag %p [%p]>{count = %u, capacity = %u, values = (\n"), bag, CFGetAllocator(bag), bag->_count, bag->_capacity); | |
309 | for (idx = 0; idx < nbuckets; idx++) { | |
310 | if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { | |
311 | CFStringRef desc = NULL; | |
312 | if (NULL != cb->copyDescription) { | |
313 | desc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), buckets[idx]._key, bag->_context); | |
314 | } | |
315 | if (NULL != desc) { | |
316 | CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ (%d)\n"), idx, desc, buckets[idx]._count); | |
317 | CFRelease(desc); | |
318 | } else { | |
319 | CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> (%d)\n"), idx, buckets[idx]._key, buckets[idx]._count); | |
320 | } | |
321 | } | |
322 | } | |
323 | CFStringAppend(result, CFSTR(")}")); | |
324 | return result; | |
325 | } | |
326 | ||
327 | static void __CFBagDeallocate(CFTypeRef cf) { | |
328 | CFMutableBagRef bag = (CFMutableBagRef)cf; | |
329 | CFAllocatorRef allocator = CFGetAllocator(bag); | |
d8925383 A |
330 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
331 | const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); | |
332 | if (cb->retain == NULL && cb->release == NULL) | |
333 | return; // XXX_PCB keep bag intact during finalization. | |
334 | } | |
9ce05555 A |
335 | if (__CFBagGetType(bag) == __kCFBagImmutable) { |
336 | __CFBitfieldSetValue(((CFRuntimeBase *)bag)->_info, 1, 0, __kCFBagFixedMutable); | |
337 | } | |
338 | CFBagRemoveAllValues(bag); | |
339 | if (__CFBagGetType(bag) == __kCFBagMutable && bag->_buckets) { | |
d8925383 | 340 | _CFAllocatorDeallocateGC(allocator, bag->_buckets); |
9ce05555 A |
341 | } |
342 | } | |
343 | ||
344 | static CFTypeID __kCFBagTypeID = _kCFRuntimeNotATypeID; | |
345 | ||
346 | static const CFRuntimeClass __CFBagClass = { | |
d8925383 | 347 | _kCFRuntimeScannedObject, |
9ce05555 A |
348 | "CFBag", |
349 | NULL, // init | |
350 | NULL, // copy | |
351 | __CFBagDeallocate, | |
352 | (void *)__CFBagEqual, | |
353 | __CFBagHash, | |
354 | NULL, // | |
355 | __CFBagCopyDescription | |
356 | }; | |
357 | ||
358 | __private_extern__ void __CFBagInitialize(void) { | |
359 | __kCFBagTypeID = _CFRuntimeRegisterClass(&__CFBagClass); | |
360 | } | |
361 | ||
362 | CFTypeID CFBagGetTypeID(void) { | |
363 | return __kCFBagTypeID; | |
364 | } | |
365 | ||
366 | static CFBagRef __CFBagInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFBagCallBacks *callBacks) { | |
367 | struct __CFBag *memory; | |
368 | UInt32 size; | |
369 | CFIndex idx; | |
d8925383 | 370 | CFBagCallBacks nonRetainingCallbacks; |
9ce05555 | 371 | __CFBitfieldSetValue(flags, 31, 2, 0); |
d8925383 A |
372 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { |
373 | if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { | |
374 | __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak | |
375 | } | |
376 | else { | |
377 | if (callBacks->retain == __CFTypeCollectionRetain && callBacks->release == __CFTypeCollectionRelease) { | |
378 | nonRetainingCallbacks = *callBacks; | |
379 | nonRetainingCallbacks.retain = NULL; | |
380 | nonRetainingCallbacks.release = NULL; | |
381 | callBacks = &nonRetainingCallbacks; | |
382 | __CFBitfieldSetValue(flags, 5, 5, 1); // setNeedsRestore | |
383 | } | |
384 | } | |
385 | } | |
9ce05555 A |
386 | if (__CFBagCallBacksMatchNull(callBacks)) { |
387 | __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasNullCallBacks); | |
388 | } else if (__CFBagCallBacksMatchCFType(callBacks)) { | |
389 | __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasCFTypeCallBacks); | |
390 | } else { | |
391 | __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasCustomCallBacks); | |
392 | } | |
393 | size = __CFBagGetSizeOfType(flags) - sizeof(CFRuntimeBase); | |
394 | switch (__CFBitfieldGetValue(flags, 1, 0)) { | |
395 | case __kCFBagImmutable: | |
396 | case __kCFBagFixedMutable: | |
397 | size += __CFBagNumBucketsForCapacity(capacity) * sizeof(struct __CFBagBucket); | |
398 | break; | |
399 | case __kCFBagMutable: | |
400 | break; | |
401 | } | |
402 | memory = (struct __CFBag *)_CFRuntimeCreateInstance(allocator, __kCFBagTypeID, size, NULL); | |
403 | if (NULL == memory) { | |
404 | return NULL; | |
405 | } | |
406 | __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); | |
407 | memory->_count = 0; | |
408 | memory->_bucketsUsed = 0; | |
409 | memory->_emptyMarker = (const void *)0xa1b1c1d3; | |
410 | memory->_deletedMarker = (const void *)0xa1b1c1d5; | |
411 | memory->_context = NULL; | |
412 | switch (__CFBitfieldGetValue(flags, 1, 0)) { | |
413 | case __kCFBagImmutable: | |
d8925383 A |
414 | if (!isStrongMemory(memory)) { // if weak, don't scan |
415 | auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); | |
416 | } | |
9ce05555 A |
417 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (immutable)"); |
418 | memory->_capacity = capacity; /* Don't round up capacity */ | |
419 | memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); | |
420 | memory->_buckets = (struct __CFBagBucket *)((uint8_t *)memory + __CFBagGetSizeOfType(flags)); | |
421 | for (idx = memory->_bucketsNum; idx--;) { | |
422 | memory->_buckets[idx]._key = memory->_emptyMarker; | |
423 | } | |
424 | break; | |
425 | case __kCFBagFixedMutable: | |
d8925383 A |
426 | if (!isStrongMemory(memory)) { // if weak, don't scan |
427 | auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); | |
428 | } | |
9ce05555 A |
429 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-fixed)"); |
430 | memory->_capacity = capacity; /* Don't round up capacity */ | |
431 | memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); | |
432 | memory->_buckets = (struct __CFBagBucket *)((uint8_t *)memory + __CFBagGetSizeOfType(flags)); | |
433 | for (idx = memory->_bucketsNum; idx--;) { | |
434 | memory->_buckets[idx]._key = memory->_emptyMarker; | |
435 | } | |
436 | break; | |
437 | case __kCFBagMutable: | |
438 | if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-variable)"); | |
439 | memory->_capacity = __CFBagRoundUpCapacity(1); | |
440 | memory->_bucketsNum = 0; | |
441 | memory->_buckets = NULL; | |
442 | break; | |
443 | } | |
444 | if (__kCFBagHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { | |
445 | const CFBagCallBacks *cb = __CFBagGetCallBacks((CFBagRef)memory); | |
446 | *(CFBagCallBacks *)cb = *callBacks; | |
447 | FAULT_CALLBACK((void **)&(cb->retain)); | |
448 | FAULT_CALLBACK((void **)&(cb->release)); | |
449 | FAULT_CALLBACK((void **)&(cb->copyDescription)); | |
450 | FAULT_CALLBACK((void **)&(cb->equal)); | |
451 | FAULT_CALLBACK((void **)&(cb->hash)); | |
452 | } | |
453 | return (CFBagRef)memory; | |
454 | } | |
455 | ||
456 | CFBagRef CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks) { | |
457 | CFBagRef result; | |
458 | UInt32 flags; | |
459 | CFIndex idx; | |
460 | CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); | |
461 | result = __CFBagInit(allocator, __kCFBagImmutable, numValues, callBacks); | |
462 | flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); | |
463 | if (flags == __kCFBagImmutable) { | |
464 | __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFBagFixedMutable); | |
465 | } | |
466 | for (idx = 0; idx < numValues; idx++) { | |
467 | CFBagAddValue((CFMutableBagRef)result, values[idx]); | |
468 | } | |
469 | __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); | |
470 | return result; | |
471 | } | |
472 | ||
473 | CFMutableBagRef CFBagCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFBagCallBacks *callBacks) { | |
474 | CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); | |
475 | return (CFMutableBagRef)__CFBagInit(allocator, (0 == capacity) ? __kCFBagMutable : __kCFBagFixedMutable, capacity, callBacks); | |
476 | } | |
477 | ||
478 | CFBagRef CFBagCreateCopy(CFAllocatorRef allocator, CFBagRef bag) { | |
479 | CFBagRef result; | |
480 | const CFBagCallBacks *cb; | |
481 | CFIndex numValues = CFBagGetCount(bag); | |
482 | const void **list, *buffer[256]; | |
d8925383 | 483 | list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK |
9ce05555 A |
484 | if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); |
485 | CFBagGetValues(bag, list); | |
d8925383 A |
486 | CFBagCallBacks patchedCB; |
487 | if (CF_IS_OBJC(__kCFBagTypeID, bag)) { | |
488 | cb = &kCFTypeBagCallBacks; | |
489 | } | |
490 | else { | |
491 | cb = __CFBagGetCallBacks(bag); | |
492 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
493 | if (needsRestore(bag)) { | |
494 | patchedCB = *cb; // copy | |
495 | cb = &patchedCB; // reset to copy | |
496 | patchedCB.retain = __CFTypeCollectionRetain; | |
497 | patchedCB.release = __CFTypeCollectionRelease; | |
498 | } | |
499 | } | |
500 | } | |
9ce05555 | 501 | result = CFBagCreate(allocator, list, numValues, cb); |
d8925383 | 502 | if (list != buffer) CFAllocatorDeallocate(allocator, list); // XXX_PCB GC OK |
9ce05555 A |
503 | return result; |
504 | } | |
505 | ||
506 | CFMutableBagRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef bag) { | |
507 | CFMutableBagRef result; | |
508 | const CFBagCallBacks *cb; | |
509 | CFIndex idx, numValues = CFBagGetCount(bag); | |
510 | const void **list, *buffer[256]; | |
511 | CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable bags, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); | |
d8925383 | 512 | list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK |
9ce05555 A |
513 | if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); |
514 | CFBagGetValues(bag, list); | |
d8925383 A |
515 | CFBagCallBacks patchedCB; |
516 | if (CF_IS_OBJC(__kCFBagTypeID, bag)) { | |
517 | cb = &kCFTypeBagCallBacks; | |
518 | } | |
519 | else { | |
520 | cb = __CFBagGetCallBacks(bag); | |
521 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
522 | if (needsRestore(bag)) { | |
523 | patchedCB = *cb; // copy | |
524 | cb = &patchedCB; // reset to copy | |
525 | patchedCB.retain = __CFTypeCollectionRetain; | |
526 | patchedCB.release = __CFTypeCollectionRelease; | |
527 | } | |
528 | } | |
529 | } | |
9ce05555 A |
530 | result = CFBagCreateMutable(allocator, capacity, cb); |
531 | if (0 == capacity) _CFBagSetCapacity(result, numValues); | |
532 | for (idx = 0; idx < numValues; idx++) { | |
533 | CFBagAddValue(result, list[idx]); | |
534 | } | |
d8925383 | 535 | if (list != buffer) CFAllocatorDeallocate(allocator, list); // GC OK |
9ce05555 A |
536 | return result; |
537 | } | |
538 | ||
d8925383 | 539 | |
9ce05555 | 540 | void _CFBagSetContext(CFBagRef bag, void *context) { |
d8925383 A |
541 | CFAllocatorRef allocator = CFGetAllocator(bag); |
542 | CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_context, context); | |
9ce05555 A |
543 | } |
544 | ||
545 | CFIndex CFBagGetCount(CFBagRef bag) { | |
546 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
547 | return bag->_count; | |
548 | } | |
549 | ||
550 | CFIndex CFBagGetCountOfValue(CFBagRef bag, const void *value) { | |
551 | struct __CFBagBucket *match; | |
552 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
553 | if (0 == bag->_count) return 0; | |
554 | __CFBagFindBuckets1(bag, value, &match); | |
555 | return (match ? match->_count : 0); | |
556 | } | |
557 | ||
558 | Boolean CFBagContainsValue(CFBagRef bag, const void *value) { | |
559 | struct __CFBagBucket *match; | |
560 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
561 | if (0 == bag->_count) return false; | |
562 | __CFBagFindBuckets1(bag, value, &match); | |
563 | return (match ? true : false); | |
564 | } | |
565 | ||
566 | const void *CFBagGetValue(CFBagRef bag, const void *value) { | |
567 | struct __CFBagBucket *match; | |
568 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
569 | if (0 == bag->_count) return NULL; | |
570 | __CFBagFindBuckets1(bag, value, &match); | |
571 | return (match ? match->_key : NULL); | |
572 | } | |
573 | ||
574 | Boolean CFBagGetValueIfPresent(CFBagRef bag, const void *candidate, const void **value) { | |
575 | struct __CFBagBucket *match; | |
576 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
577 | if (0 == bag->_count) return false; | |
578 | __CFBagFindBuckets1(bag, candidate, &match); | |
d8925383 | 579 | return (match ? ((value ? __CFObjCStrongAssign(match->_key, value) : NULL), true) : false); |
9ce05555 A |
580 | } |
581 | ||
582 | void CFBagGetValues(CFBagRef bag, const void **values) { | |
583 | struct __CFBagBucket *buckets; | |
584 | CFIndex idx, cnt, nbuckets; | |
585 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
d8925383 A |
586 | if (CF_USING_COLLECTABLE_MEMORY) { |
587 | // GC: speculatively issue a write-barrier on the copied to buffers (3743553). | |
588 | __CFObjCWriteBarrierRange(values, bag->_count * sizeof(void *)); | |
589 | } | |
9ce05555 A |
590 | buckets = bag->_buckets; |
591 | nbuckets = bag->_bucketsNum; | |
592 | for (idx = 0; idx < nbuckets; idx++) { | |
593 | if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { | |
594 | for (cnt = buckets[idx]._count; cnt--;) { | |
595 | if (values) *values++ = buckets[idx]._key; | |
596 | } | |
597 | } | |
598 | } | |
599 | } | |
600 | ||
601 | void CFBagApplyFunction(CFBagRef bag, CFBagApplierFunction applier, void *context) { | |
602 | struct __CFBagBucket *buckets; | |
603 | CFIndex idx, cnt, nbuckets; | |
604 | FAULT_CALLBACK((void **)&(applier)); | |
605 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
606 | buckets = bag->_buckets; | |
607 | nbuckets = bag->_bucketsNum; | |
608 | for (idx = 0; idx < nbuckets; idx++) { | |
609 | if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { | |
610 | for (cnt = buckets[idx]._count; cnt--;) { | |
611 | INVOKE_CALLBACK2(applier, buckets[idx]._key, context); | |
612 | } | |
613 | } | |
614 | } | |
615 | } | |
616 | ||
617 | static void __CFBagGrow(CFMutableBagRef bag, CFIndex numNewValues) { | |
618 | struct __CFBagBucket *oldbuckets = bag->_buckets; | |
619 | CFIndex idx, oldnbuckets = bag->_bucketsNum; | |
620 | CFIndex oldCount = bag->_count; | |
d8925383 | 621 | CFAllocatorRef allocator = CFGetAllocator(bag); |
9ce05555 A |
622 | bag->_capacity = __CFBagRoundUpCapacity(oldCount + numNewValues); |
623 | bag->_bucketsNum = __CFBagNumBucketsForCapacity(bag->_capacity); | |
d8925383 A |
624 | void *bucket = _CFAllocatorAllocateGC(allocator, bag->_bucketsNum * sizeof(struct __CFBagBucket), isStrongMemory(bag) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); |
625 | CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_buckets, bucket); | |
9ce05555 A |
626 | if (__CFOASafe) __CFSetLastAllocationEventName(bag->_buckets, "CFBag (store)"); |
627 | if (NULL == bag->_buckets) HALT; | |
628 | for (idx = bag->_bucketsNum; idx--;) { | |
629 | bag->_buckets[idx]._key = bag->_emptyMarker; | |
630 | } | |
631 | if (NULL == oldbuckets) return; | |
632 | for (idx = 0; idx < oldnbuckets; idx++) { | |
633 | if (__CFBagBucketIsOccupied(bag, &oldbuckets[idx])) { | |
634 | struct __CFBagBucket *match, *nomatch; | |
635 | __CFBagFindBuckets2(bag, oldbuckets[idx]._key, &match, &nomatch); | |
636 | 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); | |
d8925383 A |
637 | if (nomatch) { |
638 | CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, oldbuckets[idx]._key); | |
639 | nomatch->_count = oldbuckets[idx]._count; | |
640 | } | |
9ce05555 A |
641 | } |
642 | } | |
643 | CFAssert1(bag->_count == oldCount, __kCFLogAssertion, "%s(): bag count differs after rehashing; error", __PRETTY_FUNCTION__); | |
d8925383 | 644 | _CFAllocatorDeallocateGC(CFGetAllocator(bag), oldbuckets); |
9ce05555 A |
645 | } |
646 | ||
647 | // This function is for Foundation's benefit; no one else should use it. | |
648 | void _CFBagSetCapacity(CFMutableBagRef bag, CFIndex cap) { | |
649 | if (CF_IS_OBJC(__kCFBagTypeID, bag)) return; | |
650 | #if defined(DEBUG) | |
651 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
652 | CFAssert1(__CFBagGetType(bag) != __kCFBagImmutable && __CFBagGetType(bag) != __kCFBagFixedMutable, __kCFLogAssertion, "%s(): bag is immutable or fixed-mutable", __PRETTY_FUNCTION__); | |
653 | CFAssert3(bag->_count <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, bag->_count); | |
654 | #endif | |
655 | __CFBagGrow(bag, cap - bag->_count); | |
656 | } | |
657 | ||
658 | void CFBagAddValue(CFMutableBagRef bag, const void *value) { | |
659 | struct __CFBagBucket *match, *nomatch; | |
d8925383 | 660 | CFAllocatorRef allocator; |
9ce05555 A |
661 | const CFBagCallBacks *cb; |
662 | const void *newValue; | |
663 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
664 | switch (__CFBagGetType(bag)) { | |
665 | case __kCFBagMutable: | |
666 | if (bag->_bucketsUsed == bag->_capacity || NULL == bag->_buckets) { | |
667 | __CFBagGrow(bag, 1); | |
668 | } | |
669 | break; | |
670 | case __kCFBagFixedMutable: | |
671 | CFAssert3(bag->_count < bag->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity bag %p (capacity = %d)", __PRETTY_FUNCTION__, bag, bag->_capacity); | |
672 | break; | |
673 | default: | |
674 | CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); | |
675 | break; | |
676 | } | |
677 | __CFBagFindBuckets2(bag, value, &match, &nomatch); | |
678 | if (match) { | |
679 | match->_count++; bag->_count++; | |
680 | } else { | |
d8925383 | 681 | allocator = CFGetAllocator(bag); |
9ce05555 A |
682 | cb = __CFBagGetCallBacks(bag); |
683 | if (cb->retain) { | |
d8925383 | 684 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context); |
9ce05555 A |
685 | } else { |
686 | newValue = value; | |
687 | } | |
688 | if (bag->_emptyMarker == newValue) { | |
689 | __CFBagFindNewEmptyMarker(bag); | |
690 | } | |
691 | if (bag->_deletedMarker == newValue) { | |
692 | __CFBagFindNewDeletedMarker(bag); | |
693 | } | |
d8925383 | 694 | CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue); |
9ce05555 A |
695 | nomatch->_count = 1; |
696 | bag->_bucketsUsed++; | |
697 | bag->_count++; | |
698 | } | |
699 | } | |
700 | ||
701 | void CFBagReplaceValue(CFMutableBagRef bag, const void *value) { | |
702 | struct __CFBagBucket *match; | |
d8925383 | 703 | CFAllocatorRef allocator; |
9ce05555 A |
704 | const CFBagCallBacks *cb; |
705 | const void *newValue; | |
706 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
707 | switch (__CFBagGetType(bag)) { | |
708 | case __kCFBagMutable: | |
709 | case __kCFBagFixedMutable: | |
710 | break; | |
711 | default: | |
712 | CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); | |
713 | break; | |
714 | } | |
715 | if (0 == bag->_count) return; | |
716 | __CFBagFindBuckets1(bag, value, &match); | |
717 | if (!match) return; | |
d8925383 | 718 | allocator = CFGetAllocator(bag); |
9ce05555 A |
719 | cb = __CFBagGetCallBacks(bag); |
720 | if (cb->retain) { | |
d8925383 | 721 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context); |
9ce05555 A |
722 | } else { |
723 | newValue = value; | |
724 | } | |
725 | if (cb->release) { | |
d8925383 | 726 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context); |
9ce05555 A |
727 | match->_key = bag->_deletedMarker; |
728 | } | |
729 | if (bag->_emptyMarker == newValue) { | |
730 | __CFBagFindNewEmptyMarker(bag); | |
731 | } | |
732 | if (bag->_deletedMarker == newValue) { | |
733 | __CFBagFindNewDeletedMarker(bag); | |
734 | } | |
d8925383 | 735 | CF_WRITE_BARRIER_ASSIGN(allocator, match->_key, newValue); |
9ce05555 A |
736 | } |
737 | ||
738 | void CFBagSetValue(CFMutableBagRef bag, const void *value) { | |
739 | struct __CFBagBucket *match, *nomatch; | |
d8925383 | 740 | CFAllocatorRef allocator; |
9ce05555 A |
741 | const CFBagCallBacks *cb; |
742 | const void *newValue; | |
743 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
744 | switch (__CFBagGetType(bag)) { | |
745 | case __kCFBagMutable: | |
746 | if (bag->_bucketsUsed == bag->_capacity || NULL == bag->_buckets) { | |
747 | __CFBagGrow(bag, 1); | |
748 | } | |
749 | break; | |
750 | case __kCFBagFixedMutable: | |
751 | break; | |
752 | default: | |
753 | CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); | |
754 | break; | |
755 | } | |
756 | __CFBagFindBuckets2(bag, value, &match, &nomatch); | |
d8925383 | 757 | allocator = CFGetAllocator(bag); |
9ce05555 A |
758 | cb = __CFBagGetCallBacks(bag); |
759 | if (cb->retain) { | |
d8925383 | 760 | newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context); |
9ce05555 A |
761 | } else { |
762 | newValue = value; | |
763 | } | |
764 | if (match) { | |
765 | if (cb->release) { | |
d8925383 | 766 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context); |
9ce05555 A |
767 | match->_key = bag->_deletedMarker; |
768 | } | |
769 | if (bag->_emptyMarker == newValue) { | |
770 | __CFBagFindNewEmptyMarker(bag); | |
771 | } | |
772 | if (bag->_deletedMarker == newValue) { | |
773 | __CFBagFindNewDeletedMarker(bag); | |
774 | } | |
d8925383 | 775 | CF_WRITE_BARRIER_ASSIGN(allocator, match->_key, newValue); |
9ce05555 A |
776 | } else { |
777 | CFAssert3(__kCFBagFixedMutable != __CFBagGetType(bag) || bag->_count < bag->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity bag %p (capacity = %d)", __PRETTY_FUNCTION__, bag, bag->_capacity); | |
778 | if (bag->_emptyMarker == newValue) { | |
779 | __CFBagFindNewEmptyMarker(bag); | |
780 | } | |
781 | if (bag->_deletedMarker == newValue) { | |
782 | __CFBagFindNewDeletedMarker(bag); | |
783 | } | |
d8925383 | 784 | CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue); |
9ce05555 A |
785 | nomatch->_count = 1; |
786 | bag->_bucketsUsed++; | |
787 | bag->_count++; | |
788 | } | |
789 | } | |
790 | ||
791 | void CFBagRemoveValue(CFMutableBagRef bag, const void *value) { | |
792 | struct __CFBagBucket *match; | |
793 | const CFBagCallBacks *cb; | |
794 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
795 | switch (__CFBagGetType(bag)) { | |
796 | case __kCFBagMutable: | |
797 | case __kCFBagFixedMutable: | |
798 | break; | |
799 | default: | |
800 | CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); | |
801 | break; | |
802 | } | |
803 | if (0 == bag->_count) return; | |
804 | __CFBagFindBuckets1(bag, value, &match); | |
805 | if (!match) return; | |
806 | match->_count--; bag->_count--; | |
807 | if (0 == match->_count) { | |
808 | cb = __CFBagGetCallBacks(bag); | |
809 | if (cb->release) { | |
810 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context); | |
811 | } | |
812 | match->_key = bag->_deletedMarker; | |
813 | bag->_bucketsUsed--; | |
814 | } | |
815 | } | |
816 | ||
817 | void CFBagRemoveAllValues(CFMutableBagRef bag) { | |
818 | struct __CFBagBucket *buckets; | |
819 | const CFBagCallBacks *cb; | |
820 | CFAllocatorRef allocator; | |
821 | CFIndex idx, nbuckets; | |
822 | __CFGenericValidateType(bag, __kCFBagTypeID); | |
823 | switch (__CFBagGetType(bag)) { | |
824 | case __kCFBagMutable: | |
825 | case __kCFBagFixedMutable: | |
826 | break; | |
827 | default: | |
828 | CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); | |
829 | break; | |
830 | } | |
831 | if (0 == bag->_count) return; | |
832 | buckets = bag->_buckets; | |
833 | nbuckets = bag->_bucketsNum; | |
834 | cb = __CFBagGetCallBacks(bag); | |
835 | allocator = CFGetAllocator(bag); | |
836 | for (idx = 0; idx < nbuckets; idx++) { | |
837 | if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { | |
838 | if (cb->release) { | |
839 | INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, buckets[idx]._key, bag->_context); | |
840 | } | |
841 | buckets[idx]._key = bag->_emptyMarker; | |
842 | buckets[idx]._count = 0; | |
843 | } | |
844 | } | |
845 | bag->_bucketsUsed = 0; | |
846 | bag->_count = 0; | |
847 | } | |
848 |