mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
172 lines
5.6 KiB
Swift
172 lines
5.6 KiB
Swift
//===--- 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<Int>
|
|
|
|
@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<Int>) -> _SliceBuffer<AnyObject> {
|
|
let boundsCount = bounds.count
|
|
if boundsCount == 0 {
|
|
return _SliceBuffer(
|
|
_buffer: _ContiguousArrayBuffer<AnyObject>(),
|
|
shiftedToStartIndex: bounds.lowerBound)
|
|
}
|
|
|
|
// Look for contiguous storage in the NSArray
|
|
let cocoaStorageBaseAddress = self.contiguousStorage(self.indices)
|
|
|
|
if let cocoaStorageBaseAddress = unsafe cocoaStorageBaseAddress {
|
|
return unsafe _SliceBuffer(
|
|
owner: self.buffer,
|
|
subscriptBaseAddress: cocoaStorageBaseAddress,
|
|
indices: bounds,
|
|
hasNativeBuffer: false)
|
|
}
|
|
|
|
// No contiguous storage found; we must allocate
|
|
let result = _ContiguousArrayBuffer<AnyObject>(
|
|
_uninitializedCount: boundsCount,
|
|
minimumCapacity: 0)
|
|
|
|
let base = unsafe UnsafeMutableRawPointer(result.firstElementAddress)
|
|
.assumingMemoryBound(to: AnyObject.self)
|
|
|
|
for idx in 0..<boundsCount {
|
|
unsafe (base + idx).initialize(to: core.objectAt(idx + bounds.lowerBound))
|
|
}
|
|
|
|
return _SliceBuffer(_buffer: result, shiftedToStartIndex: bounds.lowerBound)
|
|
}
|
|
|
|
/// Returns a pointer to the first element in the given non-empty `subRange`
|
|
/// if the subRange is stored contiguously. Otherwise, return `nil`.
|
|
///
|
|
/// The "non-empty" condition saves a branch within this method that can
|
|
/// likely be better handled in a caller.
|
|
///
|
|
/// - Note: This method should only be used as an optimization; it
|
|
/// is sometimes conservative and may return `nil` even when
|
|
/// contiguous storage exists, e.g., if array doesn't have a smart
|
|
/// implementation of countByEnumerating.
|
|
internal func contiguousStorage(
|
|
_ subRange: Range<Int>
|
|
) -> UnsafeMutablePointer<AnyObject>?
|
|
{
|
|
_internalInvariant(!subRange.isEmpty)
|
|
var enumerationState = _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<Int>,
|
|
initializing target: UnsafeMutablePointer<AnyObject>
|
|
) -> UnsafeMutablePointer<AnyObject> {
|
|
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<Element>
|
|
) -> (Iterator, UnsafeMutableBufferPointer<Element>.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
|