Files
swift-mirror/test/stdlib/NewArray.swift.gyb
2014-07-22 22:29:03 +00:00

548 lines
15 KiB
Swift

%# -*- 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<T: SequenceType>(x: T) {
print("[")
var prefix = ""
for a in x {
print(prefix)
print(a)
prefix = ", "
}
println("]")
}
func bufferID<T : ArrayType>(x: T) -> Int {
return reinterpretCast(x._buffer.baseAddress) as Int
}
func checkReallocation<T : ArrayType>(
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<X>.self, "ContiguousArray")
// CHECK-NEXT: test: ContiguousArray...done
test(Array<X>.self, "Array")
// CHECK-NEXT: test: Array...done
test(Slice<X>.self, "Slice")
// CHECK-NEXT: test: Slice...done
func testAsArray() {
println("== AsArray ==")
var w: ContiguousArray<X> = [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<NSString> {
let src: ContiguousArray<NSString> = ["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)..<X(7))
// Slice it
var bSlice = b[3..<5]
println("<\(bSlice.count)>")
// CHECK-NEXT: <2>
println("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4]
// bSlice += X(11)..<X(13)
// Writing into b does not change bSlice
b[4] = 41
println("bSlice1: \(bSlice)") // CHECK-NEXT: bSlice1: [3, 4]
// Writing into bSlice does not change b
bSlice[1] = 42
println("bSlice2: \(bSlice)") // CHECK-NEXT: bSlice2: [3, 42]
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6]
var c = b
b[4..<b.count].qsort(<)
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 5, 6, 41]
printSequence(c) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6]
// Now a bridged slice
var a = Array<NSString>(
_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}<X>) {
typealias A = ${A}<X>
// First make an independent copy of the array that we can use for
// comparison later.
var source = ContiguousArray<A.Generator.Element>()
for x in make() {
source.append(x)
}
for i in indices(source) {
for j in i..<source.count {
let oldCount = j - i
for newCount in 0..<(2 * oldCount) {
let newValues = X(100)..<X(100 + newCount)
func reportFailure(inout a: A, message: String) {
print("\(message) when replacing indices \(i)...\(j) in ")
printSequence(source)
print(" with ")
printSequence(newValues)
print(" yielding ")
printSequence(a)
println("====================================")
}
var a = make()
a.replaceRange(i..<j, with: newValues)
let growth = newCount - oldCount
let expectedCount = source.count + growth
if a.count != expectedCount {
reportFailure(
&a, "\(a.count) != expected count \(expectedCount)")
}
for k in 0..<a.count {
let expectedValue = k < i ? source[k].value
: k < j + growth ? 100 + k - i
: source[k - growth].value
if a[k].value != expectedValue {
reportFailure(
&a, "a[\(k)] = \(a[k].value) != expected value \(expectedValue)")
}
}
}
}
}
}
func testReplace${A}(
makeOne: ()->${A}<X> = {
var x = ${A}<X>()
// make sure some - but not all - replacements will have to grow the buffer
x.reserveCapacity(testWidth * 3 / 2)
x += X(0)..<X(testWidth)
return x
}
) {
testReplace(makeOne)
// Create one that will not be uniquely-referenced so we can test
// the out-of-place code paths.
let r = makeOne()
testReplace({ r })
// This test should ensure r's retain isn't dropped before we start testing.
if (r.count != testWidth) {
println("something bad happened!")
}
}
println("testing subrange replacement in ${A}")
testReplace${A}()
%end
// Also test with a sub-slice of some larger buffer. The "trailing"
// case is interesting because when the buffer is uniquely referenced
// we can expect to expand the slice in-place
for (maxValue, label) in [(testWidth, "trailing"), (testWidth*2, "interior")] {
println("testing subrange replacement in \(label) Sub-Slice")
testReplaceSlice {
var a = ContiguousArray(X(-testWidth)..<X(maxValue))
a.reserveCapacity(a.count * 3 / 2)
return a[testWidth..<(2 * testWidth)]
}
}
// CHECK-NEXT: testing subrange replacement in ContiguousArray
// CHECK-NEXT: testing subrange replacement in Array
// CHECK-NEXT: testing subrange replacement in Slice
// CHECK-NEXT: testing subrange replacement in trailing Sub-Slice
// CHECK-NEXT: testing subrange replacement in interior Sub-Slice
//===--- inout violations -------------------------------------------------===//
// The user has to obey certain rules when things are passed via
// inout, but in those cases we only guarantee memory-safety, not
// coherent semantics. Let's try to force a memory-safety problem
// here. This crashes when withUnsafeMutableBufferPointer is not
// sufficiently careful.
func testInoutViolation() {
var a: [X] = [
X(10), X(8), X(6), X(4), X(2), X(0), X(9), X(7), X(5), X(3), X(1)
]
%for A in ['ContiguousArray', 'Array', 'Slice']:
if true {
var b = ${A}(a)
b.sort { x, y in
b.removeAll()
return x < y
}
}
%end
// An overload of sort for Arrays uses withUnsafeMutableBufferPointer,
// which disables bounds checks.
sort(&a) { x, y in
a = [] // Invalidate the whole array during sorting
return x < y
}
}
testInoutViolation()
//===--- single-element modifiers -----------------------------------------===//
%for A in ['ContiguousArray', 'Array', 'Slice']:
func testSingleElementModifiers${A}() {
println("testing ${A} single-argument modifiers")
// CHECK-NEXT: testing ${A} single-argument modifiers
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]
}
testSingleElementModifiers${A}()
%end
//===--- isEmpty, first, last ---------------------------------------------===//
%for A in ['ContiguousArray', 'Array', 'Slice']:
func testIsEmptyFirstLast${A}() {
println("testing ${A} isEmpty, first, and last")
// CHECK-NEXT: testing ${A} isEmpty, first, and last
println(${A}<Int>().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}<Int>().first)>") // CHECK-NEXT: nil
println("<\(${A}<Int>().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: