mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
- 1..3 now means 1,2 - 1...3 now means 1,2,3 Implements <rdar://problem/16839891> Swift SVN r18066
720 lines
20 KiB
Swift
720 lines
20 KiB
Swift
%# -*- mode: swift -*-
|
|
//===--- Arrays.swift.gyb - NativeArray, Array, and Slice -----------------===//
|
|
//
|
|
// 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.
|
|
//
|
|
// - NativeArray<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 NativeArray<T> when T is not an ObjC type.
|
|
// Otherwise, it may use an NSArray bridged from Cocoa for storage
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for Self in ['NativeArray', 'Slice', 'Array']:
|
|
struct ${Self}<T> : MutableCollection, Sliceable {
|
|
typealias Element = T
|
|
var startIndex: Int {
|
|
return 0
|
|
}
|
|
|
|
var endIndex: Int {
|
|
return buffer.count
|
|
}
|
|
|
|
subscript(index: Int) -> Element {
|
|
get {
|
|
assert(index < count, "Array index out of range")
|
|
return buffer[index]
|
|
}
|
|
nonmutating set {
|
|
assert(index < count, "Array index out of range")
|
|
buffer[index] = newValue
|
|
}
|
|
}
|
|
|
|
func generate() -> IndexingGenerator<${Self}> {
|
|
return IndexingGenerator(self)
|
|
}
|
|
|
|
typealias SliceType = Slice<T>
|
|
subscript(subRange: Range<Int>) -> SliceType {
|
|
get {
|
|
return Slice(buffer[subRange])
|
|
}
|
|
set {
|
|
if self[subRange] !== newValue {
|
|
replace(&self, subRange, newValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
//===--- private --------------------------------------------------------===//
|
|
typealias Buffer = ${'Array' if Self.startswith('New') else Self}Buffer<T>
|
|
|
|
init(_ buffer: Buffer) {
|
|
self.buffer = buffer
|
|
}
|
|
|
|
var buffer: Buffer
|
|
}
|
|
|
|
extension ${Self} : ArrayLiteralConvertible {
|
|
static func convertFromArrayLiteral(elements: Element...) -> ${Self} {
|
|
return ${Self}(_extractOrCopyToNativeArrayBuffer(elements.buffer))
|
|
}
|
|
}
|
|
|
|
extension ${Self} {
|
|
func asCocoaArray() -> CocoaArray {
|
|
return buffer.asCocoaArray()
|
|
}
|
|
}
|
|
|
|
extension ${Self} : ArrayType {
|
|
/// Construct an empty ${Self}
|
|
init() {
|
|
buffer = Buffer()
|
|
}
|
|
|
|
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 value
|
|
init(count: Int, value: T) {
|
|
assert(count >= 0)
|
|
buffer = Buffer()
|
|
reserve(count)
|
|
var p = buffer.elementStorage
|
|
for _ in 0..count {
|
|
p++.initialize(value)
|
|
}
|
|
buffer.count = count
|
|
}
|
|
|
|
/// How many elements the ${Self} stores
|
|
var count: Int {
|
|
return buffer.count
|
|
}
|
|
|
|
/// How many elements the ${Self} can store without reallocation
|
|
var capacity: Int {
|
|
return buffer.capacity
|
|
}
|
|
|
|
/// true if and only if the ${Self} is empty
|
|
var isEmpty: Bool {
|
|
return count == 0
|
|
}
|
|
|
|
/// An object that guarantees the lifetime of this array's elements
|
|
var owner: AnyObject? {
|
|
return buffer.owner
|
|
}
|
|
|
|
/// If the elements are stored contiguously, a pointer to the first
|
|
/// element. Otherwise, nil.
|
|
var elementStorage: UnsafePointer<Element> {
|
|
return buffer.elementStorage
|
|
}
|
|
|
|
//===--- basic mutations ------------------------------------------------===//
|
|
|
|
|
|
/// Returns a new array that does not share the underlying storage with this
|
|
/// array.
|
|
/// Complexity: O(N)
|
|
func copy() -> ${Self} {
|
|
var result = self
|
|
result.reserve(0)
|
|
return result
|
|
}
|
|
|
|
/// Ensure the uniqueness of the array.
|
|
/// Complexity: O(N)
|
|
mutating func unshare() {
|
|
reserve(0)
|
|
}
|
|
|
|
/// Ensure the array has enough mutable contiguous storage to store
|
|
/// newCapacity elements in. Note: does not affect count.
|
|
/// Complexity: O(N)
|
|
mutating func reserve(newCapacity: Int) {
|
|
if !buffer.requestUniqueMutableBuffer(newCapacity) {
|
|
var newBuffer = NativeArrayBuffer<T>(count: count, minimumCapacity: newCapacity)
|
|
buffer._uninitializedCopy(0..count, target: newBuffer.elementStorage)
|
|
buffer = Buffer(newBuffer)
|
|
}
|
|
assert(capacity >= newCapacity)
|
|
}
|
|
|
|
/// Append newElement to the ${Self} in O(1) (amortized)
|
|
mutating func append(newElement: T) {
|
|
_arrayAppend(&buffer, newElement)
|
|
}
|
|
|
|
/// Remove an element from the end of the ${Self} in O(1).
|
|
/// Requires: count > 0
|
|
mutating func popLast() -> T {
|
|
assert(count > 0, "can't pop from an empty ${Self}")
|
|
let c = count
|
|
let result = self[c - 1]
|
|
replace(&self, (c - 1)..c, EmptyCollection())
|
|
return result
|
|
}
|
|
|
|
/// Insert an element at the given index in O(N). Requires: atIndex
|
|
/// <= count
|
|
mutating func insert(atIndex: Int, newElement: T) {
|
|
replace(&self, atIndex..atIndex, CollectionOfOne(newElement))
|
|
}
|
|
|
|
/// Remove the element at the given index. Worst case complexity:
|
|
/// O(N). Requires: index < count
|
|
mutating func removeAt(index: Int) -> T {
|
|
let result = self[index]
|
|
replace(&self, index..(index + 1), EmptyCollection())
|
|
return result
|
|
}
|
|
|
|
/// Erase all the elements and release the storage
|
|
mutating func clear() {
|
|
buffer = Buffer()
|
|
}
|
|
|
|
/// Erase all the elements. If keepStorage is true, capacity will not change
|
|
mutating func clear(keepStorage: Bool) {
|
|
if !keepStorage {
|
|
clear()
|
|
}
|
|
else {
|
|
replace(&self, indices(self), EmptyCollection())
|
|
}
|
|
}
|
|
|
|
//===--- algorithms -----------------------------------------------------===//
|
|
func reduce<U>(initial: U, combine: (U, T)->U) -> U {
|
|
return Swift.reduce(self, initial, combine)
|
|
}
|
|
|
|
mutating func sort(isOrderedBefore: (T, T)->Bool) {
|
|
quickSort(&self, indices(self), isOrderedBefore)
|
|
}
|
|
|
|
func map<U>(transform: (T)->U) -> ${Self}<U> {
|
|
var result = ${Self}<U>()
|
|
|
|
let count = self.count
|
|
result.reserve(count)
|
|
var p = result.buffer.elementStorage
|
|
for i in 0..count {
|
|
p++.initialize( transform(self[i]) )
|
|
}
|
|
result.buffer.count = count
|
|
|
|
return result
|
|
}
|
|
}
|
|
|
|
extension ${Self} : Reflectable {
|
|
func getMirror() -> Mirror {
|
|
return _ArrayTypeMirror(self)
|
|
}
|
|
}
|
|
|
|
extension ${Self} : Printable {
|
|
var description: String {
|
|
var result = "["
|
|
var first = true
|
|
for item in self {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
result += ", "
|
|
}
|
|
print(item, &result)
|
|
}
|
|
result += "]"
|
|
return result
|
|
}
|
|
}
|
|
|
|
extension ${Self} {
|
|
@transparent
|
|
func _cPointerArgs() -> (AnyObject?, Builtin.RawPointer) {
|
|
let p = elementStorage
|
|
if _fastPath(p != nil || count == 0) {
|
|
return (owner, p.value)
|
|
}
|
|
let n = _extractOrCopyToNativeArrayBuffer(self.buffer)
|
|
return (n.owner, n.elementStorage.value)
|
|
}
|
|
|
|
// Conversion to C pointer arguments
|
|
@transparent @conversion
|
|
func __conversion() -> CConstPointer<T> {
|
|
return CConstPointer(_cPointerArgs())
|
|
}
|
|
|
|
@transparent @conversion
|
|
func __conversion() -> CConstVoidPointer {
|
|
return CConstVoidPointer(_cPointerArgs())
|
|
}
|
|
}
|
|
|
|
%if Self != 'Array': # // Array does not necessarily have contiguous storage
|
|
extension ${Self} {
|
|
func withUnsafePointerToElements<R>(body: (UnsafePointer<T>)->R) -> R {
|
|
return buffer.withUnsafePointerToElements(body)
|
|
}
|
|
}
|
|
%end
|
|
|
|
%end
|
|
|
|
extension Array {
|
|
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
|
|
}
|
|
|
|
func replace<
|
|
A: ArrayType, C: Collection
|
|
where C.GeneratorType.Element == A.Buffer.Element, A.IndexType == Int
|
|
>(
|
|
inout target: A, subRange: Range<Int>, newValues: C
|
|
) {
|
|
assert(subRange.startIndex >= 0)
|
|
assert(subRange.endIndex >= subRange.startIndex)
|
|
assert(subRange.endIndex <= target.endIndex)
|
|
|
|
let oldCount = target.count
|
|
let eraseCount = countElements(subRange)
|
|
let insertCount = numericCast(countElements(newValues)) as Int
|
|
var newBuffer = _demandUniqueMutableBuffer(
|
|
&target.buffer, oldCount + insertCount - eraseCount)
|
|
|
|
if _fastPath(!newBuffer) {
|
|
let growth = insertCount - eraseCount
|
|
|
|
let elements = target.buffer.elementStorage
|
|
assert(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)
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
_arrayOutOfPlaceUpdate(
|
|
&target.buffer, &newBuffer,
|
|
subRange.startIndex, insertCount,
|
|
_InitializeMemoryFromCollection(newValues)
|
|
)
|
|
}
|
|
}
|
|
|
|
@assignment
|
|
func += <
|
|
A: ArrayType, S: Sequence
|
|
where S.GeneratorType.Element == A.Buffer.Element
|
|
>(inout lhs: A, rhs: S) {
|
|
_arrayAppendSequence(&lhs.buffer, rhs)
|
|
}
|
|
|
|
@assignment
|
|
func += <
|
|
A: ArrayType, C: Collection
|
|
where C.GeneratorType.Element == A.Buffer.Element
|
|
>(inout lhs: A, 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.reserve(newCount > capacity ? max(newCount, capacity * 2) : newCount)
|
|
var p = lhs.buffer.elementStorage + oldCount
|
|
for x in rhs {
|
|
(p++).initialize(x)
|
|
}
|
|
lhs.buffer.count = newCount
|
|
}
|
|
|
|
@assignment
|
|
func += <
|
|
A: ArrayType
|
|
>(inout lhs: A, rhs: A.Buffer.Element) {
|
|
lhs += CollectionOfOne(rhs)
|
|
}
|
|
|
|
//===--- generic helpers --------------------------------------------------===//
|
|
/// Ensure there's a NativeArrayBuffer 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 _demandUniqueMutableBuffer<Buffer: ArrayBufferType>(
|
|
inout source: Buffer, newCount: Int, minimumCapacity: Int = 0)
|
|
-> NativeArrayBuffer<Buffer.Element>? {
|
|
|
|
assert(newCount >= 0)
|
|
|
|
let requiredCapacity = max(newCount, minimumCapacity)
|
|
|
|
if let b = source.requestUniqueMutableBuffer(requiredCapacity) {
|
|
source.count = newCount
|
|
return nil
|
|
}
|
|
|
|
let newCapacity = max(
|
|
requiredCapacity,
|
|
newCount > source.capacity ? source.capacity * 2 : source.capacity)
|
|
|
|
return NativeArrayBuffer(count: newCount, minimumCapacity: newCapacity)
|
|
}
|
|
|
|
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: NativeArrayBuffer<Buffer.Element>?,
|
|
headCount: Int, // Count of initial source elements to copy/move
|
|
newCount: Int, // Count of new elements to insert
|
|
initializeNewElements: Initializer
|
|
) {
|
|
assert(headCount >= 0)
|
|
assert(newCount >= 0)
|
|
|
|
// Count of trailing source elements to copy/move
|
|
let tailCount = dest!.count - headCount - newCount
|
|
assert(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.requestUniqueMutableBuffer(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(sourceEnd - backingEnd)
|
|
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 = _demandUniqueMutableBuffer(&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, newCapacity: Int
|
|
) {
|
|
let oldCount = buffer.count
|
|
var newBuffer = _demandUniqueMutableBuffer(&buffer, oldCount, minimumCapacity: newCapacity)
|
|
if _slowPath(newBuffer) {
|
|
_arrayOutOfPlaceUpdate(&buffer, &newBuffer, oldCount, 1, IgnorePointer())
|
|
}
|
|
}
|
|
|
|
func _extractOrCopyToNativeArrayBuffer<
|
|
Buffer: ArrayBufferType where Buffer.GeneratorType.Element == Buffer.Element
|
|
>(source: Buffer)
|
|
-> NativeArrayBuffer<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, capacity * 2)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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)
|
|
}
|
|
|
|
/// 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.
|
|
func _arrayUpCast<Derived, Base>(a: Array<Derived>) -> Array<Base> {
|
|
return Array(ArrayBuffer<Base>(castFrom: a.buffer, castKind: .Up))
|
|
}
|
|
|
|
/// Returns an Array<Derived> containing the same elements as a in
|
|
/// O(1). Requires: a is known to contain only elements of type
|
|
/// Derived, and Base is a base class or base @objc protocol (such as
|
|
/// AnyObject) of Derived.
|
|
func _arrayDownCast<Base, Derived>(a: Array<Base>) -> Array<Derived> {
|
|
return Array(ArrayBuffer<Derived>(castFrom: a.buffer, castKind: .Down))
|
|
}
|
|
|
|
/// Return the most-derived element type known to be stored in a's
|
|
/// buffer. O(1)
|
|
func _arrayElementType<T>(a: Array<T>) -> Any.Type {
|
|
return a.buffer.dynamicElementType
|
|
}
|
|
|
|
/// Implement the semantics of (a as Array<T>), where a is an
|
|
/// AnyObject[].
|
|
func _arrayBridgedDownCast<T>(a: AnyObject[]) -> (T[])? {
|
|
// If T is not bridged, conversion fails in O(1), yielding nil
|
|
if !isBridgedToObjectiveC(T.self) {
|
|
return nil
|
|
}
|
|
|
|
// Otherwise, the semantics are the same as bridging from Objective-C
|
|
return _arrayBridgeFromObjectiveC(a)
|
|
}
|
|
|
|
/// Conversion used when Objective-C APIs using NSArray are expressed
|
|
/// in Swift as T[], where T != AnyObject.
|
|
func _arrayBridgeFromObjectiveC<T>(a: AnyObject[]) -> T[] {
|
|
// If the NSArray was originally created as a Swift ArrayType<U>,
|
|
// conversion succeeds in O(1) if U is T or a subclass thereof. No
|
|
// further dynamic type checks are required.
|
|
if let n = a.buffer.requestNativeBuffer() {
|
|
if let n2 = n.asBufferOf(T.self) {
|
|
return Array(ArrayBuffer(n2))
|
|
}
|
|
}
|
|
|
|
// Otherwise, if T is a class or existential type, conversion
|
|
// succeeds in O(1), but type-checking of elements is deferred and
|
|
// on-demand. The result of subscripting is the result of bridging
|
|
// back the corresponding stored NSArray element to T. Failure to
|
|
// bridge back is a fatal error detected at runtime.
|
|
return Array(ArrayBuffer(castFrom: a.buffer, castKind: .DeferredDown))
|
|
}
|
|
|
|
/// 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.
|
|
func _arrayCheckedDownCast<Base, Derived>(a: Array<Base>) -> Array<Derived>? {
|
|
return _arrayDownCast(a)
|
|
}
|
|
|
|
/// Convert a to its corresponding bridged array type.
|
|
/// Precondition: T is bridged to objective C
|
|
/// O(1) if T is bridged verbatim, O(N) otherwise
|
|
func _arrayBridgeToObjectiveC<T: _BridgedToObjectiveC>(
|
|
a: Array<T>
|
|
) -> Array<T.ObjectiveCType> {
|
|
return Array(ArrayBuffer(a.asCocoaArray()))
|
|
}
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|