stdlib: add a hook for dynamic dispatch in CollectionType.find()

This hook allows Set.find() to be equally efficient in static and
generic contexts.

This time, with correct tests.

Swift SVN r27404
This commit is contained in:
Dmitri Hrybenko
2015-04-17 05:03:28 +00:00
parent e94b0f9b40
commit 02d254047b
5 changed files with 194 additions and 3 deletions

View File

@@ -2292,6 +2292,132 @@ SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/${dispatch}") {
% end
struct CollectionWithCustomFindMethod : CollectionType {
static var timesFindWasCalled: Int = 0
internal let _elements: [Int]
init(_ elements: [Int]) {
self._elements = elements
}
func generate() -> MinimalGenerator<MinimalEquatableValue> {
// Lie from our generate() method about sequence contents.
// Tests using this type should not call generate() anyway.
expectUnreachable()
return MinimalSequence<MinimalEquatableValue>([]).generate()
}
var startIndex: MinimalForwardIndex {
return MinimalForwardIndex(
position: 0, startIndex: 0, endIndex: _elements.endIndex)
}
var endIndex: MinimalForwardIndex {
return MinimalForwardIndex(
position: _elements.endIndex,
startIndex: 0,
endIndex: _elements.endIndex)
}
subscript(i: MinimalForwardIndex) -> MinimalEquatableValue {
return MinimalEquatableValue(_elements[i.position])
}
func _customFindEquatableElement(
element: MinimalEquatableValue
) -> MinimalForwardIndex?? {
++CollectionWithCustomFindMethod.timesFindWasCalled
for i in _elements._prext_indices {
if _elements[i] == element.value {
return MinimalForwardIndex(
position: i,
startIndex: 0,
endIndex: _elements.endIndex)
}
}
return Optional(nil)
}
}
func callStaticFind(
sequence: CollectionWithCustomFindMethod,
element: MinimalEquatableValue
) -> CollectionWithCustomFindMethod.Index? {
return sequence._prext_find(element)
}
% for dispatch in [ 'Static', 'Generic' ]:
SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
for test in findTests {
let s = CollectionWithCustomFindMethod(test.sequence)
CollectionWithCustomFindMethod.timesFindWasCalled = 0
expectEqual(
test.expected,
call${dispatch}Find(s, MinimalEquatableValue(test.element))
.map { $0.position },
stackTrace: test.loc.withCurrentLoc())
expectEqual(1, CollectionWithCustomFindMethod.timesFindWasCalled)
}
}
% end
// FIXME: underscores are a workaround for:
// <rdar://problem/20582358> Commenting out one line determines whether a
// completely different line type-checks
func callGenericFind_<
C : CollectionType where C.Generator.Element : Equatable
>(collection: C, element: C.Generator.Element) -> C.Index? {
return collection._prext_find(element)
}
func callStaticFind_(
set: Set<MinimalHashableValue>,
element: MinimalHashableValue
) -> Set<MinimalHashableValue>.Index? {
return set._prext_find(element)
}
% for dispatch in [ 'Static', 'Generic' ]:
// FIXME: implement the same optimization for Dictionary.
// FIXME: move to the file where other Set tests live.
SequenceTypeAlgorithms.test("Set<T>.find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
for test in findTests {
let s = Set<MinimalHashableValue>(
test.sequence.map { MinimalHashableValue($0) })
MinimalHashableValue.timesEqualEqualWasCalled = 0
MinimalHashableValue.timesHashValueWasCalled = 0
expectEqual(
test.expected
.map { _ in MinimalHashableValue(test.element) },
call${dispatch}Find_(s, MinimalHashableValue(test.element))
.map { s[$0] },
stackTrace: test.loc.withCurrentLoc())
if test.sequence.isEmpty {
expectEqual(
0, MinimalHashableValue.timesEqualEqualWasCalled,
stackTrace: test.loc.withCurrentLoc())
expectEqual(
0, MinimalHashableValue.timesHashValueWasCalled,
stackTrace: test.loc.withCurrentLoc())
} else {
expectNotEqual(
0, MinimalHashableValue.timesHashValueWasCalled,
stackTrace: test.loc.withCurrentLoc())
}
if test.expected != nil {
expectNotEqual(
0, MinimalHashableValue.timesEqualEqualWasCalled,
stackTrace: test.loc.withCurrentLoc())
}
}
}
% end
SequenceTypeAlgorithms.test("find/Predicate") {
for test in findTests {
let s = MinimalForwardCollection<MinimalEquatableValue>(