WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
38 changes: 29 additions & 9 deletions Sources/FoundationEssentials/Data/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -345,10 +345,19 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
public func withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType {
return try _representation.withUnsafeBytes(body)
@_alwaysEmitIntoClient
public func withUnsafeBytes<E, ResultType: ~Copyable>(_ body: (UnsafeRawBufferPointer) throws(E) -> ResultType) throws(E) -> ResultType {
try _representation.withUnsafeBytes(body)
}

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *)
@_alwaysEmitIntoClient
Expand Down Expand Up @@ -467,16 +476,27 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable
}

@_alwaysEmitIntoClient
public func withContiguousStorageIfAvailable<ResultType>(_ body: (_ buffer: UnsafeBufferPointer<UInt8>) throws -> ResultType) rethrows -> ResultType? {
return try _representation.withUnsafeBytes {
return try $0.withMemoryRebound(to: UInt8.self, body)
public func withContiguousStorageIfAvailable<E, ResultType: ~Copyable>(
_ body: (_ buffer: UnsafeBufferPointer<UInt8>) throws(E) -> ResultType
) throws(E) -> ResultType? {
try _representation.withUnsafeBytes { bytes throws(E) in
try bytes.withMemoryRebound(to: UInt8.self, body)
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
public mutating func withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType {
return try _representation.withUnsafeMutableBytes(body)
@_alwaysEmitIntoClient
public mutating func withUnsafeMutableBytes<E, ResultType: ~Copyable>(_ body: (UnsafeMutableRawBufferPointer) throws(E) -> ResultType) throws(E) -> ResultType {
try _representation.withUnsafeMutableBytes(body)
}

#if FOUNDATION_FRAMEWORK
@abi(mutating func withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal mutating func _legacy_withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

// MARK: -

Expand Down
40 changes: 28 additions & 12 deletions Sources/FoundationEssentials/Data/Representations/Data+Inline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,38 @@ extension Data {
return count
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
func withUnsafeBytes<Result>(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
let count = Int(length)
return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in
return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count))
@_alwaysEmitIntoClient
func withUnsafeBytes<E, Result: ~Copyable>(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try Swift.withUnsafeBytes(of: bytes) { [count = Int(length)] (rawBuffer) throws(E) -> Result in
try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count))
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
mutating func withUnsafeMutableBytes<Result>(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {
let count = Int(length)
return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in
return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count))

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@_alwaysEmitIntoClient
mutating func withUnsafeMutableBytes<E, Result: ~Copyable>(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try Swift.withUnsafeMutableBytes(of: &bytes) { [count = Int(length)] (rawBuffer) throws(E) -> Result in
try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count))
}
}


#if FOUNDATION_FRAMEWORK
@abi(mutating func withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal mutating func _legacy_withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@inlinable // This is @inlinable as trivially computable.
mutating func append(byte: UInt8) {
let count = self.count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,17 +157,35 @@ extension Data {
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
func withUnsafeBytes<Result>(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
return try storage.withUnsafeBytes(in: range, apply: apply)
@_alwaysEmitIntoClient
func withUnsafeBytes<E, Result: ~Copyable>(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try storage.withUnsafeBytes(in: range, apply: apply)
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
mutating func withUnsafeMutableBytes<Result>(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@_alwaysEmitIntoClient
mutating func withUnsafeMutableBytes<E, Result: ~Copyable>(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
ensureUniqueReference()
return try storage.withUnsafeMutableBytes(in: range, apply: apply)
}


#if FOUNDATION_FRAMEWORK
@abi(mutating func withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal mutating func _legacy_withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@inlinable // This is @inlinable as reasonably small.
mutating func append(contentsOf buffer: UnsafeRawBufferPointer) {
assert(endIndex + buffer.count < HalfInt.max)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,35 @@ extension Data {
return slice.range
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
func withUnsafeBytes<Result>(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
return try storage.withUnsafeBytes(in: range, apply: apply)
@_alwaysEmitIntoClient
func withUnsafeBytes<E, Result: ~Copyable>(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try storage.withUnsafeBytes(in: range, apply: apply)
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
mutating func withUnsafeMutableBytes<Result>(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@_alwaysEmitIntoClient
mutating func withUnsafeMutableBytes<E, Result: ~Copyable>(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
ensureUniqueReference()
return try storage.withUnsafeMutableBytes(in: range, apply: apply)
}


#if FOUNDATION_FRAMEWORK
@abi(mutating func withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal mutating func _legacy_withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@inlinable // This is @inlinable as reasonably small.
mutating func append(contentsOf buffer: UnsafeRawBufferPointer) {
ensureUniqueReference()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ extension Data {
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
func withUnsafeBytes<Result>(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
@_alwaysEmitIntoClient
func withUnsafeBytes<E, Result: ~Copyable>(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
switch self {
case .empty:
let empty = InlineData()
Expand All @@ -227,9 +227,18 @@ extension Data {
return try slice.withUnsafeBytes(apply)
}
}

@inlinable // This is @inlinable as a generic, trivially forwarding function.
mutating func withUnsafeMutableBytes<Result>(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@_alwaysEmitIntoClient
mutating func withUnsafeMutableBytes<E, Result: ~Copyable>(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
switch self {
case .empty:
var empty = InlineData()
Expand All @@ -247,7 +256,16 @@ extension Data {
return try slice.withUnsafeMutableBytes(apply)
}
}


#if FOUNDATION_FRAMEWORK
@abi(mutating func withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal mutating func _legacy_withUnsafeMutableBytes<ResultType>(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(body)
}
#endif // FOUNDATION_FRAMEWORK

@usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function.
func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Index, _ stop: inout Bool) -> Void) {
switch self {
Expand Down
36 changes: 26 additions & 10 deletions Sources/FoundationEssentials/Data/Representations/DataStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,34 @@ internal final class __DataStorage : @unchecked Sendable {
return UnsafeRawPointer(_bytes)?.advanced(by: -_offset)
}

@inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding.
@discardableResult
func withUnsafeBytes<Result>(in range: Range<Int>, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)))
@_alwaysEmitIntoClient
func withUnsafeBytes<E, Result: ~Copyable>(in range: Range<Int>, apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)))
}

@inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding.
@discardableResult
func withUnsafeMutableBytes<Result>(in range: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {
return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)))

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeBytes<R>(in: Range<Int>, apply: (UnsafeRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
func _legacy_withUnsafeBytes<Result>(in range: Range<Int>, apply: (UnsafeRawBufferPointer) throws -> Result) throws -> Result {
try withUnsafeBytes(in: range, apply: apply)
}

#endif // FOUNDATION_FRAMEWORK

@_alwaysEmitIntoClient
func withUnsafeMutableBytes<E, Result: ~Copyable>(in range: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result {
try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)))
}

#if FOUNDATION_FRAMEWORK
@abi(func withUnsafeMutableBytes<R>(in: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R)
@_spi(FoundationLegacyABI)
@usableFromInline
internal func _legacy_withUnsafeMutableBytes<ResultType>(in range: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType {
try withUnsafeMutableBytes(in: range, apply: apply)
}
#endif // FOUNDATION_FRAMEWORK

@inlinable // This is @inlinable as trivially computable.
var mutableBytes: UnsafeMutableRawPointer? {
return _bytes?.advanced(by: _offset &* -1) // _offset is guaranteed to be non-negative, so it can never overflow when negating
Expand Down
28 changes: 28 additions & 0 deletions Tests/FoundationEssentialsTests/DataLegacyABITests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#if FOUNDATION_FRAMEWORK
@_spi(FoundationLegacyABI) @testable import Foundation
import Testing

@Suite("Foundation Legacy ABI")
private final class FoundationLegacyABITests {

@Test func validateDataLegacyABI() {
var data = Data()

data._legacy_withUnsafeBytes { _ in }
data._legacy_withUnsafeMutableBytes { _ in }
}
}

#endif // FOUNDATION_FRAMEWORK
63 changes: 62 additions & 1 deletion Tests/FoundationEssentialsTests/DataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,67 @@ private final class DataTests {
}
}

private struct Value: ~Copyable {
var stored: Int
init(_ value: Int) { stored = value }
}

private enum LocalError: Error, Equatable { case error }

@Test func validateGeneralizedParameters_withUnsafeBytes() {
var data: Data

data = Data(repeating: 2, count: 12)
let value1 = data.withUnsafeBytes {
let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) }
return Value(sum)
}
#expect(value1.stored == 24)
#expect(throws: LocalError.error) {
try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) }
}

data = Data(repeating: 1, count: 128)
let value2 = data.withUnsafeBytes {
let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) }
return Value(sum)
}
#expect(value2.stored == 128)
#expect(throws: LocalError.error) {
try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) }
}
}

@Test func validateGeneralizedParameters_withUnsafeMutableBytes() {
var data: Data

data = Data(count: 12)
let value1 = data.withUnsafeMutableBytes {
$0.withMemoryRebound(to: UInt8.self) {
for i in $0.indices { $0[i] = 2 }
}
let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) }
return Value(sum)
}
#expect(value1.stored == 24)
#expect(throws: LocalError.error) {
try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) }
}

data = Data(count: 128)
let value2 = data.withUnsafeMutableBytes {
$0.withMemoryRebound(to: UInt8.self) {
for i in $0.indices { $0[i] = 1 }
}
let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) }
return Value(sum)
}
#expect(value2.stored == 128)
#expect(throws: LocalError.error) {
try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) }
}
}

@Test func sliceHash() {
let base1 = Data([0, 0xFF, 0xFF, 0])
let base2 = Data([0, 0xFF, 0xFF, 0])
Expand Down Expand Up @@ -1767,7 +1828,7 @@ private final class DataTests {
let byteCount = span.byteCount
#expect(byteCount == count)
let v = UInt8.random(in: 10..<100)
var sub = span.extracting(i..<i+1)
var sub = span._mutatingExtracting(i..<i+1)
sub.storeBytes(of: v, as: UInt8.self)
#expect(source[i] == v)
}
Expand Down