Skip to content

Commit 36e0e21

Browse files
committed
Add .stringValue(), .arrayValue(), and .dataValue() to Buffer
1 parent 0f9a610 commit 36e0e21

File tree

3 files changed

+85
-50
lines changed

3 files changed

+85
-50
lines changed

Sources/Subprocess/Buffer.swift

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ extension AsyncBufferSequence {
1818
/// A immutable collection of bytes
1919
public struct Buffer: Sendable {
2020
#if os(Windows)
21-
private var data: [UInt8]
21+
internal var data: [UInt8]
2222

2323
internal init(data: [UInt8]) {
2424
self.data = data
2525
}
2626
#else
27-
private var data: DispatchData
27+
internal var data: DispatchData
2828

2929
internal init(data: DispatchData) {
3030
self.data = data
@@ -54,7 +54,6 @@ extension AsyncBufferSequence.Buffer {
5454
@available(SubprocessSpan, *)
5555
#endif
5656
extension AsyncBufferSequence.Buffer {
57-
#if !SubprocessSpan
5857
/// Access the raw bytes stored in this buffer
5958
/// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter that
6059
/// points to the contiguous storage for the type. If no such storage exists,
@@ -64,13 +63,6 @@ extension AsyncBufferSequence.Buffer {
6463
/// - Returns: The return value, if any, of the body closure parameter.
6564
public func withUnsafeBytes<ResultType>(
6665
_ body: (UnsafeRawBufferPointer) throws -> ResultType
67-
) rethrows -> ResultType {
68-
return try self._withUnsafeBytes(body)
69-
}
70-
#endif // !SubprocessSpan
71-
72-
internal func _withUnsafeBytes<ResultType>(
73-
_ body: (UnsafeRawBufferPointer) throws -> ResultType
7466
) rethrows -> ResultType {
7567
#if os(Windows)
7668
return try self.data.withUnsafeBytes(body)
@@ -88,44 +80,48 @@ extension AsyncBufferSequence.Buffer {
8880
#if SubprocessSpan
8981
// Access the storge backing this Buffer
9082
public var bytes: RawSpan {
91-
var backing: SpanBacking?
92-
#if os(Windows)
93-
self.data.withUnsafeBufferPointer {
94-
backing = .pointer($0)
95-
}
96-
#else
97-
self.data.enumerateBytes { buffer, byteIndex, stop in
98-
if _fastPath(backing == nil) {
99-
// In practice, almost all `DispatchData` is contiguous
100-
backing = .pointer(buffer)
101-
} else {
102-
// This DispatchData is not contiguous. We need to copy
103-
// the bytes out
104-
let contents = Array(buffer)
105-
switch backing! {
106-
case .pointer(let ptr):
107-
// Convert the ptr to array
108-
let existing = Array(ptr)
109-
backing = .array(existing + contents)
110-
case .array(let array):
111-
backing = .array(array + contents)
83+
@lifetime(borrow self)
84+
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+
}
112107
}
113108
}
114-
}
115-
#endif
116-
guard let backing = backing else {
117-
let empty = UnsafeRawBufferPointer(start: nil, count: 0)
118-
let span = RawSpan(_unsafeBytes: empty)
119-
return _overrideLifetime(of: span, to: self)
120-
}
121-
switch backing {
122-
case .pointer(let ptr):
123-
let span = RawSpan(_unsafeElements: ptr)
124-
return _overrideLifetime(of: span, to: self)
125-
case .array(let array):
126-
let ptr = array.withUnsafeBytes { $0 }
127-
let span = RawSpan(_unsafeBytes: ptr)
128-
return _overrideLifetime(of: span, to: self)
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+
// return array.bytes
121+
let ptr = array.withUnsafeBytes { $0 }
122+
let span = RawSpan(_unsafeBytes: ptr)
123+
return _overrideLifetime(of: span, to: self)
124+
}
129125
}
130126
}
131127
#endif // SubprocessSpan
@@ -134,6 +130,34 @@ extension AsyncBufferSequence.Buffer {
134130
case pointer(UnsafeBufferPointer<UInt8>)
135131
case array([UInt8])
136132
}
133+
134+
/// Copy the buffer content out as a String encoded with the given encoding.
135+
/// - Parameter encoding: the target encoded to encode output
136+
/// - Returns: `String` value of self
137+
public func stringValue<Encoding: _UnicodeEncoding>(
138+
encodedAs encoding: Encoding.Type
139+
) -> String? {
140+
#if os(Windows)
141+
return String(validating: self.data, as: encoding)
142+
#else
143+
return self.withUnsafeBytes { ptr in
144+
return String(
145+
decoding: ptr.bindMemory(to: Encoding.CodeUnit.self).lazy.map { $0 },
146+
as: encoding
147+
)
148+
}
149+
#endif
150+
}
151+
152+
/// Copy the buffer content out as `[UInt8]`
153+
/// - Returns: Array value of self
154+
public func arrayValue() -> [UInt8] {
155+
#if os(Windows)
156+
return self.data
157+
#else
158+
return Array(self.data)
159+
#endif
160+
}
137161
}
138162

139163
// MARK: - Hashable, Equatable

Sources/Subprocess/SubprocessFoundation/Output+Foundation.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ extension OutputProtocol where Self == DataOutput {
6161
}
6262
}
6363

64+
#if SubprocessSpan
65+
@available(SubprocessSpan, *)
66+
#endif
67+
extension AsyncBufferSequence.Buffer {
68+
/// Copy the buffer content out as `Data`
69+
/// - Returns: `Data` value of self
70+
public func dataValue() -> Data {
71+
return Data(self.data)
72+
}
73+
}
74+
6475
// MARK: - Workarounds
6576
#if SubprocessSpan
6677
@available(SubprocessSpan, *)

Tests/SubprocessTests/SubprocessTests+Unix.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ extension SubprocessUnixTests {
433433
) { execution, standardOutput in
434434
var buffer = Data()
435435
for try await chunk in standardOutput {
436-
let currentChunk = chunk._withUnsafeBytes { Data($0) }
436+
let currentChunk = chunk.withUnsafeBytes { Data($0) }
437437
buffer += currentChunk
438438
}
439439
return buffer
@@ -472,7 +472,7 @@ extension SubprocessUnixTests {
472472
) { execution, standardOutput in
473473
var buffer = Data()
474474
for try await chunk in standardOutput {
475-
let currentChunk = chunk._withUnsafeBytes { Data($0) }
475+
let currentChunk = chunk.withUnsafeBytes { Data($0) }
476476
buffer += currentChunk
477477
}
478478
return buffer
@@ -620,7 +620,7 @@ extension SubprocessUnixTests {
620620
) { execution, standardOutput in
621621
var buffer = Data()
622622
for try await chunk in standardOutput {
623-
let currentChunk = chunk._withUnsafeBytes { Data($0) }
623+
let currentChunk = chunk.withUnsafeBytes { Data($0) }
624624
buffer += currentChunk
625625
}
626626
return buffer
@@ -828,7 +828,7 @@ extension SubprocessUnixTests {
828828
group.addTask {
829829
var outputs: [String] = []
830830
for try await bit in standardOutput {
831-
let bitString = bit._withUnsafeBytes { ptr in
831+
let bitString = bit.withUnsafeBytes { ptr in
832832
return String(decoding: ptr, as: UTF8.self)
833833
}.trimmingCharacters(in: .whitespacesAndNewlines)
834834
if bitString.contains("\n") {

0 commit comments

Comments
 (0)