Skip to content

Fix error handling for array parsing #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/BinaryParsing/Parser Types/ParsingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 9 additions & 4 deletions Sources/BinaryParsing/Parsers/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -118,11 +120,14 @@ extension Array {
/// - Throws: An error if one is thrown from `parser`.
@inlinable
@lifetime(&input)
public init<E>(
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
Expand Down
30 changes: 23 additions & 7 deletions Tests/BinaryParsingTests/ArrayParsingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ 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)
}

// 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)
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
}
}

Expand Down