9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
+ #if canImport(Darwin) || canImport(Glibc) || canImport(Android) || canImport(Musl)
12
13
@preconcurrency internal import Dispatch
14
+ #endif
13
15
14
16
#if SubprocessSpan
15
17
@available ( SubprocessSpan, * )
@@ -18,16 +20,32 @@ extension AsyncBufferSequence {
18
20
/// A immutable collection of bytes
19
21
public struct Buffer : Sendable {
20
22
#if os(Windows)
21
- internal var data : [ UInt8 ]
23
+ internal let data : [ UInt8 ]
22
24
23
25
internal init ( data: [ UInt8 ] ) {
24
26
self . data = data
25
27
}
28
+
29
+ internal static func createFrom( _ data: [ UInt8 ] ) -> [ Buffer ] {
30
+ return [ . init( data: data) ]
31
+ }
26
32
#else
27
- internal var data : DispatchData
33
+ // We need to keep the backingData alive while Slice is alive
34
+ internal let backingData : DispatchData
35
+ internal let data : DispatchData . Slice
28
36
29
- internal init ( data: DispatchData ) {
37
+ internal init ( data: DispatchData . Slice , backingData : DispatchData ) {
30
38
self . data = data
39
+ self . backingData = backingData
40
+ }
41
+
42
+ internal static func createFrom( _ data: DispatchData ) -> [ Buffer ] {
43
+ let slices = data. slices
44
+ // In most (all?) cases data should only have one slice
45
+ if _fastPath ( slices. count == 1 ) {
46
+ return [ . init( data: slices [ 0 ] , backingData: data) ]
47
+ }
48
+ return slices. map { . init( data: $0, backingData: data) }
31
49
}
32
50
#endif
33
51
}
@@ -64,70 +82,20 @@ extension AsyncBufferSequence.Buffer {
64
82
public func withUnsafeBytes< ResultType> (
65
83
_ body: ( UnsafeRawBufferPointer ) throws -> ResultType
66
84
) rethrows -> ResultType {
67
- #if os(Windows)
68
85
return try self . data. withUnsafeBytes ( body)
69
- #else
70
- // Although DispatchData was designed to be uncontiguous, in practice
71
- // we found that almost all DispatchData are contiguous. Therefore
72
- // we can access this body in O(1) most of the time.
73
- return try self . data. withUnsafeBytes { ptr in
74
- let bytes = UnsafeRawBufferPointer ( start: ptr, count: self . data. count)
75
- return try body ( bytes)
76
- }
77
- #endif
78
86
}
79
87
80
88
#if SubprocessSpan
81
89
// Access the storge backing this Buffer
82
90
public var bytes : RawSpan {
83
91
@lifetime ( borrow self)
84
92
borrowing get {
85
- var backing : SpanBacking ?
86
- #if os(Windows)
87
- self . data. withUnsafeBufferPointer {
88
- backing = . pointer( $0)
89
- }
90
- #else
91
- self . data. enumerateBytes { buffer, byteIndex, stop in
92
- if _fastPath ( backing == nil ) {
93
- // In practice, almost all `DispatchData` is contiguous
94
- backing = . pointer( buffer)
95
- } else {
96
- // This DispatchData is not contiguous. We need to copy
97
- // the bytes out
98
- let contents = Array ( buffer)
99
- switch backing! {
100
- case . pointer( let ptr) :
101
- // Convert the ptr to array
102
- let existing = Array ( ptr)
103
- backing = . array( existing + contents)
104
- case . array( let array) :
105
- backing = . array( array + contents)
106
- }
107
- }
108
- }
109
- #endif
110
- guard let backing = backing else {
111
- let empty = UnsafeRawBufferPointer ( start: nil , count: 0 )
112
- let span = RawSpan ( _unsafeBytes: empty)
113
- return _overrideLifetime ( of: span, to: self )
114
- }
115
- switch backing {
116
- case . pointer( let ptr) :
117
- let span = RawSpan ( _unsafeElements: ptr)
118
- return _overrideLifetime ( of: span, to: self )
119
- case . array( let array) :
120
- let span = array. span. bytes
121
- return _overrideLifetime ( of: span, to: self )
122
- }
93
+ let ptr = self . data. withUnsafeBytes { $0 }
94
+ let bytes = RawSpan ( _unsafeBytes: ptr)
95
+ return _overrideLifetime ( of: bytes, to: self )
123
96
}
124
97
}
125
98
#endif // SubprocessSpan
126
-
127
- private enum SpanBacking {
128
- case pointer( UnsafeBufferPointer < UInt8 > )
129
- case array( [ UInt8 ] )
130
- }
131
99
}
132
100
133
101
// MARK: - Hashable, Equatable
@@ -144,51 +112,53 @@ extension AsyncBufferSequence.Buffer: Equatable, Hashable {
144
112
145
113
public func hash( into hasher: inout Hasher ) {
146
114
self . data. withUnsafeBytes { ptr in
147
- let bytes = UnsafeRawBufferPointer (
148
- start: ptr,
149
- count: self . data. count
150
- )
151
- hasher. combine ( bytes: bytes)
115
+ hasher. combine ( bytes: ptr)
152
116
}
153
117
}
154
118
#endif
155
119
}
156
120
157
- // MARK: - Initializers
158
- #if SubprocessSpan
159
- @available ( SubprocessSpan, * )
160
- #endif
161
- extension String {
162
- /// Create a String with the given encoding from `Buffer`.
163
- /// - Parameters:
164
- /// - buffer: the buffer to copy from
165
- /// - encoding: the encoding to encode Self with
166
- public init ? < Encoding: _UnicodeEncoding > ( buffer: AsyncBufferSequence . Buffer , as encoding: Encoding . Type ) {
167
- #if os(Windows)
168
- let source = buffer. data. map { Encoding . CodeUnit ( $0) }
169
- self = String ( decoding: source, as: encoding)
170
- #else
171
- self = buffer. withUnsafeBytes { ptr in
172
- return String (
173
- decoding: ptr. bindMemory ( to: Encoding . CodeUnit. self) . lazy. map { $0 } ,
174
- as: encoding
175
- )
121
+ // MARK: - DispatchData.Block
122
+ #if canImport(Darwin) || canImport(Glibc) || canImport(Android) || canImport(Musl)
123
+ extension DispatchData {
124
+ /// Unfortunitely `DispatchData.Region` is not available on Linux, hence our own wrapper
125
+ internal struct Slice : @unchecked Sendable , RandomAccessCollection {
126
+ typealias Element = UInt8
127
+
128
+ internal let bytes : UnsafeBufferPointer < UInt8 >
129
+
130
+ internal var startIndex : Int { self . bytes. startIndex }
131
+ internal var endIndex : Int { self . bytes. endIndex }
132
+
133
+ internal init ( bytes: UnsafeBufferPointer < UInt8 > ) {
134
+ self . bytes = bytes
135
+ }
136
+
137
+ internal func withUnsafeBytes< ResultType> ( _ body: ( UnsafeRawBufferPointer ) throws -> ResultType ) rethrows -> ResultType {
138
+ return try body ( UnsafeRawBufferPointer ( self . bytes) )
139
+ }
140
+
141
+ @discardableResult
142
+ internal func copyBytes< DestinationType> (
143
+ to ptr: UnsafeMutableBufferPointer < DestinationType > , count: Int
144
+ ) -> Int {
145
+ self . bytes. copyBytes ( to: ptr, count: count)
146
+ }
147
+
148
+ subscript( position: Int ) -> UInt8 {
149
+ _read {
150
+ yield self . bytes [ position]
151
+ }
176
152
}
177
- #endif
178
153
}
179
- }
180
154
181
- #if SubprocessSpan
182
- @available ( SubprocessSpan, * )
183
- #endif
184
- extension Array where Element == UInt8 {
185
- /// Create an Array from `Buffer`
186
- /// - Parameter buffer: the buffer to copy from
187
- public init ( buffer: AsyncBufferSequence . Buffer ) {
188
- #if os(Windows)
189
- self = buffer. data
190
- #else
191
- self = Array ( buffer. data)
192
- #endif
155
+ internal var slices : [ Slice ] {
156
+ var slices = [ Slice] ( )
157
+ enumerateBytes { ( bytes, index, stop) in
158
+ slices. append ( Slice ( bytes: bytes) )
159
+ }
160
+ return slices
193
161
}
194
162
}
163
+
164
+ #endif
0 commit comments