Files
swift-mirror/stdlib/core/Arrays.swift.gyb
2014-07-07 09:46:07 +00:00

987 lines
29 KiB
Swift

//===--- Arrays.swift.gyb - ContiguousArray, Array, and Slice -*- swift -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Three generic, mutable array-like types with value semantics.
//
// - ContiguousArray<T> is a fast, contiguous array of T with a known
// backing store.
//
// - Slice<T> presents an arbitrary subsequence of some contiguous sequence
// of Ts.
//
// - Array<T> is like ContiguousArray<T> when T is not an ObjC type.
// Otherwise, it may use an NSArray bridged from Cocoa for storage
//
//===----------------------------------------------------------------------===//
% arrayTypes = ['ContiguousArray', 'Slice', 'Array']
% for Self in arrayTypes:
@public struct ${Self}<T> : MutableCollection, Sliceable {
@public typealias Element = T
@public var startIndex: Int {
return 0
}
@public var endIndex: Int {
return _buffer.count
}
@public subscript(index: Int) -> Element {
get {
_precondition(index < count, "${Self} index out of range")
_precondition(index >= 0, "Negative ${Self} index is out of range")
return _buffer[index]
}
set {
_precondition(index < count, "${Self} index out of range")
_precondition(index >= 0, "Negative array index is out of range")
if _buffer.isMutableAndUniquelyReferenced() {
_buffer[index] = newValue
}
else {
_subscriptSetSlowPath(&_buffer, e: newValue, index: index)
}
}
}
@public func generate() -> IndexingGenerator<${Self}> {
return IndexingGenerator(self)
}
@public typealias SliceType = Slice<T>
@public subscript(subRange: Range<Int>) -> SliceType {
get {
return Slice(_buffer[subRange])
}
set(rhs) {
if self[subRange] !== rhs {
self.replaceRange(subRange, with: rhs)
}
}
}
//===--- private --------------------------------------------------------===//
@inline(never)
func _subscriptSetSlowPath(inout buffer: _Buffer, e: Element, index : Int) {
var newBuffer = ContiguousArrayBuffer<T>(
count: buffer.count, minimumCapacity: buffer.count)
let target = buffer._uninitializedCopy(
0..<index, target: newBuffer.elementStorage)
target.initialize(e)
buffer._uninitializedCopy(
(index + 1)..<buffer.count, target: target + 1)
buffer = _Buffer(newBuffer)
}
@public
typealias _Buffer = ${'Array' if Self.startswith('New') else Self}Buffer<T>
@public
init(_ buffer: _Buffer) {
self._buffer = buffer
}
@public var _buffer: _Buffer
}
extension ${Self} : ArrayLiteralConvertible {
@public static func convertFromArrayLiteral(elements: Element...) -> ${Self} {
return ${Self}(_extractOrCopyToNativeArrayBuffer(elements._buffer))
}
}
extension ${Self} {
@public func _asCocoaArray() -> _CocoaArray {
return _buffer._asCocoaArray()
}
}
extension ${Self} : ArrayType {
/// Construct an empty ${Self}
@public init() {
_buffer = _Buffer()
}
@public init<
S: Sequence where S.GeneratorType.Element == _Buffer.Element
>(_ s: S) {
self = ${Self}(_Buffer(s~>_copyToNativeArrayBuffer()))
}
/// Construct an array of `count` elements, each initialized to
/// `repeatedValue`.
@public init(count: Int, repeatedValue: T) {
_precondition(count >= 0, "Can't construct ${Self} with count < 0")
_buffer = _Buffer()
reserveCapacity(count)
var p = _buffer.elementStorage
for _ in 0..<count {
p++.initialize(repeatedValue)
}
_buffer.count = count
}
/// How many elements the ${Self} stores
@public var count: Int {
return _buffer.count
}
/// How many elements the ${Self} can store without reallocation
@public var capacity: Int {
return _buffer.capacity
}
/// true if and only if the ${Self} is empty
@public var isEmpty: Bool {
return count == 0
}
/// An object that guarantees the lifetime of this array's elements
@public
var _owner: AnyObject? {
return _buffer.owner
}
/// If the elements are stored contiguously, a pointer to the first
/// element. Otherwise, nil.
@public var _elementStorageIfContiguous: UnsafePointer<Element> {
return _buffer.elementStorage
}
%if Self != 'Array': # // Array does not necessarily have contiguous storage
var _elementStorage: UnsafePointer<Element> {
return _buffer.elementStorage
}
%end
//===--- basic mutations ------------------------------------------------===//
/// Ensure the array has enough mutable contiguous storage to store
/// minimumCapacity elements in. Note: does not affect count.
/// Complexity: O(N)
@public mutating func reserveCapacity(minimumCapacity: Int) {
if !_buffer.requestUniqueMutableBackingBuffer(minimumCapacity) {
var newBuffer = ContiguousArrayBuffer<T>(
count: count, minimumCapacity: minimumCapacity)
_buffer._uninitializedCopy(0..<count, target: newBuffer.elementStorage)
_buffer = _Buffer(newBuffer)
}
_sanityCheck(capacity >= minimumCapacity)
}
/// Append newElement to the ${Self} in O(1) (amortized)
@public mutating func append(newElement: T) {
_arrayAppend(&_buffer, newElement)
}
/// Append elements from `sequence` to the Array
@public mutating func extend<
S : Sequence
where S.GeneratorType.Element == T
>(sequence: S) {
// Calling a helper free function instead of writing the code inline
// because of:
//
// <rdar://problem/16954386> Type checker assertion: Unable to solve for
// call to witness?
_${Self}Extend(&self, sequence)
}
/// Remove an element from the end of the ${Self} in O(1).
/// Requires: count > 0
@public mutating func removeLast() -> T {
_precondition(count > 0, "can't removeLast from an empty ${Self}")
let c = count
let result = self[c - 1]
self.replaceRange((c - 1)..<c, with: EmptyCollection())
return result
}
/// Insert an element at the given index in O(N). Requires: atIndex
/// <= count
@public mutating func insert(newElement: T, atIndex: Int) {
self.replaceRange(atIndex..<atIndex, with: CollectionOfOne(newElement))
}
/// Remove the element at the given index. Worst case complexity:
/// O(N). Requires: index < count
@public mutating func removeAtIndex(index: Int) -> T {
let result = self[index]
self.replaceRange(index..<(index + 1), with: EmptyCollection())
return result
}
/// Erase all the elements. If `keepCapacity` is `true`, `capacity`
/// will not change
@public mutating func removeAll(keepCapacity: Bool = false) {
if !keepCapacity {
_buffer = _Buffer()
}
else {
self.replaceRange(indices(self), with: EmptyCollection())
}
}
//===--- algorithms -----------------------------------------------------===//
@public func join<
S : Sequence where S.GeneratorType.Element == ${Self}<T>
>(elements: S) -> ${Self}<T> {
return Swift.join(self, elements)
}
@public func reduce<U>(initial: U, combine: (U, T)->U) -> U {
return Swift.reduce(self, initial, combine)
}
@public mutating func sort(isOrderedBefore: (T, T)->Bool) {
return withUnsafeMutableStorage {
me in Swift.sort(&me, isOrderedBefore)
return
}
}
@public func sorted(isOrderedBefore: (T, T)->Bool) -> ${Self} {
var result = self
result.sort(isOrderedBefore)
return result
}
/// Return a ${Self} containing the results of calling
/// `transform(x)` on each element `x` of `self`
@public func map<U>(transform: (T)->U) -> ${Self}<U> {
return ${Self}<U>(lazy(self).map(transform))
}
/// A ${Self} containing the elements of `self` in reverse order
@public func reverse() -> ${Self} {
return ${Self}(lazy(self).reverse())
}
/// Return a ${Self} containing the elements `x` of `self` for which
/// `includeElement(x)` is `true`
@public func filter(includeElement: (T)->Bool) -> ${Self} {
return ${Self}(lazy(self).filter(includeElement))
}
}
func _${Self}Extend<
T, S : Sequence
where S.GeneratorType.Element == T
>(inout a: ${Self}<T>, sequence: S) {
a += sequence
}
extension ${Self} : Reflectable {
@public func getMirror() -> Mirror {
return _ArrayTypeMirror(self)
}
}
extension ${Self} : Printable, DebugPrintable {
func _makeDescription(#isDebug: Bool) -> String {
var result = "["
var first = true
for item in self {
if first {
first = false
} else {
result += ", "
}
if isDebug {
debugPrint(item, &result)
} else {
print(item, &result)
}
}
result += "]"
return result
}
@public var description: String {
return _makeDescription(isDebug: false)
}
@public var debugDescription: String {
return _makeDescription(isDebug: true)
}
}
extension ${Self} {
@transparent
func _cPointerArgs() -> (AnyObject?, Builtin.RawPointer) {
let p = _elementStorageIfContiguous
if _fastPath(p != nil || count == 0) {
return (_owner, p.value)
}
let n = _extractOrCopyToNativeArrayBuffer(self._buffer)
return (n.owner, n.elementStorage.value)
}
}
extension ${Self} {
/// Call body(p), where p is a pointer to the ${Self}'s contiguous storage
%if Self != 'Array':
/// Requires: the Array's storage is not provided by an opaque NSArray
%end
@public func withUnsafePointerToElements<R>(
body: (UnsafePointer<T>) -> R
) -> R {
return _buffer.withUnsafePointerToElements(body)
}
}
extension ${Self} {
@public mutating func withUnsafeMutableStorage<R>(
body: (inout UnsafeMutableArray<T>)->R
) -> R {
// Ensure unique storage
_arrayReserve(&_buffer, 0)
// Ensure that body can't invalidate the storage or its bounds by
// moving self into a temporary working array.
var work = ${Self}()
swap(&work, &self)
// Create an UnsafeArray over work that we can pass to body
var a = UnsafeMutableArray(
start: work._buffer.elementStorage, length: work.count)
// Invoke the body
let ret = body(&a)
// Put the working array back before returning.
swap(&work, &self)
return ret
}
}
%end
extension Array {
/// This function "seeds" the ArrayLiteralConvertible protocol
@public static func convertFromHeapArray(
base: Builtin.RawPointer,
owner: Builtin.NativeObject,
count: Builtin.Word
) -> Array {
let elements = UnsafeArray(
start: reinterpretCast(base) as UnsafePointer<T>,
length: reinterpretCast(count) as Int
)
let r = Array(elements)
_fixLifetime(owner)
return r
}
}
struct _InitializeMemoryFromCollection<C: Collection> : _PointerFunction {
func call(rawMemory: UnsafePointer<C.GeneratorType.Element>) {
var p = rawMemory
for x in newValues {
p++.initialize(x)
}
}
init(_ newValues: C) {
self.newValues = newValues
}
var newValues: C
}
@inline(never)
func _arrayOutOfPlaceReplace<
B: ArrayBufferType, C: Collection
where C.GeneratorType.Element == B.Element, B.IndexType == Int
>(
inout source: B, subRange: Range<Int>, newValues: C, insertCount: Int
) {
let growth = insertCount - countElements(subRange)
let newCount = source.count + growth
var newBuffer = Optional(
_forceCreateUniqueMutableBuffer(&source, newCount, newCount))
_arrayOutOfPlaceUpdate(
&source, &newBuffer,
subRange.startIndex, insertCount,
_InitializeMemoryFromCollection(newValues)
)
}
func _arrayNonSliceInPlaceReplace<
B: ArrayBufferType, C: Collection
where C.GeneratorType.Element == B.Element, B.IndexType == Int
>(inout target: B, subRange: Range<Int>, insertCount: Int, newValues: C) {
let oldCount = target.count
let eraseCount = countElements(subRange)
let growth = insertCount - eraseCount
target.count = oldCount + growth
let elements = target.elementStorage
_sanityCheck(elements != nil)
let oldTailIndex = subRange.endIndex
let oldTailStart = elements + oldTailIndex
let newTailIndex = oldTailIndex + growth
let newTailStart = oldTailStart + growth
let tailCount = oldCount - subRange.endIndex
if growth > 0 {
// Slide the tail part of the buffer forwards, in reverse order
// so as not to self-clobber.
newTailStart.moveInitializeBackwardFrom(oldTailStart, count: tailCount)
// Assign over the original subRange
var i = newValues.startIndex
for j in subRange {
elements[j] = newValues[i++]
}
// Initialize the hole left by sliding the tail forward
for j in oldTailIndex..<newTailIndex {
(elements + j).initialize(newValues[i++])
}
}
else { // We're not growing the buffer
// Assign all the new elements into the start of the subRange
var i = subRange.startIndex
for j in indices(newValues) {
elements[i++] = newValues[j]
}
// If the size didn't change, we're done.
if growth == 0 {
return
}
// Move the tail backward to cover the shrinkage.
let shrinkage = -growth
if tailCount > shrinkage { // If the tail length exceeds the shrinkage
// Assign over the rest of the replaced range with the first
// part of the tail.
newTailStart.moveAssignFrom(oldTailStart, count: shrinkage)
// slide the rest of the tail back
oldTailStart.moveInitializeFrom(
oldTailStart + shrinkage, count: tailCount - shrinkage)
}
else { // tail fits within erased elements
// Assign over the start of the replaced range with the tail
newTailStart.moveAssignFrom(oldTailStart, count: tailCount)
// destroy elements remaining after the tail in subRange
(newTailStart + tailCount).destroy(shrinkage - tailCount)
}
}
}
func _arrayReplace<
B: ArrayBufferType, C: Collection
where C.GeneratorType.Element == B.Element, B.IndexType == Int
>(
inout target: B, subRange: Range<Int>, newValues: C
) {
_precondition(
subRange.startIndex >= 0,
"${Self} replace: subRange start is negative")
_precondition(
subRange.endIndex >= subRange.startIndex,
"${Self} replace: subRange is inside-out")
_precondition(
subRange.endIndex <= target.endIndex,
"${Self} replace: subRange extends past the end")
let oldCount = target.count
let eraseCount = countElements(subRange)
let insertCount = numericCast(countElements(newValues)) as Int
let growth = insertCount - eraseCount
if target.requestUniqueMutableBackingBuffer(oldCount + growth) {
target.replace(subRange: subRange, with: insertCount, elementsOf: newValues)
}
else {
_arrayOutOfPlaceReplace(&target, subRange, newValues, insertCount)
}
}
func _growArrayCapacity(capacity: Int) -> Int {
return capacity * 2
}
% for Self in arrayTypes:
extension ${Self} {
@public mutating func replaceRange<
C: Collection where C.GeneratorType.Element == _Buffer.Element
>(
subRange: Range<Int>, with newValues: C
) {
_arrayReplace(&self._buffer, subRange, newValues)
}
}
@assignment @public
func += <
T, S: Sequence
where S.GeneratorType.Element == T
>(inout lhs: ${Self}<T>, rhs: S) {
let oldCount = lhs.count
let capacity = lhs.capacity
let newCount = oldCount + underestimateCount(rhs)
if newCount > capacity {
lhs.reserveCapacity(
max(newCount, _growArrayCapacity(capacity)))
}
_arrayAppendSequence(&lhs._buffer, rhs)
}
@assignment @public
func += <
T, C: Collection
where C.GeneratorType.Element == T
>(inout lhs: ${Self}<T>, rhs: C) {
let rhsCount = numericCast(countElements(rhs)) as Int
let oldCount = lhs.count
let capacity = lhs.capacity
let newCount = oldCount + rhsCount
// Ensure uniqueness, mutability, and sufficient storage. Note that
// for consistency, we need unique lhs even if rhs is empty.
lhs.reserveCapacity(
newCount > capacity ?
max(newCount, _growArrayCapacity(capacity))
: newCount)
var p = lhs._buffer.elementStorage + oldCount
for x in rhs {
(p++).initialize(x)
}
lhs._buffer.count = newCount
}
@assignment @public
func += <T>(inout lhs: ${Self}<T>, rhs: T) {
_arrayAppend(&lhs._buffer, rhs)
}
% end
//===--- generic helpers --------------------------------------------------===//
/// Ensure there's a ContiguousArrayBuffer capable of storing
/// max(newCount, minimumCapacity) elements, with count set to
/// newCount.
///
/// If source has sufficient capacity, returns nil. Otherwise,
/// returns a new buffer.
///
/// NOTE: does not initialize or destroy any elements. In general,
/// the buffer that satisfies the capacity request now has a count
/// that does not match its number of initialized elements, and that
/// needs to be corrected before the buffer can go back into circulation.
func _createUniqueMutableBuffer<_Buffer: ArrayBufferType>(
inout source: _Buffer, newCount: Int, minimumCapacity: Int = 0)
-> ContiguousArrayBuffer<_Buffer.Element>? {
_sanityCheck(newCount >= 0)
let requiredCapacity = max(newCount, minimumCapacity)
if let b = source.requestUniqueMutableBackingBuffer(requiredCapacity) {
source.count = newCount
return nil
}
return _forceCreateUniqueMutableBuffer(&source, newCount, requiredCapacity)
}
func _forceCreateUniqueMutableBuffer<_Buffer: ArrayBufferType>(
inout source: _Buffer, newCount: Int, requiredCapacity: Int
) -> ContiguousArrayBuffer<_Buffer.Element> {
_sanityCheck(newCount >= 0)
_sanityCheck(requiredCapacity >= newCount)
let minimumCapacity = max(
requiredCapacity,
newCount > source.capacity
? _growArrayCapacity(source.capacity) : source.capacity)
return ContiguousArrayBuffer(
count: newCount, minimumCapacity: minimumCapacity)
}
protocol _PointerFunction {
typealias Element
func call(UnsafePointer<Element>)
}
/// initialize the elements of dest by copying the first headCount
/// items from source, calling initializeNewElements on the next
/// uninitialized element, and finally by copying the last N items
/// from source into the N remaining uninitialized elements of dest.
///
/// As an optimization, may move elements out of source rather than
/// copying when it isUniquelyReferenced.
func _arrayOutOfPlaceUpdate<
_Buffer: ArrayBufferType, Initializer: _PointerFunction
where Initializer.Element == _Buffer.Element
>(
inout source: _Buffer,
inout dest: ContiguousArrayBuffer<_Buffer.Element>?,
headCount: Int, // Count of initial source elements to copy/move
newCount: Int, // Count of new elements to insert
initializeNewElements: Initializer
) {
_sanityCheck(headCount >= 0)
_sanityCheck(newCount >= 0)
// Count of trailing source elements to copy/move
let tailCount = dest!.count - headCount - newCount
_sanityCheck(headCount + tailCount <= source.count)
let sourceCount = source.count
let oldCount = sourceCount - headCount - tailCount
let destStart = dest!.elementStorage
let newStart = destStart + headCount
let newEnd = newStart + newCount
// Check to see if we have storage we can move from
if let backing = source.requestUniqueMutableBackingBuffer(sourceCount) {
let sourceStart = source.elementStorage
let oldStart = sourceStart + headCount
// Destroy any items that may be lurking in a SliceBuffer before
// its real first element
let backingStart = backing.elementStorage
let sourceOffset = sourceStart - backingStart
backingStart.destroy(sourceOffset)
// Move the head items
destStart.moveInitializeFrom(sourceStart, count: headCount)
// Destroy unused source items
oldStart.destroy(oldCount)
initializeNewElements.call(newStart)
// Move the tail items
newEnd.moveInitializeFrom(oldStart + oldCount, count: tailCount)
// Destroy any items that may be lurking in a SliceBuffer after
// its real last element
let backingEnd = backingStart + backing.count
let sourceEnd = sourceStart + sourceCount
sourceEnd.destroy(backingEnd - sourceEnd)
backing.count = 0
}
else {
let newStart = source._uninitializedCopy(0..<headCount, target: destStart)
initializeNewElements.call(newStart)
source._uninitializedCopy(headCount + oldCount..<sourceCount,
target: newEnd)
}
source = _Buffer(dest!)
}
struct _InitializePointer<T> : _PointerFunction {
func call(rawMemory: UnsafePointer<T>) {
// FIXME: maybe we should move here instead of copying?
rawMemory.initialize(newValue)
}
@transparent
init(_ newValue: T) {
self.newValue = newValue
}
var newValue: T
}
func _arrayAppend<_Buffer: ArrayBufferType>(
inout buffer: _Buffer, newValue: _Buffer.Element
) {
let oldCount = buffer.count
var newBuffer = _createUniqueMutableBuffer(&buffer, oldCount + 1)
if _fastPath(!newBuffer) {
(buffer.elementStorage + oldCount).initialize(newValue)
}
else {
_arrayOutOfPlaceUpdate(
&buffer, &newBuffer, oldCount, 1, _InitializePointer(newValue))
}
}
struct _IgnorePointer<T> : _PointerFunction {
func call(_:UnsafePointer<T>) {}
}
func _arrayReserve<_Buffer: ArrayBufferType>(
inout buffer: _Buffer, minimumCapacity: Int
) {
let oldCount = buffer.count
var newBuffer = _createUniqueMutableBuffer(
&buffer, oldCount, minimumCapacity: minimumCapacity)
if _slowPath(newBuffer) {
_arrayOutOfPlaceUpdate(&buffer, &newBuffer, oldCount, 0, _IgnorePointer())
}
}
@public func _extractOrCopyToNativeArrayBuffer<
_Buffer: ArrayBufferType
where _Buffer.GeneratorType.Element == _Buffer.Element
>(source: _Buffer)
-> ContiguousArrayBuffer<_Buffer.Element>
{
if let n = source.requestNativeBuffer() {
return n
}
return _copyCollectionToNativeArrayBuffer(source)
}
/// Append items from newItems to buffer
func _arrayAppendSequence<
_Buffer: ArrayBufferType,
S: Sequence where S.GeneratorType.Element == _Buffer.Element
>(
inout buffer: _Buffer, newItems: S
) {
var stream = newItems.generate()
var nextItem = stream.next()
if !nextItem {
return
}
// This will force uniqueness
_arrayAppend(&buffer, nextItem!)
var count = buffer.count
nextItem = stream.next()
while nextItem {
let capacity = buffer.capacity
let base = buffer.elementStorage
while nextItem && count < capacity {
(base + count++).initialize(nextItem!)
nextItem = stream.next()
}
buffer.count = count
if nextItem {
_arrayReserve(&buffer, _growArrayCapacity(capacity))
}
}
}
/// Returns true iff these arrays reference exactly the same elements.
func ===<T: ArrayType, U: ArrayType>(lhs: T, rhs: U) -> Bool {
let lhsCount = lhs.count
if lhsCount != rhs.count {
return false
}
return lhsCount == 0 || lhs._buffer.identity == rhs._buffer.identity
}
/// Returns false iff these arrays reference exactly the same
/// elements.
func !==<T: ArrayType, U: ArrayType>(lhs: T, rhs: U) -> Bool {
return !(lhs === rhs)
}
% for Self in arrayTypes:
// NOTE: The '==' and '!=' below only handles array types
// that are the same, e.g. Array<Int> and Array<Int>, not
// Slice<Int> and Array<Int>.
/// Returns true if these arrays contain the same elements.
@public func ==<T: Equatable>(lhs: ${Self}<T>, rhs: ${Self}<T>) -> Bool {
let lhsCount = lhs.count
if lhsCount != rhs.count {
return false
}
// Test referential equality.
if lhsCount == 0 || lhs._buffer.identity == rhs._buffer.identity {
return true
}
var streamLHS = lhs.generate()
var streamRHS = rhs.generate()
var nextLHS = streamLHS.next()
while nextLHS {
let nextRHS = streamRHS.next()
if nextLHS != nextRHS {
return false
}
nextLHS = streamLHS.next()
}
return true
}
/// Returns true if the arrays do not contain the same elements.
@public func !=<T: Equatable>(lhs: ${Self}<T>, rhs: ${Self}<T>) -> Bool {
return !(lhs == rhs)
}
%end
/// Returns an Array<Base> containing the same elements as a in
/// O(1). Requires: Base is a base class or base @objc protocol (such
/// as AnyObject) of Derived.
@public func _arrayUpCast<Derived, Base>(a: Array<Derived>) -> Array<Base> {
return Array(a._buffer.castToBufferOf(Base.self))
}
/// Implements the semantics of `x as [Derived]` where `x` has type
/// `[Base]` and `Derived` is a verbatim-bridged trivial subtype of
/// `Base`.
@public func _arrayDownCast<Base, Derived>(a: Array<Base>) -> [Derived] {
_sanityCheck(_isBridgedVerbatimToObjectiveC(Base.self))
_sanityCheck(_isBridgedVerbatimToObjectiveC(Derived.self))
let native = a._buffer.requestNativeBuffer()
// Fast path: a native buffer that already stores elements of the
// Derived type.
if _fastPath(native) {
if _fastPath(native!.storesOnlyElementsOfType(Derived.self)) {
return Array(a._buffer.castToBufferOf(Derived.self))
}
}
// FIXME: Make these checks deferred.
let result: [Derived]? = _arrayDownCastConditional(a)
_precondition(result, "array cannot be downcast to array of derived")
return result!
}
/// Implements the semantics of `x as? [Derived]` where `x` has type
/// `[Base]` and `Derived` is a verbatim-bridged trivial subtype of
/// `Base`.
///
/// Returns an Array<Derived> containing the same elements as a in
/// O(1) iff a's buffer elements are dynamically known to have
/// type Derived or a type derived from Derived.
@public func _arrayDownCastConditional<Base, Derived>(
a: Array<Base>
) -> [Derived]? {
_sanityCheck(_isBridgedVerbatimToObjectiveC(Base.self))
_sanityCheck(_isBridgedVerbatimToObjectiveC(Derived.self))
if _fastPath(!a.isEmpty) {
let native = a._buffer.requestNativeBuffer()
if _fastPath(native) {
if native!.storesOnlyElementsOfType(Derived.self) {
return Array(a._buffer.castToBufferOf(Derived.self))
}
return nil
}
// slow path: we store an NSArray
// We can skip the check if Derived happens to be AnyObject
if !(AnyObject.self is Derived.Type) {
for element in a {
// FIXME: reinterpretCast works around <rdar://problem/16953026>
if !(reinterpretCast(element) as AnyObject is Derived) {
return nil
}
}
}
return Array(a._buffer.castToBufferOf(Derived.self))
}
return []
}
/// Convert a to its corresponding bridged array type.
/// Precondition: T is bridged non-verbatim to objective C
/// O(N), because each element must be bridged separately.
@public func _arrayBridgeToObjectiveC<BridgesToDerived, Base>(
source: Array<BridgesToDerived>
) -> Array<Base> {
_sanityCheck(_isBridgedVerbatimToObjectiveC(Base.self))
_sanityCheck(!_isBridgedVerbatimToObjectiveC(BridgesToDerived.self))
var buf = ContiguousArrayBuffer<Base>(count: source.count, minimumCapacity: 0)
var p = buf._unsafeElementStorage
for value in source {
let bridged: AnyObject? = _bridgeToObjectiveC(value)
_precondition(bridged, "array element cannot be bridged to Objective-C")
p++.initialize(reinterpretCast(bridged!))
}
return Array(ArrayBuffer(buf))
}
/// Try to convert the source array of objects to an array of values
/// produced by bridging the objects from Objective-C to \c
/// BridgesToDerived.
///
/// Precondition: Base is a class type.
/// Precondition: BridgesToDerived is bridged non-verbatim to Objective-C.
/// O(n), because each element must be bridged separately.
@public func _arrayBridgeFromObjectiveC<Base, BridgesToDerived>(
source: Array<Base>
) -> Array<BridgesToDerived> {
let result: Array<BridgesToDerived>?
= _arrayBridgeFromObjectiveCConditional(source);
_precondition(result, "array cannot be bridged from Objective-C")
return result!
}
/// Try to convert the source array of objects to an array of values
/// produced by bridging the objects from Objective-C to \c
/// BridgesToDerived.
///
/// Precondition: Base is a class type.
/// Precondition: BridgesToDerived is bridged non-verbatim to Objective-C.
/// O(n), because each element must be bridged separately.
@public func _arrayBridgeFromObjectiveCConditional<Base, BridgesToDerived>(
source: Array<Base>
) -> Array<BridgesToDerived>? {
_sanityCheck(_isBridgedVerbatimToObjectiveC(Base.self))
_sanityCheck(!_isBridgedVerbatimToObjectiveC(BridgesToDerived.self))
var buf = ContiguousArrayBuffer<BridgesToDerived>(count: source.count,
minimumCapacity: 0)
var p = buf._unsafeElementStorage
ElementwiseBridging:
do {
for object: Base in source {
let value = Swift._bridgeFromObjectiveCConditional(
reinterpretCast(object), BridgesToDerived.self)
if _slowPath(!value) {
break ElementwiseBridging
}
p++.initialize(value!)
}
return Array(ArrayBuffer(buf))
}
while false
// Don't destroy anything we never created.
buf.count = p - buf._unsafeElementStorage
// Report failure
return nil
}
// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End: