File: ReadableFileStream.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (125 lines) | stat: -rw-r--r-- 4,006 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

internal import SystemPackage
internal import class Dispatch.DispatchQueue

/// Type-erasure wrapper over underlying file system readable streams.
package enum ReadableFileStream: AsyncSequence {
    package typealias Element = ArraySlice<UInt8>

    case real(RealReadableFileStream)
    case mock(MockReadableFileStream)

    package enum Iterator: AsyncIteratorProtocol {
        case real(RealReadableFileStream.Iterator)
        case mock(MockReadableFileStream.Iterator)

        package func next() async throws -> ArraySlice<UInt8>? {
            switch self {
            case .real(let local):
                return try await local.next()
            case .mock(let virtual):
                return try await virtual.next()
            }
        }
    }

    package func makeAsyncIterator() -> Iterator {
        switch self {
        case .real(let real):
            return .real(real.makeAsyncIterator())
        case .mock(let mock):
            return .mock(mock.makeAsyncIterator())
        }
    }
}

/// A stream of file contents from the real file system provided by the OS.
package struct RealReadableFileStream: AsyncSequence {
    package typealias Element = ArraySlice<UInt8>

    let fileDescriptor: FileDescriptor
    let ioQueue: DispatchQueue
    let readChunkSize: Int

    package final class Iterator: AsyncIteratorProtocol {
        init(_ fileDescriptor: FileDescriptor, ioQueue: DispatchQueue, readChunkSize: Int) {
            self.fileDescriptor = fileDescriptor
            self.ioQueue = ioQueue
            self.chunkSize = readChunkSize
        }

        private let fileDescriptor: FileDescriptor
        private let ioQueue: DispatchQueue
        private let chunkSize: Int

        package func next() async throws -> ArraySlice<UInt8>? {
            let chunkSize = self.chunkSize
            let fileDescriptor = self.fileDescriptor

            return try await ioQueue.scheduleOnQueue {
                var buffer = [UInt8](repeating: 0, count: chunkSize)

                let bytesRead = try buffer.withUnsafeMutableBytes {
                    try fileDescriptor.read(into: $0)
                }

                guard bytesRead > 0 else {
                    return nil
                }

                buffer.removeLast(chunkSize - bytesRead)
                return buffer[...]
            }
        }
    }

    package func makeAsyncIterator() -> Iterator {
        Iterator(self.fileDescriptor, ioQueue: ioQueue, readChunkSize: self.readChunkSize)
    }
}


/// A stream of file contents backed by an in-memory array of bytes.
package struct MockReadableFileStream: AsyncSequence {
    package typealias Element = ArraySlice<UInt8>

    package final class Iterator: AsyncIteratorProtocol {
        init(bytes: [UInt8], chunkSize: Int) {
            self.bytes = bytes
            self.chunkSize = chunkSize
        }

        private let chunkSize: Int
        var bytes: [UInt8]
        private var position = 0

        package func next() async throws -> ArraySlice<UInt8>? {
            let nextPosition = Swift.min(bytes.count, position + chunkSize)

            guard nextPosition > position else {
                return nil
            }

            defer { self.position = nextPosition }
            return self.bytes[position..<nextPosition]
        }
    }

    let bytes: [UInt8]
    let chunkSize: Int

    package func makeAsyncIterator() -> Iterator {
        Iterator(bytes: self.bytes, chunkSize: self.chunkSize)
    }
}