2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFBag.h>
29 #include "CFInternal.h"
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
};
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
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
50 CF_INLINE CFIndex
__CFBagRoundUpCapacity(CFIndex capacity
) {
52 for (idx
= 0; idx
< 42 && __CFBagCapacities
[idx
] < (uint32_t)capacity
; idx
++);
54 return __CFBagCapacities
[idx
];
57 CF_INLINE CFIndex
__CFBagNumBucketsForCapacity(CFIndex capacity
) {
59 for (idx
= 0; idx
< 42 && __CFBagCapacities
[idx
] < (uint32_t)capacity
; idx
++);
61 return __CFBagBuckets
[idx
];
65 __kCFBagImmutable
= 0, /* unchangable and fixed capacity */
66 __kCFBagMutable
= 1, /* changeable and variable capacity */
67 __kCFBagFixedMutable
= 3 /* changeable and fixed capacity */
71 __kCFBagHasNullCallBacks
= 0,
72 __kCFBagHasCFTypeCallBacks
= 1,
73 __kCFBagHasCustomCallBacks
= 3 /* callbacks are at end of header */
77 __kCFCollectionIsWeak
= 0,
78 __kCFCollectionIsStrong
= 1,
82 struct __CFBagBucket
{
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 */
99 CF_INLINE
bool __CFBagBucketIsEmpty(CFBagRef bag
, const struct __CFBagBucket
*bucket
) {
100 return (bag
->_emptyMarker
== bucket
->_key
);
103 CF_INLINE
bool __CFBagBucketIsDeleted(CFBagRef bag
, const struct __CFBagBucket
*bucket
) {
104 return (bag
->_deletedMarker
== bucket
->_key
);
107 CF_INLINE
bool __CFBagBucketIsOccupied(CFBagRef bag
, const struct __CFBagBucket
*bucket
) {
108 return (bag
->_emptyMarker
!= bucket
->_key
&& bag
->_deletedMarker
!= bucket
->_key
);
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 */
113 /* Bits 4-5 are used by GC */
115 static bool isStrongMemory(CFTypeRef collection
) {
116 return ! __CFBitfieldGetValue(((const CFRuntimeBase
*)collection
)->_info
, 4, 4);
119 static bool needsRestore(CFTypeRef collection
) {
120 return __CFBitfieldGetValue(((const CFRuntimeBase
*)collection
)->_info
, 5, 5);
124 CF_INLINE CFIndex
__CFBagGetType(CFBagRef bag
) {
125 return __CFBitfieldGetValue(((const CFRuntimeBase
*)bag
)->_info
, 1, 0);
128 CF_INLINE CFIndex
__CFBagGetSizeOfType(CFIndex t
) {
129 CFIndex size
= sizeof(struct __CFBag
);
130 if (__CFBitfieldGetValue(t
, 3, 2) == __kCFBagHasCustomCallBacks
) {
131 size
+= sizeof(CFBagCallBacks
);
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
:
146 result
= (CFBagCallBacks
*)((uint8_t *)bag
+ sizeof(struct __CFBag
));
150 CF_INLINE
bool __CFBagCallBacksMatchNull(const CFBagCallBacks
*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
));
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
));
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;
178 struct __CFBagBucket
*currentBucket
= buckets
+ probe
;
179 if (__CFBagBucketIsEmpty(bag
, currentBucket
)) {
181 } else if (__CFBagBucketIsDeleted(bag
, currentBucket
)) {
183 } else if (currentBucket
->_key
== key
|| (cb
->equal
&& INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb
->equal
, currentBucket
->_key
, key
, bag
->_context
))) {
184 *match
= currentBucket
;
187 probe
= (probe
+ probeskip
) % bag
->_bucketsNum
;
188 if (start
== probe
) return;
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;
202 struct __CFBagBucket
*currentBucket
= buckets
+ probe
;
203 if (__CFBagBucketIsEmpty(bag
, currentBucket
)) {
204 if (!*nomatch
) *nomatch
= currentBucket
;
206 } else if (__CFBagBucketIsDeleted(bag
, currentBucket
)) {
207 if (!*nomatch
) *nomatch
= currentBucket
;
208 } else if (!*match
&& (currentBucket
->_key
== key
|| (cb
->equal
&& INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb
->equal
, currentBucket
->_key
, key
, bag
->_context
)))) {
209 *match
= currentBucket
;
212 probe
= (probe
+ probeskip
) % bag
->_bucketsNum
;
213 if (start
== probe
) return;
217 static void __CFBagFindNewEmptyMarker(CFBagRef bag
) {
218 struct __CFBagBucket
*buckets
;
219 const void *newEmpty
;
221 CFIndex idx
, nbuckets
;
222 buckets
= bag
->_buckets
;
223 nbuckets
= bag
->_bucketsNum
;
224 newEmpty
= bag
->_emptyMarker
;
226 (intptr_t)newEmpty
-= 2;
228 for (idx
= 0; idx
< nbuckets
; idx
++) {
229 if (newEmpty
== buckets
[idx
]._key
) {
235 for (idx
= 0; idx
< nbuckets
; idx
++) {
236 if (bag
->_emptyMarker
== buckets
[idx
]._key
) {
237 buckets
[idx
]._key
= newEmpty
;
240 ((struct __CFBag
*)bag
)->_emptyMarker
= newEmpty
;
243 static void __CFBagFindNewDeletedMarker(CFBagRef bag
) {
244 struct __CFBagBucket
*buckets
;
245 const void *newDeleted
;
247 CFIndex idx
, nbuckets
;
248 buckets
= bag
->_buckets
;
249 nbuckets
= bag
->_bucketsNum
;
250 newDeleted
= bag
->_deletedMarker
;
252 (intptr_t)newDeleted
+= 2;
254 for (idx
= 0; idx
< nbuckets
; idx
++) {
255 if (newDeleted
== buckets
[idx
]._key
) {
261 for (idx
= 0; idx
< nbuckets
; idx
++) {
262 if (bag
->_deletedMarker
== buckets
[idx
]._key
) {
263 buckets
[idx
]._key
= newDeleted
;
266 ((struct __CFBag
*)bag
)->_deletedMarker
= newDeleted
;
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
)) {
293 static CFHashCode
__CFBagHash(CFTypeRef cf
) {
294 CFBagRef bag
= (CFBagRef
)cf
;
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
);
316 CFStringAppendFormat(result
, NULL
, CFSTR("\t%u : %@ (%d)\n"), idx
, desc
, buckets
[idx
]._count
);
319 CFStringAppendFormat(result
, NULL
, CFSTR("\t%u : <%p> (%d)\n"), idx
, buckets
[idx
]._key
, buckets
[idx
]._count
);
323 CFStringAppend(result
, CFSTR(")}"));
327 static void __CFBagDeallocate(CFTypeRef cf
) {
328 CFMutableBagRef bag
= (CFMutableBagRef
)cf
;
329 CFAllocatorRef allocator
= CFGetAllocator(bag
);
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.
335 if (__CFBagGetType(bag
) == __kCFBagImmutable
) {
336 __CFBitfieldSetValue(((CFRuntimeBase
*)bag
)->_info
, 1, 0, __kCFBagFixedMutable
);
338 CFBagRemoveAllValues(bag
);
339 if (__CFBagGetType(bag
) == __kCFBagMutable
&& bag
->_buckets
) {
340 _CFAllocatorDeallocateGC(allocator
, bag
->_buckets
);
344 static CFTypeID __kCFBagTypeID
= _kCFRuntimeNotATypeID
;
346 static const CFRuntimeClass __CFBagClass
= {
347 _kCFRuntimeScannedObject
,
352 (void *)__CFBagEqual
,
355 __CFBagCopyDescription
358 __private_extern__
void __CFBagInitialize(void) {
359 __kCFBagTypeID
= _CFRuntimeRegisterClass(&__CFBagClass
);
362 CFTypeID
CFBagGetTypeID(void) {
363 return __kCFBagTypeID
;
366 static CFBagRef
__CFBagInit(CFAllocatorRef allocator
, UInt32 flags
, CFIndex capacity
, const CFBagCallBacks
*callBacks
) {
367 struct __CFBag
*memory
;
370 CFBagCallBacks nonRetainingCallbacks
;
371 __CFBitfieldSetValue(flags
, 31, 2, 0);
372 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
373 if (!callBacks
|| (callBacks
->retain
== NULL
&& callBacks
->release
== NULL
)) {
374 __CFBitfieldSetValue(flags
, 4, 4, 1); // setWeak
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
386 if (__CFBagCallBacksMatchNull(callBacks
)) {
387 __CFBitfieldSetValue(flags
, 3, 2, __kCFBagHasNullCallBacks
);
388 } else if (__CFBagCallBacksMatchCFType(callBacks
)) {
389 __CFBitfieldSetValue(flags
, 3, 2, __kCFBagHasCFTypeCallBacks
);
391 __CFBitfieldSetValue(flags
, 3, 2, __kCFBagHasCustomCallBacks
);
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
);
399 case __kCFBagMutable
:
402 memory
= (struct __CFBag
*)_CFRuntimeCreateInstance(allocator
, __kCFBagTypeID
, size
, NULL
);
403 if (NULL
== memory
) {
406 __CFBitfieldSetValue(memory
->_base
._info
, 6, 0, flags
);
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
:
414 if (!isStrongMemory(memory
)) { // if weak, don't scan
415 auto_zone_set_layout_type(__CFCollectableZone
, memory
, AUTO_OBJECT_UNSCANNED
);
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
;
425 case __kCFBagFixedMutable
:
426 if (!isStrongMemory(memory
)) { // if weak, don't scan
427 auto_zone_set_layout_type(__CFCollectableZone
, memory
, AUTO_OBJECT_UNSCANNED
);
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
;
437 case __kCFBagMutable
:
438 if (__CFOASafe
) __CFSetLastAllocationEventName(memory
, "CFBag (mutable-variable)");
439 memory
->_capacity
= __CFBagRoundUpCapacity(1);
440 memory
->_bucketsNum
= 0;
441 memory
->_buckets
= NULL
;
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
));
453 return (CFBagRef
)memory
;
456 CFBagRef
CFBagCreate(CFAllocatorRef allocator
, const void **values
, CFIndex numValues
, const CFBagCallBacks
*callBacks
) {
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
);
466 for (idx
= 0; idx
< numValues
; idx
++) {
467 CFBagAddValue((CFMutableBagRef
)result
, values
[idx
]);
469 __CFBitfieldSetValue(((CFRuntimeBase
*)result
)->_info
, 1, 0, flags
);
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
);
478 CFBagRef
CFBagCreateCopy(CFAllocatorRef allocator
, CFBagRef bag
) {
480 const CFBagCallBacks
*cb
;
481 CFIndex numValues
= CFBagGetCount(bag
);
482 const void **list
, *buffer
[256];
483 list
= (numValues
<= 256) ? buffer
: CFAllocatorAllocate(allocator
, numValues
* sizeof(void *), 0); // XXX_PCB GC OK
484 if (list
!= buffer
&& __CFOASafe
) __CFSetLastAllocationEventName(list
, "CFBag (temp)");
485 CFBagGetValues(bag
, list
);
486 CFBagCallBacks patchedCB
;
487 if (CF_IS_OBJC(__kCFBagTypeID
, bag
)) {
488 cb
= &kCFTypeBagCallBacks
;
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
;
501 result
= CFBagCreate(allocator
, list
, numValues
, cb
);
502 if (list
!= buffer
) CFAllocatorDeallocate(allocator
, list
); // XXX_PCB GC OK
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
);
512 list
= (numValues
<= 256) ? buffer
: CFAllocatorAllocate(allocator
, numValues
* sizeof(void *), 0); // XXX_PCB GC OK
513 if (list
!= buffer
&& __CFOASafe
) __CFSetLastAllocationEventName(list
, "CFBag (temp)");
514 CFBagGetValues(bag
, list
);
515 CFBagCallBacks patchedCB
;
516 if (CF_IS_OBJC(__kCFBagTypeID
, bag
)) {
517 cb
= &kCFTypeBagCallBacks
;
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
;
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
]);
535 if (list
!= buffer
) CFAllocatorDeallocate(allocator
, list
); // GC OK
540 void _CFBagSetContext(CFBagRef bag
, void *context
) {
541 CFAllocatorRef allocator
= CFGetAllocator(bag
);
542 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, bag
, bag
->_context
, context
);
545 CFIndex
CFBagGetCount(CFBagRef bag
) {
546 __CFGenericValidateType(bag
, __kCFBagTypeID
);
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);
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);
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
);
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
);
579 return (match
? ((value
? __CFObjCStrongAssign(match
->_key
, value
) : NULL
), true) : false);
582 void CFBagGetValues(CFBagRef bag
, const void **values
) {
583 struct __CFBagBucket
*buckets
;
584 CFIndex idx
, cnt
, nbuckets
;
585 __CFGenericValidateType(bag
, __kCFBagTypeID
);
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 *));
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
;
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
);
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
;
621 CFAllocatorRef allocator
= CFGetAllocator(bag
);
622 bag
->_capacity
= __CFBagRoundUpCapacity(oldCount
+ numNewValues
);
623 bag
->_bucketsNum
= __CFBagNumBucketsForCapacity(bag
->_capacity
);
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
);
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
;
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
);
638 CF_WRITE_BARRIER_ASSIGN(allocator
, nomatch
->_key
, oldbuckets
[idx
]._key
);
639 nomatch
->_count
= oldbuckets
[idx
]._count
;
643 CFAssert1(bag
->_count
== oldCount
, __kCFLogAssertion
, "%s(): bag count differs after rehashing; error", __PRETTY_FUNCTION__
);
644 _CFAllocatorDeallocateGC(CFGetAllocator(bag
), oldbuckets
);
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;
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
);
655 __CFBagGrow(bag
, cap
- bag
->_count
);
658 void CFBagAddValue(CFMutableBagRef bag
, const void *value
) {
659 struct __CFBagBucket
*match
, *nomatch
;
660 CFAllocatorRef allocator
;
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
) {
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
);
674 CFAssert2(__CFBagGetType(bag
) != __kCFBagImmutable
, __kCFLogAssertion
, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__
, bag
);
677 __CFBagFindBuckets2(bag
, value
, &match
, &nomatch
);
679 match
->_count
++; bag
->_count
++;
681 allocator
= CFGetAllocator(bag
);
682 cb
= __CFBagGetCallBacks(bag
);
684 newValue
= (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef
, const void *, void *))cb
->retain
), allocator
, value
, bag
->_context
);
688 if (bag
->_emptyMarker
== newValue
) {
689 __CFBagFindNewEmptyMarker(bag
);
691 if (bag
->_deletedMarker
== newValue
) {
692 __CFBagFindNewDeletedMarker(bag
);
694 CF_WRITE_BARRIER_ASSIGN(allocator
, nomatch
->_key
, newValue
);
701 void CFBagReplaceValue(CFMutableBagRef bag
, const void *value
) {
702 struct __CFBagBucket
*match
;
703 CFAllocatorRef allocator
;
704 const CFBagCallBacks
*cb
;
705 const void *newValue
;
706 __CFGenericValidateType(bag
, __kCFBagTypeID
);
707 switch (__CFBagGetType(bag
)) {
708 case __kCFBagMutable
:
709 case __kCFBagFixedMutable
:
712 CFAssert2(__CFBagGetType(bag
) != __kCFBagImmutable
, __kCFLogAssertion
, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__
, bag
);
715 if (0 == bag
->_count
) return;
716 __CFBagFindBuckets1(bag
, value
, &match
);
718 allocator
= CFGetAllocator(bag
);
719 cb
= __CFBagGetCallBacks(bag
);
721 newValue
= (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef
, const void *, void *))cb
->retain
), allocator
, value
, bag
->_context
);
726 INVOKE_CALLBACK3(((void (*)(CFAllocatorRef
, const void *, void *))cb
->release
), allocator
, match
->_key
, bag
->_context
);
727 match
->_key
= bag
->_deletedMarker
;
729 if (bag
->_emptyMarker
== newValue
) {
730 __CFBagFindNewEmptyMarker(bag
);
732 if (bag
->_deletedMarker
== newValue
) {
733 __CFBagFindNewDeletedMarker(bag
);
735 CF_WRITE_BARRIER_ASSIGN(allocator
, match
->_key
, newValue
);
738 void CFBagSetValue(CFMutableBagRef bag
, const void *value
) {
739 struct __CFBagBucket
*match
, *nomatch
;
740 CFAllocatorRef allocator
;
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
) {
750 case __kCFBagFixedMutable
:
753 CFAssert2(__CFBagGetType(bag
) != __kCFBagImmutable
, __kCFLogAssertion
, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__
, bag
);
756 __CFBagFindBuckets2(bag
, value
, &match
, &nomatch
);
757 allocator
= CFGetAllocator(bag
);
758 cb
= __CFBagGetCallBacks(bag
);
760 newValue
= (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef
, const void *, void *))cb
->retain
), allocator
, value
, bag
->_context
);
766 INVOKE_CALLBACK3(((void (*)(CFAllocatorRef
, const void *, void *))cb
->release
), allocator
, match
->_key
, bag
->_context
);
767 match
->_key
= bag
->_deletedMarker
;
769 if (bag
->_emptyMarker
== newValue
) {
770 __CFBagFindNewEmptyMarker(bag
);
772 if (bag
->_deletedMarker
== newValue
) {
773 __CFBagFindNewDeletedMarker(bag
);
775 CF_WRITE_BARRIER_ASSIGN(allocator
, match
->_key
, newValue
);
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
);
781 if (bag
->_deletedMarker
== newValue
) {
782 __CFBagFindNewDeletedMarker(bag
);
784 CF_WRITE_BARRIER_ASSIGN(allocator
, nomatch
->_key
, newValue
);
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
:
800 CFAssert2(__CFBagGetType(bag
) != __kCFBagImmutable
, __kCFLogAssertion
, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__
, bag
);
803 if (0 == bag
->_count
) return;
804 __CFBagFindBuckets1(bag
, value
, &match
);
806 match
->_count
--; bag
->_count
--;
807 if (0 == match
->_count
) {
808 cb
= __CFBagGetCallBacks(bag
);
810 INVOKE_CALLBACK3(((void (*)(CFAllocatorRef
, const void *, void *))cb
->release
), CFGetAllocator(bag
), match
->_key
, bag
->_context
);
812 match
->_key
= bag
->_deletedMarker
;
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
:
828 CFAssert2(__CFBagGetType(bag
) != __kCFBagImmutable
, __kCFLogAssertion
, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__
, bag
);
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
])) {
839 INVOKE_CALLBACK3(((void (*)(CFAllocatorRef
, const void *, void *))cb
->release
), allocator
, buckets
[idx
]._key
, bag
->_context
);
841 buckets
[idx
]._key
= bag
->_emptyMarker
;
842 buckets
[idx
]._count
= 0;
845 bag
->_bucketsUsed
= 0;