//===--- CocoaArray.swift - A subset of the NSArray interface -------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // To implement bridging, the core standard library needs to interact // a little bit with Cocoa. Because we want to keep the core // decoupled from the Foundation module, we can't use NSArray // directly. We _can_, however, use an @objc protocol with a // compatible API. That's _NSArrayCore. // //===----------------------------------------------------------------------===// #if _runtime(_ObjC) import SwiftShims /// A wrapper around any `_NSArrayCore` (represented as AnyObject) that gives it /// `Collection` conformance. Why not make `_NSArrayCore` conform directly? /// It's a class, and I don't want to pay for the dynamic dispatch overhead. @usableFromInline @frozen internal struct _CocoaArrayWrapper: RandomAccessCollection { @usableFromInline typealias Indices = Range @usableFromInline internal var buffer: AnyObject @usableFromInline @_transparent internal init(_ buffer: AnyObject) { self.buffer = buffer } internal var core: _NSArrayCore { @inline(__always) get { return unsafe unsafeBitCast(buffer, to: _NSArrayCore.self) } } @inlinable internal var startIndex: Int { return 0 } @usableFromInline internal var endIndex: Int { @_effects(releasenone) get { core.count } } @usableFromInline internal subscript(i: Int) -> AnyObject { @_effects(releasenone) get { core.objectAt(i) } } @usableFromInline internal subscript(bounds: Range) -> _SliceBuffer { let boundsCount = bounds.count if boundsCount == 0 { return _SliceBuffer( _buffer: _ContiguousArrayBuffer(), shiftedToStartIndex: bounds.lowerBound) } // Look for contiguous storage in the NSArray let cocoaStorageBaseAddress = unsafe self.contiguousStorage(self.indices) if let cocoaStorageBaseAddress = unsafe cocoaStorageBaseAddress { return _SliceBuffer( owner: self.buffer, subscriptBaseAddress: cocoaStorageBaseAddress, indices: bounds, hasNativeBuffer: false) } // No contiguous storage found; we must allocate let result = _ContiguousArrayBuffer( _uninitializedCount: boundsCount, minimumCapacity: 0) let base = unsafe UnsafeMutableRawPointer(result.firstElementAddress) .assumingMemoryBound(to: AnyObject.self) for idx in 0.. ) -> UnsafeMutablePointer? { _internalInvariant(!subRange.isEmpty) var enumerationState = unsafe _makeSwiftNSFastEnumerationState() // This function currently returns nil unless the first // subRange.upperBound items are stored contiguously. This is an // acceptable conservative behavior, but could potentially be // optimized for other cases. let contiguousCount = unsafe withUnsafeMutablePointer(to: &enumerationState) { unsafe core.countByEnumerating(with: $0, objects: nil, count: 0) } return unsafe contiguousCount >= subRange.upperBound ? UnsafeMutableRawPointer(enumerationState.itemsPtr!) .assumingMemoryBound(to: AnyObject.self) + subRange.lowerBound : nil } @usableFromInline __consuming internal func _copyContents( subRange bounds: Range, initializing target: UnsafeMutablePointer ) -> UnsafeMutablePointer { return unsafe withExtendedLifetime(buffer) { let nsSubRange = SwiftShims._SwiftNSRange( location: bounds.lowerBound, length: bounds.upperBound - bounds.lowerBound) // Copies the references out of the NSArray without retaining them unsafe core.getObjects(target, range: nsSubRange) // Make another pass to retain the copied objects var result = unsafe target for _ in bounds { unsafe result.initialize(to: result.pointee) unsafe result += 1 } return unsafe result } } @_alwaysEmitIntoClient internal __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer ) -> (Iterator, UnsafeMutableBufferPointer.Index) { guard buffer.count > 0 else { return (makeIterator(), 0) } let start = buffer.baseAddress! let c = Swift.min(self.count, buffer.count) _ = unsafe _copyContents(subRange: 0 ..< c, initializing: start) return (IndexingIterator(_elements: self, _position: c), c) } } @available(*, unavailable) extension _CocoaArrayWrapper: Sendable {} #endif