diff --git a/Sources/Foundation/DataProtocol.swift b/Sources/Foundation/DataProtocol.swift index 3e9f88cf22..b37dbd6a39 100644 --- a/Sources/Foundation/DataProtocol.swift +++ b/Sources/Foundation/DataProtocol.swift @@ -136,60 +136,56 @@ extension DataProtocol { return self.copyBytes(to: UnsafeMutableRawBufferPointer(start: ptr.baseAddress, count: ptr.count * MemoryLayout.stride), from: range) } + private func matches(_ data: D, from index: Index) -> Bool { + var haystackIndex = index + var needleIndex = data.startIndex + + while true { + guard self[haystackIndex] == data[needleIndex] else { return false } + + haystackIndex = self.index(after: haystackIndex) + needleIndex = data.index(after: needleIndex) + if needleIndex == data.endIndex { + // i.e. needle is found. + return true + } else if haystackIndex == endIndex { + return false + } + } + } + public func firstRange(of data: D, in range: R) -> Range? where R.Bound == Index { let r = range.relative(to: self) - let rangeCount = distance(from: r.lowerBound, to: r.upperBound) - if rangeCount < data.count { + let length = data.count + + if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) { return nil } - var haystackIndex = r.lowerBound - let haystackEnd = index(r.upperBound, offsetBy: -data.count) - while haystackIndex < haystackEnd { - var compareIndex = haystackIndex - var needleIndex = data.startIndex - let needleEnd = data.endIndex - var matched = true - while compareIndex < haystackEnd && needleIndex < needleEnd { - if self[compareIndex] != data[needleIndex] { - matched = false - break - } - needleIndex = data.index(after: needleIndex) - compareIndex = index(after: compareIndex) - } - if matched { - return haystackIndex..= length { + if matches(data, from: position) { + return position..(of data: D, in range: R) -> Range? where R.Bound == Index { let r = range.relative(to: self) - let rangeCount = distance(from: r.lowerBound, to: r.upperBound) - if rangeCount < data.count { + let length = data.count + + if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) { return nil } - var haystackIndex = r.upperBound - let haystackStart = index(r.lowerBound, offsetBy: data.count) - while haystackIndex > haystackStart { - var compareIndex = haystackIndex - var needleIndex = data.endIndex - let needleStart = data.startIndex - var matched = true - while compareIndex > haystackStart && needleIndex > needleStart { - if self[compareIndex] != data[needleIndex] { - matched = false - break - } - needleIndex = data.index(before: needleIndex) - compareIndex = index(before: compareIndex) - } - if matched { - return compareIndex..= r.lowerBound { + if matches(data, from: position) { + return position..? = nil, expectedStartIndex: Int?, + message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) + { + if let index = expectedStartIndex { + let expectedRange: Range = index..<(index + fragment.count) + if let someRange = range { + XCTAssertEqual(data.firstRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line) + } else { + XCTAssertEqual(data.firstRange(of: fragment), expectedRange, message(), file: file, line: line) + } + } else { + if let someRange = range { + XCTAssertNil(data.firstRange(of: fragment, in: someRange), message(), file: file, line: line) + } else { + XCTAssertNil(data.firstRange(of: fragment), message(), file: file, line: line) + } + } + } + + assertFirstRange(base, base, expectedStartIndex: base.startIndex) + assertFirstRange(base, subdata, expectedStartIndex: 2) + assertFirstRange(base, oneByte, expectedStartIndex: 2) + + assertFirstRange(subdata, base, expectedStartIndex: nil) + assertFirstRange(subdata, subdata, expectedStartIndex: subdata.startIndex) + assertFirstRange(subdata, oneByte, expectedStartIndex: subdata.startIndex) + + assertFirstRange(oneByte, base, expectedStartIndex: nil) + assertFirstRange(oneByte, subdata, expectedStartIndex: nil) + assertFirstRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex) + + assertFirstRange(base, subdata, range: 1...14, expectedStartIndex: 2) + assertFirstRange(base, subdata, range: 6...8, expectedStartIndex: 6) + assertFirstRange(base, subdata, range: 8...10, expectedStartIndex: nil) + + assertFirstRange(base, oneByte, range: 1...14, expectedStartIndex: 2) + assertFirstRange(base, oneByte, range: 6...6, expectedStartIndex: 6) + assertFirstRange(base, oneByte, range: 8...9, expectedStartIndex: nil) + } + + do { // lastRange(of:in:) + func assertLastRange(_ data: Data, _ fragment: Data, range: ClosedRange? = nil, expectedStartIndex: Int?, + message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) + { + if let index = expectedStartIndex { + let expectedRange: Range = index..<(index + fragment.count) + if let someRange = range { + XCTAssertEqual(data.lastRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line) + } else { + XCTAssertEqual(data.lastRange(of: fragment), expectedRange, message(), file: file, line: line) + } + } else { + if let someRange = range { + XCTAssertNil(data.lastRange(of: fragment, in: someRange), message(), file: file, line: line) + } else { + XCTAssertNil(data.lastRange(of: fragment), message(), file: file, line: line) + } + } + } + + assertLastRange(base, base, expectedStartIndex: base.startIndex) + assertLastRange(base, subdata, expectedStartIndex: 10) + assertLastRange(base, oneByte, expectedStartIndex: 14) + + assertLastRange(subdata, base, expectedStartIndex: nil) + assertLastRange(subdata, subdata, expectedStartIndex: subdata.startIndex) + assertLastRange(subdata, oneByte, expectedStartIndex: subdata.startIndex) + + assertLastRange(oneByte, base, expectedStartIndex: nil) + assertLastRange(oneByte, subdata, expectedStartIndex: nil) + assertLastRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex) + + assertLastRange(base, subdata, range: 1...14, expectedStartIndex: 10) + assertLastRange(base, subdata, range: 6...8, expectedStartIndex: 6) + assertLastRange(base, subdata, range: 8...10, expectedStartIndex: nil) + + assertLastRange(base, oneByte, range: 1...14, expectedStartIndex: 14) + assertLastRange(base, oneByte, range: 6...6, expectedStartIndex: 6) + assertLastRange(base, oneByte, range: 8...9, expectedStartIndex: nil) + } + } + // Check all of the NSMutableData constructors are available. func test_initNSMutableData() { let mData = NSMutableData()