7
7
8
8
@usableFromInline
9
9
struct SearchCache < Element: Hashable > {
10
- @usableFromInline
11
- let length : Int
12
10
@usableFromInline
13
11
let skipTable : [ Element : Int ]
14
- // TODO: Store pattern in array
12
+ @usableFromInline
13
+ let target : [ Element ]
15
14
16
15
@usableFromInline
17
- init < Target: BidirectionalCollection > ( _ target: Target )
18
- where Target. SubSequence. Element == Element {
19
- length = target. count
20
- var skipTable = [ Element: Int] ( minimumCapacity: length)
21
- for (i, c) in target. dropLast ( ) . enumerated ( ) {
22
- skipTable [ c] = length - i - 1
16
+ init < Target: Sequence > ( _ target: Target ) where Target. Element == Element {
17
+ let newtarget = Array ( target)
18
+ var skipTable = [ Element: Int] ( minimumCapacity: newtarget. count)
19
+ for (i, c) in newtarget [ ... ] . dropLast ( ) . enumerated ( ) {
20
+ skipTable [ c] = newtarget. count - i - 1
23
21
}
24
22
self . skipTable = skipTable
23
+ self . target = newtarget
25
24
}
26
25
}
27
26
@@ -30,17 +29,25 @@ extension BidirectionalCollection where Element: Hashable {
30
29
/// - Parameters:
31
30
/// - target: The sequence of elements to search for.
32
31
/// - start: Where to start the search from.
33
- /// - cache: When searching for the same sequence multiple times, use a SearchCache for improved performance.
34
32
/// - Returns: The range where `target` was found, or nil if not found.
35
33
@inlinable
36
- func range< Target: BidirectionalCollection >
37
- ( of target: Target , from start: Index ? = nil , cache: SearchCache < Target . Element > ? = nil ) -> Range < Index > ?
34
+ func range< Target: Sequence > ( of target: Target , from start: Index ? = nil ) -> Range < Index > ?
38
35
where Target. Element == Element {
36
+ self . range ( of: SearchCache ( target) , from: start)
37
+ }
38
+
39
+ /// Finds the next occurrence of `cache.target` in this collection, using the pre-created `cache`.
40
+ /// - Parameters:
41
+ /// - cache: When searching for the same sequence multiple times, use a SearchCache for improved performance.
42
+ /// - start: Where to start the search from.
43
+ /// - Returns: The range where `target` was found, or nil if not found.
44
+ @inlinable
45
+ func range( of cache: SearchCache < Element > , from start: Index ? = nil ) -> Range < Index > ? {
39
46
// https://en.wikipedia.org/wiki/Boyer–Moore–Horspool_algorithm
40
- let cache = cache ?? SearchCache ( target)
41
- guard cache . length > 0 else { return nil }
47
+ let target = cache. target
48
+ guard !target . isEmpty else { return nil }
42
49
43
- var pos = self . index ( start ?? self . startIndex, offsetBy: cache . length - 1 , limitedBy: endIndex) ?? endIndex
50
+ var pos = self . index ( start ?? self . startIndex, offsetBy: target . count - 1 , limitedBy: endIndex) ?? endIndex
44
51
45
52
while pos < endIndex {
46
53
var i = pos
@@ -55,7 +62,7 @@ extension BidirectionalCollection where Element: Hashable {
55
62
}
56
63
}
57
64
58
- let advance = cache. skipTable [ self [ pos] ] ?? cache . length
65
+ let advance = cache. skipTable [ self [ pos] ] ?? target . count
59
66
pos = self . index ( pos, offsetBy: advance, limitedBy: endIndex) ?? endIndex
60
67
}
61
68
0 commit comments