mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
...unless the type has less accessibility than the protocol, in which case they must be as accessible as the type. This restriction applies even with access control checking disabled, but shouldn't affect any decls not already marked with access control modifiers. Swift SVN r19382
969 lines
28 KiB
Swift
969 lines
28 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 --------------------------------------------------------===//
|
|
|
|
@noinline
|
|
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)
|
|
}
|
|
|
|
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 value
|
|
@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) {
|
|
Swift.sort(&self, isOrderedBefore)
|
|
}
|
|
|
|
@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 {
|
|
_arrayReserve(&_buffer, 0)
|
|
var a = UnsafeMutableArray(start: _buffer.elementStorage, length: count)
|
|
let ret = body(&a)
|
|
_fixLifetime(_buffer)
|
|
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
|
|
}
|
|
|
|
@noinline
|
|
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:
|