Skip to content

Commit b7bfec5

Browse files
authored
Merge pull request #2499 from YOCKOW/sr-10689
2 parents 78f2ce5 + ca8f3bd commit b7bfec5

File tree

2 files changed

+128
-40
lines changed

2 files changed

+128
-40
lines changed

Sources/Foundation/DataProtocol.swift

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -136,34 +136,41 @@ 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
guard !data.isEmpty else {
141159
return nil
142160
}
143161
let r = range.relative(to: self)
144-
let rangeCount = distance(from: r.lowerBound, to: r.upperBound)
145-
if rangeCount < data.count {
162+
let length = data.count
163+
164+
if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) {
146165
return nil
147166
}
148-
var haystackIndex = r.lowerBound
149-
let haystackEnd = index(r.upperBound, offsetBy: -data.count)
150-
while haystackIndex < haystackEnd {
151-
var compareIndex = haystackIndex
152-
var needleIndex = data.startIndex
153-
let needleEnd = data.endIndex
154-
var matched = true
155-
while compareIndex < haystackEnd && needleIndex < needleEnd {
156-
if self[compareIndex] != data[needleIndex] {
157-
matched = false
158-
break
159-
}
160-
needleIndex = data.index(after: needleIndex)
161-
compareIndex = index(after: compareIndex)
162-
}
163-
if matched {
164-
return haystackIndex..<compareIndex
167+
168+
var position = r.lowerBound
169+
while position < r.upperBound && distance(from: position, to: r.upperBound) >= length {
170+
if matches(data, from: position) {
171+
return position..<index(position, offsetBy: length)
165172
}
166-
haystackIndex = index(after: haystackIndex)
173+
position = index(after: position)
167174
}
168175
return nil
169176
}
@@ -173,29 +180,18 @@ extension DataProtocol {
173180
return nil
174181
}
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
}

Tests/Foundation/Tests/TestNSData.swift

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ class TestNSData: LoopbackServerTest {
253253
("test_base64DecodeWithPadding1", test_base64DecodeWithPadding1),
254254
("test_base64DecodeWithPadding2", test_base64DecodeWithPadding2),
255255
("test_rangeOfData", test_rangeOfData),
256+
("test_sr10689_rangeOfDataProtocol", test_sr10689_rangeOfDataProtocol),
256257
("test_initNSMutableData()", test_initNSMutableData),
257258
("test_initNSMutableDataWithLength", test_initNSMutableDataWithLength),
258259
("test_initNSMutableDataWithCapacity", test_initNSMutableDataWithCapacity),
@@ -949,6 +950,97 @@ class TestNSData: LoopbackServerTest {
949950

950951
}
951952

953+
func test_sr10689_rangeOfDataProtocol() {
954+
// https://bugs.swift.org/browse/SR-10689
955+
956+
let base = Data([0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03,
957+
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03])
958+
let subdata = base[10..<13] // [0x02, 0x03, 0x00]
959+
let oneByte = base[14..<15] // [0x02]
960+
961+
do { // firstRange(of:in:)
962+
func assertFirstRange(_ data: Data, _ fragment: Data, range: ClosedRange<Int>? = nil, expectedStartIndex: Int?,
963+
message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line)
964+
{
965+
if let index = expectedStartIndex {
966+
let expectedRange: Range<Int> = index..<(index + fragment.count)
967+
if let someRange = range {
968+
XCTAssertEqual(data.firstRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line)
969+
} else {
970+
XCTAssertEqual(data.firstRange(of: fragment), expectedRange, message(), file: file, line: line)
971+
}
972+
} else {
973+
if let someRange = range {
974+
XCTAssertNil(data.firstRange(of: fragment, in: someRange), message(), file: file, line: line)
975+
} else {
976+
XCTAssertNil(data.firstRange(of: fragment), message(), file: file, line: line)
977+
}
978+
}
979+
}
980+
981+
assertFirstRange(base, base, expectedStartIndex: base.startIndex)
982+
assertFirstRange(base, subdata, expectedStartIndex: 2)
983+
assertFirstRange(base, oneByte, expectedStartIndex: 2)
984+
985+
assertFirstRange(subdata, base, expectedStartIndex: nil)
986+
assertFirstRange(subdata, subdata, expectedStartIndex: subdata.startIndex)
987+
assertFirstRange(subdata, oneByte, expectedStartIndex: subdata.startIndex)
988+
989+
assertFirstRange(oneByte, base, expectedStartIndex: nil)
990+
assertFirstRange(oneByte, subdata, expectedStartIndex: nil)
991+
assertFirstRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex)
992+
993+
assertFirstRange(base, subdata, range: 1...14, expectedStartIndex: 2)
994+
assertFirstRange(base, subdata, range: 6...8, expectedStartIndex: 6)
995+
assertFirstRange(base, subdata, range: 8...10, expectedStartIndex: nil)
996+
997+
assertFirstRange(base, oneByte, range: 1...14, expectedStartIndex: 2)
998+
assertFirstRange(base, oneByte, range: 6...6, expectedStartIndex: 6)
999+
assertFirstRange(base, oneByte, range: 8...9, expectedStartIndex: nil)
1000+
}
1001+
1002+
do { // lastRange(of:in:)
1003+
func assertLastRange(_ data: Data, _ fragment: Data, range: ClosedRange<Int>? = nil, expectedStartIndex: Int?,
1004+
message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line)
1005+
{
1006+
if let index = expectedStartIndex {
1007+
let expectedRange: Range<Int> = index..<(index + fragment.count)
1008+
if let someRange = range {
1009+
XCTAssertEqual(data.lastRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line)
1010+
} else {
1011+
XCTAssertEqual(data.lastRange(of: fragment), expectedRange, message(), file: file, line: line)
1012+
}
1013+
} else {
1014+
if let someRange = range {
1015+
XCTAssertNil(data.lastRange(of: fragment, in: someRange), message(), file: file, line: line)
1016+
} else {
1017+
XCTAssertNil(data.lastRange(of: fragment), message(), file: file, line: line)
1018+
}
1019+
}
1020+
}
1021+
1022+
assertLastRange(base, base, expectedStartIndex: base.startIndex)
1023+
assertLastRange(base, subdata, expectedStartIndex: 10)
1024+
assertLastRange(base, oneByte, expectedStartIndex: 14)
1025+
1026+
assertLastRange(subdata, base, expectedStartIndex: nil)
1027+
assertLastRange(subdata, subdata, expectedStartIndex: subdata.startIndex)
1028+
assertLastRange(subdata, oneByte, expectedStartIndex: subdata.startIndex)
1029+
1030+
assertLastRange(oneByte, base, expectedStartIndex: nil)
1031+
assertLastRange(oneByte, subdata, expectedStartIndex: nil)
1032+
assertLastRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex)
1033+
1034+
assertLastRange(base, subdata, range: 1...14, expectedStartIndex: 10)
1035+
assertLastRange(base, subdata, range: 6...8, expectedStartIndex: 6)
1036+
assertLastRange(base, subdata, range: 8...10, expectedStartIndex: nil)
1037+
1038+
assertLastRange(base, oneByte, range: 1...14, expectedStartIndex: 14)
1039+
assertLastRange(base, oneByte, range: 6...6, expectedStartIndex: 6)
1040+
assertLastRange(base, oneByte, range: 8...9, expectedStartIndex: nil)
1041+
}
1042+
}
1043+
9521044
// Check all of the NSMutableData constructors are available.
9531045
func test_initNSMutableData() {
9541046
let mData = NSMutableData()

0 commit comments

Comments
 (0)