Skip to content

Commit 1c6d95b

Browse files
committed
Fix readSizeLimit being ignored in a specific case
When reading data up to a certain delimiter with the delimiter already present in the buffer (for a GenericStreamReader), the readSizeLimit was ignored.
1 parent 55e69c4 commit 1c6d95b

File tree

2 files changed

+41
-8
lines changed

2 files changed

+41
-8
lines changed

Sources/StreamReader/Implementations/GenericStreamReader.swift

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,26 @@ public final class GenericStreamReader : StreamReader {
4545
public private(set) var currentReadPosition = 0
4646

4747
/**
48-
The maximum number of bytes that can be returned by the read methods (when updating read position),
48+
The maximum total number of bytes that can be returned by the read methods (when updating read position),
4949
and also the number of bytes that can be read from the underlying stream.
5050

5151
Changing this to a greater value will force ``streamHasReachedEOF`` to `false`,
5252
but next read might reach `EOF` directly regardless.
5353

54-
Changing this to a lower value will not change ``streamHasReachedEOF`` at all. */
54+
Changing this to a lower value will not change ``streamHasReachedEOF`` at all.
55+
56+
The value sets the _total_ number of bytes allowed to be read.
57+
Let’s see an example, reading a stream whose content is `11 22 33 44 55 66 77 88 99 00` (10 bytes):
58+
- Set the limit to `nil` (no limit);
59+
- Read 5 bytes from the stream -> ok, return `11 22 33 44 55`;
60+
- Set the limit to 7;
61+
- Read 3 bytes from the stream (w/o allowing reading less) -> throws notEnoughData;
62+
- Set the limit to 8;
63+
- Read 3 bytes from the stream (w/o allowing reading less) -> ok, returns `66 77 88`;
64+
- Read data up to delimiter `99`, excluding the delimiter, w/o failing if not found
65+
-> ok, returns an empty Data, _and an empty delimiter_ (the delimiter was not read as it’s past the read limit);
66+
- Set the limit 9;
67+
- Read data up to delimiter `99`, excluding the delimiter, w/o failing if not found -> ok, returns empty Data and `99` in the delimiter. */
5568
public var readSizeLimit: Int? {
5669
didSet {
5770
if readSizeLimit ?? Int.max > oldValue ?? Int.max {
@@ -162,11 +175,13 @@ public final class GenericStreamReader : StreamReader {
162175
var unmatchedDelimiters = Array(delimiters.filter{ !$0.isEmpty }.enumerated())
163176
let (minDelimiterLength, maxDelimiterLength) = (unmatchedDelimiters.map(\.element.count).min() ?? 0, unmatchedDelimiters.map(\.element.count).max() ?? 0)
164177

178+
let allowedToBeRead = readSizeLimit.flatMap{ $0 - currentReadPosition } ?? .max
179+
165180
var searchOffset = 0
166181
repeat {
167182
assert(bufferValidLength - searchOffset >= 0, "INTERNAL LOGIC ERROR")
168183
let bufferStart = buffer + bufferStartPos
169-
let bufferSearchData = UnsafeRawBufferPointer(start: bufferStart + searchOffset, count: bufferValidLength - searchOffset)
184+
let bufferSearchData = UnsafeRawBufferPointer(start: bufferStart + searchOffset, count: min(bufferValidLength - searchOffset, allowedToBeRead))
170185
if let match = matchDelimiters(inData: bufferSearchData, dataStartOffset: searchOffset, usingMatchingMode: matchingMode, includeDelimiter: includeDelimiter, minDelimiterLength: minDelimiterLength, withUnmatchedDelimiters: &unmatchedDelimiters, bestMatch: &bestMatch) {
171186
if updateReadPosition {
172187
bufferStartPos += match.length
@@ -202,13 +217,13 @@ public final class GenericStreamReader : StreamReader {
202217
}
203218

204219
/* No match, no error on no match, we return the whole data. */
205-
let returnedLength = bufferValidLength
220+
let returnedLength = min(bufferValidLength, allowedToBeRead)
206221
let bufferStart = buffer + bufferStartPos
207222

208223
if updateReadPosition {
209-
currentReadPosition += bufferValidLength
210-
bufferStartPos += bufferValidLength
211-
bufferValidLength = 0
224+
bufferStartPos += returnedLength
225+
bufferValidLength -= returnedLength
226+
currentReadPosition += returnedLength
212227
}
213228

214229
return try handler(UnsafeRawBufferPointer(start: bufferStart, count: returnedLength), Data())

Tests/StreamReaderTests/StreamReaderTests.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,29 @@ class StreamReaderTests : XCTestCase {
415415
XCTAssertGreaterThan(try reader.readStreamInBuffer(size: 4, allowMoreThanOneRead: false, bypassUnderlyingStreamReadSizeLimit: true), 0)
416416
reader.readSizeLimit = 1
417417
XCTAssertThrowsError(try reader.readData(size: 2))
418-
let (checkedData, checkedDelimiter) = try reader.readData(upTo: [Data(hexEncoded: "45")!], matchingMode: .anyMatchWins, includeDelimiter: false)
418+
let (checkedData, checkedDelimiter) = try reader.readData(upTo: [Data(hexEncoded: "45")!], matchingMode: .anyMatchWins, failIfNotFound: false, includeDelimiter: false)
419419
XCTAssertEqual(checkedData, Data(hexEncoded: "01")!)
420420
XCTAssertEqual(checkedDelimiter, Data())
421421
}
422422

423+
func testReadLimitFromDoc() throws {
424+
try runTest(hexDataString: "11 22 33 44 55 66 77 88 99 00", bufferSizes: [1, 42], bufferSizeIncrements: Array(1...5), underlyingStreamReadSizeLimits: [nil]){ reader, data, limit, bufferSize, bufferSizeIncrement, underlyingStreamReadSizeLimit in
425+
try (reader as? GenericStreamReader)?.readStreamInBuffer(size: 42) /* <- This is the clever bit. Setting the buffer size to the same size does the same but does not guarantee it. */
426+
XCTAssertEqual(try reader.readData(size: 5, allowReadingLess: false), Data(hexEncoded: "11 22 33 44 55")!)
427+
reader.readSizeLimit = 7
428+
XCTAssertThrowsError(try reader.readData(size: 3, allowReadingLess: false))
429+
reader.readSizeLimit = 8
430+
XCTAssertEqual(try reader.readData(size: 3, allowReadingLess: false), Data(hexEncoded: "66 77 88")!)
431+
let (checkedData1, checkedDelimiter1) = try reader.readData(upTo: [Data(hexEncoded: "99")!], matchingMode: .anyMatchWins, failIfNotFound: false, includeDelimiter: false)
432+
XCTAssertEqual(checkedData1, Data())
433+
XCTAssertEqual(checkedDelimiter1, Data())
434+
reader.readSizeLimit = 9
435+
let (checkedData2, checkedDelimiter2) = try reader.readData(upTo: [Data(hexEncoded: "99")!], matchingMode: .anyMatchWins, failIfNotFound: false, includeDelimiter: false)
436+
XCTAssertEqual(checkedData2, Data())
437+
XCTAssertEqual(checkedDelimiter2, Data(hexEncoded: "99")!)
438+
}
439+
}
440+
423441
@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, *)
424442
func testReadErrorFromFileHandle() throws {
425443
let tmpFileURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("StreamReaderTest_\(Int.random(in: 0..<4242))")

0 commit comments

Comments
 (0)