__private_extern__ CFArrayRef __CFArrayCreateCopy0(CFAllocatorRef allocator, CFArrayRef array) { CFArrayRef result; const CFArrayCallBacks *cb; struct __CFArrayBucket *buckets; CFAllocatorRef bucketsAllocator; void* bucketsBase; CFIndex numValues = CFArrayGetCount(array); CFIndex idx; if (CF_IS_OBJC(__kCFArrayTypeID, array)) { cb = &kCFTypeArrayCallBacks; } else { cb = __CFArrayGetCallBacks(array); } result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, cb); cb = __CFArrayGetCallBacks(result); // GC: use the new array's callbacks so we don't leak. buckets = __CFArrayGetBucketsPtr(result); bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? (void *)auto_zone_base_pointer(objc_collectableZone(), buckets) : NULL; for (idx = 0; idx < numValues; idx++) { const void *value = CFArrayGetValueAtIndex(array, idx); if (NULL != cb->retain) { value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); } __CFAssignWithWriteBarrier((void **)&buckets->_item, (void *)value); buckets++; } __CFArraySetCount(result, numValues); return result; }
__private_extern__ CFArrayRef __CFArrayCreate0(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks) { CFArrayRef result; const CFArrayCallBacks *cb; struct __CFArrayBucket *buckets; CFAllocatorRef bucketsAllocator; void* bucketsBase; CFIndex idx; CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, callBacks); cb = __CFArrayGetCallBacks(result); buckets = __CFArrayGetBucketsPtr(result); bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? (void *)auto_zone_base_pointer(objc_collectableZone(), buckets) : NULL; if (NULL != cb->retain) { for (idx = 0; idx < numValues; idx++) { __CFAssignWithWriteBarrier((void **)&buckets->_item, (void *)INVOKE_CALLBACK2(cb->retain, allocator, *values)); values++; buckets++; } } else { for (idx = 0; idx < numValues; idx++) { __CFAssignWithWriteBarrier((void **)&buckets->_item, (void *)*values); values++; buckets++; } } __CFArraySetCount(result, numValues); return result; }
void CFArrayRemoveAllValues(CFMutableArrayRef array) { CF_OBJC_FUNCDISPATCHV(__kCFArrayTypeID, void, (NSMutableArray *)array, removeAllObjects); __CFGenericValidateType(array, __kCFArrayTypeID); CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); CHECK_FOR_MUTATION(array); BEGIN_MUTATION(array); __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); __CFArraySetCount(array, 0); array->_mutations++; END_MUTATION(array); }
static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) { struct __CFArray *memory; UInt32 size; __CFBitfieldSetValue(flags, 31, 2, 0); if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak } } if (__CFArrayCallBacksMatchNull(callBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasNullCallBacks); } else if (__CFArrayCallBacksMatchCFType(callBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasCFTypeCallBacks); } else { __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasCustomCallBacks); } size = __CFArrayGetSizeOfType(flags) - sizeof(CFRuntimeBase); switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFArrayImmutable: size += capacity * sizeof(struct __CFArrayBucket); break; case __kCFArrayDeque: break; } memory = (struct __CFArray*)_CFRuntimeCreateInstance(allocator, __kCFArrayTypeID, size, NULL); if (NULL == memory) { return NULL; } __CFBitfieldSetValue(memory->_base._cfinfo[CF_INFO_BITS], 6, 0, flags); __CFArraySetCount((CFArrayRef)memory, 0); switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFArrayImmutable: if (isWeakMemory(memory)) { // if weak, don't scan auto_zone_set_unscanned(objc_collectableZone(), memory); } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (immutable)"); break; case __kCFArrayDeque: if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-variable)"); ((struct __CFArray *)memory)->_mutations = 1; ((struct __CFArray *)memory)->_mutInProgress = 0; ((struct __CFArray*)memory)->_store = NULL; break; } if (__kCFArrayHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { CFArrayCallBacks *cb = (CFArrayCallBacks *)__CFArrayGetCallBacks((CFArrayRef)memory); *cb = *callBacks; FAULT_CALLBACK((void **)&(cb->retain)); FAULT_CALLBACK((void **)&(cb->release)); FAULT_CALLBACK((void **)&(cb->copyDescription)); FAULT_CALLBACK((void **)&(cb->equal)); } return (CFArrayRef)memory; }
__private_extern__ CFArrayRef __CFArrayCreateTransfer(CFAllocatorRef allocator, const void **values, CFIndex numValues) { CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); UInt32 flags = __kCFArrayImmutable; __CFBitfieldSetValue(flags, 31, 2, 0); __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasCFTypeCallBacks); UInt32 size = __CFArrayGetSizeOfType(flags) - sizeof(CFRuntimeBase); size += numValues * sizeof(struct __CFArrayBucket); struct __CFArray *memory = (struct __CFArray*)_CFRuntimeCreateInstance(allocator, __kCFArrayTypeID, size, NULL); if (NULL == memory) { return NULL; } __CFBitfieldSetValue(memory->_base._cfinfo[CF_INFO_BITS], 6, 0, flags); __CFArraySetCount(memory, numValues); memmove(__CFArrayGetBucketsPtr(memory), values, sizeof(void *) * numValues); if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (immutable)"); return (CFArrayRef)memory; }
static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool releaseStorageIfPossible) { const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); CFAllocatorRef allocator; CFIndex idx; switch (__CFArrayGetType(array)) { case __kCFArrayImmutable: if (NULL != cb->release && 0 < range.length && !hasBeenFinalized(array)) { // if we've been finalized then we know that // 1) we're using the standard callback on GC memory // 2) the slots don't' need to be zeroed struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); allocator = __CFGetAllocator(array); for (idx = 0; idx < range.length; idx++) { INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); buckets[idx + range.location]._item = NULL; // GC: break strong reference. } } break; case __kCFArrayDeque: { struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; if (0 < range.length && NULL != deque && !hasBeenFinalized(array)) { struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); if (NULL != cb->release) { allocator = __CFGetAllocator(array); for (idx = 0; idx < range.length; idx++) { INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); buckets[idx + range.location]._item = NULL; // GC: break strong reference. } } else { for (idx = 0; idx < range.length; idx++) { buckets[idx + range.location]._item = NULL; // GC: break strong reference. } } } if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { allocator = __CFGetAllocator(array); if (NULL != deque) if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) CFAllocatorDeallocate(allocator, deque); __CFArraySetCount(array, 0); // GC: _count == 0 ==> _store == NULL. ((struct __CFArray *)array)->_store = NULL; } break; } } }
static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool releaseStorageIfPossible) { const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); CFAllocatorRef allocator; CFIndex idx; switch (__CFArrayGetType(array)) { case __kCFArrayImmutable: if (NULL != cb->release && 0 < range.length) { struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); allocator = __CFGetAllocator(array); for (idx = 0; idx < range.length; idx++) { INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); } memset(buckets + range.location, 0, sizeof(struct __CFArrayBucket) * range.length); } break; case __kCFArrayDeque: { struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; if (0 < range.length && NULL != deque) { struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); if (NULL != cb->release) { allocator = __CFGetAllocator(array); for (idx = 0; idx < range.length; idx++) { INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); } } memset(buckets + range.location, 0, sizeof(struct __CFArrayBucket) * range.length); } if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { allocator = __CFGetAllocator(array); if (NULL != deque) CFAllocatorDeallocate(allocator, deque); __CFArraySetCount(array, 0); ((struct __CFArray *)array)->_store = NULL; } break; } } }
// This function does no ObjC dispatch or argument checking; // It should only be called from places where that dispatch and check has already been done, or NSCFArray void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount) { CHECK_FOR_MUTATION(array); BEGIN_MUTATION(array); const CFArrayCallBacks *cb; CFIndex idx, cnt, futureCnt; const void **newv, *buffer[256]; cnt = __CFArrayGetCount(array); futureCnt = cnt - range.length + newCount; CFAssert1(newCount <= futureCnt, __kCFLogAssertion, "%s(): internal error 1", __PRETTY_FUNCTION__); cb = __CFArrayGetCallBacks(array); CFAllocatorRef allocator = __CFGetAllocator(array); /* Retain new values if needed, possibly allocating a temporary buffer for them */ if (NULL != cb->retain && !hasBeenFinalized(array)) { newv = (newCount <= 256) ? (const void **)buffer : (const void **)CFAllocatorAllocate(kCFAllocatorSystemDefault, newCount * sizeof(void *), 0); // GC OK if (newv != buffer && __CFOASafe) __CFSetLastAllocationEventName(newv, "CFArray (temp)"); for (idx = 0; idx < newCount; idx++) { newv[idx] = (void *)INVOKE_CALLBACK2(cb->retain, allocator, (void *)newValues[idx]); } } else { newv = newValues; } array->_mutations++; /* Now, there are three regions of interest, each of which may be empty: * A: the region from index 0 to one less than the range.location * B: the region of the range * C: the region from range.location + range.length to the end * Note that index 0 is not necessarily at the lowest-address edge * of the available storage. The values in region B need to get * released, and the values in regions A and C (depending) need * to get shifted if the number of new values is different from * the length of the range being replaced. */ if (0 < range.length) { __CFArrayReleaseValues(array, range, false); } // region B elements are now "dead" if (0) { } else if (NULL == array->_store) { if (0) { } else if (0 <= futureCnt) { struct __CFArrayDeque *deque; CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt); CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); deque = (struct __CFArrayDeque *)CFAllocatorAllocate(_CFConvertAllocatorToGCRefZeroEquivalent(allocator), size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = (capacity - newCount) / 2; deque->_capacity = capacity; __CFAssignWithWriteBarrier((void **)&array->_store, (void *)deque); } } else { // Deque // reposition regions A and C for new region B elements in gap if (0) { } else if (range.length != newCount) { __CFArrayRepositionDequeRegions(array, range, newCount); } } // copy in new region B elements if (0 < newCount) { if (0) { } else { // Deque struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); objc_memmove_collectable(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket)); } } __CFArraySetCount(array, futureCnt); if (newv != buffer && newv != newValues) CFAllocatorDeallocate(kCFAllocatorSystemDefault, newv); END_MUTATION(array); }