Skip to content

Commit 5196b1e

Browse files
committed
[SE-0456, -0467] Data+Span+MutableSpan
1 parent cb06baa commit 5196b1e

File tree

4 files changed

+301
-3
lines changed

4 files changed

+301
-3
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ list(APPEND _SwiftFoundation_versions
111111
list(APPEND _SwiftFoundation_availability_names
112112
"FoundationPreview"
113113
"FoundationPredicate"
114-
"FoundationPredicateRegex")
114+
"FoundationPredicateRegex"
115+
"FoundationSpan")
115116

116117
# The aligned availability for each name (in the same order)
117118
list(APPEND _SwiftFoundation_availability_releases

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import CompilerPluginSupport
99
let availabilityTags: [_Availability] = [
1010
_Availability("FoundationPreview"), // Default FoundationPreview availability,
1111
_Availability("FoundationPredicate"), // Predicate relies on pack parameter runtime support
12-
_Availability("FoundationPredicateRegex") // Predicate regexes rely on new stdlib APIs
12+
_Availability("FoundationPredicateRegex"), // Predicate regexes rely on new stdlib APIs
13+
_Availability("FoundationSpan"), // Availability of Span types
1314
]
1415
let versionNumbers = ["0.1", "0.2", "0.3", "0.4", "6.0.2", "6.1", "6.2"]
1516

@@ -134,6 +135,9 @@ let package = Package(
134135
] + wasiLibcCSettings,
135136
swiftSettings: [
136137
.enableExperimentalFeature("VariadicGenerics"),
138+
.enableExperimentalFeature("LifetimeDependence"),
139+
.enableExperimentalFeature("AddressableTypes"),
140+
.enableExperimentalFeature("BuiltinModule"),
137141
.enableExperimentalFeature("AccessLevelOnImport")
138142
] + availabilityMacros + featureSettings,
139143
linkerSettings: [

Sources/FoundationEssentials/Data/Data.swift

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#endif
5050

5151
internal import _FoundationCShims
52+
import Builtin
5253

5354
#if canImport(Darwin)
5455
import Darwin
@@ -604,6 +605,7 @@ internal final class __DataStorage : @unchecked Sendable {
604605

605606
@frozen
606607
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
608+
@_addressableForDependencies
607609
public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection, MutableDataProtocol, ContiguousBytes, Sendable {
608610

609611
public typealias Index = Int
@@ -2198,7 +2200,105 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
21982200
public func withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType {
21992201
return try _representation.withUnsafeBytes(body)
22002202
}
2201-
2203+
2204+
#if compiler(>=6.2)
2205+
@available(FoundationSpan 6.2, *)
2206+
public var bytes: RawSpan {
2207+
@lifetime(borrow self)
2208+
borrowing get {
2209+
let buffer: UnsafeRawBufferPointer
2210+
switch _representation {
2211+
case .empty:
2212+
buffer = UnsafeRawBufferPointer(_empty: ())
2213+
case .inline:
2214+
buffer = unsafe UnsafeRawBufferPointer(
2215+
start: UnsafeRawPointer(Builtin.addressOfBorrow(self)),
2216+
count: _representation.count
2217+
)
2218+
case .large(let slice):
2219+
buffer = unsafe UnsafeRawBufferPointer(
2220+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2221+
)
2222+
case .slice(let slice):
2223+
buffer = unsafe UnsafeRawBufferPointer(
2224+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2225+
)
2226+
}
2227+
let span = unsafe RawSpan(_unsafeBytes: buffer)
2228+
return unsafe _overrideLifetime(span, borrowing: self)
2229+
}
2230+
}
2231+
2232+
@available(FoundationSpan 6.2, *)
2233+
public var span: Span<UInt8> {
2234+
@lifetime(borrow self)
2235+
borrowing get {
2236+
let span = unsafe bytes._unsafeView(as: UInt8.self)
2237+
return _overrideLifetime(span, borrowing: self)
2238+
}
2239+
}
2240+
2241+
@available(FoundationSpan 6.2, *)
2242+
public var mutableBytes: MutableRawSpan {
2243+
@lifetime(&self)
2244+
mutating get {
2245+
let buffer: UnsafeMutableRawBufferPointer
2246+
switch _representation {
2247+
case .empty:
2248+
buffer = UnsafeMutableRawBufferPointer(_empty: ())
2249+
case .inline:
2250+
buffer = unsafe UnsafeMutableRawBufferPointer(
2251+
start: UnsafeMutableRawPointer(Builtin.addressOfBorrow(self)),
2252+
count: _representation.count
2253+
)
2254+
case .large(let slice):
2255+
buffer = unsafe UnsafeMutableRawBufferPointer(
2256+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2257+
)
2258+
case .slice(let slice):
2259+
buffer = unsafe UnsafeMutableRawBufferPointer(
2260+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2261+
)
2262+
}
2263+
let span = unsafe MutableRawSpan(_unsafeBytes: buffer)
2264+
return unsafe _overrideLifetime(span, mutating: &self)
2265+
}
2266+
}
2267+
2268+
@available(FoundationSpan 6.2, *)
2269+
public var mutableSpan: MutableSpan<UInt8> {
2270+
@lifetime(&self)
2271+
mutating get {
2272+
#if false
2273+
var bytes = mutableBytes
2274+
let span = unsafe bytes._unsafeMutableView(as: UInt8.self)
2275+
return _overrideLifetime(span, mutating: &self)
2276+
#else
2277+
let buffer: UnsafeMutableRawBufferPointer
2278+
switch _representation {
2279+
case .empty:
2280+
buffer = UnsafeMutableRawBufferPointer(_empty: ())
2281+
case .inline:
2282+
buffer = unsafe UnsafeMutableRawBufferPointer(
2283+
start: UnsafeMutableRawPointer(Builtin.addressOfBorrow(self)),
2284+
count: _representation.count
2285+
)
2286+
case .large(let slice):
2287+
buffer = unsafe UnsafeMutableRawBufferPointer(
2288+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2289+
)
2290+
case .slice(let slice):
2291+
buffer = unsafe UnsafeMutableRawBufferPointer(
2292+
start: slice.storage.mutableBytes?.advanced(by: slice.startIndex), count: slice.count
2293+
)
2294+
}
2295+
let span = unsafe MutableSpan<UInt8>(_unsafeBytes: buffer)
2296+
return unsafe _overrideLifetime(span, mutating: &self)
2297+
#endif
2298+
}
2299+
}
2300+
#endif
2301+
22022302
@_alwaysEmitIntoClient
22032303
public func withContiguousStorageIfAvailable<ResultType>(_ body: (_ buffer: UnsafeBufferPointer<UInt8>) throws -> ResultType) rethrows -> ResultType? {
22042304
return try _representation.withUnsafeBytes {
@@ -2870,3 +2970,62 @@ extension Data : Codable {
28702970
}
28712971
}
28722972
}
2973+
2974+
#if compiler(>=6.2)
2975+
// TODO: remove once _overrideLifetime is public in the standard library
2976+
2977+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
2978+
/// a value identical to `dependent` with a lifetime dependency on the caller's
2979+
/// borrow scope of the `source` argument.
2980+
@unsafe
2981+
@_unsafeNonescapableResult
2982+
@_alwaysEmitIntoClient
2983+
@_transparent
2984+
@lifetime(borrow source)
2985+
internal func _overrideLifetime<
2986+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
2987+
>(
2988+
_ dependent: consuming T, borrowing source: borrowing U
2989+
) -> T {
2990+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
2991+
// should be expressed by a builtin that is hidden within the function body.
2992+
dependent
2993+
}
2994+
2995+
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
2996+
/// a value identical to `dependent` that inherits all lifetime dependencies from
2997+
/// the `source` argument.
2998+
@unsafe
2999+
@_unsafeNonescapableResult
3000+
@_alwaysEmitIntoClient
3001+
@_transparent
3002+
@lifetime(copy source)
3003+
internal func _overrideLifetime<
3004+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
3005+
>(
3006+
_ dependent: consuming T, copying source: borrowing U
3007+
) -> T {
3008+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
3009+
// should be expressed by a builtin that is hidden within the function body.
3010+
dependent
3011+
}
3012+
3013+
/// Unsafely discard any lifetime dependency on the `dependent` argument.
3014+
/// Return a value identical to `dependent` with a lifetime dependency
3015+
/// on the caller's exclusive borrow scope of the `source` argument.
3016+
@unsafe
3017+
@_unsafeNonescapableResult
3018+
@_alwaysEmitIntoClient
3019+
@_transparent
3020+
@lifetime(&source)
3021+
internal func _overrideLifetime<
3022+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
3023+
>(
3024+
_ dependent: consuming T,
3025+
mutating source: inout U
3026+
) -> T {
3027+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
3028+
// should be expressed by a builtin that is hidden within the function body.
3029+
dependent
3030+
}
3031+
#endif

Tests/FoundationEssentialsTests/DataTests.swift

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,140 @@ class DataTests : XCTestCase {
16341634
// source.advanced(by: 5)
16351635
}
16361636

1637+
@available(FoundationSpan 6.2, *)
1638+
func test_InlineDataSpan() throws {
1639+
var source = Data()
1640+
var span = source.span
1641+
XCTAssertTrue(span.isEmpty)
1642+
1643+
source.append(contentsOf: [1, 2, 3])
1644+
span = source.span
1645+
XCTAssertFalse(span.isEmpty)
1646+
XCTAssertEqual(span.count, source.count)
1647+
XCTAssertEqual(span[0], 1)
1648+
}
1649+
1650+
@available(FoundationSpan 6.2, *)
1651+
func test_InlineSliceDataSpan() throws {
1652+
let source = Data(0 ... .max)
1653+
let span = source.span
1654+
XCTAssertEqual(span.count, source.count)
1655+
XCTAssertEqual(span[span.indices.last!], .max)
1656+
}
1657+
1658+
@available(FoundationSpan 6.2, *)
1659+
func test_LargeSliceDataSpan() throws {
1660+
#if _pointerBitWidth(_64)
1661+
let count = Int(Int32.max)
1662+
#elseif _pointerBitWidth(_32)
1663+
let count = Int(Int16.max)
1664+
#else
1665+
#error("This test needs updating")
1666+
#endif
1667+
1668+
let source = Data(repeating: 0, count: count).dropFirst()
1669+
XCTAssertNotEqual(source.startIndex, 0)
1670+
let span = source.span
1671+
XCTAssertFalse(span.isEmpty)
1672+
}
1673+
1674+
@available(FoundationSpan 6.2, *)
1675+
func test_InlineDataMutableSpan() throws {
1676+
var source = Data()
1677+
var span = source.mutableSpan
1678+
XCTAssertTrue(span.isEmpty)
1679+
1680+
source.append(contentsOf: [1, 2, 3])
1681+
let count = source.count
1682+
span = source.mutableSpan
1683+
let i = try XCTUnwrap(span.indices.randomElement())
1684+
XCTAssertFalse(span.isEmpty)
1685+
XCTAssertEqual(span.count, count)
1686+
let v = UInt8.random(in: 10..<100)
1687+
span[i] = v
1688+
XCTAssertEqual(source[i], v)
1689+
}
1690+
1691+
@available(FoundationSpan 6.2, *)
1692+
func test_InlineSliceDataMutableSpan() throws {
1693+
var source = Data(0..<100)
1694+
let count = source.count
1695+
var span = source.mutableSpan
1696+
XCTAssertEqual(span.count, count)
1697+
let i = try XCTUnwrap(span.indices.randomElement())
1698+
span[i] = .max
1699+
XCTAssertEqual(source[i], .max)
1700+
}
1701+
1702+
@available(FoundationSpan 6.2, *)
1703+
func test_LargeSliceDataMutableSpan() throws {
1704+
#if _pointerBitWidth(_64)
1705+
var count = Int(Int32.max)
1706+
#elseif _pointerBitWidth(_32)
1707+
var count = Int(Int16.max)
1708+
#else
1709+
#error("This test needs updating")
1710+
#endif
1711+
1712+
var source = Data(repeating: 0, count: count).dropFirst()
1713+
XCTAssertNotEqual(source.startIndex, 0)
1714+
count = source.count
1715+
var span = source.mutableSpan
1716+
XCTAssertEqual(span.count, count)
1717+
let i = try XCTUnwrap(span.indices.dropFirst().randomElement())
1718+
span[i] = .max
1719+
XCTAssertEqual(source[i], 0)
1720+
XCTAssertEqual(source[i+1], .max)
1721+
}
1722+
1723+
@available(FoundationSpan 6.2, *)
1724+
func test_InlineDataMutableRawSpan() throws {
1725+
var source = Data()
1726+
var span = source.mutableBytes
1727+
XCTAssertTrue(span.isEmpty)
1728+
1729+
source.append(contentsOf: [1, 2, 3])
1730+
let count = source.count
1731+
span = source.mutableBytes
1732+
let i = try XCTUnwrap(span.byteOffsets.randomElement())
1733+
XCTAssertFalse(span.isEmpty)
1734+
XCTAssertEqual(span.byteCount, count)
1735+
let v = UInt8.random(in: 10..<100)
1736+
span.storeBytes(of: v, toByteOffset: i, as: UInt8.self)
1737+
XCTAssertEqual(source[i], v)
1738+
}
1739+
1740+
@available(FoundationSpan 6.2, *)
1741+
func test_InlineSliceDataMutableRawSpan() throws {
1742+
var source = Data(0..<100)
1743+
let count = source.count
1744+
var span = source.mutableBytes
1745+
XCTAssertEqual(span.byteCount, count)
1746+
let i = try XCTUnwrap(span.byteOffsets.randomElement())
1747+
span.storeBytes(of: -1, toByteOffset: i, as: Int8.self)
1748+
XCTAssertEqual(source[i], .max)
1749+
}
1750+
1751+
@available(FoundationSpan 6.2, *)
1752+
func test_LargeSliceDataMutableRawSpan() throws {
1753+
#if _pointerBitWidth(_64)
1754+
var count = Int(Int32.max)
1755+
#elseif _pointerBitWidth(_32)
1756+
var count = Int(Int16.max)
1757+
#else
1758+
#error("This test needs updating")
1759+
#endif
1760+
1761+
var source = Data(repeating: 0, count: count).dropFirst()
1762+
XCTAssertNotEqual(source.startIndex, 0)
1763+
count = source.count
1764+
var span = source.mutableBytes
1765+
XCTAssertEqual(span.byteCount, count)
1766+
let i = try XCTUnwrap(span.byteOffsets.dropFirst().randomElement())
1767+
span.storeBytes(of: -1, toByteOffset: i, as: Int8.self)
1768+
XCTAssertEqual(source[i], 0)
1769+
XCTAssertEqual(source[i+1], .max)
1770+
}
16371771

16381772
#if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653
16391773
func test_bounding_failure_subdata() {

0 commit comments

Comments
 (0)