%# -*- 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 --check-prefix=CHECK --check-prefix=CHECK-%target-runtime // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest //===----------------------------------------------------------------------===// 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 == LifetimeTracked, T.Index == Int, T : RandomAccessCollection >(_: T.Type, _ label: String) { print("test: \(label)...", terminator: "") var x: T = [ LifetimeTracked(1), LifetimeTracked(2), LifetimeTracked(3), LifetimeTracked(4), LifetimeTracked(5) ] checkEqual(x, LifetimeTracked(1)...LifetimeTracked(5), true) x.reserveCapacity(x.count + 2) checkEqual(x, LifetimeTracked(1)...LifetimeTracked(5), true) let bufferId0 = bufferID(x) // Append a range of integers x += LifetimeTracked(0).. () ) { 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(LifetimeTracked(42)) } checkReallocations(x, "+=") { (x: inout T) -> () in x.append(LifetimeTracked(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 = [LifetimeTracked(4), LifetimeTracked(2), LifetimeTracked(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() #if _runtime(_ObjC) 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 ns as! [NSString] } } func testCocoa() { print("== Cocoa ==") // CHECK-objc: == Cocoa == var a = nsArrayOfStrings() printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] a.append("qux") printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz, qux] a = nsArrayOfStrings() printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] let b = a a[1] = "garply" printSequence(a) // CHECK-objc-NEXT: [foo, garply, baz] // Mutating an element in a has no effect on b printSequence(b) // CHECK-objc-NEXT: [foo, bar, baz] a = nsArrayOfStrings() a.insert("bag", at: 2) printSequence(a) // CHECK-objc-NEXT: [foo, bar, bag, baz] a = nsArrayOfStrings() a.reserveCapacity(30) printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] print(a.capacity >= 30) // CHECK-objc-NEXT: true // Prove that we create contiguous storage for an opaque NSArray a.withUnsafeBufferPointer { (p) -> () in print(p[0]) // CHECK-objc-NEXT: foo } } testCocoa() #endif // _runtime(_ObjC) extension ArraySlice { mutating func qsort(_ compare: (Element, Element) -> Bool) { _introSort(&self, subRange: Range(self.indices), isOrderedBefore: compare) } } func testSlice() { print("== ArraySlice ==") // CHECK: == ArraySlice == // do some tests on the shared semantics var b = ContiguousArray(LifetimeTracked(0)..") // CHECK-NEXT: <5> print("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4, 5, 6, 7] // bSlice += LifetimeTracked(11)..( _ArrayBuffer(nsArray: nsArrayOfStrings()._buffer._asCocoaArray())) printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] var aSlice = a[1..<3] // CHECK-objc-NEXT: [bar, baz] printSequence(aSlice) // Writing into aSlice works aSlice[1] = "buzz" // CHECK-objc-NEXT: [buzz, baz] printSequence(aSlice) // ...and doesn't affect a printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] // Appending to aSlice works... aSlice.append("fodder") print("<\(aSlice.count)>") // CHECK-objc-NEXT: <3> printSequence(aSlice) // CHECK-objc-NEXT: [buzz, baz, fodder] // And doesn't change a printSequence(a) // CHECK-objc-NEXT: [foo, bar, baz] #endif } 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, { LifetimeTracked(100).. ${A} = { var x = ${A}() // make sure some - but not all - replacements will have to grow the buffer x.reserveCapacity(testWidth * 3 / 2) x += LifetimeTracked(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}(LifetimeTracked(0)..