%# -*- mode: swift -*- //===--- Array.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 // //===----------------------------------------------------------------------===// // RUN-DISABLED: %target-run-simple-swift | FileCheck %s // RUN: rm -rf %t && mkdir -p %t && %S/../../utils/gyb %s -o %t/NewArray.swift // RUN: %S/../../utils/line-directive %t/NewArray.swift -- %target-build-swift -module-cache-path %t/clang-module-cache %t/NewArray.swift -o %t/a.out -Xfrontend -disable-access-control // RUN: %target-run %t/a.out | %S/../../utils/line-directive %t/NewArray.swift -- FileCheck %t/NewArray.swift var xCount = 0 var xSerial = 0 // Instead of testing with Int elements, we use this wrapper class // that can help us track allocations and find issues with object // lifetime inside Array implementations. class X : ForwardIndexType, Comparable, Printable, IntegerLiteralConvertible { init(_ value: Int) { ++xCount serial = ++xSerial self.value = value } deinit { assert(serial > 0, "double destruction!") --xCount serial = -serial } var description: String { assert(serial > 0, "dead X!") return value.description } func successor() -> X { return X(self.value.successor()) } class func convertFromIntegerLiteral(value: Int) -> X { return X(value) } var value: Int var serial: Int } func == (x: X, y: X) -> Bool { return x.value == y.value } func < (x: X, y: X) -> Bool { return x.value < y.value } //===----------------------------------------------------------------------===// func printSequence(x: T) { print("[") var prefix = "" for a in x { print(prefix) print(a) prefix = ", " } println("]") } func bufferID(x: T) -> Int { return reinterpretCast(x._buffer.baseAddress) as Int } func checkReallocation( x: T, lastBuffer: Int, reallocationExpected: Bool ) -> Int { let currentBuffer = bufferID(x) if (currentBuffer != lastBuffer) != reallocationExpected { let message = reallocationExpected ? "lack of" : "" println("unexpected \(message) reallocation") } return currentBuffer } func checkEqual< S1 : SequenceType, S2 : SequenceType where S1.Generator.Element == S2.Generator.Element, S1.Generator.Element : Equatable >(a1: S1, a2: S2, expected: Bool) { if equal(a1, a2) != expected { let un = expected ? "un" : "" println("unexpectedly \(un)equal sequences!") } } func test< T: ArrayType where T.Generator.Element == T._Buffer.Element, T._Buffer.Element == T.Element, T.Element == X, T.Index == Int >(_: T.Type, label: String) { print("test: \(label)...") var x: T = [1, 2, 3, 4, 5] checkEqual(x, 1...5, true) x.reserveCapacity(x.count + 2) checkEqual(x, 1...5, true) let bufferId0 = bufferID(x) // Append a range of integers x += 0..<2 let bufferId1 = checkReallocation(x, bufferId0, false) for i in x.count..<(x.capacity + 1) { let bufferId1a = checkReallocation(x, bufferId1, false) x.append(13) } let bufferId2 = checkReallocation(x, bufferId1, true) let y = x x[x.endIndex.predecessor()] = 17 let bufferId3 = checkReallocation(x, bufferId2, true) checkEqual(x, y, false) func checkReallocations( var a: T, growthDescription: String, growBy1: (inout _: T)->() ) -> () { var reallocations = 0 // Note: right now this test is dependent on a growth factor of 2. // It's possible that factor will change, but (cursory) testing // has shown that using 1.5, the other popular growth factor, // slows things down. for _ in a.count..<(a.capacity * 4) { let oldId = bufferID(a) growBy1(&a) if oldId != bufferID(a) { ++reallocations } } if reallocations > 3 { println( "Unexpectedly found \(reallocations) reallocations " + "of \(label) when growing via \(growthDescription)") } } checkReallocations(x, "append") { (inout x: T)->() in x.append(42) } checkReallocations(x, "+=") { (inout x: T)->() in x.append(42) } println("done.") } println("testing...") // CHECK: testing... test(ContiguousArray.self, "ContiguousArray") // CHECK-NEXT: test: ContiguousArray...done test(Array.self, "Array") // CHECK-NEXT: test: Array...done test(Slice.self, "Slice") // CHECK-NEXT: test: Slice...done func testAsArray() { println("== AsArray ==") var w: ContiguousArray = [4, 2, 1] // CHECK: == AsArray == let x = ContiguousArray(w) println(bufferID(w) == bufferID(x)) // CHECK-NEXT: false let y = Array(x) println(bufferID(x) == bufferID(y)) // CHECK-NEXT: false // Because of their indirection, arrays of classes can share // buffers. let y1 = Array(y) println(bufferID(y1) == bufferID(y)) // However, I'm not implementing that optimization yet, thus false. // CHECK-NEXT: false let z = Slice(y) println(bufferID(y) == bufferID(z)) // CHECK-NEXT: false w = ContiguousArray(z) println(bufferID(w) == bufferID(z)) // CHECK-NEXT: false } testAsArray() import Foundation func nsArrayOfStrings() -> Array { let src: ContiguousArray = ["foo", "bar", "baz"] return src.withUnsafePointerToElements { let ns = NSArray(objects: UnsafePointer($0), count: src.count) return Array(_ArrayBuffer(reinterpretCast(ns) as _CocoaArrayType)) } } func testCocoa() { println("== Cocoa ==") // CHECK: == Cocoa == var a = nsArrayOfStrings() printSequence(a) // CHECK-NEXT: [foo, bar, baz] a.append("qux") printSequence(a) // CHECK-NEXT: [foo, bar, baz, qux] a = nsArrayOfStrings() printSequence(a) // CHECK-NEXT: [foo, bar, baz] var b = a a[1] = "garply" printSequence(a) // CHECK-NEXT: [foo, garply, baz] // Mutating an element in a has no effect on b printSequence(b) // CHECK-NEXT: [foo, bar, baz] a = nsArrayOfStrings() a.insert("bag", atIndex:2) printSequence(a) // CHECK-NEXT: [foo, bar, bag, baz] a = nsArrayOfStrings() a.reserveCapacity(30) printSequence(a) // CHECK-NEXT: [foo, bar, baz] println(a.capacity >= 30) // CHECK-NEXT: true // Prove that we create contiguous storage for an opaque NSArray a.withUnsafePointerToElements { (p)->() in println(p.memory) // CHECK-NEXT: foo } } testCocoa() extension Slice { mutating func qsort(compare: (T,T)->Bool) { _quickSort(&self, indices(self), compare) } } func testSlice() { println("== Slice ==") // CHECK: == Slice == // do some tests on the shared semantics var b = ContiguousArray(X(0)..") // CHECK-NEXT: <2> println("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4] // bSlice += X(11)..( _ArrayBuffer(nsArrayOfStrings()._asCocoaArray())) printSequence(a) // CHECK-NEXT: [foo, bar, baz] var aSlice = a[1..<3] // CHECK-NEXT: [bar, baz] printSequence(aSlice) // Writing into aSlice works aSlice[0] = "buzz" // CHECK-NEXT: [buzz, baz] printSequence(aSlice) // ...and doesn't affect a printSequence(a) // CHECK-NEXT: [foo, bar, baz] // Appending to aSlice works... aSlice.append("fodder") println("<\(aSlice.count)>") // CHECK-NEXT: <3> printSequence(aSlice) // CHECK-NEXT: [buzz, baz, fodder] // And doesn't change a printSequence(a) // CHECK-NEXT: [foo, bar, baz] } testSlice() //===--- sub-range replacement --------------------------------------------===// // Size of the array on which we're going to test "replace." // testing time grows roughly as the cube of this constant let testWidth = 11 %for A in ['ContiguousArray', 'Array', 'Slice']: func testReplace(make: ()->${A}) { typealias A = ${A} // First make an independent copy of the array that we can use for // comparison later. var source = ContiguousArray() for x in make() { source.append(x) } for i in indices(source) { for j in i..${A} = { var x = ${A}() // make sure some - but not all - replacements will have to grow the buffer x.reserveCapacity(testWidth * 3 / 2) x += X(0)..().isEmpty) // CHECK-NEXT: true println(${A}(42...42).isEmpty) // CHECK-NEXT: false println("<\(${A}(3...42).first!)>") // CHECK-NEXT: <3> println("<\(${A}(3...42).last!)>") // CHECK-NEXT: <42> println("<\(${A}().first)>") // CHECK-NEXT: nil println("<\(${A}().last)>") // CHECK-NEXT: nil var a = ${A}(X(0)..<10) println(a.removeLast().value) // CHECK-NEXT: 9 printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8] a.insert(42, atIndex: 4) printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8] println(a.removeAtIndex(2).value) // CHECK-NEXT: 2 printSequence(a) // CHECK-NEXT: [0, 1, 3, 42, 4, 5, 6, 7, 8] } testIsEmptyFirstLast${A}() %end //===--- Regression Tests -------------------------------------------------===// func rdar16958865() { var a: [Int] = [] a += SequenceOf([ 42, 4242 ]) // CHECK-NEXT: [42, 4242] println(a) } rdar16958865() import SpriteKit class Rdar16914909 : NSObject { var basicColorSet = [SKColor]() func doColorStuff() { basicColorSet.append(SKColor.lightGrayColor()) println("appended") } } Rdar16914909().doColorStuff() // CHECK-NEXT: appended println("leaks = \(xCount)") // CHECK-NEXT: leaks = 0 // CHECK-NEXT: all done. println("all done.") // ${'Local Variables'}: // eval: (read-only-mode 1) // End: