Skip to content

Commit 1cf2e3b

Browse files
committed
[Foundation] DataProtocol: Reimplement firstRange(of:in:)/lastRange(of:in:) to fix SR-10689.
Cherry-pick from #2499
1 parent 3aae97d commit 1cf2e3b

File tree

1 file changed

+37
-41
lines changed

1 file changed

+37
-41
lines changed

Darwin/Foundation-swiftoverlay/DataProtocol.swift

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -142,60 +142,56 @@ extension DataProtocol {
142142
return self.copyBytes(to: UnsafeMutableRawBufferPointer(start: ptr.baseAddress, count: ptr.count * MemoryLayout<DestinationType>.stride), from: range)
143143
}
144144

145+
private func matches<D: DataProtocol>(_ data: D, from index: Index) -> Bool {
146+
var haystackIndex = index
147+
var needleIndex = data.startIndex
148+
149+
while true {
150+
guard self[haystackIndex] == data[needleIndex] else { return false }
151+
152+
haystackIndex = self.index(after: haystackIndex)
153+
needleIndex = data.index(after: needleIndex)
154+
if needleIndex == data.endIndex {
155+
// i.e. needle is found.
156+
return true
157+
} else if haystackIndex == endIndex {
158+
return false
159+
}
160+
}
161+
}
162+
145163
public func firstRange<D: DataProtocol, R: RangeExpression>(of data: D, in range: R) -> Range<Index>? where R.Bound == Index {
146164
let r = range.relative(to: self)
147-
let rangeCount = distance(from: r.lowerBound, to: r.upperBound)
148-
if rangeCount < data.count {
165+
let length = data.count
166+
167+
if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) {
149168
return nil
150169
}
151-
var haystackIndex = r.lowerBound
152-
let haystackEnd = index(r.upperBound, offsetBy: -data.count)
153-
while haystackIndex < haystackEnd {
154-
var compareIndex = haystackIndex
155-
var needleIndex = data.startIndex
156-
let needleEnd = data.endIndex
157-
var matched = true
158-
while compareIndex < haystackEnd && needleIndex < needleEnd {
159-
if self[compareIndex] != data[needleIndex] {
160-
matched = false
161-
break
162-
}
163-
needleIndex = data.index(after: needleIndex)
164-
compareIndex = index(after: compareIndex)
170+
171+
var position = r.lowerBound
172+
while position < r.upperBound && distance(from: position, to: r.upperBound) >= length {
173+
if matches(data, from: position) {
174+
return position..<index(position, offsetBy: length)
165175
}
166-
if matched {
167-
return haystackIndex..<compareIndex
168-
}
169-
haystackIndex = index(after: haystackIndex)
176+
position = index(after: position)
170177
}
171178
return nil
172179
}
173-
180+
174181
public func lastRange<D: DataProtocol, R: RangeExpression>(of data: D, in range: R) -> Range<Index>? where R.Bound == Index {
175182
let r = range.relative(to: self)
176-
let rangeCount = distance(from: r.lowerBound, to: r.upperBound)
177-
if rangeCount < data.count {
183+
let length = data.count
184+
185+
if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) {
178186
return nil
179187
}
180-
var haystackIndex = r.upperBound
181-
let haystackStart = index(r.lowerBound, offsetBy: data.count)
182-
while haystackIndex > haystackStart {
183-
var compareIndex = haystackIndex
184-
var needleIndex = data.endIndex
185-
let needleStart = data.startIndex
186-
var matched = true
187-
while compareIndex > haystackStart && needleIndex > needleStart {
188-
if self[compareIndex] != data[needleIndex] {
189-
matched = false
190-
break
191-
}
192-
needleIndex = data.index(before: needleIndex)
193-
compareIndex = index(before: compareIndex)
194-
}
195-
if matched {
196-
return compareIndex..<haystackIndex
188+
189+
var position = index(r.upperBound, offsetBy: -length)
190+
while position >= r.lowerBound {
191+
if matches(data, from: position) {
192+
return position..<index(position, offsetBy: length)
197193
}
198-
haystackIndex = index(before: haystackIndex)
194+
position = index(before: position)
199195
}
200196
return nil
201197
}

0 commit comments

Comments
 (0)