//===--- ArrayBridge.swift - Array <=> NSArray bridging ----------------===// // // 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 // //===----------------------------------------------------------------------===// import SwiftShims // The empty array prototype. We use the same object for all empty // [Native]Arrays. let emptyNSSwiftArray : NSSwiftArray = reinterpretCast(NativeArrayBuffer(0,0)) // The class that implements the storage for a NativeArray @final class NativeArrayStorage : NSSwiftArray { typealias Masquerade = HeapBufferStorage deinit { let b = HeapBuffer( reinterpretCast(self) as Masquerade ) for i in 0...b.value.count { (b.elementStorage + i).destroy() } b._value.destroy() } @override func _objectAtIndex(indexAndUnused: (Int, Bool)) -> AnyObject { let index = indexAndUnused.0 let b = HeapBuffer( reinterpretCast(self) as Masquerade ) return bridgeToObjectiveC((b.elementStorage + index).get())! } } struct NativeArrayBuffer : ArrayBufferType, LogicValue { /// Make a buffer with uninitialized elements. After using this /// method, you must either initialize the count elements at the /// result's .elementStorage or set the result's .count to zero. init(count: Int, minimumCapacity: Int) { base = HeapBuffer( NativeArrayStorage.self, ArrayBody(), max(count, minimumCapacity)) base.value = ArrayBody( count, base._capacity(), isBridgedVerbatimToObjectiveC(T.self)) } init(storage: NativeArrayStorage?) { base = reinterpretCast(storage) } /// Append x to this buffer, growing it by 1 element. mutating func append(x: T) { self += x } func getLogicValue() -> Bool { return base.getLogicValue() } /// If the elements are stored contiguously, a pointer to the first /// element. Otherwise, nil. var elementStorage: UnsafePointer { return base ? base.elementStorage : nil } func withUnsafePointerToElements(body: (UnsafePointer)->R) -> R { let p = base.elementStorage return withExtendedLifetime(base) { body(p) } } mutating func take() -> NativeArrayBuffer { if !base { return NativeArrayBuffer() } assert(base.isUniquelyReferenced(), "Can't \"take\" a shared array buffer") let result = self base = Base() return result } //===--- ArrayBufferType conformance ------------------------------------===// /// The type of elements stored in the buffer typealias Element = T /// create an empty buffer init() { base = HeapBuffer() } /// Adopt the storage of x init(other: NativeArrayBuffer) { self = other } mutating func requestUniqueMutableBuffer(minimumCapacity: Int) -> NativeArrayBuffer? { return isUniquelyReferenced() && capacity >= minimumCapacity ? self : nil } /// If this buffer is backed by a NativeArrayBuffer, return it. /// Otherwise, return nil. Note: the result's elementStorage may /// not match ours, if we are a SliceBuffer. func requestNativeBuffer() -> NativeArrayBuffer? { return self } /// Convert to a NativeArrayBuffer storing the same elements. func toNativeBuffer() -> NativeArrayBuffer { return self } /// Get/set the value of the ith element subscript(i: Int) -> T { get { assert(i >= 0 && i < count, "Array index out of range") return elementStorage[i] } @!mutating set { assert(i >= 0 && i < count, "Array index out of range") elementStorage[i] = newValue } } /// How many elements the buffer stores var count: Int { get { return base ? base.value.count : 0 } @!mutating set { assert(newValue >= 0) assert( newValue <= capacity, "Can't grow an array buffer past its capacity") assert(base || newValue == 0) if base { base.value.count = newValue } } } /// How many elements the buffer can store without reallocation var capacity: Int { return base ? base.value.capacity : 0 } /// Copy the given subRange of this buffer into uninitialized memory /// starting at target. Return a pointer past-the-end of the /// just-initialized memory. func _uninitializedCopy( subRange: Range, target: UnsafePointer ) -> UnsafePointer { assert(subRange.startIndex >= 0) assert(subRange.endIndex >= subRange.startIndex) assert(subRange.endIndex <= count) var dst = target var src = elementStorage + subRange.startIndex for i in subRange { dst++.initialize(src++.get()) } return dst } /// Return a SliceBuffer containing the given subRange of values /// from this buffer. subscript(subRange: Range) -> SliceBuffer { return SliceBuffer( base.storage, elementStorage + subRange.startIndex, subRange.endIndex - subRange.startIndex, true) } /// Return true iff this buffer's storage is uniquely-referenced. /// NOTE: this does not mean the buffer is mutable. Other factors /// may need to be considered, such as whether the buffer could be /// some immutable Cocoa container. mutating func isUniquelyReferenced() -> Bool { return base.isUniquelyReferenced() } /// Returns true iff this buffer is mutable. NOTE: a true result /// does not mean the buffer is uniquely-referenced. func isMutable() -> Bool { return true } /// Convert to an NSArray in O(1). /// Precondition: isBridgedToObjectiveC(Element.self) func asCocoaArray() -> CocoaArray { assert( isBridgedToObjectiveC(T.self), "Array element type is not bridged to ObjectiveC") return count > 0 ? reinterpretCast(base.storage) : emptyNSSwiftArray } /// An object that keeps the elements stored in this buffer alive var owner: AnyObject? { return storage } //===--- private --------------------------------------------------------===// var storage: NativeArrayStorage? { return reinterpretCast(base.storage) } typealias Base = HeapBuffer var base: Base } /// Append the elements of rhs to lhs func += < T, C: Collection where C._Element == T > ( inout lhs: NativeArrayBuffer, rhs: C ) { let oldCount = lhs.count let newCount = oldCount + numericCast(countElements(rhs)) if _fastPath(newCount <= lhs.capacity) { lhs.count = newCount (lhs.elementStorage + oldCount).initializeFrom(rhs) } else { let newLHS = NativeArrayBuffer(newCount, lhs.capacity * 2) if lhs.base { newLHS.elementStorage.moveInitializeFrom(lhs.elementStorage, oldCount) lhs.base.value.count = 0 } lhs.base = newLHS.base (lhs.base.elementStorage + oldCount).initializeFrom(rhs) } } /// Append rhs to lhs func += (inout lhs: NativeArrayBuffer, rhs: T) { lhs += CollectionOfOne(rhs) } func === ( lhs: NativeArrayBuffer, rhs: NativeArrayBuffer ) -> Bool { return lhs.base == rhs.base } func !== ( lhs: NativeArrayBuffer, rhs: NativeArrayBuffer ) -> Bool { return lhs.base != rhs.base } extension NativeArrayBuffer : Collection { var startIndex: Int { return 0 } var endIndex: Int { return count } func generate() -> IndexingGenerator { return IndexingGenerator(self) } } func ~> < S: _Sequence_ >( source: S, _: (_AsNativeArrayBuffer,()) ) -> NativeArrayBuffer { var result = NativeArrayBuffer() // Using GeneratorSequence here essentially promotes the sequence to // a Sequence from _Sequence_ so we can iterate the elements for x in GeneratorSequence(source.generate()) { result.append(x) } return result.take() } func ~> < // FIXME: prevents this from being ": // Collection" C: protocol<_Collection,_Sequence_> >( source: C, _:(_AsNativeArrayBuffer, ()) ) -> NativeArrayBuffer { return _collectionAsNativeArrayBuffer(source) } func _collectionAsNativeArrayBuffer>( source: C ) -> NativeArrayBuffer { let count = countElements(source) if count == 0 { return NativeArrayBuffer() } var result = NativeArrayBuffer( count: numericCast(count), minimumCapacity: 0 ) var p = result.elementStorage for x in GeneratorSequence(source.generate()) { (p++).initialize(x) } return result } protocol _ArrayType : Collection { var count: Int {get} typealias Buffer : ArrayBufferType var buffer: Buffer {get} } /* // FIXME: Disabled pending func ~> < A: _ArrayType where A.Buffer.Element == A.Buffer.GeneratorType.Element >( source: A, _:(_AsNativeArrayBuffer,()) ) -> NativeArrayBuffer { return _toNativeArrayBuffer(source.buffer) } */ func asNativeArrayBuffer(source: S) -> NativeArrayBuffer { return source~>_asNativeArrayBuffer() }