File: NIOAny.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (264 lines) | stat: -rw-r--r-- 10,376 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// `NIOAny` is an opaque container for values of *any* type, similar to Swift's builtin `Any` type. Contrary to
/// `Any` the overhead of `NIOAny` depends on the the type of the wrapped value. Certain types that are important
/// for the performance of a SwiftNIO application like `ByteBuffer`, `FileRegion` and `AddressEnvelope<ByteBuffer>` can be expected
/// to be wrapped almost without overhead. All others will have similar performance as if they were passed as an `Any` as
/// `NIOAny` just like `Any` will contain them within an existential container.
///
/// The most important use-cases for `NIOAny` are values travelling through the `ChannelPipeline` whose type can't
/// be calculated at compile time. For example:
///
///  - the `channelRead` of any `ChannelInboundHandler`
///  - the `write` method of a `ChannelOutboundHandler`
///
/// The abstraction that delivers a `NIOAny` to user code must provide a mechanism to unwrap a `NIOAny` as a
/// certain type known at run-time. Canonical example:
///
///     class SandwichHandler: ChannelInboundHandler {
///         typealias InboundIn = Bacon /* we expected to be delivered `Bacon` ... */
///         typealias InboundOut = Sandwich /* ... and we will make and deliver a `Sandwich` from that */
///
///         func channelRead(context: ChannelHandlerContext, data: NIOAny) {
///              /* we receive the `Bacon` as a `NIOAny` as at compile-time the exact configuration of the channel
///                 pipeline can't be computed. The pipeline can't be computed at compile time as it can change
///                 dynamically at run-time. Yet, we assert that in any configuration the channel handler before
///                 `SandwichHandler` does actually send us a stream of `Bacon`.
///              */
///              let bacon = self.unwrapInboundIn(data) /* `Bacon` or crash */
///              let sandwich = makeSandwich(bacon)
///              context.fireChannelRead(self.wrapInboundOut(sandwich)) /* as promised we deliver a wrapped `Sandwich` */
///         }
///     }
public struct NIOAny {
    @usableFromInline
    /* private but _versioned */ let _storage: _NIOAny

    /// Wrap a value in a `NIOAny`. In most cases you should not create a `NIOAny` directly using this constructor.
    /// The abstraction that accepts values of type `NIOAny` must also provide a mechanism to do the wrapping. An
    /// example is a `ChannelInboundHandler` which provides `self.wrapInboundOut(aValueOfTypeInboundOut)`.
    @inlinable
    public init<T>(_ value: T) {
        self._storage = _NIOAny(value)
    }

    @usableFromInline
    enum _NIOAny {
        case ioData(IOData)
        case bufferEnvelope(AddressedEnvelope<ByteBuffer>)
        case other(Any)

        @inlinable
        init<T>(_ value: T) {
            switch value {
            case let value as ByteBuffer:
                self = .ioData(.byteBuffer(value))
            case let value as FileRegion:
                self = .ioData(.fileRegion(value))
            case let value as IOData:
                self = .ioData(value)
            case let value as AddressedEnvelope<ByteBuffer>:
                self = .bufferEnvelope(value)
            default:
                assert(!(value is NIOAny))
                self = .other(value)
            }
        }
    }

    /// Try unwrapping the wrapped message as `ByteBuffer`.
    ///
    /// returns: The wrapped `ByteBuffer` or `nil` if the wrapped message is not a `ByteBuffer`.
    @inlinable
    func tryAsByteBuffer() -> ByteBuffer? {
        if case .ioData(.byteBuffer(let bb)) = self._storage {
            return bb
        } else {
            return nil
        }
    }

    /// Force unwrapping the wrapped message as `ByteBuffer`.
    ///
    /// returns: The wrapped `ByteBuffer` or crash if the wrapped message is not a `ByteBuffer`.
    @inlinable
    func forceAsByteBuffer() -> ByteBuffer {
        if let v = tryAsByteBuffer() {
            return v
        } else {
            fatalError("tried to decode as type \(ByteBuffer.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)")
        }
    }

    /// Try unwrapping the wrapped message as `IOData`.
    ///
    /// returns: The wrapped `IOData` or `nil` if the wrapped message is not a `IOData`.
    @inlinable
    func tryAsIOData() -> IOData? {
        if case .ioData(let data) = self._storage {
            return data
        } else {
            return nil
        }
    }

    /// Force unwrapping the wrapped message as `IOData`.
    ///
    /// returns: The wrapped `IOData` or crash if the wrapped message is not a `IOData`.
    @inlinable
    func forceAsIOData() -> IOData {
        if let v = tryAsIOData() {
            return v
        } else {
            fatalError("tried to decode as type \(IOData.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)")
        }
    }

    /// Try unwrapping the wrapped message as `FileRegion`.
    ///
    /// returns: The wrapped `FileRegion` or `nil` if the wrapped message is not a `FileRegion`.
    @inlinable
    func tryAsFileRegion() -> FileRegion? {
        if case .ioData(.fileRegion(let f)) = self._storage {
            return f
        } else {
            return nil
        }
    }

    /// Force unwrapping the wrapped message as `FileRegion`.
    ///
    /// returns: The wrapped `FileRegion` or crash if the wrapped message is not a `FileRegion`.
    @inlinable
    func forceAsFileRegion() -> FileRegion {
        if let v = tryAsFileRegion() {
            return v
        } else {
            fatalError("tried to decode as type \(FileRegion.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)")
        }
    }

    /// Try unwrapping the wrapped message as `AddressedEnvelope<ByteBuffer>`.
    ///
    /// returns: The wrapped `AddressedEnvelope<ByteBuffer>` or `nil` if the wrapped message is not an `AddressedEnvelope<ByteBuffer>`.
    @inlinable
    func tryAsByteEnvelope() -> AddressedEnvelope<ByteBuffer>? {
        if case .bufferEnvelope(let e) = self._storage {
            return e
        } else {
            return nil
        }
    }

    /// Force unwrapping the wrapped message as `AddressedEnvelope<ByteBuffer>`.
    ///
    /// returns: The wrapped `AddressedEnvelope<ByteBuffer>` or crash if the wrapped message is not an `AddressedEnvelope<ByteBuffer>`.
    @inlinable
    func forceAsByteEnvelope() -> AddressedEnvelope<ByteBuffer> {
        if let e = tryAsByteEnvelope() {
            return e
        } else {
            fatalError("tried to decode as type \(AddressedEnvelope<ByteBuffer>.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)")
        }
    }

    /// Try unwrapping the wrapped message as `T`.
    ///
    /// returns: The wrapped `T` or `nil` if the wrapped message is not a `T`.
    @inlinable
    func tryAsOther<T>(type: T.Type = T.self) -> T? {
        switch self._storage {
        case .bufferEnvelope(let v):
            return v as? T
        case .ioData(let v):
            return v as? T
        case .other(let v):
            return v as? T
        }
    }

    /// Force unwrapping the wrapped message as `T`.
    ///
    /// returns: The wrapped `T` or crash if the wrapped message is not a `T`.
    @inlinable
    func forceAsOther<T>(type: T.Type = T.self) -> T {
        if let v = tryAsOther(type: type) {
            return v
        } else {
            fatalError("tried to decode as type \(T.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)")
        }
    }

    /// Force unwrapping the wrapped message as `T`.
    ///
    /// returns: The wrapped `T` or crash if the wrapped message is not a `T`.
    @inlinable
    func forceAs<T>(type: T.Type = T.self) -> T {
        switch T.self {
        case let t where t == ByteBuffer.self:
            return self.forceAsByteBuffer() as! T
        case let t where t == FileRegion.self:
            return self.forceAsFileRegion() as! T
        case let t where t == IOData.self:
            return self.forceAsIOData() as! T
        case let t where t == AddressedEnvelope<ByteBuffer>.self:
            return self.forceAsByteEnvelope() as! T
        default:
            return self.forceAsOther(type: type)
        }
    }

    /// Try unwrapping the wrapped message as `T`.
    ///
    /// returns: The wrapped `T` or `nil` if the wrapped message is not a `T`.
    @inlinable
    func tryAs<T>(type: T.Type = T.self) -> T? {
        switch T.self {
        case let t where t == ByteBuffer.self:
            return self.tryAsByteBuffer() as! T?
        case let t where t == FileRegion.self:
            return self.tryAsFileRegion() as! T?
        case let t where t == IOData.self:
            return self.tryAsIOData() as! T?
        case let t where t == AddressedEnvelope<ByteBuffer>.self:
            return self.tryAsByteEnvelope() as! T?
        default:
            return self.tryAsOther(type: type)
        }
    }

    /// Unwrap the wrapped message.
    ///
    /// returns: The wrapped message.
    @inlinable
    func asAny() -> Any {
        switch self._storage {
        case .ioData(.byteBuffer(let bb)):
            return bb
        case .ioData(.fileRegion(let f)):
            return f
        case .bufferEnvelope(let e):
            return e
        case .other(let o):
            return o
        }
    }
}

extension NIOAny: CustomStringConvertible {
    public var description: String {
        return "NIOAny { \(self.asAny()) }"
    }
}