2 * Copyright (c) 2009 Apple 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@
25 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
29 #include <CoreFoundation/CFArray.h>
30 #include "CFStorage.h"
31 #include <CoreFoundation/CFPriv.h>
32 #include "CFInternal.h"
34 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
35 #include <libkern/OSAtomic.h>
38 __private_extern__
void _CFStorageSetWeak(CFStorageRef storage
);
40 const CFArrayCallBacks kCFTypeArrayCallBacks
= {0, __CFTypeCollectionRetain
, __CFTypeCollectionRelease
, CFCopyDescription
, CFEqual
};
41 static const CFArrayCallBacks __kCFNullArrayCallBacks
= {0, NULL
, NULL
, NULL
, NULL
};
43 struct __CFArrayBucket
{
48 __CF_MAX_BUCKETS_PER_DEQUE
= 262140
51 CF_INLINE CFIndex
__CFArrayDequeRoundUpCapacity(CFIndex capacity
) {
52 if (capacity
< 4) return 4;
53 return __CFMin((1 << flsl(capacity
)), __CF_MAX_BUCKETS_PER_DEQUE
);
56 struct __CFArrayDeque
{
61 uint32_t _pad
; // GC: pointers must be 8-byte aligned for the collector to find them.
63 /* struct __CFArrayBucket buckets follow here */
68 CFIndex _count
; /* number of objects */
70 int32_t _mutInProgress
;
71 void *_store
; /* can be NULL when MutableDeque */
76 __kCFArrayImmutable
= 0,
82 __kCFArrayHasNullCallBacks
= 0,
83 __kCFArrayHasCFTypeCallBacks
= 1,
84 __kCFArrayHasCustomCallBacks
= 3 /* callbacks are at end of header */
88 Bits 4 & 5 are reserved for GC use.
89 Bit 4, if set, indicates that the array is weak.
90 Bit 5 marks whether finalization has occured and, thus, whether to continue to do special retain/release processing of elements.
93 CF_INLINE
bool isStrongMemory(CFTypeRef collection
) {
94 return __CFBitfieldGetValue(((const CFRuntimeBase
*)collection
)->_cfinfo
[CF_INFO_BITS
], 4, 4) == 0;
97 CF_INLINE
bool isWeakMemory(CFTypeRef collection
) {
98 return __CFBitfieldGetValue(((const CFRuntimeBase
*)collection
)->_cfinfo
[CF_INFO_BITS
], 4, 4) != 0;
101 CF_INLINE
bool hasBeenFinalized(CFTypeRef collection
) {
102 return __CFBitfieldGetValue(((const CFRuntimeBase
*)collection
)->_cfinfo
[CF_INFO_BITS
], 5, 5) != 0;
105 CF_INLINE
void markFinalized(CFTypeRef collection
) {
106 __CFBitfieldSetValue(((CFRuntimeBase
*)collection
)->_cfinfo
[CF_INFO_BITS
], 5, 5, 1);
109 CF_INLINE CFIndex
__CFArrayGetType(CFArrayRef array
) {
110 return __CFBitfieldGetValue(((const CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 1, 0);
113 CF_INLINE CFIndex
__CFArrayGetSizeOfType(CFIndex t
) {
115 size
+= sizeof(struct __CFArray
);
116 if (__CFBitfieldGetValue(t
, 3, 2) == __kCFArrayHasCustomCallBacks
) {
117 size
+= sizeof(CFArrayCallBacks
);
122 CF_INLINE CFIndex
__CFArrayGetCount(CFArrayRef array
) {
123 return array
->_count
;
126 CF_INLINE
void __CFArraySetCount(CFArrayRef array
, CFIndex v
) {
127 ((struct __CFArray
*)array
)->_count
= v
;
130 /* Only applies to immutable and mutable-deque-using arrays;
131 * Returns the bucket holding the left-most real value in the latter case. */
132 CF_INLINE
struct __CFArrayBucket
*__CFArrayGetBucketsPtr(CFArrayRef array
) {
133 switch (__CFArrayGetType(array
)) {
134 case __kCFArrayImmutable
:
135 return (struct __CFArrayBucket
*)((uint8_t *)array
+ __CFArrayGetSizeOfType(((CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
]));
136 case __kCFArrayDeque
: {
137 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
138 return (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
) + deque
->_leftIdx
* sizeof(struct __CFArrayBucket
));
144 /* This shouldn't be called if the array count is 0. */
145 CF_INLINE
struct __CFArrayBucket
*__CFArrayGetBucketAtIndex(CFArrayRef array
, CFIndex idx
) {
146 switch (__CFArrayGetType(array
)) {
147 case __kCFArrayImmutable
:
148 case __kCFArrayDeque
:
149 return __CFArrayGetBucketsPtr(array
) + idx
;
150 case __kCFArrayStorage
: {
151 CFStorageRef store
= (CFStorageRef
)array
->_store
;
152 return (struct __CFArrayBucket
*)CFStorageGetValueAtIndex(store
, idx
, NULL
);
158 CF_INLINE CFArrayCallBacks
*__CFArrayGetCallBacks(CFArrayRef array
) {
159 CFArrayCallBacks
*result
= NULL
;
160 switch (__CFBitfieldGetValue(((const CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 3, 2)) {
161 case __kCFArrayHasNullCallBacks
:
162 return (CFArrayCallBacks
*)&__kCFNullArrayCallBacks
;
163 case __kCFArrayHasCFTypeCallBacks
:
164 return (CFArrayCallBacks
*)&kCFTypeArrayCallBacks
;
165 case __kCFArrayHasCustomCallBacks
:
168 switch (__CFArrayGetType(array
)) {
169 case __kCFArrayImmutable
:
170 result
= (CFArrayCallBacks
*)((uint8_t *)array
+ sizeof(struct __CFArray
));
172 case __kCFArrayDeque
:
173 case __kCFArrayStorage
:
174 result
= (CFArrayCallBacks
*)((uint8_t *)array
+ sizeof(struct __CFArray
));
180 CF_INLINE
bool __CFArrayCallBacksMatchNull(const CFArrayCallBacks
*c
) {
182 (c
->retain
== __kCFNullArrayCallBacks
.retain
&&
183 c
->release
== __kCFNullArrayCallBacks
.release
&&
184 c
->copyDescription
== __kCFNullArrayCallBacks
.copyDescription
&&
185 c
->equal
== __kCFNullArrayCallBacks
.equal
));
188 CF_INLINE
bool __CFArrayCallBacksMatchCFType(const CFArrayCallBacks
*c
) {
189 return (&kCFTypeArrayCallBacks
== c
||
190 (c
->retain
== kCFTypeArrayCallBacks
.retain
&&
191 c
->release
== kCFTypeArrayCallBacks
.release
&&
192 c
->copyDescription
== kCFTypeArrayCallBacks
.copyDescription
&&
193 c
->equal
== kCFTypeArrayCallBacks
.equal
));
197 #define CHECK_FOR_MUTATION(A) do { if ((A)->_mutInProgress) CFLog(3, CFSTR("*** %s: function called while the array (%p) is being mutated in this or another thread"), __PRETTY_FUNCTION__, (A)); } while (0)
198 #define BEGIN_MUTATION(A) do { OSAtomicAdd32Barrier(1, &((struct __CFArray *)(A))->_mutInProgress); } while (0)
199 #define END_MUTATION(A) do { OSAtomicAdd32Barrier(-1, &((struct __CFArray *)(A))->_mutInProgress); } while (0)
201 #define CHECK_FOR_MUTATION(A) do { } while (0)
202 #define BEGIN_MUTATION(A) do { } while (0)
203 #define END_MUTATION(A) do { } while (0)
206 struct _releaseContext
{
207 void (*release
)(CFAllocatorRef
, const void *);
208 CFAllocatorRef allocator
;
211 static void __CFArrayStorageRelease(const void *itemptr
, void *context
) {
212 struct _releaseContext
*rc
= (struct _releaseContext
*)context
;
213 INVOKE_CALLBACK2(rc
->release
, rc
->allocator
, *(const void **)itemptr
);
214 *(const void **)itemptr
= NULL
; // GC: clear item to break strong reference.
217 static void __CFArrayReleaseValues(CFArrayRef array
, CFRange range
, bool releaseStorageIfPossible
) {
218 const CFArrayCallBacks
*cb
= __CFArrayGetCallBacks(array
);
219 CFAllocatorRef allocator
;
221 switch (__CFArrayGetType(array
)) {
222 case __kCFArrayImmutable
:
223 if (NULL
!= cb
->release
&& 0 < range
.length
&& !hasBeenFinalized(array
)) {
224 // if we've been finalized then we know that
225 // 1) we're using the standard callback on GC memory
226 // 2) the slots don't' need to be zeroed
227 struct __CFArrayBucket
*buckets
= __CFArrayGetBucketsPtr(array
);
228 allocator
= __CFGetAllocator(array
);
229 for (idx
= 0; idx
< range
.length
; idx
++) {
230 INVOKE_CALLBACK2(cb
->release
, allocator
, buckets
[idx
+ range
.location
]._item
);
231 buckets
[idx
+ range
.location
]._item
= NULL
; // GC: break strong reference.
235 case __kCFArrayDeque
: {
236 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
237 if (0 < range
.length
&& NULL
!= deque
&& !hasBeenFinalized(array
)) {
238 struct __CFArrayBucket
*buckets
= __CFArrayGetBucketsPtr(array
);
239 if (NULL
!= cb
->release
) {
240 allocator
= __CFGetAllocator(array
);
241 for (idx
= 0; idx
< range
.length
; idx
++) {
242 INVOKE_CALLBACK2(cb
->release
, allocator
, buckets
[idx
+ range
.location
]._item
);
243 buckets
[idx
+ range
.location
]._item
= NULL
; // GC: break strong reference.
246 for (idx
= 0; idx
< range
.length
; idx
++) {
247 buckets
[idx
+ range
.location
]._item
= NULL
; // GC: break strong reference.
251 if (releaseStorageIfPossible
&& 0 == range
.location
&& __CFArrayGetCount(array
) == range
.length
) {
252 allocator
= __CFGetAllocator(array
);
253 if (NULL
!= deque
) _CFAllocatorDeallocateGC(allocator
, deque
);
254 __CFArraySetCount(array
, 0); // GC: _count == 0 ==> _store == NULL.
255 ((struct __CFArray
*)array
)->_store
= NULL
;
259 case __kCFArrayStorage
: {
260 CFStorageRef store
= (CFStorageRef
)array
->_store
;
261 if (NULL
!= cb
->release
&& 0 < range
.length
&& !hasBeenFinalized(array
)) {
262 struct _releaseContext context
;
263 allocator
= __CFGetAllocator(array
);
264 context
.release
= cb
->release
;
265 context
.allocator
= allocator
;
266 CFStorageApplyFunction(store
, range
, __CFArrayStorageRelease
, &context
);
268 if (releaseStorageIfPossible
&& 0 == range
.location
&& __CFArrayGetCount(array
) == range
.length
) {
270 __CFArraySetCount(array
, 0); // GC: _count == 0 ==> _store == NULL.
271 ((struct __CFArray
*)array
)->_store
= NULL
;
272 __CFBitfieldSetValue(((CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 1, 0, __kCFArrayDeque
);
280 CF_INLINE
void __CFArrayValidateRange(CFArrayRef array
, CFRange range
, const char *func
) {
281 CFAssert3(0 <= range
.location
&& range
.location
<= CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): range.location index (%d) out of bounds (0, %d)", func
, range
.location
, CFArrayGetCount(array
));
282 CFAssert2(0 <= range
.length
, __kCFLogAssertion
, "%s(): range.length (%d) cannot be less than zero", func
, range
.length
);
283 CFAssert3(range
.location
+ range
.length
<= CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): ending index (%d) out of bounds (0, %d)", func
, range
.location
+ range
.length
, CFArrayGetCount(array
));
286 #define __CFArrayValidateRange(a,r,f)
289 static Boolean
__CFArrayEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
290 CFArrayRef array1
= (CFArrayRef
)cf1
;
291 CFArrayRef array2
= (CFArrayRef
)cf2
;
292 const CFArrayCallBacks
*cb1
, *cb2
;
294 if (array1
== array2
) return true;
295 cnt
= __CFArrayGetCount(array1
);
296 if (cnt
!= __CFArrayGetCount(array2
)) return false;
297 cb1
= __CFArrayGetCallBacks(array1
);
298 cb2
= __CFArrayGetCallBacks(array2
);
299 if (cb1
->equal
!= cb2
->equal
) return false;
300 if (0 == cnt
) return true; /* after function comparison! */
301 for (idx
= 0; idx
< cnt
; idx
++) {
302 const void *val1
= __CFArrayGetBucketAtIndex(array1
, idx
)->_item
;
303 const void *val2
= __CFArrayGetBucketAtIndex(array2
, idx
)->_item
;
305 if (NULL
== cb1
->equal
) return false;
306 if (!INVOKE_CALLBACK2(cb1
->equal
, val1
, val2
)) return false;
312 static CFHashCode
__CFArrayHash(CFTypeRef cf
) {
313 CFArrayRef array
= (CFArrayRef
)cf
;
314 return __CFArrayGetCount(array
);
317 static CFStringRef
__CFArrayCopyDescription(CFTypeRef cf
) {
318 CFArrayRef array
= (CFArrayRef
)cf
;
319 CFMutableStringRef result
;
320 const CFArrayCallBacks
*cb
;
321 CFAllocatorRef allocator
;
323 cnt
= __CFArrayGetCount(array
);
324 allocator
= CFGetAllocator(array
);
325 result
= CFStringCreateMutable(allocator
, 0);
326 switch (__CFArrayGetType(array
)) {
327 case __kCFArrayImmutable
:
328 CFStringAppendFormat(result
, NULL
, CFSTR("<CFArray %p [%p]>{type = immutable, count = %u, values = (%s"), cf
, allocator
, cnt
, cnt
? "\n" : "");
330 case __kCFArrayDeque
:
331 CFStringAppendFormat(result
, NULL
, CFSTR("<CFArray %p [%p]>{type = mutable-small, count = %u, values = (%s"), cf
, allocator
, cnt
, cnt
? "\n" : "");
333 case __kCFArrayStorage
:
334 CFStringAppendFormat(result
, NULL
, CFSTR("<CFArray %p [%p]>{type = mutable-large, count = %u, values = (%s"), cf
, allocator
, cnt
, cnt
? "\n" : "");
337 cb
= __CFArrayGetCallBacks(array
);
338 for (idx
= 0; idx
< cnt
; idx
++) {
339 CFStringRef desc
= NULL
;
340 const void *val
= __CFArrayGetBucketAtIndex(array
, idx
)->_item
;
341 if (NULL
!= cb
->copyDescription
) {
342 desc
= (CFStringRef
)INVOKE_CALLBACK1(cb
->copyDescription
, val
);
345 CFStringAppendFormat(result
, NULL
, CFSTR("\t%u : %@\n"), idx
, desc
);
348 CFStringAppendFormat(result
, NULL
, CFSTR("\t%u : <%p>\n"), idx
, val
);
351 CFStringAppend(result
, CFSTR(")}"));
356 static void __CFArrayDeallocate(CFTypeRef cf
) {
357 CFArrayRef array
= (CFArrayRef
)cf
;
358 BEGIN_MUTATION(array
);
359 // Under GC, keep contents alive when we know we can, either standard callbacks or NULL
360 // if (__CFBitfieldGetValue(cf->info, 5, 4)) return; // bits only ever set under GC
361 CFAllocatorRef allocator
= __CFGetAllocator(array
);
362 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
363 // XXX_PCB keep array intact during finalization.
364 const CFArrayCallBacks
*cb
= __CFArrayGetCallBacks(array
);
365 if (cb
->retain
== NULL
&& cb
->release
== NULL
) {
369 if (cb
== &kCFTypeArrayCallBacks
|| cb
->release
== kCFTypeArrayCallBacks
.release
) {
371 for (CFIndex idx
= 0; idx
< __CFArrayGetCount(array
); idx
++) {
372 const void *item
= __CFArrayGetBucketAtIndex(array
, 0 + idx
)->_item
;
373 kCFTypeArrayCallBacks
.release(kCFAllocatorSystemDefault
, item
);
379 __CFArrayReleaseValues(array
, CFRangeMake(0, __CFArrayGetCount(array
)), true);
383 static CFTypeID __kCFArrayTypeID
= _kCFRuntimeNotATypeID
;
385 static const CFRuntimeClass __CFArrayClass
= {
386 _kCFRuntimeScannedObject
,
394 __CFArrayCopyDescription
397 __private_extern__
void __CFArrayInitialize(void) {
398 __kCFArrayTypeID
= _CFRuntimeRegisterClass(&__CFArrayClass
);
401 CFTypeID
CFArrayGetTypeID(void) {
402 return __kCFArrayTypeID
;
405 static CFArrayRef
__CFArrayInit(CFAllocatorRef allocator
, UInt32 flags
, CFIndex capacity
, const CFArrayCallBacks
*callBacks
) {
406 struct __CFArray
*memory
;
408 __CFBitfieldSetValue(flags
, 31, 2, 0);
409 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
410 if (!callBacks
|| (callBacks
->retain
== NULL
&& callBacks
->release
== NULL
)) {
411 __CFBitfieldSetValue(flags
, 4, 4, 1); // setWeak
414 if (__CFArrayCallBacksMatchNull(callBacks
)) {
415 __CFBitfieldSetValue(flags
, 3, 2, __kCFArrayHasNullCallBacks
);
416 } else if (__CFArrayCallBacksMatchCFType(callBacks
)) {
417 __CFBitfieldSetValue(flags
, 3, 2, __kCFArrayHasCFTypeCallBacks
);
419 __CFBitfieldSetValue(flags
, 3, 2, __kCFArrayHasCustomCallBacks
);
421 size
= __CFArrayGetSizeOfType(flags
) - sizeof(CFRuntimeBase
);
422 switch (__CFBitfieldGetValue(flags
, 1, 0)) {
423 case __kCFArrayImmutable
:
424 size
+= capacity
* sizeof(struct __CFArrayBucket
);
426 case __kCFArrayDeque
:
427 case __kCFArrayStorage
:
430 memory
= (struct __CFArray
*)_CFRuntimeCreateInstance(allocator
, __kCFArrayTypeID
, size
, NULL
);
431 if (NULL
== memory
) {
434 __CFBitfieldSetValue(memory
->_base
._cfinfo
[CF_INFO_BITS
], 6, 0, flags
);
435 __CFArraySetCount((CFArrayRef
)memory
, 0);
436 switch (__CFBitfieldGetValue(flags
, 1, 0)) {
437 case __kCFArrayImmutable
:
438 if (isWeakMemory(memory
)) { // if weak, don't scan
439 auto_zone_set_unscanned(auto_zone(), memory
);
441 if (__CFOASafe
) __CFSetLastAllocationEventName(memory
, "CFArray (immutable)");
443 case __kCFArrayDeque
:
444 case __kCFArrayStorage
:
445 if (__CFOASafe
) __CFSetLastAllocationEventName(memory
, "CFArray (mutable-variable)");
446 ((struct __CFArray
*)memory
)->_mutations
= 1;
447 ((struct __CFArray
*)memory
)->_mutInProgress
= 0;
448 ((struct __CFArray
*)memory
)->_store
= NULL
;
451 if (__kCFArrayHasCustomCallBacks
== __CFBitfieldGetValue(flags
, 3, 2)) {
452 CFArrayCallBacks
*cb
= (CFArrayCallBacks
*)__CFArrayGetCallBacks((CFArrayRef
)memory
);
454 FAULT_CALLBACK((void **)&(cb
->retain
));
455 FAULT_CALLBACK((void **)&(cb
->release
));
456 FAULT_CALLBACK((void **)&(cb
->copyDescription
));
457 FAULT_CALLBACK((void **)&(cb
->equal
));
459 return (CFArrayRef
)memory
;
462 CFArrayRef
CFArrayCreate(CFAllocatorRef allocator
, const void **values
, CFIndex numValues
, const CFArrayCallBacks
*callBacks
) {
464 const CFArrayCallBacks
*cb
;
465 struct __CFArrayBucket
*buckets
;
466 CFAllocatorRef bucketsAllocator
;
469 CFAssert2(0 <= numValues
, __kCFLogAssertion
, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__
, numValues
);
470 result
= __CFArrayInit(allocator
, __kCFArrayImmutable
, numValues
, callBacks
);
471 cb
= __CFArrayGetCallBacks(result
);
472 buckets
= __CFArrayGetBucketsPtr(result
);
473 bucketsAllocator
= isStrongMemory(result
) ? allocator
: kCFAllocatorNull
;
474 bucketsBase
= CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator
) ? (void *)auto_zone_base_pointer(auto_zone(), buckets
) : NULL
;
475 if (NULL
!= cb
->retain
) {
476 for (idx
= 0; idx
< numValues
; idx
++) {
477 __CFAssignWithWriteBarrier((void **)&buckets
->_item
, (void *)INVOKE_CALLBACK2(cb
->retain
, allocator
, *values
));
483 for (idx
= 0; idx
< numValues
; idx
++) {
484 __CFAssignWithWriteBarrier((void **)&buckets
->_item
, (void *)*values
);
489 __CFArraySetCount(result
, numValues
);
493 CFMutableArrayRef
CFArrayCreateMutable(CFAllocatorRef allocator
, CFIndex capacity
, const CFArrayCallBacks
*callBacks
) {
494 CFAssert2(0 <= capacity
, __kCFLogAssertion
, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__
, capacity
);
495 CFAssert2(capacity
<= LONG_MAX
/ sizeof(void *), __kCFLogAssertion
, "%s(): capacity (%d) is too large for this architecture", __PRETTY_FUNCTION__
, capacity
);
496 return (CFMutableArrayRef
)__CFArrayInit(allocator
, __kCFArrayDeque
, capacity
, callBacks
);
499 CFArrayRef
CFArrayCreateCopy(CFAllocatorRef allocator
, CFArrayRef array
) {
501 const CFArrayCallBacks
*cb
;
502 struct __CFArrayBucket
*buckets
;
503 CFAllocatorRef bucketsAllocator
;
505 CFIndex numValues
= CFArrayGetCount(array
);
507 if (CF_IS_OBJC(__kCFArrayTypeID
, array
)) {
508 cb
= &kCFTypeArrayCallBacks
;
510 cb
= __CFArrayGetCallBacks(array
);
512 result
= __CFArrayInit(allocator
, __kCFArrayImmutable
, numValues
, cb
);
513 cb
= __CFArrayGetCallBacks(result
); // GC: use the new array's callbacks so we don't leak.
514 buckets
= __CFArrayGetBucketsPtr(result
);
515 bucketsAllocator
= isStrongMemory(result
) ? allocator
: kCFAllocatorNull
;
516 bucketsBase
= CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator
) ? (void *)auto_zone_base_pointer(auto_zone(), buckets
) : NULL
;
517 for (idx
= 0; idx
< numValues
; idx
++) {
518 const void *value
= CFArrayGetValueAtIndex(array
, idx
);
519 if (NULL
!= cb
->retain
) {
520 value
= (void *)INVOKE_CALLBACK2(cb
->retain
, allocator
, value
);
522 __CFAssignWithWriteBarrier((void **)&buckets
->_item
, (void *)value
);
525 __CFArraySetCount(result
, numValues
);
529 CFMutableArrayRef
CFArrayCreateMutableCopy(CFAllocatorRef allocator
, CFIndex capacity
, CFArrayRef array
) {
530 CFMutableArrayRef result
;
531 const CFArrayCallBacks
*cb
;
532 CFIndex idx
, numValues
= CFArrayGetCount(array
);
534 if (CF_IS_OBJC(__kCFArrayTypeID
, array
)) {
535 cb
= &kCFTypeArrayCallBacks
;
538 cb
= __CFArrayGetCallBacks(array
);
540 flags
= __kCFArrayDeque
;
541 result
= (CFMutableArrayRef
)__CFArrayInit(allocator
, flags
, capacity
, cb
);
542 if (0 == capacity
) _CFArraySetCapacity(result
, numValues
);
543 for (idx
= 0; idx
< numValues
; idx
++) {
544 const void *value
= CFArrayGetValueAtIndex(array
, idx
);
545 CFArrayAppendValue(result
, value
);
550 CFIndex
CFArrayGetCount(CFArrayRef array
) {
551 CF_OBJC_FUNCDISPATCH0(__kCFArrayTypeID
, CFIndex
, array
, "count");
552 __CFGenericValidateType(array
, __kCFArrayTypeID
);
553 CHECK_FOR_MUTATION(array
);
554 return __CFArrayGetCount(array
);
558 CFIndex
CFArrayGetCountOfValue(CFArrayRef array
, CFRange range
, const void *value
) {
559 const CFArrayCallBacks
*cb
;
560 CFIndex idx
, count
= 0;
561 // CF: this ignores range
562 CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID
, CFIndex
, array
, "_cfcountOccurrences:", value
);
563 __CFGenericValidateType(array
, __kCFArrayTypeID
);
564 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
565 CHECK_FOR_MUTATION(array
);
566 cb
= __CFArrayGetCallBacks(array
);
567 for (idx
= 0; idx
< range
.length
; idx
++) {
568 const void *item
= __CFArrayGetBucketAtIndex(array
, range
.location
+ idx
)->_item
;
569 if (value
== item
|| (cb
->equal
&& INVOKE_CALLBACK2(cb
->equal
, value
, item
))) {
576 Boolean
CFArrayContainsValue(CFArrayRef array
, CFRange range
, const void *value
) {
578 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, char, array
, "containsObject:inRange:", value
, range
);
579 __CFGenericValidateType(array
, __kCFArrayTypeID
);
580 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
581 CHECK_FOR_MUTATION(array
);
582 const CFArrayCallBacks
*cb
= __CFArrayGetCallBacks(array
);
583 for (idx
= 0; idx
< range
.length
; idx
++) {
584 const void *item
= __CFArrayGetBucketAtIndex(array
, range
.location
+ idx
)->_item
;
585 if (value
== item
|| (cb
->equal
&& INVOKE_CALLBACK2(cb
->equal
, value
, item
))) {
592 const void *CFArrayGetValueAtIndex(CFArrayRef array
, CFIndex idx
) {
593 CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID
, void *, array
, "objectAtIndex:", idx
);
594 __CFGenericValidateType(array
, __kCFArrayTypeID
);
595 CFAssert2(0 <= idx
&& idx
< __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__
, idx
);
596 CHECK_FOR_MUTATION(array
);
597 return __CFArrayGetBucketAtIndex(array
, idx
)->_item
;
600 // This is for use by NSCFArray; it avoids ObjC dispatch, and checks for out of bounds
601 const void *_CFArrayCheckAndGetValueAtIndex(CFArrayRef array
, CFIndex idx
) {
602 CHECK_FOR_MUTATION(array
);
603 if (0 <= idx
&& idx
< __CFArrayGetCount(array
)) return __CFArrayGetBucketAtIndex(array
, idx
)->_item
;
608 void CFArrayGetValues(CFArrayRef array
, CFRange range
, const void **values
) {
609 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, void, array
, "getObjects:range:", values
, range
);
610 __CFGenericValidateType(array
, __kCFArrayTypeID
);
611 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
612 CFAssert1(NULL
!= values
, __kCFLogAssertion
, "%s(): pointer to values may not be NULL", __PRETTY_FUNCTION__
);
613 CHECK_FOR_MUTATION(array
);
614 if (0 < range
.length
) {
615 switch (__CFArrayGetType(array
)) {
616 case __kCFArrayImmutable
:
617 case __kCFArrayDeque
:
618 objc_memmove_collectable(values
, __CFArrayGetBucketsPtr(array
) + range
.location
, range
.length
* sizeof(struct __CFArrayBucket
));
620 case __kCFArrayStorage
: {
621 CFStorageRef store
= (CFStorageRef
)array
->_store
;
622 CFStorageGetValues(store
, range
, values
);
630 CF_EXPORT
unsigned long _CFArrayFastEnumeration(CFArrayRef array
, struct __objcFastEnumerationStateEquivalent
*state
, void *stackbuffer
, unsigned long count
) {
631 CHECK_FOR_MUTATION(array
);
632 if (array
->_count
== 0) return 0;
633 enum { ATSTART
= 0, ATEND
= 1 };
634 switch (__CFArrayGetType(array
)) {
635 case __kCFArrayImmutable
:
636 if (state
->state
== ATSTART
) { /* first time */
637 static const unsigned long const_mu
= 1;
638 state
->state
= ATEND
;
639 state
->mutationsPtr
= (unsigned long *)&const_mu
;
640 state
->itemsPtr
= (unsigned long *)__CFArrayGetBucketsPtr(array
);
641 return array
->_count
;
644 case __kCFArrayDeque
:
645 if (state
->state
== ATSTART
) { /* first time */
646 state
->state
= ATEND
;
647 state
->mutationsPtr
= (unsigned long *)&array
->_mutations
;
648 state
->itemsPtr
= (unsigned long *)__CFArrayGetBucketsPtr(array
);
649 return array
->_count
;
652 case __kCFArrayStorage
:
653 state
->mutationsPtr
= (unsigned long *)&array
->_mutations
;
654 return _CFStorageFastEnumeration((CFStorageRef
)array
->_store
, state
, stackbuffer
, count
);
660 void CFArrayApplyFunction(CFArrayRef array
, CFRange range
, CFArrayApplierFunction applier
, void *context
) {
662 FAULT_CALLBACK((void **)&(applier
));
663 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, void, array
, "_cfapply:context:", applier
, context
);
664 __CFGenericValidateType(array
, __kCFArrayTypeID
);
665 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
666 CFAssert1(NULL
!= applier
, __kCFLogAssertion
, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__
);
667 CHECK_FOR_MUTATION(array
);
668 for (idx
= 0; idx
< range
.length
; idx
++) {
669 const void *item
= __CFArrayGetBucketAtIndex(array
, range
.location
+ idx
)->_item
;
670 INVOKE_CALLBACK2(applier
, item
, context
);
674 CFIndex
CFArrayGetFirstIndexOfValue(CFArrayRef array
, CFRange range
, const void *value
) {
675 const CFArrayCallBacks
*cb
;
677 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, CFIndex
, array
, "_cfindexOfObject:inRange:", value
, range
);
678 __CFGenericValidateType(array
, __kCFArrayTypeID
);
679 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
680 CHECK_FOR_MUTATION(array
);
681 cb
= __CFArrayGetCallBacks(array
);
682 for (idx
= 0; idx
< range
.length
; idx
++) {
683 const void *item
= __CFArrayGetBucketAtIndex(array
, range
.location
+ idx
)->_item
;
684 if (value
== item
|| (cb
->equal
&& INVOKE_CALLBACK2(cb
->equal
, value
, item
)))
685 return idx
+ range
.location
;
690 CFIndex
CFArrayGetLastIndexOfValue(CFArrayRef array
, CFRange range
, const void *value
) {
691 const CFArrayCallBacks
*cb
;
693 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, CFIndex
, array
, "_cflastIndexOfObject:inRange:", value
, range
);
694 __CFGenericValidateType(array
, __kCFArrayTypeID
);
695 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
696 CHECK_FOR_MUTATION(array
);
697 cb
= __CFArrayGetCallBacks(array
);
698 for (idx
= range
.length
; idx
--;) {
699 const void *item
= __CFArrayGetBucketAtIndex(array
, range
.location
+ idx
)->_item
;
700 if (value
== item
|| (cb
->equal
&& INVOKE_CALLBACK2(cb
->equal
, value
, item
)))
701 return idx
+ range
.location
;
706 void CFArrayAppendValue(CFMutableArrayRef array
, const void *value
) {
707 CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID
, void, array
, "addObject:", value
);
708 __CFGenericValidateType(array
, __kCFArrayTypeID
);
709 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
710 CHECK_FOR_MUTATION(array
);
711 _CFArrayReplaceValues(array
, CFRangeMake(__CFArrayGetCount(array
), 0), &value
, 1);
714 void CFArraySetValueAtIndex(CFMutableArrayRef array
, CFIndex idx
, const void *value
) {
715 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, void, array
, "setObject:atIndex:", value
, idx
);
716 __CFGenericValidateType(array
, __kCFArrayTypeID
);
717 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
718 CFAssert2(0 <= idx
&& idx
<= __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__
, idx
);
719 CHECK_FOR_MUTATION(array
);
720 if (idx
== __CFArrayGetCount(array
)) {
721 _CFArrayReplaceValues(array
, CFRangeMake(idx
, 0), &value
, 1);
723 BEGIN_MUTATION(array
);
724 const void *old_value
;
725 const CFArrayCallBacks
*cb
= __CFArrayGetCallBacks(array
);
726 CFAllocatorRef allocator
= __CFGetAllocator(array
);
727 struct __CFArrayBucket
*bucket
= __CFArrayGetBucketAtIndex(array
, idx
);
728 if (NULL
!= cb
->retain
&& !hasBeenFinalized(array
)) {
729 value
= (void *)INVOKE_CALLBACK2(cb
->retain
, allocator
, value
);
731 old_value
= bucket
->_item
;
732 __CFAssignWithWriteBarrier((void **)&bucket
->_item
, (void *)value
); // GC: handles deque/CFStorage cases.
733 if (NULL
!= cb
->release
&& !hasBeenFinalized(array
)) {
734 INVOKE_CALLBACK2(cb
->release
, allocator
, old_value
);
741 void CFArrayInsertValueAtIndex(CFMutableArrayRef array
, CFIndex idx
, const void *value
) {
742 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, void, array
, "insertObject:atIndex:", value
, idx
);
743 __CFGenericValidateType(array
, __kCFArrayTypeID
);
744 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
745 CFAssert2(0 <= idx
&& idx
<= __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__
, idx
);
746 CHECK_FOR_MUTATION(array
);
747 _CFArrayReplaceValues(array
, CFRangeMake(idx
, 0), &value
, 1);
750 void CFArrayExchangeValuesAtIndices(CFMutableArrayRef array
, CFIndex idx1
, CFIndex idx2
) {
752 struct __CFArrayBucket
*bucket1
, *bucket2
;
753 CFAllocatorRef bucketsAllocator
;
754 CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID
, void, array
, "_cfexchange::", idx1
, idx2
);
755 __CFGenericValidateType(array
, __kCFArrayTypeID
);
756 CFAssert2(0 <= idx1
&& idx1
< __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index #1 (%d) out of bounds", __PRETTY_FUNCTION__
, idx1
);
757 CFAssert2(0 <= idx2
&& idx2
< __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index #2 (%d) out of bounds", __PRETTY_FUNCTION__
, idx2
);
758 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
759 CHECK_FOR_MUTATION(array
);
760 BEGIN_MUTATION(array
);
761 bucket1
= __CFArrayGetBucketAtIndex(array
, idx1
);
762 bucket2
= __CFArrayGetBucketAtIndex(array
, idx2
);
763 tmp
= bucket1
->_item
;
764 bucketsAllocator
= isStrongMemory(array
) ? __CFGetAllocator(array
) : kCFAllocatorNull
;
765 // XXX these aren't needed.
766 __CFAssignWithWriteBarrier((void **)&bucket1
->_item
, (void *)bucket2
->_item
);
767 __CFAssignWithWriteBarrier((void **)&bucket2
->_item
, (void *)tmp
);
772 void CFArrayRemoveValueAtIndex(CFMutableArrayRef array
, CFIndex idx
) {
773 CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID
, void, array
, "removeObjectAtIndex:", idx
);
774 __CFGenericValidateType(array
, __kCFArrayTypeID
);
775 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
776 CFAssert2(0 <= idx
&& idx
< __CFArrayGetCount(array
), __kCFLogAssertion
, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__
, idx
);
777 CHECK_FOR_MUTATION(array
);
778 _CFArrayReplaceValues(array
, CFRangeMake(idx
, 1), NULL
, 0);
781 void CFArrayRemoveAllValues(CFMutableArrayRef array
) {
782 CF_OBJC_FUNCDISPATCH0(__kCFArrayTypeID
, void, array
, "removeAllObjects");
783 __CFGenericValidateType(array
, __kCFArrayTypeID
);
784 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
785 CHECK_FOR_MUTATION(array
);
786 BEGIN_MUTATION(array
);
787 __CFArrayReleaseValues(array
, CFRangeMake(0, __CFArrayGetCount(array
)), true);
788 __CFArraySetCount(array
, 0);
793 static void __CFArrayConvertDequeToStore(CFMutableArrayRef array
) {
794 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
795 struct __CFArrayBucket
*raw_buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
));
797 CFIndex count
= __CFArrayGetCount(array
);
798 CFAllocatorRef allocator
= __CFGetAllocator(array
);
799 Boolean collectableMemory
= CF_IS_COLLECTABLE_ALLOCATOR(allocator
);
800 if (collectableMemory
) auto_zone_retain(auto_zone(), deque
);
801 store
= CFStorageCreate(allocator
, sizeof(const void *));
802 if (__CFOASafe
) __CFSetLastAllocationEventName(store
, "CFArray (store-storage)");
803 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)store
);
804 CFMakeCollectable(store
); // GC: now safe to unroot the store.
805 CFStorageInsertValues(store
, CFRangeMake(0, count
));
806 CFStorageReplaceValues(store
, CFRangeMake(0, count
), raw_buckets
+ deque
->_leftIdx
);
807 CFAllocatorDeallocate(__CFGetAllocator(array
), deque
);
808 __CFBitfieldSetValue(((CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 1, 0, __kCFArrayStorage
);
811 static void __CFArrayConvertStoreToDeque(CFMutableArrayRef array
) {
812 CFStorageRef store
= (CFStorageRef
)array
->_store
;
813 struct __CFArrayDeque
*deque
;
814 struct __CFArrayBucket
*raw_buckets
;
815 CFIndex count
= CFStorageGetCount(store
);// storage, not array, has correct count at this point
816 // do not resize down to a completely tight deque
817 CFIndex capacity
= __CFArrayDequeRoundUpCapacity(count
+ 6);
818 CFIndex size
= sizeof(struct __CFArrayDeque
) + capacity
* sizeof(struct __CFArrayBucket
);
819 CFAllocatorRef allocator
= __CFGetAllocator(array
);
820 Boolean collectableMemory
= CF_IS_COLLECTABLE_ALLOCATOR(allocator
);
821 if (collectableMemory
) CFRetain(store
); // GC: need to root the CFStorage
822 deque
= (struct __CFArrayDeque
*)CFAllocatorAllocate(allocator
, size
, isStrongMemory(array
) ? __kCFAllocatorGCScannedMemory
: 0);
823 if (__CFOASafe
) __CFSetLastAllocationEventName(deque
, "CFArray (store-deque)");
824 deque
->_leftIdx
= (capacity
- count
) / 2;
825 deque
->_capacity
= capacity
;
827 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)deque
);
828 if (collectableMemory
) auto_zone_release(auto_zone(), deque
);
829 raw_buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
));
830 CFStorageGetValues(store
, CFRangeMake(0, count
), raw_buckets
+ deque
->_leftIdx
);
832 __CFBitfieldSetValue(((CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 1, 0, __kCFArrayDeque
);
835 // may move deque storage, as it may need to grow deque
836 static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array
, CFRange range
, CFIndex newCount
) {
837 // newCount elements are going to replace the range, and the result will fit in the deque
838 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
839 struct __CFArrayBucket
*buckets
;
840 CFIndex cnt
, futureCnt
, numNewElems
;
841 CFIndex L
, A
, B
, C
, R
;
843 buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
));
844 cnt
= __CFArrayGetCount(array
);
845 futureCnt
= cnt
- range
.length
+ newCount
;
847 L
= deque
->_leftIdx
; // length of region to left of deque
848 A
= range
.location
; // length of region in deque to left of replaced range
849 B
= range
.length
; // length of replaced range
850 C
= cnt
- B
- A
; // length of region in deque to right of replaced range
851 R
= deque
->_capacity
- cnt
- L
; // length of region to right of deque
852 numNewElems
= newCount
- B
;
854 CFIndex wiggle
= deque
->_capacity
>> 17;
855 if (wiggle
< 4) wiggle
= 4;
856 if (deque
->_capacity
< (uint32_t)futureCnt
|| (cnt
< futureCnt
&& L
+ R
< wiggle
)) {
857 // must be inserting or space is tight, reallocate and re-center everything
858 CFIndex capacity
= __CFArrayDequeRoundUpCapacity(futureCnt
+ wiggle
);
859 CFIndex size
= sizeof(struct __CFArrayDeque
) + capacity
* sizeof(struct __CFArrayBucket
);
860 CFAllocatorRef allocator
= __CFGetAllocator(array
);
861 struct __CFArrayDeque
*newDeque
= (struct __CFArrayDeque
*)CFAllocatorAllocate(allocator
, size
, isStrongMemory(array
) ? __kCFAllocatorGCScannedMemory
: 0);
862 if (__CFOASafe
) __CFSetLastAllocationEventName(newDeque
, "CFArray (store-deque)");
863 struct __CFArrayBucket
*newBuckets
= (struct __CFArrayBucket
*)((uint8_t *)newDeque
+ sizeof(struct __CFArrayDeque
));
865 CFIndex newL
= (capacity
- futureCnt
) / 2;
866 CFIndex oldC0
= oldL
+ A
+ B
;
867 CFIndex newC0
= newL
+ A
+ newCount
;
868 newDeque
->_leftIdx
= newL
;
869 newDeque
->_capacity
= capacity
;
871 if (0 < A
) objc_memmove_collectable(newBuckets
+ newL
, buckets
+ oldL
, A
* sizeof(struct __CFArrayBucket
));
872 if (0 < C
) objc_memmove_collectable(newBuckets
+ newC0
, buckets
+ oldC0
, C
* sizeof(struct __CFArrayBucket
));
873 if (deque
) _CFAllocatorDeallocateGC(allocator
, deque
);
874 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)newDeque
);
875 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) auto_zone_release(auto_zone(), newDeque
);
879 if ((numNewElems
< 0 && C
< A
) || (numNewElems
<= R
&& C
< A
)) { // move C
880 // deleting: C is smaller
881 // inserting: C is smaller and R has room
882 CFIndex oldC0
= L
+ A
+ B
;
883 CFIndex newC0
= L
+ A
+ newCount
;
884 if (0 < C
) objc_memmove_collectable(buckets
+ newC0
, buckets
+ oldC0
, C
* sizeof(struct __CFArrayBucket
));
885 // GrP GC: zero-out newly exposed space on the right, if any
886 if (oldC0
> newC0
) memset(buckets
+ newC0
+ C
, 0, (oldC0
- newC0
) * sizeof(struct __CFArrayBucket
));
887 } else if ((numNewElems
< 0) || (numNewElems
<= L
&& A
<= C
)) { // move A
888 // deleting: A is smaller or equal (covers remaining delete cases)
889 // inserting: A is smaller and L has room
891 CFIndex newL
= L
- numNewElems
;
892 deque
->_leftIdx
= newL
;
893 if (0 < A
) objc_memmove_collectable(buckets
+ newL
, buckets
+ oldL
, A
* sizeof(struct __CFArrayBucket
));
894 // GrP GC: zero-out newly exposed space on the left, if any
895 if (newL
> oldL
) memset(buckets
+ oldL
, 0, (newL
- oldL
) * sizeof(struct __CFArrayBucket
));
897 // now, must be inserting, and either:
898 // A<=C, but L doesn't have room (R might have, but don't care)
899 // C<A, but R doesn't have room (L might have, but don't care)
900 // re-center everything
902 CFIndex newL
= (L
+ R
- numNewElems
) / 2;
903 CFIndex oldBias
= deque
->_bias
;
904 deque
->_bias
= (newL
< oldL
) ? -1 : 1;
906 newL
= newL
- newL
/ 2;
907 } else if (0 < oldBias
) {
908 newL
= newL
+ newL
/ 2;
910 CFIndex oldC0
= oldL
+ A
+ B
;
911 CFIndex newC0
= newL
+ A
+ newCount
;
912 deque
->_leftIdx
= newL
;
914 if (0 < A
) objc_memmove_collectable(buckets
+ newL
, buckets
+ oldL
, A
* sizeof(struct __CFArrayBucket
));
915 if (0 < C
) objc_memmove_collectable(buckets
+ newC0
, buckets
+ oldC0
, C
* sizeof(struct __CFArrayBucket
));
916 // GrP GC: zero-out newly exposed space on the right, if any
917 if (oldC0
> newC0
) memset(buckets
+ newC0
+ C
, 0, (oldC0
- newC0
) * sizeof(struct __CFArrayBucket
));
919 if (0 < C
) objc_memmove_collectable(buckets
+ newC0
, buckets
+ oldC0
, C
* sizeof(struct __CFArrayBucket
));
920 if (0 < A
) objc_memmove_collectable(buckets
+ newL
, buckets
+ oldL
, A
* sizeof(struct __CFArrayBucket
));
921 // GrP GC: zero-out newly exposed space on the left, if any
922 if (newL
> oldL
) memset(buckets
+ oldL
, 0, (newL
- oldL
) * sizeof(struct __CFArrayBucket
));
927 static void __CFArrayHandleOutOfMemory(CFTypeRef obj
, CFIndex numBytes
) {
928 CFStringRef msg
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Attempt to allocate %ld bytes for CFArray failed"), numBytes
);
930 CFLog(kCFLogLevelCritical
, CFSTR("%@"), msg
);
936 // This function is for Foundation's benefit; no one else should use it.
937 void _CFArraySetCapacity(CFMutableArrayRef array
, CFIndex cap
) {
938 if (CF_IS_OBJC(__kCFArrayTypeID
, array
)) return;
939 __CFGenericValidateType(array
, __kCFArrayTypeID
);
940 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
941 CFAssert3(__CFArrayGetCount(array
) <= cap
, __kCFLogAssertion
, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__
, cap
, __CFArrayGetCount(array
));
942 CHECK_FOR_MUTATION(array
);
943 BEGIN_MUTATION(array
);
944 // Currently, attempting to set the capacity of an array which is the CFStorage
945 // variant, or set the capacity larger than __CF_MAX_BUCKETS_PER_DEQUE, has no
946 // effect. The primary purpose of this API is to help avoid a bunch of the
947 // resizes at the small capacities 4, 8, 16, etc.
948 if (__CFArrayGetType(array
) == __kCFArrayDeque
) {
949 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
950 CFIndex capacity
= __CFArrayDequeRoundUpCapacity(cap
);
951 CFIndex size
= sizeof(struct __CFArrayDeque
) + capacity
* sizeof(struct __CFArrayBucket
);
952 CFAllocatorRef allocator
= __CFGetAllocator(array
);
953 Boolean collectableMemory
= CF_IS_COLLECTABLE_ALLOCATOR(allocator
);
955 deque
= (struct __CFArrayDeque
*)CFAllocatorAllocate(allocator
, size
, isStrongMemory(array
) ? __kCFAllocatorGCScannedMemory
: 0);
956 if (NULL
== deque
) __CFArrayHandleOutOfMemory(array
, size
);
957 if (__CFOASafe
) __CFSetLastAllocationEventName(deque
, "CFArray (store-deque)");
958 deque
->_leftIdx
= capacity
/ 2;
960 struct __CFArrayDeque
*olddeque
= deque
;
961 CFIndex oldcap
= deque
->_capacity
;
962 deque
= (struct __CFArrayDeque
*)CFAllocatorAllocate(allocator
, size
, isStrongMemory(array
) ? __kCFAllocatorGCScannedMemory
: 0);
963 if (NULL
== deque
) __CFArrayHandleOutOfMemory(array
, size
);
964 objc_memmove_collectable(deque
, olddeque
, sizeof(struct __CFArrayDeque
) + oldcap
* sizeof(struct __CFArrayBucket
));
965 _CFAllocatorDeallocateGC(allocator
, olddeque
);
966 if (__CFOASafe
) __CFSetLastAllocationEventName(deque
, "CFArray (store-deque)");
968 deque
->_capacity
= capacity
;
970 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)deque
);
971 if (collectableMemory
) auto_zone_release(auto_zone(), deque
);
977 void CFArrayReplaceValues(CFMutableArrayRef array
, CFRange range
, const void **newValues
, CFIndex newCount
) {
978 CF_OBJC_FUNCDISPATCH3(__kCFArrayTypeID
, void, array
, "replaceObjectsInRange:withObjects:count:", range
, (void **)newValues
, newCount
);
979 __CFGenericValidateType(array
, __kCFArrayTypeID
);
980 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
981 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
982 CFAssert2(0 <= newCount
, __kCFLogAssertion
, "%s(): newCount (%d) cannot be less than zero", __PRETTY_FUNCTION__
, newCount
);
983 CHECK_FOR_MUTATION(array
);
984 return _CFArrayReplaceValues(array
, range
, newValues
, newCount
);
987 // This function does no ObjC dispatch or argument checking;
988 // It should only be called from places where that dispatch and check has already been done, or NSCFArray
989 void _CFArrayReplaceValues(CFMutableArrayRef array
, CFRange range
, const void **newValues
, CFIndex newCount
) {
990 CHECK_FOR_MUTATION(array
);
991 BEGIN_MUTATION(array
);
992 const CFArrayCallBacks
*cb
;
993 CFAllocatorRef allocator
;
994 CFIndex idx
, cnt
, futureCnt
;
995 const void **newv
, *buffer
[256];
996 cnt
= __CFArrayGetCount(array
);
997 futureCnt
= cnt
- range
.length
+ newCount
;
998 CFAssert1(newCount
<= futureCnt
, __kCFLogAssertion
, "%s(): internal error 1", __PRETTY_FUNCTION__
);
999 cb
= __CFArrayGetCallBacks(array
);
1000 allocator
= __CFGetAllocator(array
);
1002 /* Retain new values if needed, possibly allocating a temporary buffer for them */
1003 if (NULL
!= cb
->retain
&& !hasBeenFinalized(array
)) {
1004 newv
= (newCount
<= 256) ? (const void **)buffer
: (const void **)CFAllocatorAllocate(allocator
, newCount
* sizeof(void *), 0); // GC OK
1005 if (newv
!= buffer
&& __CFOASafe
) __CFSetLastAllocationEventName(newv
, "CFArray (temp)");
1006 for (idx
= 0; idx
< newCount
; idx
++) {
1007 newv
[idx
] = (void *)INVOKE_CALLBACK2(cb
->retain
, allocator
, (void *)newValues
[idx
]);
1012 array
->_mutations
++;
1014 /* Now, there are three regions of interest, each of which may be empty:
1015 * A: the region from index 0 to one less than the range.location
1016 * B: the region of the range
1017 * C: the region from range.location + range.length to the end
1018 * Note that index 0 is not necessarily at the lowest-address edge
1019 * of the available storage. The values in region B need to get
1020 * released, and the values in regions A and C (depending) need
1021 * to get shifted if the number of new values is different from
1022 * the length of the range being replaced.
1024 if (0 < range
.length
) {
1025 __CFArrayReleaseValues(array
, range
, false);
1027 // region B elements are now "dead"
1028 if (__kCFArrayStorage
== __CFArrayGetType(array
)) {
1029 CFStorageRef store
= (CFStorageRef
)array
->_store
;
1030 // reposition regions A and C for new region B elements in gap
1031 if (range
.length
< newCount
) {
1032 CFStorageInsertValues(store
, CFRangeMake(range
.location
+ range
.length
, newCount
- range
.length
));
1033 } else if (newCount
< range
.length
) {
1034 CFStorageDeleteValues(store
, CFRangeMake(range
.location
+ newCount
, range
.length
- newCount
));
1036 if (futureCnt
<= __CF_MAX_BUCKETS_PER_DEQUE
/ 2) {
1037 __CFArrayConvertStoreToDeque(array
);
1039 } else if (NULL
== array
->_store
) {
1040 if (__CF_MAX_BUCKETS_PER_DEQUE
<= futureCnt
) {
1041 CFStorageRef store
= CFStorageCreate(allocator
, sizeof(const void *));
1042 if (! isStrongMemory(array
)) _CFStorageSetWeak(store
);
1043 if (__CFOASafe
) __CFSetLastAllocationEventName(store
, "CFArray (store-storage)");
1044 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)store
);
1045 CFMakeCollectable(store
);
1046 CFStorageInsertValues(store
, CFRangeMake(0, newCount
));
1047 __CFBitfieldSetValue(((CFRuntimeBase
*)array
)->_cfinfo
[CF_INFO_BITS
], 1, 0, __kCFArrayStorage
);
1048 } else if (0 <= futureCnt
) {
1049 struct __CFArrayDeque
*deque
;
1050 CFIndex capacity
= __CFArrayDequeRoundUpCapacity(futureCnt
);
1051 CFIndex size
= sizeof(struct __CFArrayDeque
) + capacity
* sizeof(struct __CFArrayBucket
);
1052 deque
= (struct __CFArrayDeque
*)CFAllocatorAllocate(allocator
, size
, isStrongMemory(array
) ? __kCFAllocatorGCScannedMemory
: 0);
1053 if (__CFOASafe
) __CFSetLastAllocationEventName(deque
, "CFArray (store-deque)");
1054 deque
->_leftIdx
= (capacity
- newCount
) / 2;
1055 deque
->_capacity
= capacity
;
1057 __CFAssignWithWriteBarrier((void **)&array
->_store
, (void *)deque
);
1058 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) auto_zone_release(auto_zone(), deque
); // GC: now safe to unroot the array body.
1061 // reposition regions A and C for new region B elements in gap
1062 if (__CF_MAX_BUCKETS_PER_DEQUE
<= futureCnt
) {
1064 __CFArrayConvertDequeToStore(array
);
1065 store
= (CFStorageRef
)array
->_store
;
1066 if (range
.length
< newCount
) {
1067 CFStorageInsertValues(store
, CFRangeMake(range
.location
+ range
.length
, newCount
- range
.length
));
1068 } else if (newCount
< range
.length
) { // this won't happen, but is here for completeness
1069 CFStorageDeleteValues(store
, CFRangeMake(range
.location
+ newCount
, range
.length
- newCount
));
1071 } else if (range
.length
!= newCount
) {
1072 __CFArrayRepositionDequeRegions(array
, range
, newCount
);
1075 // copy in new region B elements
1077 if (__kCFArrayStorage
== __CFArrayGetType(array
)) {
1078 CFStorageRef store
= (CFStorageRef
)array
->_store
;
1079 CFStorageReplaceValues(store
, CFRangeMake(range
.location
, newCount
), newv
);
1081 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
1082 struct __CFArrayBucket
*raw_buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
));
1083 objc_memmove_collectable(raw_buckets
+ deque
->_leftIdx
+ range
.location
, newv
, newCount
* sizeof(struct __CFArrayBucket
));
1086 __CFArraySetCount(array
, futureCnt
);
1087 if (newv
!= buffer
&& newv
!= newValues
) CFAllocatorDeallocate(allocator
, newv
);
1088 END_MUTATION(array
);
1091 struct _acompareContext
{
1092 CFComparatorFunction func
;
1096 static CFComparisonResult
__CFArrayCompareValues(const void *v1
, const void *v2
, struct _acompareContext
*context
) {
1097 const void **val1
= (const void **)v1
;
1098 const void **val2
= (const void **)v2
;
1099 return (CFComparisonResult
)(INVOKE_CALLBACK3(context
->func
, *val1
, *val2
, context
->context
));
1102 void CFArraySortValues(CFMutableArrayRef array
, CFRange range
, CFComparatorFunction comparator
, void *context
) {
1103 FAULT_CALLBACK((void **)&(comparator
));
1104 CF_OBJC_FUNCDISPATCH3(__kCFArrayTypeID
, void, array
, "sortUsingFunction:context:range:", comparator
, context
, range
);
1105 __CFGenericValidateType(array
, __kCFArrayTypeID
);
1106 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
1107 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
1108 CFAssert1(NULL
!= comparator
, __kCFLogAssertion
, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__
);
1109 CHECK_FOR_MUTATION(array
);
1110 BEGIN_MUTATION(array
);
1111 array
->_mutations
++;
1113 if (1 < range
.length
) {
1114 struct _acompareContext ctx
;
1115 struct __CFArrayBucket
*bucket
;
1116 ctx
.func
= comparator
;
1117 ctx
.context
= context
;
1118 switch (__CFArrayGetType(array
)) {
1119 case __kCFArrayDeque
:
1120 bucket
= __CFArrayGetBucketsPtr(array
) + range
.location
;
1121 CFQSortArray(bucket
, range
.length
, sizeof(void *), (CFComparatorFunction
)__CFArrayCompareValues
, &ctx
);
1123 case __kCFArrayStorage
: {
1124 CFStorageRef store
= (CFStorageRef
)array
->_store
;
1125 const void **values
, *buffer
[256];
1126 values
= (range
.length
<= 256) ? (const void **)buffer
: (const void **)CFAllocatorAllocate(kCFAllocatorSystemDefault
, range
.length
* sizeof(void *), 0); // GC OK
1127 if (values
!= buffer
&& __CFOASafe
) __CFSetLastAllocationEventName(values
, "CFArray (temp)");
1128 CFStorageGetValues(store
, range
, values
);
1129 CFQSortArray(values
, range
.length
, sizeof(void *), (CFComparatorFunction
)__CFArrayCompareValues
, &ctx
);
1130 CFStorageReplaceValues(store
, range
, values
);
1131 if (values
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, values
); // GC OK
1136 END_MUTATION(array
);
1139 CFIndex
CFArrayBSearchValues(CFArrayRef array
, CFRange range
, const void *value
, CFComparatorFunction comparator
, void *context
) {
1140 __CFGenericValidateType(array
, __kCFArrayTypeID
);
1141 __CFArrayValidateRange(array
, range
, __PRETTY_FUNCTION__
);
1142 CFAssert1(NULL
!= comparator
, __kCFLogAssertion
, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__
);
1143 bool isObjC
= CF_IS_OBJC(__kCFArrayTypeID
, array
);
1144 FAULT_CALLBACK((void **)&(comparator
));
1145 if (!isObjC
) CHECK_FOR_MUTATION(array
);
1147 if (range
.length
<= 0) return range
.location
;
1148 if (isObjC
|| __kCFArrayStorage
== __CFArrayGetType(array
)) {
1151 item
= CFArrayGetValueAtIndex(array
, range
.location
+ range
.length
- 1);
1152 if ((CFComparisonResult
)(INVOKE_CALLBACK3(comparator
, item
, value
, context
)) < 0) {
1153 return range
.location
+ range
.length
;
1155 item
= CFArrayGetValueAtIndex(array
, range
.location
);
1156 if ((CFComparisonResult
)(INVOKE_CALLBACK3(comparator
, value
, item
, context
)) < 0) {
1157 return range
.location
;
1159 lg
= flsl(range
.length
) - 1; // lg2(range.length)
1160 item
= CFArrayGetValueAtIndex(array
, range
.location
+ -1 + (1 << lg
));
1161 idx
= range
.location
+ ((CFComparisonResult
)(INVOKE_CALLBACK3(comparator
, item
, value
, context
)) < 0) ? range
.length
- (1 << lg
) : -1;
1163 item
= CFArrayGetValueAtIndex(array
, range
.location
+ idx
+ (1 << lg
));
1164 if ((CFComparisonResult
)(INVOKE_CALLBACK3(comparator
, item
, value
, context
)) < 0) {
1170 struct _acompareContext ctx
;
1171 ctx
.func
= comparator
;
1172 ctx
.context
= context
;
1173 idx
= CFBSearch(&value
, sizeof(void *), __CFArrayGetBucketsPtr(array
) + range
.location
, range
.length
, (CFComparatorFunction
)__CFArrayCompareValues
, &ctx
);
1175 return idx
+ range
.location
;
1178 void CFArrayAppendArray(CFMutableArrayRef array
, CFArrayRef otherArray
, CFRange otherRange
) {
1180 __CFGenericValidateType(array
, __kCFArrayTypeID
);
1181 __CFGenericValidateType(otherArray
, __kCFArrayTypeID
);
1182 CFAssert1(__CFArrayGetType(array
) != __kCFArrayImmutable
, __kCFLogAssertion
, "%s(): array is immutable", __PRETTY_FUNCTION__
);
1183 __CFArrayValidateRange(otherArray
, otherRange
, __PRETTY_FUNCTION__
);
1184 CHECK_FOR_MUTATION(array
);
1185 for (idx
= otherRange
.location
; idx
< otherRange
.location
+ otherRange
.length
; idx
++) {
1186 CFArrayAppendValue(array
, CFArrayGetValueAtIndex(otherArray
, idx
));
1190 // ----====---- ----====---- ----====---- ----====----
1192 __private_extern__ Boolean
__CFArray6130(CFMutableArrayRef array
, CFIndex
*p
, void **list
) {
1193 if (CF_IS_OBJC(__kCFArrayTypeID
, array
)) return false;
1194 CHECK_FOR_MUTATION(array
);
1195 if (__kCFArrayStorage
== __CFArrayGetType(array
)) {
1196 CFStorageRef store
= (CFStorageRef
)array
->_store
;
1197 CFRange range
= {0, 0};
1199 for (CFIndex idx
= 0; idx
< __CFArrayGetCount(array
); idx
++) {
1200 if (range
.location
+ range
.length
- 1 < idx
) {
1201 bytes
= CFStorageGetValueAtIndex(store
, idx
, &range
);
1203 ((void **)bytes
)[idx
- range
.location
] = list
[p
[idx
]];
1205 } else if (kCFUseCollectableAllocator
) { // Deque
1206 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
1207 struct __CFArrayBucket
*raw_buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
)) + deque
->_leftIdx
;
1208 for (CFIndex idx
= 0; idx
< __CFArrayGetCount(array
); idx
++) {
1209 struct __CFArrayBucket
*dest
= raw_buckets
+ idx
;
1210 objc_memmove_collectable(dest
, list
+ p
[idx
], sizeof(struct __CFArrayBucket
));
1213 struct __CFArrayDeque
*deque
= (struct __CFArrayDeque
*)array
->_store
;
1214 struct __CFArrayBucket
*raw_buckets
= (struct __CFArrayBucket
*)((uint8_t *)deque
+ sizeof(struct __CFArrayDeque
)) + deque
->_leftIdx
;
1215 for (CFIndex idx
= 0; idx
< __CFArrayGetCount(array
); idx
++) {
1216 raw_buckets
[idx
]._item
= list
[p
[idx
]];