Created
September 9, 2015 22:07
-
-
Save josephlord/40676938db05c693299a to your computer and use it in GitHub Desktop.
As presented at iOSDevUK briefly during the Swift Perfromance talk. See blog.human-friendly.com for details. This is exploratory and example code, not yet performance tested. I plan to blog more about the array type later.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| final class Bar { | |
| var i = 0 | |
| } | |
| struct Foo { | |
| var i:Int { | |
| get { | |
| return bar.i | |
| } | |
| set { | |
| if !isUniquelyReferencedNonObjC(&bar) { | |
| bar = Bar() | |
| } | |
| bar.i = newValue | |
| } | |
| } | |
| private var bar = Bar() | |
| } | |
| var f = Foo() | |
| isUniquelyReferencedNonObjC(&f.bar) | |
| var f2 = f | |
| isUniquelyReferencedNonObjC(&f.bar) | |
| f.i = 5 | |
| isUniquelyReferencedNonObjC(&f.bar) | |
| f.i | |
| f2.i | |
| extension Foo : CustomDebugStringConvertible { | |
| var debugDescription:String { | |
| get { | |
| return "Foo: \(i)" | |
| } | |
| } | |
| } | |
| var x = Foo() | |
| x.i = 4 | |
| var y = x | |
| y.i = 5 | |
| x | |
| // Slightly more realistic example but more a demo of a simplified array than something worth using in place of normal arrays | |
| private final class FLArrayInternal<Element> : NonObjectiveCBase { | |
| let arrayCount:Int | |
| /** | |
| Only allocates does not initialize | |
| :param: count Array size | |
| */ | |
| private init(count:Int) { | |
| arrayCount = count | |
| buffer = UnsafeMutablePointer.alloc(count) | |
| } | |
| convenience init(count:Int, unsafePointer:UnsafeMutablePointer<Element>) { | |
| self.init(count:count) | |
| buffer.initializeFrom(unsafePointer, count: count) | |
| } | |
| private let buffer:UnsafeMutablePointer<Element> | |
| deinit { | |
| buffer.destroy(arrayCount) | |
| buffer.dealloc(arrayCount) | |
| } | |
| } | |
| struct FLArray<E> { | |
| typealias Element = E | |
| let count:Int | |
| private var internalArray:FLArrayInternal<Element> | |
| } | |
| extension FLArray : ArrayLiteralConvertible { | |
| init(array:[Element]) { | |
| count = array.count | |
| var arr = array | |
| internalArray = arr.withUnsafeMutableBufferPointer { (inout buf:UnsafeMutableBufferPointer<E>) in | |
| return FLArrayInternal<E>(count: array.count, unsafePointer: buf.baseAddress) | |
| } | |
| } | |
| init(count:Int, repeatedValue:Element) { | |
| self.init(uninitializedSize:count) | |
| for i in 0..<count { | |
| internalArray.buffer[i] = repeatedValue | |
| } | |
| } | |
| init(arrayLiteral elements: FLArray.Element...) { | |
| self.init(array:elements) | |
| } | |
| init<C : CollectionType where C.Generator.Element == Element>(collection: C) { | |
| self.init(count: Int(collection.count.toIntMax()), sequence:collection) | |
| } | |
| init<S : SequenceType where S.Generator.Element == Element>(count:Int,sequence: S) { | |
| self.init(uninitializedSize:count) | |
| for (i,e) in sequence.enumerate() { | |
| assert(i < count) | |
| internalArray.buffer[i] = e | |
| } | |
| } | |
| private init(uninitializedSize:Int) { | |
| self.count = uninitializedSize | |
| internalArray = FLArrayInternal(count: uninitializedSize) | |
| } | |
| } | |
| extension FLArray : SequenceType { | |
| typealias Generator = FLArrayGenerator<Element> | |
| func underestimateCount()->Int { | |
| return count | |
| } | |
| func generate()->FLArrayGenerator<Element> { | |
| return FLArrayGenerator(array: self) | |
| } | |
| } | |
| class FLArrayGenerator<Element> : GeneratorType { | |
| private let count:Int | |
| private var index:Int = 0 | |
| // Interesting decision point. By keeping the struct we don't update but will cause a copy made by mutations, the generator will remain valid | |
| // We could just take a reference to the underlying internal class and the generator would return current values for array elements | |
| private let array:FLArray<Element> | |
| func next() -> Element? { | |
| guard index < count else { return nil } | |
| return array.internalArray.buffer[index++] | |
| } | |
| init(array:FLArray<Element>) { | |
| self.array = array | |
| count = array.count | |
| } | |
| } | |
| // Mutation | |
| extension FLArray { | |
| private mutating func prepareForMutation() { | |
| if !isUniquelyReferencedNonObjC(&internalArray) { | |
| internalArray = FLArrayInternal<Element>(count: count, unsafePointer: internalArray.buffer) | |
| } | |
| } | |
| subscript(position: Int)->Element { | |
| get { | |
| return internalArray.buffer[position] | |
| } | |
| set { | |
| prepareForMutation() | |
| internalArray.buffer[position] = newValue | |
| } | |
| } | |
| } | |
| extension FLArray { | |
| func map<T>(@noescape transform: (Element) throws -> T) rethrows -> FLArray<T> { | |
| let ret_array = FLArray<T>(uninitializedSize: self.count) | |
| let buffer = ret_array.internalArray.buffer | |
| for (i,v) in enumerate() { | |
| buffer[i] = try transform(v) | |
| } | |
| return ret_array | |
| } | |
| } | |
| /*extension FLArray : CollectionType { | |
| }*/ | |
| extension FLArray : CustomDebugStringConvertible { | |
| var debugDescription:String { | |
| let strings:FLArray<String> = self.map { "\($0)" } | |
| return "[" + strings.joinWithSeparator(",") + "]" | |
| } | |
| } | |
| public func testArrayPrint() { | |
| let x:FLArray = [1,2,sizeof(FLArray<Double>),strideof(FLArray<Double>)] | |
| print(x) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment