Skip to content

Adopt strict memory safety annotations in BinaryParsing #14

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 3 commits into from
Jun 17, 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
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ let package = Package(
.enableExperimentalFeature("Span"),
.enableExperimentalFeature("ValueGenerics"),
.enableExperimentalFeature("LifetimeDependence"),
.strictMemorySafety(),
]
),
// .target(
Expand Down
1 change: 0 additions & 1 deletion Sources/BinaryParsing/Operations/OptionalOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ extension Collection {

@inlinable
public subscript(ifInBounds range: Range<Index>) -> SubSequence? {
let bounds = startIndex...endIndex
guard range.lowerBound >= startIndex, range.upperBound <= endIndex
else { return nil }
return self[range]
Expand Down
18 changes: 9 additions & 9 deletions Sources/BinaryParsing/Parser Types/ParserSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ extension RandomAccessCollection<UInt8> {
) throws(ThrownParsingError) -> T? {
#if canImport(Foundation)
if let data = self as? Foundation.Data {
let result = data.withUnsafeBytes { buffer in
var span = ParserSpan(_unsafeBytes: buffer)
let result = unsafe data.withUnsafeBytes { buffer in
var span = unsafe ParserSpan(_unsafeBytes: buffer)
return Result<T, ThrownParsingError> { try body(&span) }
}
switch result {
Expand All @@ -63,7 +63,7 @@ extension RandomAccessCollection<UInt8> {

let result = self.withContiguousStorageIfAvailable { buffer in
let rawBuffer = UnsafeRawBufferPointer(buffer)
var span = ParserSpan(_unsafeBytes: rawBuffer)
var span = unsafe ParserSpan(_unsafeBytes: rawBuffer)
return Result<T, ThrownParsingError> { try body(&span) }
}
switch result {
Expand Down Expand Up @@ -118,8 +118,8 @@ extension Data: ParserSpanProvider {
public func withParserSpan<T, E>(
_ body: (inout ParserSpan) throws(E) -> T
) throws(E) -> T {
let result = withUnsafeBytes { buffer in
var span = ParserSpan(_unsafeBytes: buffer)
let result = unsafe withUnsafeBytes { buffer in
var span = unsafe ParserSpan(_unsafeBytes: buffer)
return Result<T, E> { () throws(E) in try body(&span) }
}
switch result {
Expand All @@ -134,8 +134,8 @@ extension [UInt8]: ParserSpanProvider {
public func withParserSpan<T, E>(
_ body: (inout ParserSpan) throws(E) -> T
) throws(E) -> T {
let result = self.withUnsafeBytes { rawBuffer in
var span = ParserSpan(_unsafeBytes: rawBuffer)
let result = unsafe self.withUnsafeBytes { rawBuffer in
var span = unsafe ParserSpan(_unsafeBytes: rawBuffer)
return Result<T, E> { () throws(E) in try body(&span) }
}
switch result {
Expand All @@ -149,8 +149,8 @@ extension ArraySlice<UInt8>: ParserSpanProvider {
public func withParserSpan<T, E>(
_ body: (inout ParserSpan) throws(E) -> T
) throws(E) -> T {
let result = self.withUnsafeBytes { rawBuffer in
var span = ParserSpan(_unsafeBytes: rawBuffer)
let result = unsafe self.withUnsafeBytes { rawBuffer in
var span = unsafe ParserSpan(_unsafeBytes: rawBuffer)
return Result<T, E> { () throws(E) in try body(&span) }
}
switch result {
Expand Down
19 changes: 11 additions & 8 deletions Sources/BinaryParsing/Parser Types/ParserSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ public struct ParserSpan: ~Escapable, BitwiseCopyable {
self._upperBound = _bytes.byteCount
}

@unsafe
@inlinable
@lifetime(borrow buffer)
public init(_unsafeBytes buffer: UnsafeRawBufferPointer) {
self._bytes = RawSpan(_unsafeBytes: buffer)
self._bytes = unsafe RawSpan(_unsafeBytes: buffer)
self._lowerBound = 0
self._upperBound = _bytes.byteCount
}
Expand Down Expand Up @@ -111,7 +112,7 @@ extension ParserSpan {
subscript(offset i: Int) -> UInt8 {
precondition(i >= 0)
precondition(i < count)
return _bytes.unsafeLoad(
return unsafe _bytes.unsafeLoad(
fromUncheckedByteOffset: _lowerBound &+ i,
as: UInt8.self)
}
Expand All @@ -124,10 +125,10 @@ extension ParserSpan {
public func withUnsafeBytes<T, E>(
_ body: (UnsafeRawBufferPointer) throws(E) -> T
) throws(E) -> T {
try _bytes.withUnsafeBytes { (fullBuffer) throws(E) in
let buffer = UnsafeRawBufferPointer(
try unsafe _bytes.withUnsafeBytes { (fullBuffer) throws(E) in
let buffer = unsafe UnsafeRawBufferPointer(
rebasing: fullBuffer[_lowerBound..<_upperBound])
return try body(buffer)
return try unsafe body(buffer)
}
}
}
Expand All @@ -153,25 +154,27 @@ extension ParserSpan {
@discardableResult
mutating func consume() -> UInt8? {
guard !isEmpty else { return nil }
return consumeUnchecked()
return unsafe consumeUnchecked()
}

@unsafe
@inlinable
@lifetime(copy self)
mutating func consumeUnchecked(type: UInt8.Type = UInt8.self) -> UInt8 {
defer { _lowerBound &+= 1 }
return _bytes.unsafeLoad(
return unsafe _bytes.unsafeLoad(
fromUncheckedByteOffset: _lowerBound,
as: UInt8.self)
}

@unsafe
@inlinable
@lifetime(copy self)
mutating func consumeUnchecked<T: FixedWidthInteger & BitwiseCopyable>(
type: T.Type
) -> T {
defer { _lowerBound += MemoryLayout<T>.stride }
return _bytes.unsafeLoadUnaligned(
return unsafe _bytes.unsafeLoadUnaligned(
fromUncheckedByteOffset: _lowerBound,
as: T.self)
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/BinaryParsing/Parsers/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ extension Array where Element == UInt8 {
throws(ParsingError)
{
defer { _ = input.divide(atOffset: input.count) }
self = input.withUnsafeBytes { buffer in
Array(buffer)
self = unsafe input.withUnsafeBytes { buffer in
unsafe Array(buffer)
}
}

Expand All @@ -27,8 +27,8 @@ extension Array where Element == UInt8 {
throws(ParsingError)
{
let slice = try input._divide(atByteOffset: byteCount)
self = slice.withUnsafeBytes { buffer in
Array(buffer)
self = unsafe slice.withUnsafeBytes { buffer in
unsafe Array(buffer)
}
}
}
Expand Down
Loading