%# -*- mode: swift -*- //===--- NewArray.swift.gyb -----------------------------------*- swift -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 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 %t/NewArray.swift -o %t/a.out -Xfrontend -disable-access-control // RUN: %target-run %t/a.out 2>&1 | %S/../../utils/line-directive %t/NewArray.swift -- FileCheck %t/NewArray.swift // REQUIRES: executable_test // XFAIL: linux var xCount = 0 var xSerial = 0 import StdlibUnittest // 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. final class X : ForwardIndex, Comparable, CustomStringConvertible, IntegerLiteralConvertible { required init(_ value: Int) { xCount += 1 xSerial += 1 serial = xSerial self.value = value } deinit { assert(serial > 0, "double destruction!") xCount -= 1 serial = -serial } var description: String { assert(serial > 0, "dead X!") return value.description } func successor() -> Self { return self.dynamicType.init(self.value.successor()) } convenience init(integerLiteral value: Int) { self.init(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("[", terminator: "") var prefix = "" for a in x { print(prefix, terminator: "") print(a, terminator: "") prefix = ", " } print("]") } typealias BufferID = UnsafePointer func bufferID(x: T) -> BufferID { return x._buffer.identity } func checkReallocation( x: T, _ lastBuffer: BufferID, _ reallocationExpected: Bool ) -> BufferID { let currentBuffer = bufferID(x) if (currentBuffer != lastBuffer) != reallocationExpected { let message = reallocationExpected ? "lack of" : "" print("unexpected \(message) reallocation") } return currentBuffer } func checkEqual< S1 : Sequence, S2 : Sequence where S1.Iterator.Element == S2.Iterator.Element, S1.Iterator.Element : Equatable >(a1: S1, _ a2: S2, _ expected: Bool) { if a1.elementsEqual(a2) != expected { let un = expected ? "un" : "" print("unexpectedly \(un)equal sequences!") } } func test< T : _ArrayProtocol where T.Iterator.Element == T._Buffer.Element, T._Buffer.Element == T.Element, T.Element == X, T.Index == Int >(_: T.Type, _ label: String) { print("test: \(label)...", terminator: "") 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( a: T, _ growthDescription: String, _ growBy1: (_: inout T)->() ) { var a = a 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 += 1 } } if reallocations > 3 { print( "Unexpectedly found \(reallocations) reallocations " + "of \(label) when growing via \(growthDescription)") } } checkReallocations(x, "append") { (x: inout T) -> () in x.append(42) } checkReallocations(x, "+=") { (x: inout T) -> () in x.append(42) } print("done.") } print("testing...") // CHECK: testing... test(ContiguousArray.self, "ContiguousArray") // CHECK-NEXT: test: ContiguousArray...done test(Array.self, "Array") // CHECK-NEXT: test: Array...done test(ArraySlice.self, "ArraySlice") // CHECK-NEXT: test: ArraySlice...done func testAsArray() { print("== AsArray ==") var w: ContiguousArray = [4, 2, 1] // CHECK: == AsArray == let x = ContiguousArray(w) print(bufferID(w) == bufferID(x)) // CHECK-NEXT: true let y = Array(x) print(bufferID(x) == bufferID(y)) // CHECK-NEXT: true // Because of their indirection, arrays of classes can share // buffers. let y1 = Array(y) print(bufferID(y1) == bufferID(y)) // CHECK-NEXT: true let z = ArraySlice(y) print(bufferID(y) == bufferID(z)) // CHECK-NEXT: true w = ContiguousArray(z) print(bufferID(w) == bufferID(z)) // CHECK-NEXT: true let zz = y[0..<2] print(bufferID(zz)) // CHECK-NEXT: 0x } testAsArray() import Foundation func nsArrayOfStrings() -> Array { let src: ContiguousArray = ["foo", "bar", "baz"] return src.withUnsafeBufferPointer { let ns = NSArray(objects: UnsafePointer($0.baseAddress), count: $0.count) return _convertNSArrayToArray(ns) } } func testCocoa() { print("== 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", at: 2) printSequence(a) // CHECK-NEXT: [foo, bar, bag, baz] a = nsArrayOfStrings() a.reserveCapacity(30) printSequence(a) // CHECK-NEXT: [foo, bar, baz] print(a.capacity >= 30) // CHECK-NEXT: true // Prove that we create contiguous storage for an opaque NSArray a.withUnsafeBufferPointer { (p) -> () in print(p[0]) // CHECK-NEXT: foo } } testCocoa() extension ArraySlice { mutating func qsort(compare: (Element, Element) -> Bool) { _introSort(&self, subRange: self.indices, isOrderedBefore: compare) } } func testSlice() { print("== ArraySlice ==") // CHECK: == ArraySlice == // do some tests on the shared semantics var b = ContiguousArray(X(0)..") // CHECK-NEXT: <5> print("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4, 5, 6, 7] // bSlice += X(11)..( _ArrayBuffer(nsArray: nsArrayOfStrings()._buffer._asCocoaArray())) printSequence(a) // CHECK-NEXT: [foo, bar, baz] var aSlice = a[1..<3] // CHECK-NEXT: [bar, baz] printSequence(aSlice) // Writing into aSlice works aSlice[1] = "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") print("<\(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 %arrayTypes = ['ContiguousArray', 'Array', 'ArraySlice'] %for A in arrayTypes: func testReplace(make: () -> ${A}) { checkRangeReplaceable(make, { X(100).. ${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 print(${A}(42...42).isEmpty) // CHECK-NEXT: false print("<\(${A}(3...42).first!)>") // CHECK-NEXT: <3> print("<\(${A}(3...42).last!)>") // CHECK-NEXT: <42> print("<\(${A}().first)>") // CHECK-NEXT: nil print("<\(${A}().last)>") // CHECK-NEXT: nil var a = ${A}(X(0)..<10) print(a.removeLast().value) // CHECK-NEXT: 9 printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8] a.insert(42, at: 4) printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8] print(a.remove(at: 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 += AnySequence([ 42, 4242 ]) // CHECK-NEXT: [42, 4242] print(a) } rdar16958865() #if os(OSX) import AppKit typealias OSColor = NSColor #else import UIKit typealias OSColor = UIColor #endif class Rdar16914909 : NSObject { var basicColorSet = [OSColor]() func doColorStuff() { basicColorSet.append(OSColor.lightGray()) print("appended") } } Rdar16914909().doColorStuff() // CHECK-NEXT: appended print("leaks = \(xCount)") // CHECK-NEXT: leaks = 0 // CHECK-NEXT: all done. print("all done.") // ${'Local Variables'}: // eval: (read-only-mode 1) // End: