-
Notifications
You must be signed in to change notification settings - Fork 668
Add AsyncSequence helpers #1939
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
Conversation
4e590a8
to
3884bd1
Compare
3884bd1
to
fbdfadc
Compare
5613b51
to
7d09613
Compare
public typealias AsyncIterator = Iterator | ||
|
||
@usableFromInline | ||
let upstream: Upstream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this may as well be var
. It doesn't really matter but it can't hurt.
/// - Parameter maxBytes: The maximum number of bytes that the result ByteBuffer is allowed to have. | ||
/// - Returns: A ``NIO/ByteBuffer`` that holds all the bytes of the AsyncSequence | ||
@inlinable | ||
public func consume(maxBytes: Int) async throws -> ByteBuffer? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, is returning nil
here a good idea? I'm a bit inclined to want to return an empty buffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consume
feels to me like we're actually "reading" the bytes, collect might be more appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if an additional lower-level variant accepting an inout ByteBuffer
would also be useful? Callers could for example, reserve capacity or collect from multiple sequences into the same buffer without an intermediate copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it would be more useful as a general "collect bytes from this stream of buffers" API on ByteBuffer itself.
Which means I think there might be benefit to moving it, and loosening the constraints to allow for async sequences of any buffer type (Data, Array, etc).
switch try await upstream.next() { | ||
case .some(let nextBuffer) where nextBuffer.readableBytes == 0: | ||
// we received an empty buffer. for this reason, let's continue and get the | ||
// next buffer fro, the sequence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// next buffer fro, the sequence | |
// next buffer from the sequence |
|
||
case .some(var nextBuffer): | ||
assert(nextBuffer.readableBytes > 0) | ||
let result = nextBuffer.readInteger(as: UInt8.self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be banged? We assert that there must readable bytes so reading one mustn't fail
import struct NIO.ByteBuffer | ||
|
||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) | ||
public struct NIOByteBufferToUInt8AsyncSequence<Upstream: AsyncSequence>: AsyncSequence where Upstream.Element == ByteBuffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the name. URLSession
uses AsyncBytes
(i.e. URLSession.AsyncBytes
) which I think is quite clear. How about ByteBuffer.AsyncBytes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not basically an async version of FlattenSequence/.joined() from the standard library, specialised for ByteBuffer?
So... ByteBuffer.FlattenSequence, perhaps?
/// Consume an ``Swift/AsyncSequence`` of ``NIO/ByteBuffer``s into a single `ByteBuffer`. | ||
/// | ||
/// - Parameter maxBytes: The maximum number of bytes that the result ByteBuffer is allowed to have. | ||
/// - Returns: A ``NIO/ByteBuffer`` that holds all the bytes of the AsyncSequence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily all the bytes
/// - Parameter maxBytes: The maximum number of bytes that the result ByteBuffer is allowed to have. | ||
/// - Returns: A ``NIO/ByteBuffer`` that holds all the bytes of the AsyncSequence | ||
@inlinable | ||
public func consume(maxBytes: Int) async throws -> ByteBuffer? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consume
feels to me like we're actually "reading" the bytes, collect might be more appropriate.
extension AsyncSequence where Element == ByteBuffer { | ||
/// Transform an AsyncSequence of ByteBuffers into an AsyncSequence of single bytes. | ||
@inlinable | ||
public func toBytes() -> NIOByteBufferToUInt8AsyncSequence<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a computed property?
for try await byte in body.bytes {
// ...
}
vs.
for try await byte in body.toBytes() {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
joined() seems like a better name for this, to mirror both FlattenSequence and the String concatenation helper from the standard library.
/// - Parameter maxBytes: The maximum number of bytes that the result ByteBuffer is allowed to have. | ||
/// - Returns: A ``NIO/ByteBuffer`` that holds all the bytes of the AsyncSequence | ||
@inlinable | ||
public func consume(maxBytes: Int) async throws -> ByteBuffer? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if an additional lower-level variant accepting an inout ByteBuffer
would also be useful? Callers could for example, reserve capacity or collect from multiple sequences into the same buffer without an intermediate copy.
This PR does not have any tests yet. It is here, to demonstrate some
AsyncSequence
andByteBuffer
helpers that we might want to add to make our consumers lifes' easier.Motivation:
In the [RFC] for an async/await API in AsyncHTTPClient @adam-fowler asked, if there could be a helper method to transform an AsyncSequence of
ByteBuffer
s into a singleByteBuffer
. Following our strict rule of implementing conformances only to types you we own we must add those convenience helpers to SwiftNIO directly.Modifications:
func consume(maxBytes: Int) async throws -> ByteBuffer
that consumes an AsyncSequence ofByteBuffer
s into a singleByteBuffer
func bytes()
that translates anAsyncSequence
ofByteBuffer
s into an AsyncSequence of Bytes (UInt8
)