diff --git a/Sources/BinaryParsing/Parser Types/ParsingError.swift b/Sources/BinaryParsing/Parser Types/ParsingError.swift index 5b7dfb9..733ac61 100644 --- a/Sources/BinaryParsing/Parser Types/ParsingError.swift +++ b/Sources/BinaryParsing/Parser Types/ParsingError.swift @@ -121,7 +121,7 @@ extension ParsingError.Status: CustomStringConvertible { /// In a build for embedded Swift, `ThrownParsingError` instead aliases the /// specific `ParsingError` type. Because embedded Swift supports only /// fully-typed throws, and not the existential `any Error`, this allows you to -/// still take use error-throwing APIs in an embedded context. +/// still use error-throwing APIs in an embedded context. public typealias ThrownParsingError = any Error #else // Documentation is built using the non-embedded build. diff --git a/Sources/BinaryParsing/Parsers/Array.swift b/Sources/BinaryParsing/Parsers/Array.swift index ce298b7..147c816 100644 --- a/Sources/BinaryParsing/Parsers/Array.swift +++ b/Sources/BinaryParsing/Parsers/Array.swift @@ -80,7 +80,9 @@ extension Array { count: some FixedWidthInteger, parser: (inout ParserSpan) throws -> Element ) throws { - let count = try Int(throwingOnOverflow: count) + guard let count = Int(exactly: count), count >= 0 else { + throw ParsingError(statusOnly: .invalidValue) + } self = [] self.reserveCapacity(count) // This doesn't throw (e.g. on empty) because `parser` can produce valid @@ -118,11 +120,14 @@ extension Array { /// - Throws: An error if one is thrown from `parser`. @inlinable @lifetime(&input) - public init( + public init( parsing input: inout ParserSpan, count: Int, - parser: (inout ParserSpan) throws(E) -> Element - ) throws(E) { + parser: (inout ParserSpan) throws(ThrownParsingError) -> Element + ) throws(ThrownParsingError) { + guard count >= 0 else { + throw ParsingError(statusOnly: .invalidValue) + } self = [] self.reserveCapacity(count) // This doesn't throw (e.g. on empty) because `parser` can produce valid diff --git a/Tests/BinaryParsingTests/ArrayParsingTests.swift b/Tests/BinaryParsingTests/ArrayParsingTests.swift index 78973d0..06dbe08 100644 --- a/Tests/BinaryParsingTests/ArrayParsingTests.swift +++ b/Tests/BinaryParsingTests/ArrayParsingTests.swift @@ -21,8 +21,8 @@ struct ArrayParsingTests { @Test func parseRemainingBytes() throws { - try testBuffer.withParserSpan { span in - let parsedArray = try Array(parsingRemainingBytes: &span) + testBuffer.withParserSpan { span in + let parsedArray = Array(parsingRemainingBytes: &span) #expect(parsedArray == testBuffer) #expect(span.count == 0) } @@ -30,14 +30,14 @@ struct ArrayParsingTests { // Test parsing after consuming part of the buffer try testBuffer.withParserSpan { span in try span.seek(toRelativeOffset: 3) - let parsedArray = try Array(parsingRemainingBytes: &span) + let parsedArray = Array(parsingRemainingBytes: &span) #expect(parsedArray[...] == testBuffer.dropFirst(3)) #expect(span.count == 0) } // Test with an empty span - try emptyBuffer.withParserSpan { span in - let parsedArray = try [UInt8](parsingRemainingBytes: &span) + emptyBuffer.withParserSpan { span in + let parsedArray = [UInt8](parsingRemainingBytes: &span) #expect(parsedArray.isEmpty) } } @@ -68,6 +68,14 @@ struct ArrayParsingTests { } #expect(span.count == testBuffer.count) } + + // Negative 'byteCount' + testBuffer.withParserSpan { span in + #expect(throws: ParsingError.self) { + _ = try [UInt8](parsing: &span, byteCount: -1) + } + #expect(span.count == testBuffer.count) + } } @Test @@ -118,13 +126,21 @@ struct ArrayParsingTests { #expect(span.count == 0) } - // Non-'Int' count that would overflow - _ = testBuffer.withParserSpan { span in + // Error checking + testBuffer.withParserSpan { span in + // Overflow when 'Int' #expect(throws: ParsingError.self) { _ = try [UInt8](parsing: &span, count: UInt.max) { input in try UInt8(parsing: &input) } } + + // Negative count + #expect(throws: ParsingError.self) { + _ = try [UInt8](parsing: &span, count: -1) { input in + try UInt8(parsing: &input) + } + } } }