stdlib: add an extension point for SequenceType.contains()

This makes the protocol extension as fast as static dispatch for
Set.contains().

Swift SVN r27396
This commit is contained in:
Dmitri Hrybenko
2015-04-17 02:09:59 +00:00
parent 0d6374bb58
commit 2bccb13463
4 changed files with 109 additions and 1 deletions

View File

@@ -82,6 +82,12 @@ extension _SequenceDefaultsType {
final public func _prext_underestimateCount() -> Int {
return 0
}
final public func _customContainsEquatableElement(
element: Generator.Element
) -> Bool? {
return nil
}
}
/// This protocol is an implementation detail of `SequenceType`; do
@@ -108,6 +114,10 @@ public protocol _Sequence_Type
///
/// Complexity: O(N)
func _prext_underestimateCount() -> Int
func _customContainsEquatableElement(
element: Generator.Element
) -> Bool?
}
/// A type that can be iterated with a `for`\ ...\ `in` loop.

View File

@@ -632,6 +632,14 @@ public struct Set<T : Hashable> :
}
return result
}
//
// `SequenceType` conformance
//
public func _customContainsEquatableElement(member: T) -> Bool? {
return contains(member)
}
}
/// Check for both subset and equality relationship between

View File

@@ -261,7 +261,10 @@ extension SequenceType ${"" if preds else "where Self.Generator.Element : Compar
extension SequenceType where Self.Generator.Element : Equatable {
/// Return `true` iff `x` is in `self`.
final public func _prext_contains(element: ${GElement}) -> Bool {
// FIXME: dynamic dispatch for Set and Dictionary.
if let result? = _customContainsEquatableElement(element) {
return result
}
for e in self {
if e == element {
return true

View File

@@ -1179,6 +1179,93 @@ SequenceTypeAlgorithms.test("contains/WhereElementIsEquatable/${dispatch}") {
% end
struct SequenceWithCustomContainsMethod : SequenceType {
static var timesContainsWasCalled: 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()
}
func _customContainsEquatableElement(
element: MinimalEquatableValue
) -> Bool? {
++SequenceWithCustomContainsMethod.timesContainsWasCalled
for e in _elements {
if e == element.value {
return true
}
}
return false
}
}
func callStaticContains(
sequence: SequenceWithCustomContainsMethod,
element: MinimalEquatableValue) -> Bool {
return sequence._prext_contains(element)
}
% for dispatch in [ 'Static', 'Generic' ]:
SequenceTypeAlgorithms.test("contains/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
for test in findTests {
let s = SequenceWithCustomContainsMethod(test.sequence)
SequenceWithCustomContainsMethod.timesContainsWasCalled = 0
expectEqual(
test.expected != nil,
call${dispatch}Contains(s, MinimalEquatableValue(test.element)),
stackTrace: test.loc.withCurrentLoc())
expectEqual(1, SequenceWithCustomContainsMethod.timesContainsWasCalled)
}
}
% end
% 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>.contains/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 != nil,
s._prext_contains(MinimalHashableValue(test.element)),
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("contains/Predicate") {
for test in findTests {
let s = MinimalSequence<OpaqueValue<Int>>(