mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Revert "Restrict the array-bridged conversion to non-verbatim bridging." Revert "[stdlib] Fix T[].bridgeFromObjectiveC" Revert "[stdlib] Fix T[].bridgeFromObjectiveC" Revert "[stdlib] Move _arrayBridgedDownCast to Foundation" Revert "Replace "can" with "cannot" in a message." Revert "Implement support for non-verbatim T[] -> AnyObject[] upcasts." This reverts commit r18291. This reverts commit r18290. This reverts commit r18288. This reverts commit r18287. This reverts commit r18286. This reverts commit r18293. This reverts commit r18283. Sorry for the number of reverts, but I needed to do this many to get a clean revert to r18283. Swift SVN r18296
801 lines
22 KiB
Swift
801 lines
22 KiB
Swift
%# -*- mode: swift -*-
|
|
//===--- Arrays.swift.gyb - ContiguousArray, 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.
|
|
//
|
|
// - 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for Self in ['ContiguousArray', '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()
|
|
reserveCapacity(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.reserveCapacity(0)
|
|
return result
|
|
}
|
|
|
|
/// Ensure the uniqueness of the array.
|
|
/// Complexity: O(N)
|
|
mutating func unshare() {
|
|
reserveCapacity(0)
|
|
}
|
|
|
|
/// Ensure the array has enough mutable contiguous storage to store
|
|
/// newCapacity elements in. Note: does not affect count.
|
|
/// Complexity: O(N)
|
|
mutating func reserveCapacity(newCapacity: Int) {
|
|
if !buffer.requestUniqueMutableBuffer(newCapacity) {
|
|
var newBuffer = ContiguousArrayBuffer<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.reserveCapacity(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, 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
|
|
}
|
|
|
|
var description: String {
|
|
return _makeDescription(isDebug: false)
|
|
}
|
|
|
|
var debugDescription: String {
|
|
return _makeDescription(isDebug: true)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
/// This function "seeds" the ArrayLiteralConvertible protocol
|
|
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.reserveCapacity(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 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 _demandUniqueMutableBuffer<Buffer: ArrayBufferType>(
|
|
inout source: Buffer, newCount: Int, minimumCapacity: Int = 0)
|
|
-> ContiguousArrayBuffer<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 ContiguousArrayBuffer(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: ContiguousArrayBuffer<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)
|
|
-> 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, 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)
|
|
}
|
|
|
|
% for Self in ['ContiguousArray', 'Slice', 'Array']:
|
|
// 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.
|
|
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.
|
|
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.
|
|
func _arrayUpCast<Derived, Base>(a: Array<Derived>) -> Array<Base> {
|
|
return Array(a.buffer.castToBufferOf(Base.self))
|
|
}
|
|
|
|
// ** Currently used to implement
|
|
// ** Array<T>.bridgeFromObjectiveC(x: NSArray) -> Array<T>?
|
|
|
|
/// 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
|
|
}
|
|
|
|
// 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 r = _arrayCheckedDownCast(a) as T[]? {
|
|
return r
|
|
}
|
|
|
|
// 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.
|
|
if _isClassOrObjCExistential(T.self) {
|
|
return Array(a.buffer.castToBufferOf(T.self))
|
|
}
|
|
|
|
// Otherwise, conversion is O(N), and succeeds iff every element
|
|
// bridges back to T
|
|
let n = ContiguousArrayBuffer<T>(count: a.count, minimumCapacity: 0)
|
|
for (i, srcElement: AnyObject) in enumerate(a) {
|
|
if let dstElement = bridgeFromObjectiveC(srcElement, T.self) {
|
|
(n._unsafeElementStorage + i).initialize(dstElement)
|
|
}
|
|
else {
|
|
n.count = 0
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return Array(ArrayBuffer(n))
|
|
}
|
|
|
|
/// 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.
|
|
func _arrayCheckedDownCast<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 {
|
|
if !(element is Derived) {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
return Array(a.buffer.castToBufferOf(Derived.self))
|
|
}
|
|
return []
|
|
}
|
|
|
|
/// 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> {
|
|
// FIXME: This is completely unused by any test! It is, however,
|
|
// expected to be found by the compiler. We need to find out what
|
|
// it is.
|
|
_fatalError("This is never used!'")
|
|
return Array(ArrayBuffer(a.asCocoaArray()))
|
|
}
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|