mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The case where Array stores class instances no longer requires an intermediate indirect buffer object. Also fixes <rdar://problem/17348939> These are the speed changes < 0.95x and > 1.05x, as I measured them. Although I don't have great confidence in these numbers, some are consistent with Arnold's measurements, FWIW. -O: 0.93 ArrayLiteral 1.56 Ary 1.39 Ary2 1.06 CaptureProp 1.93 ClassArrayGetter 1.08 DeltaBlue 1.08 DollarChain 1.13 InsertionSort 1.08 PrimeNum 1.11 RC4 0.93 Rectangles 1.08 SwiftStructuresBubbleSort -Onone: 1.06 ArrayLiteral 1.10 ArraySubscript 1.12 Ary 1.12 Ary2 1.10 Ary3 1.20 ClassArrayGetter 1.19 DeltaBlue 1.08 DollarChain 1.09 Hash 1.07 Havlak 1.10 HeapSort 1.11 ImageProc 1.17 InsertionSort 1.18 Memset 1.11 NBody 1.07 PrimeNum 1.11 QuickSort 1.12 RC4 1.08 Rectangles 1.07 SelectionSort 1.06 StringBuilder 1.12 SwiftStructuresBubbleSort 1.10 Walsh 1.12 XorLoop -Ounchecked: 0.91 ArrayLiteral 1.74 Ary 1.53 Ary2 2.08 ClassArrayGetter 1.19 DeltaBlue 1.06 DollarChain 1.07 Havlak 1.09 ImageProc 1.22 InsertionSort 0.89 PopFrontArrayGeneric 1.11 PrimeNum 0.85 QuickSort 1.10 RC4 1.12 Rectangles 1.06 StrToInt 1.11 SwiftStructuresBubbleSort Swift SVN r23551
403 lines
12 KiB
Swift
403 lines
12 KiB
Swift
//===--- ArrayBuffer.swift - Dynamic storage for Swift Array --------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This is the class that implements the storage and object management for
|
|
// Swift Array.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if _runtime(_ObjC)
|
|
import SwiftShims
|
|
|
|
internal typealias _ArrayBridgeStorage
|
|
= _BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCoreType>
|
|
|
|
public struct _ArrayBuffer<T> : _ArrayBufferType {
|
|
|
|
public typealias Element = T
|
|
|
|
/// create an empty buffer
|
|
public init() {
|
|
_storage = _ArrayBridgeStorage(native: _emptyArrayStorage)
|
|
}
|
|
|
|
public init(nsArray: _NSArrayCoreType) {
|
|
_sanityCheck(_isClassOrObjCExistential(T.self))
|
|
_storage = _ArrayBridgeStorage(objC: nsArray)
|
|
}
|
|
|
|
/// Returns an `_ArrayBuffer<U>` containing the same elements.
|
|
/// Requires: the elements actually have dynamic type `U`, and `U`
|
|
/// is a class or `@objc` existential.
|
|
func castToBufferOf<U>(_: U.Type) -> _ArrayBuffer<U> {
|
|
_sanityCheck(_isClassOrObjCExistential(T.self))
|
|
_sanityCheck(_isClassOrObjCExistential(U.self))
|
|
return _ArrayBuffer<U>(storage: _storage)
|
|
}
|
|
|
|
var needsElementTypeCheck: Bool {
|
|
// NSArray's need an element typecheck when the element type isn't AnyObject
|
|
return _isClassOrObjCExistential(T.self)
|
|
&& _storage.isObjC
|
|
&& !(AnyObject.self is T.Type)
|
|
}
|
|
|
|
//===--- private --------------------------------------------------------===//
|
|
internal init(storage: _ArrayBridgeStorage) {
|
|
_storage = storage
|
|
}
|
|
|
|
internal var _storage: _ArrayBridgeStorage
|
|
}
|
|
|
|
extension _ArrayBuffer {
|
|
/// Adopt the storage of source
|
|
public
|
|
init(_ source: NativeBuffer) {
|
|
_storage = _ArrayBridgeStorage(native: source._storage)
|
|
}
|
|
|
|
/// Return true iff this buffer's storage is uniquely-referenced.
|
|
mutating func isUniquelyReferenced() -> Bool {
|
|
if !_isClassOrObjCExistential(T.self) {
|
|
return _storage.isUniquelyReferenced_native_noSpareBits()
|
|
}
|
|
return _storage.isUniquelyReferencedNative()
|
|
}
|
|
|
|
/// Convert to an NSArray.
|
|
/// Precondition: _isBridgedToObjectiveC(Element.self)
|
|
/// O(1) if the element type is bridged verbatim, O(N) otherwise
|
|
public func _asCocoaArray() -> _NSArrayCoreType {
|
|
_sanityCheck(
|
|
_isBridgedToObjectiveC(T.self),
|
|
"Array element type is not bridged to ObjectiveC")
|
|
|
|
return _fastPath(_isNative) ? _native._asCocoaArray() : _nonNative
|
|
}
|
|
|
|
/// If this buffer is backed by a uniquely-referenced mutable
|
|
/// _ContiguousArrayBuffer that can be grown in-place to allow the self
|
|
/// buffer store minimumCapacity elements, returns that buffer.
|
|
/// Otherwise, returns nil
|
|
public
|
|
mutating func requestUniqueMutableBackingBuffer(minimumCapacity: Int)
|
|
-> NativeBuffer?
|
|
{
|
|
if _fastPath(isUniquelyReferenced()) {
|
|
let b = _native
|
|
if _fastPath(b.capacity >= minimumCapacity) {
|
|
return b
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public
|
|
mutating func isMutableAndUniquelyReferenced() -> Bool {
|
|
return isUniquelyReferenced()
|
|
}
|
|
|
|
/// If this buffer is backed by a `_ContiguousArrayBuffer`
|
|
/// containing the same number of elements as `self`, return it.
|
|
/// Otherwise, return `nil`.
|
|
public func requestNativeBuffer() -> NativeBuffer? {
|
|
if !_isClassOrObjCExistential(T.self) {
|
|
return _native
|
|
}
|
|
return _fastPath(_storage.isNative) ? _native : nil
|
|
}
|
|
|
|
/// Replace the given subRange with the first newCount elements of
|
|
/// the given collection.
|
|
///
|
|
/// Requires: this buffer is backed by a uniquely-referenced
|
|
/// _ContiguousArrayBuffer
|
|
public
|
|
mutating func replace<C: CollectionType where C.Generator.Element == Element>(
|
|
#subRange: Range<Int>, with newCount: Int, elementsOf newValues: C
|
|
) {
|
|
_arrayNonSliceInPlaceReplace(&self, subRange, newCount, newValues)
|
|
}
|
|
|
|
// We have two versions of type check: one that takes a range and the other
|
|
// checks one element. The reason for this is that the ARC optimizer does not
|
|
// handle loops atm. and so can get blocked by the presence of a loop (over
|
|
// the range). This loop is not necessary for a single element access.
|
|
func _typeCheck(index: Int) {
|
|
if !_isClassOrObjCExistential(T.self) {
|
|
return
|
|
}
|
|
|
|
if _slowPath(needsElementTypeCheck) {
|
|
_sanityCheck(
|
|
!_isNative, "A native array that needs a type check?")
|
|
|
|
let ns = _nonNative
|
|
// Could be sped up, e.g. by using
|
|
// enumerateObjectsAtIndexes:options:usingBlock:
|
|
_precondition(ns.objectAtIndex(index) is T,
|
|
"NSArray element failed to match the Swift Array Element type")
|
|
}
|
|
}
|
|
|
|
func _typeCheck(subRange: Range<Int>) {
|
|
if !_isClassOrObjCExistential(T.self) {
|
|
return
|
|
}
|
|
|
|
if _slowPath(needsElementTypeCheck) {
|
|
for i in subRange {
|
|
_typeCheck(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Copy the given subRange of this buffer into uninitialized memory
|
|
/// starting at target. Return a pointer past-the-end of the
|
|
/// just-initialized memory.
|
|
@inline(never) // The copy loop blocks retain release matching.
|
|
public
|
|
func _uninitializedCopy(subRange: Range<Int>, target: UnsafeMutablePointer<T>)
|
|
-> UnsafeMutablePointer<T> {
|
|
_typeCheck(subRange)
|
|
if _fastPath(_isNative) {
|
|
return _native._uninitializedCopy(subRange, target: target)
|
|
}
|
|
|
|
let nonNative = _nonNative
|
|
|
|
let nsSubRange = SwiftShims._SwiftNSRange(
|
|
location:subRange.startIndex,
|
|
length: subRange.endIndex - subRange.startIndex)
|
|
|
|
let buffer = UnsafeMutablePointer<AnyObject>(target)
|
|
|
|
// Copies the references out of the NSArray without retaining them
|
|
nonNative.getObjects(buffer, range: nsSubRange)
|
|
|
|
// Make another pass to retain the copied objects
|
|
var result = target
|
|
for i in subRange {
|
|
result.initialize(result.memory)
|
|
++result
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// Return a _SliceBuffer containing the given subRange of values
|
|
/// from this buffer.
|
|
public
|
|
subscript(subRange: Range<Int>) -> _SliceBuffer<T> {
|
|
_typeCheck(subRange)
|
|
|
|
if _fastPath(_isNative) {
|
|
return _native[subRange]
|
|
}
|
|
|
|
let nonNative = self._nonNative
|
|
|
|
let subRangeCount = Swift.count(subRange)
|
|
|
|
// Look for contiguous storage in the NSArray
|
|
let cocoa = _CocoaArrayWrapper(nonNative)
|
|
let start = cocoa.contiguousStorage(subRange)
|
|
if start != nil {
|
|
return _SliceBuffer(owner: nonNative, start: UnsafeMutablePointer(start),
|
|
count: subRangeCount, hasNativeBuffer: false)
|
|
}
|
|
|
|
// No contiguous storage found; we must allocate
|
|
var result = _ContiguousArrayBuffer<T>(
|
|
count: subRangeCount, minimumCapacity: 0)
|
|
|
|
// Tell Cocoa to copy the objects into our storage
|
|
cocoa.buffer.getObjects(
|
|
UnsafeMutablePointer(result.baseAddress),
|
|
range: _SwiftNSRange(location: subRange.startIndex, length: subRangeCount)
|
|
)
|
|
|
|
return _SliceBuffer(result)
|
|
}
|
|
|
|
/// If the elements are stored contiguously, a pointer to the first
|
|
/// element. Otherwise, nil.
|
|
public
|
|
var baseAddress: UnsafeMutablePointer<T> {
|
|
if (_fastPath(_isNative)) {
|
|
return _native.baseAddress
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// How many elements the buffer stores
|
|
public
|
|
var count: Int {
|
|
get {
|
|
return _fastPath(_isNative) ? _native.count : _nonNative.count
|
|
}
|
|
set {
|
|
_sanityCheck(_isNative, "attempting to update count of Cocoa array")
|
|
_native.count = newValue
|
|
}
|
|
}
|
|
|
|
/// Return whether the given `index` is valid for subscripting, i.e. `0
|
|
/// ≤ index < count`
|
|
internal func _isValidSubscript(index : Int) -> Bool {
|
|
if _fastPath(_isNative) {
|
|
/// Note we call through to the native buffer here as it has a more
|
|
/// optimal implementation than just doing 'index < count'
|
|
return _native._isValidSubscript(index)
|
|
}
|
|
return index >= 0 && index < count
|
|
}
|
|
|
|
/// How many elements the buffer can store without reallocation
|
|
public
|
|
var capacity: Int {
|
|
return _fastPath(_isNative) ? _native.capacity : _nonNative.count
|
|
}
|
|
|
|
/// Get/set the value of the ith element
|
|
public
|
|
subscript(i: Int) -> T {
|
|
get {
|
|
if _isClassOrObjCExistential(T.self) {
|
|
_typeCheck(i)
|
|
}
|
|
if _fastPath(_isNative) {
|
|
return _native[i]
|
|
}
|
|
return unsafeBitCast(_nonNative.objectAtIndex(i), T.self)
|
|
}
|
|
|
|
nonmutating set {
|
|
if _fastPath(_isNative) {
|
|
_native[i] = newValue
|
|
}
|
|
else {
|
|
var refCopy = self
|
|
refCopy.replace(
|
|
subRange: i...i, with: 1, elementsOf: CollectionOfOne(newValue))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Call `body(p)`, where `p` is an `UnsafeBufferPointer` over the
|
|
/// underlying contiguous storage. If no such storage exists, it is
|
|
/// created on-demand.
|
|
public
|
|
func withUnsafeBufferPointer<R>(
|
|
body: (UnsafeBufferPointer<Element>)->R
|
|
) -> R {
|
|
if _fastPath(_isNative) {
|
|
let ret = body(UnsafeBufferPointer(start: self.baseAddress, count: count))
|
|
_fixLifetime(self)
|
|
return ret
|
|
}
|
|
return ContiguousArray(self).withUnsafeBufferPointer(body)
|
|
}
|
|
|
|
/// Call `body(p)`, where `p` is an `UnsafeMutableBufferPointer`
|
|
/// over the underlying contiguous storage. Requires: such
|
|
/// contiguous storage exists or the buffer is empty
|
|
public
|
|
mutating func withUnsafeMutableBufferPointer<R>(
|
|
body: (UnsafeMutableBufferPointer<T>)->R
|
|
) -> R {
|
|
_sanityCheck(
|
|
baseAddress != nil || count == 0,
|
|
"Array is bridging an opaque NSArray; can't get a pointer to the elements"
|
|
)
|
|
let ret = body(
|
|
UnsafeMutableBufferPointer(start: baseAddress, count: count))
|
|
_fixLifetime(self)
|
|
return ret
|
|
}
|
|
|
|
/// An object that keeps the elements stored in this buffer alive
|
|
public
|
|
var owner: AnyObject {
|
|
return _fastPath(_isNative) ? _native._storage : _nonNative
|
|
}
|
|
|
|
/// A value that identifies the storage used by the buffer. Two
|
|
/// buffers address the same elements when they have the same
|
|
/// identity and count.
|
|
public var identity: UnsafePointer<Void> {
|
|
if _isNative {
|
|
return _native.identity
|
|
}
|
|
else {
|
|
return unsafeAddressOf(_nonNative)
|
|
}
|
|
}
|
|
|
|
//===--- CollectionType conformance -------------------------------------===//
|
|
/// The position of the first element in a non-empty collection.
|
|
///
|
|
/// Identical to `endIndex` in an empty collection.
|
|
public
|
|
var startIndex: Int {
|
|
return 0
|
|
}
|
|
|
|
/// The collection's "past the end" position.
|
|
///
|
|
/// `endIndex` is not a valid argument to `subscript`, and is always
|
|
/// reachable from `startIndex` by zero or more applications of
|
|
/// `successor()`.
|
|
public var endIndex: Int {
|
|
return count
|
|
}
|
|
|
|
/// Return a *generator* over the elements of this *sequence*.
|
|
///
|
|
/// Complexity: O(1)
|
|
public func generate() -> IndexingGenerator<_ArrayBuffer> {
|
|
return IndexingGenerator(self)
|
|
}
|
|
|
|
//===--- private --------------------------------------------------------===//
|
|
typealias Storage = _ContiguousArrayStorage<T>
|
|
typealias NativeBuffer = _ContiguousArrayBuffer<T>
|
|
|
|
func _invariantCheck() -> Bool {
|
|
return true
|
|
}
|
|
|
|
var _isNative: Bool {
|
|
if !_isClassOrObjCExistential(T.self) {
|
|
return true
|
|
}
|
|
else {
|
|
return _storage.isNative
|
|
}
|
|
}
|
|
|
|
/// Our native representation.
|
|
///
|
|
/// Requires: `_isNative`
|
|
var _native: NativeBuffer {
|
|
return NativeBuffer(
|
|
_isClassOrObjCExistential(T.self)
|
|
? _storage.nativeInstance : _storage.nativeInstance_noSpareBits)
|
|
}
|
|
|
|
var _nonNative: _NSArrayCoreType {
|
|
_sanityCheck(_isClassOrObjCExistential(T.self))
|
|
return _storage.objCInstance
|
|
}
|
|
}
|
|
#endif
|