Skip to content

Commit ca8f3bd

Browse files
committed
DataProtocol: Reimplement firstRange(of:in:)/lastRange(of:in:) to fix SR-10689.
1 parent 46ce38c commit ca8f3bd

File tree

1 file changed

+36
-40
lines changed

1 file changed

+36
-40
lines changed

Sources/Foundation/DataProtocol.swift

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -136,60 +136,56 @@ extension DataProtocol {
136136
return self.copyBytes(to: UnsafeMutableRawBufferPointer(start: ptr.baseAddress, count: ptr.count * MemoryLayout<DestinationType>.stride), from: range)
137137
}
138138

139+
private func matches<D: DataProtocol>(_ data: D, from index: Index) -> Bool {
140+
var haystackIndex = index
141+
var needleIndex = data.startIndex
142+
143+
while true {
144+
guard self[haystackIndex] == data[needleIndex] else { return false }
145+
146+
haystackIndex = self.index(after: haystackIndex)
147+
needleIndex = data.index(after: needleIndex)
148+
if needleIndex == data.endIndex {
149+
// i.e. needle is found.
150+
return true
151+
} else if haystackIndex == endIndex {
152+
return false
153+
}
154+
}
155+
}
156+
139157
public func firstRange<D: DataProtocol, R: RangeExpression>(of data: D, in range: R) -> Range<Index>? where R.Bound == Index {
140158
let r = range.relative(to: self)
141-
let rangeCount = distance(from: r.lowerBound, to: r.upperBound)
142-
if rangeCount < data.count {
159+
let length = data.count
160+
161+
if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) {
143162
return nil
144163
}
145-
var haystackIndex = r.lowerBound
146-
let haystackEnd = index(r.upperBound, offsetBy: -data.count)
147-
while haystackIndex < haystackEnd {
148-
var compareIndex = haystackIndex
149-
var needleIndex = data.startIndex
150-
let needleEnd = data.endIndex
151-
var matched = true
152-
while compareIndex < haystackEnd && needleIndex < needleEnd {
153-
if self[compareIndex] != data[needleIndex] {
154-
matched = false
155-
break
156-
}
157-
needleIndex = data.index(after: needleIndex)
158-
compareIndex = index(after: compareIndex)
159-
}
160-
if matched {
161-
return haystackIndex..<compareIndex
164+
165+
var position = r.lowerBound
166+
while position < r.upperBound && distance(from: position, to: r.upperBound) >= length {
167+
if matches(data, from: position) {
168+
return position..<index(position, offsetBy: length)
162169
}
163-
haystackIndex = index(after: haystackIndex)
170+
position = index(after: position)
164171
}
165172
return nil
166173
}
167174

168175
public func lastRange<D: DataProtocol, R: RangeExpression>(of data: D, in range: R) -> Range<Index>? where R.Bound == Index {
169176
let r = range.relative(to: self)
170-
let rangeCount = distance(from: r.lowerBound, to: r.upperBound)
171-
if rangeCount < data.count {
177+
let length = data.count
178+
179+
if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) {
172180
return nil
173181
}
174-
var haystackIndex = r.upperBound
175-
let haystackStart = index(r.lowerBound, offsetBy: data.count)
176-
while haystackIndex > haystackStart {
177-
var compareIndex = haystackIndex
178-
var needleIndex = data.endIndex
179-
let needleStart = data.startIndex
180-
var matched = true
181-
while compareIndex > haystackStart && needleIndex > needleStart {
182-
if self[compareIndex] != data[needleIndex] {
183-
matched = false
184-
break
185-
}
186-
needleIndex = data.index(before: needleIndex)
187-
compareIndex = index(before: compareIndex)
188-
}
189-
if matched {
190-
return compareIndex..<haystackIndex
182+
183+
var position = index(r.upperBound, offsetBy: -length)
184+
while position >= r.lowerBound {
185+
if matches(data, from: position) {
186+
return position..<index(position, offsetBy: length)
191187
}
192-
haystackIndex = index(before: haystackIndex)
188+
position = index(before: position)
193189
}
194190
return nil
195191
}

0 commit comments

Comments
 (0)