static void __CFArrayDeallocate(CFTypeRef cf) { CFArrayRef array = (CFArrayRef)cf; BEGIN_MUTATION(array); #if DEPLOYMENT_TARGET_MACOSX // Under GC, keep contents alive when we know we can, either standard callbacks or NULL // if (__CFBitfieldGetValue(cf->info, 5, 4)) return; // bits only ever set under GC CFAllocatorRef allocator = __CFGetAllocator(array); if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { // XXX_PCB keep array intact during finalization. const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); if (cb->retain == NULL && cb->release == NULL) { END_MUTATION(array); return; } if (cb == &kCFTypeArrayCallBacks || cb->release == kCFTypeArrayCallBacks.release) { markFinalized(cf); for (CFIndex idx = 0; idx < __CFArrayGetCount(array); idx++) { const void *item = CFArrayGetValueAtIndex(array, 0 + idx); kCFTypeArrayCallBacks.release(kCFAllocatorSystemDefault, item); } END_MUTATION(array); return; } } #endif __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); END_MUTATION(array); }
void CFArraySetValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *value) { CF_OBJC_FUNCDISPATCHV(__kCFArrayTypeID, void, (NSMutableArray *)array, setObject:(id)value atIndex:(NSUInteger)idx); __CFGenericValidateType(array, __kCFArrayTypeID); CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); CFAssert2(0 <= idx && idx <= __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); CHECK_FOR_MUTATION(array); if (idx == __CFArrayGetCount(array)) { _CFArrayReplaceValues(array, CFRangeMake(idx, 0), &value, 1); } else { BEGIN_MUTATION(array); const void *old_value; const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); CFAllocatorRef allocator = __CFGetAllocator(array); struct __CFArrayBucket *bucket = __CFArrayGetBucketAtIndex(array, idx); if (NULL != cb->retain && !hasBeenFinalized(array)) { value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); } old_value = bucket->_item; __CFAssignWithWriteBarrier((void **)&bucket->_item, (void *)value); // GC: handles deque/CFStorage cases. if (NULL != cb->release && !hasBeenFinalized(array)) { INVOKE_CALLBACK2(cb->release, allocator, old_value); } array->_mutations++; END_MUTATION(array); } }
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); }
// NB: AddressBook on the Phone is a fragile flower, so this function cannot do anything // that causes the values to be retained or released. void CFArrayExchangeValuesAtIndices(CFMutableArrayRef array, CFIndex idx1, CFIndex idx2) { const void *tmp; struct __CFArrayBucket *bucket1, *bucket2; CF_OBJC_FUNCDISPATCHV(__kCFArrayTypeID, void, (NSMutableArray *)array, exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2); __CFGenericValidateType(array, __kCFArrayTypeID); CFAssert2(0 <= idx1 && idx1 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #1 (%d) out of bounds", __PRETTY_FUNCTION__, idx1); CFAssert2(0 <= idx2 && idx2 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #2 (%d) out of bounds", __PRETTY_FUNCTION__, idx2); CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); CHECK_FOR_MUTATION(array); BEGIN_MUTATION(array); bucket1 = __CFArrayGetBucketAtIndex(array, idx1); bucket2 = __CFArrayGetBucketAtIndex(array, idx2); tmp = bucket1->_item; // XXX these aren't needed. __CFAssignWithWriteBarrier((void **)&bucket1->_item, (void *)bucket2->_item); __CFAssignWithWriteBarrier((void **)&bucket2->_item, (void *)tmp); array->_mutations++; END_MUTATION(array); }
// This function is for Foundation's benefit; no one else should use it. void _CFArraySetCapacity(CFMutableArrayRef array, CFIndex cap) { if (CF_IS_OBJC(__kCFArrayTypeID, array)) return; __CFGenericValidateType(array, __kCFArrayTypeID); CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); CFAssert3(__CFArrayGetCount(array) <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, __CFArrayGetCount(array)); CHECK_FOR_MUTATION(array); BEGIN_MUTATION(array); // Currently, attempting to set the capacity of an array which is the CFStorage // variant, or set the capacity larger than __CF_MAX_BUCKETS_PER_DEQUE, has no // effect. The primary purpose of this API is to help avoid a bunch of the // resizes at the small capacities 4, 8, 16, etc. if (__CFArrayGetType(array) == __kCFArrayDeque) { struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; CFIndex capacity = __CFArrayDequeRoundUpCapacity(cap); CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); CFAllocatorRef allocator = __CFGetAllocator(array); allocator = _CFConvertAllocatorToGCRefZeroEquivalent(allocator); Boolean collectableMemory = CF_IS_COLLECTABLE_ALLOCATOR(allocator); if (NULL == deque) { deque = (struct __CFArrayDeque *)CFAllocatorAllocate(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); if (NULL == deque) __CFArrayHandleOutOfMemory(array, size); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = capacity / 2; } else { struct __CFArrayDeque *olddeque = deque; CFIndex oldcap = deque->_capacity; deque = (struct __CFArrayDeque *)CFAllocatorAllocate(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); if (NULL == deque) __CFArrayHandleOutOfMemory(array, size); objc_memmove_collectable(deque, olddeque, sizeof(struct __CFArrayDeque) + oldcap * sizeof(struct __CFArrayBucket)); if (!collectableMemory) CFAllocatorDeallocate(allocator, olddeque); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); } deque->_capacity = capacity; __CFAssignWithWriteBarrier((void **)&array->_store, (void *)deque); } END_MUTATION(array); }
// 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); }
static void __CFArrayDeallocate(CFTypeRef cf) { CFArrayRef array = (CFArrayRef)cf; BEGIN_MUTATION(array); __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); END_MUTATION(array); }