Skip to content

Commit 9bbe6e7

Browse files
author
Kyle Macomber
authored
Merge pull request #28639 from YOCKOW/sr-10689-cherry-pick
SR-10689: Fix bugs of DataProtocol's firstRange(of:in:)/lastRange(of:in:).
2 parents 08771ab + 421af22 commit 9bbe6e7

File tree

2 files changed

+131
-41
lines changed

2 files changed

+131
-41
lines changed

stdlib/public/Darwin/Foundation/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
}

test/stdlib/TestData.swift

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,99 @@ class TestData : TestDataSuper {
10771077
expectEqual(slice4[0], 8)
10781078
}
10791079

1080+
func test_rangeOfDataProtocol() {
1081+
// https://bugs.swift.org/browse/SR-10689
1082+
1083+
let base = Data([0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03,
1084+
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03])
1085+
let subdata = base[10..<13] // [0x02, 0x03, 0x00]
1086+
let oneByte = base[14..<15] // [0x02]
1087+
1088+
do { // firstRange(of:in:)
1089+
func assertFirstRange(_ data: Data, _ fragment: Data, range: ClosedRange<Int>? = nil,
1090+
expectedStartIndex: Int?,
1091+
_ message: @autoclosure () -> String = "",
1092+
file: String = #file, line: UInt = #line) {
1093+
if let index = expectedStartIndex {
1094+
let expectedRange: Range<Int> = index..<(index + fragment.count)
1095+
if let someRange = range {
1096+
expectEqual(data.firstRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line)
1097+
} else {
1098+
expectEqual(data.firstRange(of: fragment), expectedRange, message(), file: file, line: line)
1099+
}
1100+
} else {
1101+
if let someRange = range {
1102+
expectNil(data.firstRange(of: fragment, in: someRange), message(), file: file, line: line)
1103+
} else {
1104+
expectNil(data.firstRange(of: fragment), message(), file: file, line: line)
1105+
}
1106+
}
1107+
}
1108+
1109+
assertFirstRange(base, base, expectedStartIndex: base.startIndex)
1110+
assertFirstRange(base, subdata, expectedStartIndex: 2)
1111+
assertFirstRange(base, oneByte, expectedStartIndex: 2)
1112+
1113+
assertFirstRange(subdata, base, expectedStartIndex: nil)
1114+
assertFirstRange(subdata, subdata, expectedStartIndex: subdata.startIndex)
1115+
assertFirstRange(subdata, oneByte, expectedStartIndex: subdata.startIndex)
1116+
1117+
assertFirstRange(oneByte, base, expectedStartIndex: nil)
1118+
assertFirstRange(oneByte, subdata, expectedStartIndex: nil)
1119+
assertFirstRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex)
1120+
1121+
assertFirstRange(base, subdata, range: 1...14, expectedStartIndex: 2)
1122+
assertFirstRange(base, subdata, range: 6...8, expectedStartIndex: 6)
1123+
assertFirstRange(base, subdata, range: 8...10, expectedStartIndex: nil)
1124+
1125+
assertFirstRange(base, oneByte, range: 1...14, expectedStartIndex: 2)
1126+
assertFirstRange(base, oneByte, range: 6...6, expectedStartIndex: 6)
1127+
assertFirstRange(base, oneByte, range: 8...9, expectedStartIndex: nil)
1128+
}
1129+
1130+
do { // lastRange(of:in:)
1131+
func assertLastRange(_ data: Data, _ fragment: Data, range: ClosedRange<Int>? = nil,
1132+
expectedStartIndex: Int?,
1133+
_ message: @autoclosure () -> String = "",
1134+
file: String = #file, line: UInt = #line) {
1135+
if let index = expectedStartIndex {
1136+
let expectedRange: Range<Int> = index..<(index + fragment.count)
1137+
if let someRange = range {
1138+
expectEqual(data.lastRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line)
1139+
} else {
1140+
expectEqual(data.lastRange(of: fragment), expectedRange, message(), file: file, line: line)
1141+
}
1142+
} else {
1143+
if let someRange = range {
1144+
expectNil(data.lastRange(of: fragment, in: someRange), message(), file: file, line: line)
1145+
} else {
1146+
expectNil(data.lastRange(of: fragment), message(), file: file, line: line)
1147+
}
1148+
}
1149+
}
1150+
1151+
assertLastRange(base, base, expectedStartIndex: base.startIndex)
1152+
assertLastRange(base, subdata, expectedStartIndex: 10)
1153+
assertLastRange(base, oneByte, expectedStartIndex: 14)
1154+
1155+
assertLastRange(subdata, base, expectedStartIndex: nil)
1156+
assertLastRange(subdata, subdata, expectedStartIndex: subdata.startIndex)
1157+
assertLastRange(subdata, oneByte, expectedStartIndex: subdata.startIndex)
1158+
1159+
assertLastRange(oneByte, base, expectedStartIndex: nil)
1160+
assertLastRange(oneByte, subdata, expectedStartIndex: nil)
1161+
assertLastRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex)
1162+
1163+
assertLastRange(base, subdata, range: 1...14, expectedStartIndex: 10)
1164+
assertLastRange(base, subdata, range: 6...8, expectedStartIndex: 6)
1165+
assertLastRange(base, subdata, range: 8...10, expectedStartIndex: nil)
1166+
1167+
assertLastRange(base, oneByte, range: 1...14, expectedStartIndex: 14)
1168+
assertLastRange(base, oneByte, range: 6...6, expectedStartIndex: 6)
1169+
assertLastRange(base, oneByte, range: 8...9, expectedStartIndex: nil)
1170+
}
1171+
}
1172+
10801173
func test_sliceAppending() {
10811174
// https://bugs.swift.org/browse/SR-4473
10821175
var fooData = Data()
@@ -4022,6 +4115,7 @@ DataTests.test("test_noCopyBehavior") { TestData().test_noCopyBehavior() }
40224115
DataTests.test("test_doubleDeallocation") { TestData().test_doubleDeallocation() }
40234116
DataTests.test("test_repeatingValueInitialization") { TestData().test_repeatingValueInitialization() }
40244117
DataTests.test("test_rangeZoo") { TestData().test_rangeZoo() }
4118+
DataTests.test("test_rangeOfDataProtocol") { TestData().test_rangeOfDataProtocol() }
40254119
DataTests.test("test_sliceAppending") { TestData().test_sliceAppending() }
40264120
DataTests.test("test_replaceSubrange") { TestData().test_replaceSubrange() }
40274121
DataTests.test("test_sliceWithUnsafeBytes") { TestData().test_sliceWithUnsafeBytes() }

0 commit comments

Comments
 (0)