Skip to content

Commit

Permalink
Add accessors for Foundation async sequences
Browse files Browse the repository at this point in the history
  • Loading branch information
Zollerboy1 committed Jun 1, 2023
1 parent 252959d commit 4c9c743
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 138 deletions.
18 changes: 9 additions & 9 deletions Sources/SwiftCommand/AsyncCharacterSequence.swift
Expand Up @@ -4,23 +4,23 @@ public struct AsyncCharacterSequence<Base>: AsyncSequence
where Base: AsyncSequence, Base.Element == UInt8 {
@usableFromInline
internal typealias Underlying = AsyncUnicodeScalarSequence<Base>

/// The type of element produced by this asynchronous sequence.
public typealias Element = Character

/// The type of asynchronous iterator that produces elements of this
/// asynchronous sequence.
public struct AsyncIterator: AsyncIteratorProtocol {
@usableFromInline
internal var _remaining: Underlying.AsyncIterator
@usableFromInline
internal var _accumulator: String

fileprivate init(_underlying underlying: Underlying.AsyncIterator) {
self._remaining = underlying
self._accumulator = ""
}

/// Asynchronously advances to the next element and returns it, or ends
/// the sequence if there is no next element.
///
Expand All @@ -34,21 +34,21 @@ where Base: AsyncSequence, Base.Element == UInt8 {
return self._accumulator.removeFirst()
}
}

if self._accumulator.count > 0 {
return self._accumulator.removeFirst()
} else {
return nil
}
}
}

private let underlying: Underlying

internal init(_base base: Base) {
self.underlying = .init(_base: base)
}

/// Creates the asynchronous iterator that produces elements of this
/// asynchronous sequence.
///
Expand All @@ -62,7 +62,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
extension AsyncSequence where Self.Element == UInt8 {
/// A non-blocking sequence of `Character`s created by decoding the
/// elements of `self` as utf-8.
public var characters: AsyncCharacterSequence<Self> {
public var characterSequence: AsyncCharacterSequence<Self> {
AsyncCharacterSequence(_base: self)
}
}
56 changes: 28 additions & 28 deletions Sources/SwiftCommand/AsyncLineSequence.swift
Expand Up @@ -4,25 +4,25 @@ public struct AsyncLineSequence<Base>: AsyncSequence
where Base: AsyncSequence, Base.Element == UInt8 {
/// The type of element produced by this asynchronous sequence.
public typealias Element = String

/// The type of asynchronous iterator that produces elements of this
/// asynchronous sequence.
public struct AsyncIterator: AsyncIteratorProtocol {
public typealias Element = String

@usableFromInline
internal var _base: Base.AsyncIterator
@usableFromInline
internal var _buffer: Array<UInt8>
@usableFromInline
internal var _leftover: UInt8?

internal init(_base base: Base.AsyncIterator) {
self._base = base
self._buffer = []
self._leftover = nil
}

/// Asynchronously advances to the next element and returns it, or ends
/// the sequence if there is no next element.
///
Expand All @@ -44,31 +44,31 @@ where Base: AsyncSequence, Base.Element == UInt8 {
let _SEPARATOR_CONTINUATION: UInt8 = 0x80
let _SEPARATOR_SUFFIX_LINE: UInt8 = 0xA8
let _SEPARATOR_SUFFIX_PARAGRAPH: UInt8 = 0xA9

func yield() -> String? {
defer {
self._buffer.removeAll(keepingCapacity: true)
}

if self._buffer.isEmpty {
return nil
}

return String(decoding: self._buffer, as: UTF8.self)
}

func nextByte() async throws -> UInt8? {
defer {
self._leftover = nil
}

if let leftover = self._leftover {
return leftover
}

return try await self._base.next()
}

while let first = try await nextByte() {
switch first {
case _CR:
Expand All @@ -78,21 +78,21 @@ where Base: AsyncSequence, Base.Element == UInt8 {
// if we ran out of bytes, the last byte was a CR
return result
}

if next != _LF {
self._leftover = next
}

if let result = result {
return result
}

continue
case _LF..<_CR:
guard let result = yield() else {
continue
}

return result
case _NEL_PREFIX:
// this may be used to compose other UTF8 characters
Expand All @@ -102,15 +102,15 @@ where Base: AsyncSequence, Base.Element == UInt8 {
self._buffer.append(first)
return yield()
}

if next != _NEL_SUFFIX {
self._buffer.append(first)
self._buffer.append(next)
} else {
guard let result = yield() else {
continue
}

return result
}
case _SEPARATOR_PREFIX:
Expand All @@ -121,31 +121,31 @@ where Base: AsyncSequence, Base.Element == UInt8 {
self._buffer.append(first)
return yield()
}

guard next == _SEPARATOR_CONTINUATION else {
self._buffer.append(first)
self._buffer.append(next)
continue
}

guard let fin = try await self._base.next() else {
self._buffer.append(first)
self._buffer.append(next)
return yield()
}

guard fin == _SEPARATOR_SUFFIX_LINE
|| fin == _SEPARATOR_SUFFIX_PARAGRAPH else {
self._buffer.append(first)
self._buffer.append(next)
self._buffer.append(fin)
continue
}

if let result = yield() {
return result
}

continue
default:
self._buffer.append(first)
Expand All @@ -156,18 +156,18 @@ where Base: AsyncSequence, Base.Element == UInt8 {
if !self._buffer.isEmpty {
return yield()
}

return nil
}

}

private let base: Base

internal init(_base base: Base) {
self.base = base
}

/// Creates the asynchronous iterator that produces elements of this
/// asynchronous sequence.
///
Expand All @@ -181,7 +181,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
extension AsyncSequence where Self.Element == UInt8 {
/// A non-blocking sequence of newline-separated `String`s created by
/// decoding the elements of `self` as utf-8.
public var lines: AsyncLineSequence<Self> {
public var lineSequence: AsyncLineSequence<Self> {
AsyncLineSequence(_base: self)
}
}
36 changes: 18 additions & 18 deletions Sources/SwiftCommand/AsyncUnicodeScalarSequence.swift
Expand Up @@ -4,47 +4,47 @@ public struct AsyncUnicodeScalarSequence<Base>: AsyncSequence
where Base: AsyncSequence, Base.Element == UInt8 {
/// The type of element produced by this asynchronous sequence.
public typealias Element = UnicodeScalar

/// The type of asynchronous iterator that produces elements of this
/// asynchronous sequence.
public struct AsyncIterator: AsyncIteratorProtocol {
@usableFromInline
internal var _base: Base.AsyncIterator
@usableFromInline
internal var _leftover: UInt8?

internal init(_base base: Base.AsyncIterator) {
self._base = base
self._leftover = nil
}

@inlinable
internal func _expectedContinuationCountForByte(_ byte: UInt8) -> Int? {
if byte & 0b11100000 == 0b11000000 {
return 1
}

if byte & 0b11110000 == 0b11100000 {
return 2
}

if byte & 0b11111000 == 0b11110000 {
return 3
}

if byte & 0b10000000 == 0b00000000 {
return 0
}

if byte & 0b11000000 == 0b10000000 {
// is a continuation itself
return nil
}

// is an invalid value
return nil
}

@inlinable
internal mutating func _nextComplexScalar(_ first: UInt8) async rethrows
-> UnicodeScalar? {
Expand All @@ -54,7 +54,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
// replacement character directly
return "\u{FFFD}"
}

var bytes: (UInt8, UInt8, UInt8, UInt8) = (first, 0, 0, 0)
var numContinuations = 0
while numContinuations < expectedContinuationCount,
Expand All @@ -66,7 +66,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
self._leftover = next
break
}

numContinuations += 1
withUnsafeMutableBytes(of: &bytes) {
$0[numContinuations] = next
Expand All @@ -76,7 +76,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
return String(decoding: $0, as: UTF8.self).unicodeScalars.first
}
}

/// Asynchronously advances to the next element and returns it, or ends
/// the sequence if there is no next element.
///
Expand All @@ -93,20 +93,20 @@ where Base: AsyncSequence, Base.Element == UInt8 {
_onFastPath()
return UnicodeScalar(byte)
}

return try await self._nextComplexScalar(byte)
}

return nil
}
}

private let base: Base

internal init(_base base: Base) {
self.base = base
}

/// Creates the asynchronous iterator that produces elements of this
/// asynchronous sequence.
///
Expand All @@ -120,7 +120,7 @@ where Base: AsyncSequence, Base.Element == UInt8 {
extension AsyncSequence where Self.Element == UInt8 {
/// A non-blocking sequence of `UnicodeScalar`s created by decoding the
/// elements of `self` as utf-8.
public var unicodeScalars: AsyncUnicodeScalarSequence<Self> {
public var unicodeScalarSequence: AsyncUnicodeScalarSequence<Self> {
AsyncUnicodeScalarSequence(_base: self)
}
}

0 comments on commit 4c9c743

Please sign in to comment.