mirror of
https://github.com/apple/swift-nio.git
synced 2026-05-20 20:30:36 +00:00
Adopt swift-format (#2794)
* Apply formatting
* Apply no block comments rule
* Apply OmitExplicitReturns
* Apple OnlyOneTrailingClosureArgument
* Apply NoAssignmentInExpressions
* Fix up DontRepeatTypeInStaticProperties lint errors
* Apply `OrderedImports`
* Apply `ReplaceForEachWithForLoop`
* format file
* Enable the formatting pipeline
* Adopt `AmbiguousTrailingClosureOverload`
* Fix license header
* Fix format check
* Fix `EndOfLineComment`
* Fix CI
* Adapt CI script to check if changes when running formatting
* Separate lint and format into to steps
* Fix format
* Adopt `UseEarlyExits`
* Revert "Adopt `UseEarlyExits`"
This reverts commit d1ac5bbe12.
This commit is contained in:
@@ -10,7 +10,6 @@ jobs:
|
||||
uses: ./.github/workflows/pull_request_soundness.yml
|
||||
with:
|
||||
license_header_check_project_name: "SwiftNIO"
|
||||
format_check_enabled: false
|
||||
|
||||
call-pull-request-unit-tests-workflow:
|
||||
name: Unit tests
|
||||
|
||||
@@ -123,5 +123,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Run format check
|
||||
run: swift format lint --parallel --recursive --strict
|
||||
- name: Mark the workspace as safe
|
||||
# https://github.com/actions/checkout/issues/766
|
||||
run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
|
||||
- name: Run format lint check
|
||||
run: swift format lint --strict --recursive --parallel .
|
||||
- name: Run format and check for modified files
|
||||
run: |
|
||||
swift format format --parallel --recursive --in-place .
|
||||
git diff-index --quiet HEAD
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"version" : 1,
|
||||
"indentation" : {
|
||||
"spaces" : 4
|
||||
},
|
||||
"tabWidth" : 4,
|
||||
"fileScopedDeclarationPrivacy" : {
|
||||
"accessLevel" : "private"
|
||||
},
|
||||
"spacesAroundRangeFormationOperators" : false,
|
||||
"indentConditionalCompilationBlocks" : false,
|
||||
"indentSwitchCaseLabels" : false,
|
||||
"lineBreakAroundMultilineExpressionChainComponents" : false,
|
||||
"lineBreakBeforeControlFlowKeywords" : false,
|
||||
"lineBreakBeforeEachArgument" : true,
|
||||
"lineBreakBeforeEachGenericRequirement" : true,
|
||||
"lineLength" : 120,
|
||||
"maximumBlankLines" : 1,
|
||||
"respectsExistingLineBreaks" : true,
|
||||
"prioritizeKeepingFunctionOutputTogether" : true,
|
||||
"rules" : {
|
||||
"AllPublicDeclarationsHaveDocumentation" : false,
|
||||
"AlwaysUseLiteralForEmptyCollectionInit" : false,
|
||||
"AlwaysUseLowerCamelCase" : false,
|
||||
"AmbiguousTrailingClosureOverload" : true,
|
||||
"BeginDocumentationCommentWithOneLineSummary" : false,
|
||||
"DoNotUseSemicolons" : true,
|
||||
"DontRepeatTypeInStaticProperties" : true,
|
||||
"FileScopedDeclarationPrivacy" : true,
|
||||
"FullyIndirectEnum" : true,
|
||||
"GroupNumericLiterals" : true,
|
||||
"IdentifiersMustBeASCII" : true,
|
||||
"NeverForceUnwrap" : false,
|
||||
"NeverUseForceTry" : false,
|
||||
"NeverUseImplicitlyUnwrappedOptionals" : false,
|
||||
"NoAccessLevelOnExtensionDeclaration" : true,
|
||||
"NoAssignmentInExpressions" : true,
|
||||
"NoBlockComments" : true,
|
||||
"NoCasesWithOnlyFallthrough" : true,
|
||||
"NoEmptyTrailingClosureParentheses" : true,
|
||||
"NoLabelsInCasePatterns" : true,
|
||||
"NoLeadingUnderscores" : false,
|
||||
"NoParensAroundConditions" : true,
|
||||
"NoVoidReturnOnFunctionSignature" : true,
|
||||
"OmitExplicitReturns" : true,
|
||||
"OneCasePerLine" : true,
|
||||
"OneVariableDeclarationPerLine" : true,
|
||||
"OnlyOneTrailingClosureArgument" : true,
|
||||
"OrderedImports" : true,
|
||||
"ReplaceForEachWithForLoop" : true,
|
||||
"ReturnVoidInsteadOfEmptyTuple" : true,
|
||||
"UseEarlyExits" : false,
|
||||
"UseExplicitNilCheckInConditions" : false,
|
||||
"UseLetInEveryBoundCaseVariable" : false,
|
||||
"UseShorthandTypeNames" : true,
|
||||
"UseSingleLinePropertyGetter" : false,
|
||||
"UseSynthesizedInitializer" : false,
|
||||
"UseTripleSlashForDocumentationComments" : true,
|
||||
"UseWhereClausesInForLoops" : false,
|
||||
"ValidateDocumentationComments" : false
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import NIOEmbedded
|
||||
|
||||
let benchmarks = {
|
||||
let defaultMetrics: [BenchmarkMetric] = [
|
||||
.mallocCountTotal,
|
||||
.mallocCountTotal
|
||||
]
|
||||
|
||||
Benchmark(
|
||||
@@ -28,7 +28,7 @@ let benchmarks = {
|
||||
// Elide the cost of the 'EmbeddedChannel'. It's only used for its pipeline.
|
||||
var channels: [EmbeddedChannel] = []
|
||||
channels.reserveCapacity(benchmark.scaledIterations.count)
|
||||
for _ in 0 ..< benchmark.scaledIterations.count {
|
||||
for _ in 0..<benchmark.scaledIterations.count {
|
||||
channels.append(EmbeddedChannel())
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next()
|
||||
|
||||
let benchmarks = {
|
||||
let defaultMetrics: [BenchmarkMetric] = [
|
||||
.mallocCountTotal,
|
||||
.mallocCountTotal
|
||||
]
|
||||
|
||||
Benchmark(
|
||||
|
||||
@@ -22,7 +22,7 @@ func runTCPEchoAsyncChannel(numberOfWrites: Int, eventLoop: EventLoop) async thr
|
||||
port: 0
|
||||
) { channel in
|
||||
channel.eventLoop.makeCompletedFuture {
|
||||
return try NIOAsyncChannel(
|
||||
try NIOAsyncChannel(
|
||||
wrappingChannelSynchronously: channel,
|
||||
configuration: .init(
|
||||
inboundType: ByteBuffer.self,
|
||||
@@ -38,7 +38,7 @@ func runTCPEchoAsyncChannel(numberOfWrites: Int, eventLoop: EventLoop) async thr
|
||||
port: serverChannel.channel.localAddress!.port!
|
||||
) { channel in
|
||||
channel.eventLoop.makeCompletedFuture {
|
||||
return try NIOAsyncChannel(
|
||||
try NIOAsyncChannel(
|
||||
wrappingChannelSynchronously: channel,
|
||||
configuration: .init(
|
||||
inboundType: ByteBuffer.self,
|
||||
@@ -55,7 +55,9 @@ func runTCPEchoAsyncChannel(numberOfWrites: Int, eventLoop: EventLoop) async thr
|
||||
group.addTask {
|
||||
try await serverChannel.executeThenClose { serverChannelInbound in
|
||||
for try await connectionChannel in serverChannelInbound {
|
||||
try await connectionChannel.executeThenClose { connectionChannelInbound, connectionChannelOutbound in
|
||||
try await connectionChannel.executeThenClose {
|
||||
connectionChannelInbound,
|
||||
connectionChannelOutbound in
|
||||
for try await inboundData in connectionChannelInbound {
|
||||
try await connectionChannelOutbound.write(inboundData)
|
||||
}
|
||||
@@ -68,7 +70,7 @@ func runTCPEchoAsyncChannel(numberOfWrites: Int, eventLoop: EventLoop) async thr
|
||||
// This child task is collecting the echoed back responses.
|
||||
group.addTask {
|
||||
var receivedData = 0
|
||||
for try await inboundData in inbound {
|
||||
for try await inboundData in inbound {
|
||||
receivedData += inboundData.readableBytes
|
||||
|
||||
if receivedData == numberOfWrites * messageSize {
|
||||
|
||||
@@ -25,9 +25,9 @@ import Glibc
|
||||
typealias EnqueueGlobalHook = @convention(thin) (UnownedJob, @convention(thin) (UnownedJob) -> Void) -> Void
|
||||
|
||||
var swiftTaskEnqueueGlobalHook: EnqueueGlobalHook? {
|
||||
get { _swiftTaskEnqueueGlobalHook.pointee }
|
||||
set { _swiftTaskEnqueueGlobalHook.pointee = newValue }
|
||||
get { _swiftTaskEnqueueGlobalHook.pointee }
|
||||
set { _swiftTaskEnqueueGlobalHook.pointee = newValue }
|
||||
}
|
||||
|
||||
private let _swiftTaskEnqueueGlobalHook: UnsafeMutablePointer<EnqueueGlobalHook?> =
|
||||
dlsym(dlopen(nil, RTLD_LAZY), "swift_task_enqueueGlobal_hook").assumingMemoryBound(to: EnqueueGlobalHook?.self)
|
||||
dlsym(dlopen(nil, RTLD_LAZY), "swift_task_enqueueGlobal_hook").assumingMemoryBound(to: EnqueueGlobalHook?.self)
|
||||
|
||||
@@ -5,7 +5,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "benchmarks",
|
||||
platforms: [
|
||||
.macOS("14"),
|
||||
.macOS("14")
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../"),
|
||||
|
||||
+4
-3
@@ -19,12 +19,13 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "AtomicCounter",
|
||||
products: [
|
||||
.library(name: "AtomicCounter", type: .dynamic, targets: ["AtomicCounter"]),
|
||||
.library(name: "AtomicCounter", type: .dynamic, targets: ["AtomicCounter"])
|
||||
],
|
||||
dependencies: [ ],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "AtomicCounter",
|
||||
dependencies: []),
|
||||
dependencies: []
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
+3
-3
@@ -19,12 +19,12 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "HookedFunctions",
|
||||
products: [
|
||||
.library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"]),
|
||||
.library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "../AtomicCounter/", branch: "main"),
|
||||
.package(url: "../AtomicCounter/", branch: "main")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "HookedFunctions", dependencies: ["AtomicCounter"]),
|
||||
.target(name: "HookedFunctions", dependencies: ["AtomicCounter"])
|
||||
]
|
||||
)
|
||||
|
||||
+3
-3
@@ -19,12 +19,12 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "HookedFunctions",
|
||||
products: [
|
||||
.library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"]),
|
||||
.library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "../AtomicCounter/", branch: "main"),
|
||||
.package(url: "../AtomicCounter/", branch: "main")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "HookedFunctions", dependencies: ["AtomicCounter"]),
|
||||
.target(name: "HookedFunctions", dependencies: ["AtomicCounter"])
|
||||
]
|
||||
)
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import AtomicCounter
|
||||
import Foundation
|
||||
|
||||
#if canImport(Darwin)
|
||||
import Darwin
|
||||
#elseif canImport(Glibc)
|
||||
@@ -24,7 +25,7 @@ import Glibc
|
||||
|
||||
func waitForThreadsToQuiesce(shouldReachZero: Bool) {
|
||||
func getUnfreed() -> Int {
|
||||
return AtomicCounter.read_malloc_counter() - AtomicCounter.read_free_counter()
|
||||
AtomicCounter.read_malloc_counter() - AtomicCounter.read_free_counter()
|
||||
}
|
||||
|
||||
var oldNumberOfUnfreed = getUnfreed()
|
||||
@@ -35,7 +36,8 @@ func waitForThreadsToQuiesce(shouldReachZero: Bool) {
|
||||
return
|
||||
}
|
||||
count += 1
|
||||
usleep(shouldReachZero ? 50_000 : 200_000) // allocs/frees happen on multiple threads, allow some cool down time
|
||||
// allocs/frees happen on multiple threads, allow some cool down time
|
||||
usleep(shouldReachZero ? 50_000 : 200_000)
|
||||
let newNumberOfUnfreed = getUnfreed()
|
||||
if oldNumberOfUnfreed == newNumberOfUnfreed && (!shouldReachZero || newNumberOfUnfreed <= 0) {
|
||||
// nothing happened in the last 100ms, let's assume everything's
|
||||
@@ -57,7 +59,11 @@ struct Measurement {
|
||||
}
|
||||
|
||||
extension Array where Element == Measurement {
|
||||
private func printIntegerMetric(_ keyPath: KeyPath<Measurement, Int>, description desc: String, metricName k: String) {
|
||||
private func printIntegerMetric(
|
||||
_ keyPath: KeyPath<Measurement, Int>,
|
||||
description desc: String,
|
||||
metricName k: String
|
||||
) {
|
||||
let vs = self.map { $0[keyPath: keyPath] }
|
||||
print("\(desc).\(k): \(vs.min() ?? -1)")
|
||||
}
|
||||
@@ -90,13 +96,13 @@ func measureAll(trackFDs: Bool, _ fn: () -> Int) -> [Measurement] {
|
||||
AtomicCounter.begin_tracking_fds()
|
||||
}
|
||||
|
||||
#if canImport(Darwin)
|
||||
#if canImport(Darwin)
|
||||
autoreleasepool {
|
||||
_ = fn()
|
||||
}
|
||||
#else
|
||||
#else
|
||||
_ = fn()
|
||||
#endif
|
||||
#endif
|
||||
waitForThreadsToQuiesce(shouldReachZero: !throwAway)
|
||||
let frees = AtomicCounter.read_free_counter()
|
||||
let mallocs = AtomicCounter.read_malloc_counter()
|
||||
@@ -121,7 +127,7 @@ func measureAll(trackFDs: Bool, _ fn: () -> Int) -> [Measurement] {
|
||||
)
|
||||
}
|
||||
|
||||
_ = measureOne(throwAway: true, trackFDs: trackFDs, fn) /* pre-heat and throw away */
|
||||
_ = measureOne(throwAway: true, trackFDs: trackFDs, fn) // pre-heat and throw away
|
||||
|
||||
var measurements: [Measurement] = []
|
||||
for _ in 0..<10 {
|
||||
@@ -132,19 +138,19 @@ func measureAll(trackFDs: Bool, _ fn: () -> Int) -> [Measurement] {
|
||||
return measurements
|
||||
}
|
||||
|
||||
func measureAndPrint(desc: String, trackFDs: Bool, fn: () -> Int) -> Void {
|
||||
func measureAndPrint(desc: String, trackFDs: Bool, fn: () -> Int) {
|
||||
let measurements = measureAll(trackFDs: trackFDs, fn)
|
||||
measurements.printTotalAllocations(description: desc)
|
||||
measurements.printRemainingAllocations(description: desc)
|
||||
measurements.printTotalAllocatedBytes(description: desc)
|
||||
measurements.printLeakedFDs(description: desc)
|
||||
|
||||
|
||||
print("DEBUG: \(measurements)")
|
||||
}
|
||||
|
||||
public func measure(identifier: String, trackFDs: Bool = false, _ body: () -> Int) {
|
||||
measureAndPrint(desc: identifier, trackFDs: trackFDs) {
|
||||
return body()
|
||||
body()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +166,7 @@ func measureAll(trackFDs: Bool, _ fn: @escaping () async -> Int) -> [Measurement
|
||||
}
|
||||
group.wait()
|
||||
}
|
||||
|
||||
|
||||
if trackFDs {
|
||||
AtomicCounter.begin_tracking_fds()
|
||||
}
|
||||
@@ -169,13 +175,13 @@ func measureAll(trackFDs: Bool, _ fn: @escaping () async -> Int) -> [Measurement
|
||||
AtomicCounter.reset_malloc_counter()
|
||||
AtomicCounter.reset_malloc_bytes_counter()
|
||||
|
||||
#if canImport(Darwin)
|
||||
#if canImport(Darwin)
|
||||
autoreleasepool {
|
||||
run(fn)
|
||||
}
|
||||
#else
|
||||
#else
|
||||
run(fn)
|
||||
#endif
|
||||
#endif
|
||||
waitForThreadsToQuiesce(shouldReachZero: !throwAway)
|
||||
let frees = AtomicCounter.read_free_counter()
|
||||
let mallocs = AtomicCounter.read_malloc_counter()
|
||||
@@ -200,7 +206,7 @@ func measureAll(trackFDs: Bool, _ fn: @escaping () async -> Int) -> [Measurement
|
||||
)
|
||||
}
|
||||
|
||||
_ = measureOne(throwAway: true, trackFDs: trackFDs, fn) /* pre-heat and throw away */
|
||||
_ = measureOne(throwAway: true, trackFDs: trackFDs, fn) // pre-heat and throw away
|
||||
|
||||
var measurements: [Measurement] = []
|
||||
for _ in 0..<10 {
|
||||
@@ -212,7 +218,7 @@ func measureAll(trackFDs: Bool, _ fn: @escaping () async -> Int) -> [Measurement
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
func measureAndPrint(desc: String, trackFDs: Bool, fn: @escaping () async -> Int) -> Void {
|
||||
func measureAndPrint(desc: String, trackFDs: Bool, fn: @escaping () async -> Int) {
|
||||
let measurements = measureAll(trackFDs: trackFDs, fn)
|
||||
measurements.printTotalAllocations(description: desc)
|
||||
measurements.printRemainingAllocations(description: desc)
|
||||
|
||||
@@ -14,54 +14,56 @@
|
||||
|
||||
import Foundation
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
import NIOHTTP1
|
||||
import NIOPosix
|
||||
|
||||
let localhostPickPort = try! SocketAddress.makeAddressResolvingHost("127.0.0.1", port: 0)
|
||||
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
|
||||
|
||||
final class RepeatedRequests: ChannelInboundHandler {
|
||||
typealias InboundIn = HTTPClientResponsePart
|
||||
typealias OutboundOut = HTTPClientRequestPart
|
||||
typealias InboundIn = HTTPClientResponsePart
|
||||
typealias OutboundOut = HTTPClientRequestPart
|
||||
|
||||
private let numberOfRequests: Int
|
||||
private var remainingNumberOfRequests: Int
|
||||
private let isDonePromise: EventLoopPromise<Int>
|
||||
static var requestHead: HTTPRequestHead {
|
||||
private let numberOfRequests: Int
|
||||
private var remainingNumberOfRequests: Int
|
||||
private let isDonePromise: EventLoopPromise<Int>
|
||||
static var requestHead: HTTPRequestHead {
|
||||
var head = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/allocation-test-1")
|
||||
head.headers.add(name: "Host", value: "foo-\(ObjectIdentifier(self)).com")
|
||||
return head
|
||||
}
|
||||
head.headers.add(name: "Host", value: "foo-\(ObjectIdentifier(self)).com")
|
||||
return head
|
||||
}
|
||||
|
||||
init(numberOfRequests: Int, eventLoop: EventLoop) {
|
||||
self.remainingNumberOfRequests = numberOfRequests
|
||||
self.numberOfRequests = numberOfRequests
|
||||
self.isDonePromise = eventLoop.makePromise()
|
||||
}
|
||||
init(numberOfRequests: Int, eventLoop: EventLoop) {
|
||||
self.remainingNumberOfRequests = numberOfRequests
|
||||
self.numberOfRequests = numberOfRequests
|
||||
self.isDonePromise = eventLoop.makePromise()
|
||||
}
|
||||
|
||||
func wait() throws -> Int {
|
||||
let reqs = try self.isDonePromise.futureResult.wait()
|
||||
precondition(reqs == self.numberOfRequests)
|
||||
return reqs
|
||||
}
|
||||
func wait() throws -> Int {
|
||||
let reqs = try self.isDonePromise.futureResult.wait()
|
||||
precondition(reqs == self.numberOfRequests)
|
||||
return reqs
|
||||
}
|
||||
|
||||
func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
context.channel.close(promise: nil)
|
||||
self.isDonePromise.fail(error)
|
||||
}
|
||||
func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
context.channel.close(promise: nil)
|
||||
self.isDonePromise.fail(error)
|
||||
}
|
||||
|
||||
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
let respPart = Self.unwrapInboundIn(data)
|
||||
if case .end(nil) = respPart {
|
||||
if self.remainingNumberOfRequests <= 0 {
|
||||
context.channel.close().map { self.numberOfRequests - self.remainingNumberOfRequests }.cascade(to: self.isDonePromise)
|
||||
} else {
|
||||
self.remainingNumberOfRequests -= 1
|
||||
context.write(Self.wrapOutboundOut(.head(RepeatedRequests.requestHead)), promise: nil)
|
||||
context.writeAndFlush(Self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
let respPart = self.unwrapInboundIn(data)
|
||||
if case .end(nil) = respPart {
|
||||
if self.remainingNumberOfRequests <= 0 {
|
||||
context.channel.close().map { self.numberOfRequests - self.remainingNumberOfRequests }.cascade(
|
||||
to: self.isDonePromise
|
||||
)
|
||||
} else {
|
||||
self.remainingNumberOfRequests -= 1
|
||||
context.write(self.wrapOutboundOut(.head(RepeatedRequests.requestHead)), promise: nil)
|
||||
context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SimpleHTTPServer: ChannelInboundHandler {
|
||||
@@ -94,10 +96,13 @@ private final class SimpleHTTPServer: ChannelInboundHandler {
|
||||
}
|
||||
|
||||
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
if case .head(let req) = Self.unwrapInboundIn(data), req.uri == "/allocation-test-1" {
|
||||
context.write(Self.wrapOutboundOut(.head(self.responseHead)), promise: nil)
|
||||
context.write(Self.wrapOutboundOut(.body(.byteBuffer(self.responseBody(allocator: context.channel.allocator)))), promise: nil)
|
||||
context.writeAndFlush(Self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||
if case .head(let req) = self.unwrapInboundIn(data), req.uri == "/allocation-test-1" {
|
||||
context.write(self.wrapOutboundOut(.head(self.responseHead)), promise: nil)
|
||||
context.write(
|
||||
self.wrapOutboundOut(.body(.byteBuffer(self.responseBody(allocator: context.channel.allocator)))),
|
||||
promise: nil
|
||||
)
|
||||
context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +111,10 @@ func doRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> I
|
||||
let serverChannel = try ServerBootstrap(group: group)
|
||||
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
|
||||
.childChannelInitializer { channel in
|
||||
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true,
|
||||
withErrorHandling: false).flatMap {
|
||||
channel.pipeline.configureHTTPServerPipeline(
|
||||
withPipeliningAssistance: true,
|
||||
withErrorHandling: false
|
||||
).flatMap {
|
||||
channel.pipeline.addHandler(SimpleHTTPServer())
|
||||
}
|
||||
}.bind(to: localhostPickPort).wait()
|
||||
@@ -116,7 +123,6 @@ func doRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> I
|
||||
try! serverChannel.close().wait()
|
||||
}
|
||||
|
||||
|
||||
let repeatedRequestsHandler = RepeatedRequests(numberOfRequests: numberOfRequests, eventLoop: group.next())
|
||||
|
||||
let clientChannel = try ClientBootstrap(group: group)
|
||||
@@ -176,42 +182,42 @@ enum UDPShared {
|
||||
public typealias InboundIn = AddressedEnvelope<ByteBuffer>
|
||||
public typealias OutboundOut = AddressedEnvelope<ByteBuffer>
|
||||
private var repetitionsRemaining: Int
|
||||
|
||||
|
||||
private let remoteAddress: SocketAddress
|
||||
|
||||
|
||||
init(remoteAddress: SocketAddress, numberOfRepetitions: Int) {
|
||||
self.remoteAddress = remoteAddress
|
||||
self.repetitionsRemaining = numberOfRepetitions
|
||||
}
|
||||
|
||||
|
||||
public func channelActive(context: ChannelHandlerContext) {
|
||||
// Channel is available. It's time to send the message to the server to initialize the ping-pong sequence.
|
||||
self.sendSomeDataIfDesiredOrClose(context: context)
|
||||
}
|
||||
|
||||
|
||||
private func sendSomeDataIfDesiredOrClose(context: ChannelHandlerContext) {
|
||||
if repetitionsRemaining > 0 {
|
||||
repetitionsRemaining -= 1
|
||||
|
||||
|
||||
// Set the transmission data.
|
||||
let line = "Something to send there and back again."
|
||||
let buffer = context.channel.allocator.buffer(string: line)
|
||||
|
||||
|
||||
// Forward the data.
|
||||
let envolope = AddressedEnvelope<ByteBuffer>(remoteAddress: remoteAddress, data: buffer)
|
||||
|
||||
context.writeAndFlush(Self.wrapOutboundOut(envolope), promise: nil)
|
||||
|
||||
context.writeAndFlush(self.wrapOutboundOut(envolope), promise: nil)
|
||||
} else {
|
||||
// We're all done - hurrah!
|
||||
context.close(promise: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
// Got back a response - maybe send some more.
|
||||
self.sendSomeDataIfDesiredOrClose(context: context)
|
||||
}
|
||||
|
||||
|
||||
public func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
// Errors should never happen.
|
||||
fatalError("EchoHandlerClient received errorCaught")
|
||||
@@ -222,20 +228,24 @@ enum UDPShared {
|
||||
let serverChannel = try DatagramBootstrap(group: group)
|
||||
// Set the handlers that are applied to the bound channel
|
||||
.channelInitializer { channel in
|
||||
return channel.pipeline.addHandler(EchoHandler())
|
||||
channel.pipeline.addHandler(EchoHandler())
|
||||
}
|
||||
.bind(to: localhostPickPort).wait()
|
||||
|
||||
defer {
|
||||
try! serverChannel.close().wait()
|
||||
}
|
||||
|
||||
|
||||
let remoteAddress = serverChannel.localAddress!
|
||||
|
||||
let clientChannel = try DatagramBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
channel.pipeline.addHandler(EchoHandlerClient(remoteAddress: remoteAddress,
|
||||
numberOfRepetitions: numberOfRequests))
|
||||
channel.pipeline.addHandler(
|
||||
EchoHandlerClient(
|
||||
remoteAddress: remoteAddress,
|
||||
numberOfRepetitions: numberOfRequests
|
||||
)
|
||||
)
|
||||
}
|
||||
.bind(to: localhostPickPort).wait()
|
||||
|
||||
|
||||
+9
-4
@@ -15,10 +15,12 @@
|
||||
import NIOCore
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
fileprivate typealias SequenceProducer = NIOAsyncSequenceProducer<Int, NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark, Delegate>
|
||||
private typealias SequenceProducer = NIOAsyncSequenceProducer<
|
||||
Int, NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark, Delegate
|
||||
>
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
fileprivate final class Delegate: NIOAsyncSequenceProducerDelegate, @unchecked Sendable {
|
||||
private final class Delegate: NIOAsyncSequenceProducerDelegate, @unchecked Sendable {
|
||||
private let elements = Array(repeating: 1, count: 1000)
|
||||
|
||||
var source: SequenceProducer.Source!
|
||||
@@ -36,7 +38,10 @@ func run(identifier: String) {
|
||||
}
|
||||
measure(identifier: identifier) {
|
||||
let delegate = Delegate()
|
||||
let producer = SequenceProducer.makeSequence(backPressureStrategy: .init(lowWatermark: 100, highWatermark: 500), delegate: delegate)
|
||||
let producer = SequenceProducer.makeSequence(
|
||||
backPressureStrategy: .init(lowWatermark: 100, highWatermark: 500),
|
||||
delegate: delegate
|
||||
)
|
||||
let sequence = producer.sequence
|
||||
delegate.source = producer.source
|
||||
|
||||
@@ -44,7 +49,7 @@ func run(identifier: String) {
|
||||
for await i in sequence {
|
||||
counter += i
|
||||
|
||||
if counter == 10000000 {
|
||||
if counter == 10_000_000 {
|
||||
return counter
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -12,11 +12,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import NIOCore
|
||||
import DequeModule
|
||||
import NIOCore
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
fileprivate struct Delegate: NIOAsyncWriterSinkDelegate, Sendable {
|
||||
private struct Delegate: NIOAsyncWriterSinkDelegate, Sendable {
|
||||
typealias Element = Int
|
||||
|
||||
func didYield(contentsOf sequence: Deque<Int>) {}
|
||||
@@ -33,10 +33,10 @@ func run(identifier: String) {
|
||||
let newWriter = NIOAsyncWriter<Int, Delegate>.makeWriter(isWritable: true, delegate: delegate)
|
||||
let writer = newWriter.writer
|
||||
|
||||
for i in 0..<1000000 {
|
||||
for i in 0..<1_000_000 {
|
||||
try! await writer.yield(i)
|
||||
}
|
||||
|
||||
return 1000000
|
||||
return 1_000_000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOEmbedded
|
||||
|
||||
fileprivate final class SimpleHandler: ChannelInboundHandler {
|
||||
private final class SimpleHandler: ChannelInboundHandler {
|
||||
typealias InboundIn = NIOAny
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func run(identifier: String) {
|
||||
}
|
||||
try! channel.pipeline.addHandlers([
|
||||
SimpleHandler(),
|
||||
SimpleHandler()
|
||||
SimpleHandler(),
|
||||
]).wait()
|
||||
}
|
||||
return iterations
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOEmbedded
|
||||
|
||||
fileprivate final class SimpleHandler: ChannelInboundHandler {
|
||||
private final class SimpleHandler: ChannelInboundHandler {
|
||||
typealias InboundIn = NIOAny
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func run(identifier: String) {
|
||||
_ = try! channel.finish()
|
||||
}
|
||||
try! channel.pipeline.syncOperations.addHandlers([
|
||||
SimpleHandler(),
|
||||
SimpleHandler()
|
||||
SimpleHandler(),
|
||||
SimpleHandler(),
|
||||
])
|
||||
}
|
||||
return iterations
|
||||
|
||||
+5
-2
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOEmbedded
|
||||
|
||||
fileprivate final class RemovableHandler: ChannelInboundHandler, RemovableChannelHandler {
|
||||
private final class RemovableHandler: ChannelInboundHandler, RemovableChannelHandler {
|
||||
typealias InboundIn = NIOAny
|
||||
|
||||
static let name: String = "RemovableHandler"
|
||||
@@ -32,7 +32,10 @@ fileprivate final class RemovableHandler: ChannelInboundHandler, RemovableChanne
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func addRemoveBench(iterations: Int, _ removalOperation: (Channel, RemovableHandler) -> EventLoopFuture<Void>) -> Int {
|
||||
private func addRemoveBench(
|
||||
iterations: Int,
|
||||
_ removalOperation: (Channel, RemovableHandler) -> EventLoopFuture<Void>
|
||||
) -> Int {
|
||||
let channel = EmbeddedChannel()
|
||||
defer {
|
||||
_ = try! channel.finish()
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ func run(identifier: String) {
|
||||
}
|
||||
|
||||
let server = try! ServerBootstrap(group: group)
|
||||
.bind(host: "127.0.0.1", port: 0)
|
||||
.wait()
|
||||
.bind(host: "127.0.0.1", port: 0)
|
||||
.wait()
|
||||
defer {
|
||||
try! server.close().wait()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOEmbedded
|
||||
|
||||
fileprivate final class SimpleHandler: ChannelInboundHandler {
|
||||
private final class SimpleHandler: ChannelInboundHandler {
|
||||
typealias InboundIn = NIOAny
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOEmbedded
|
||||
|
||||
fileprivate final class SimpleHandler: ChannelInboundHandler {
|
||||
private final class SimpleHandler: ChannelInboundHandler {
|
||||
typealias InboundIn = NIOAny
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ func run(identifier: String) {
|
||||
let serverConnection = try! ServerBootstrap(group: group)
|
||||
.bind(host: "localhost", port: 0)
|
||||
.wait()
|
||||
|
||||
|
||||
let serverAddress = serverConnection.localAddress!
|
||||
let clientBootstrap = ClientBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
@@ -48,7 +48,7 @@ func run(identifier: String) {
|
||||
let iterations = 1000
|
||||
for _ in 0..<iterations {
|
||||
let conn = clientBootstrap.connect(to: serverAddress)
|
||||
|
||||
|
||||
let _: Void? = try? conn.flatMap { channel in
|
||||
(channel as! SocketOptionProvider).setSoLinger(linger(l_onoff: 1, l_linger: 0)).flatMap {
|
||||
channel.closeFuture
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
fileprivate final class DoNothingHandler: ChannelInboundHandler {
|
||||
private final class DoNothingHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = ByteBuffer
|
||||
public typealias OutboundOut = ByteBuffer
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func run(identifier: String) {
|
||||
measure(identifier: identifier) {
|
||||
let numberOfIterations = 1000
|
||||
let doNothingHandler = DoNothingHandler()
|
||||
for _ in 0 ..< numberOfIterations {
|
||||
for _ in 0..<numberOfIterations {
|
||||
_ = ClientBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
channel.pipeline.addHTTPClientHandlers().flatMap {
|
||||
|
||||
+11
-11
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
fileprivate final class ReceiveAndCloseHandler: ChannelInboundHandler {
|
||||
private final class ReceiveAndCloseHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = ByteBuffer
|
||||
public typealias OutboundOut = ByteBuffer
|
||||
|
||||
@@ -30,15 +30,15 @@ fileprivate final class ReceiveAndCloseHandler: ChannelInboundHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class LingerSettingHandler: ChannelInboundHandler {
|
||||
private final class LingerSettingHandler: ChannelInboundHandler {
|
||||
typealias InboundIn = ByteBuffer
|
||||
typealias OutboundOut = ByteBuffer
|
||||
|
||||
|
||||
public func handlerAdded(context: ChannelHandlerContext) {
|
||||
(context.channel as? SocketOptionProvider)?.setSoLinger(linger(l_onoff: 1, l_linger: 0))
|
||||
.whenFailure({ error in fatalError("Failed to set linger \(error)") })
|
||||
}
|
||||
|
||||
|
||||
func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
fatalError("unexpected \(error)")
|
||||
}
|
||||
@@ -51,10 +51,10 @@ func run(identifier: String) {
|
||||
}
|
||||
|
||||
let serverChannel = try! ServerBootstrap(group: group)
|
||||
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
|
||||
.childChannelInitializer { channel in
|
||||
channel.pipeline.addHandler(ReceiveAndCloseHandler())
|
||||
}.bind(host: "127.0.0.1", port: 0).wait()
|
||||
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
|
||||
.childChannelInitializer { channel in
|
||||
channel.pipeline.addHandler(ReceiveAndCloseHandler())
|
||||
}.bind(host: "127.0.0.1", port: 0).wait()
|
||||
defer {
|
||||
try! serverChannel.close().wait()
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func run(identifier: String) {
|
||||
let buffer = ByteBuffer(integer: 1, as: UInt8.self)
|
||||
let el = group.next()
|
||||
|
||||
for _ in 0 ..< numberOfIterations {
|
||||
for _ in 0..<numberOfIterations {
|
||||
try! el.flatSubmit {
|
||||
clientBootstrap.connect(to: serverAddress).flatMap { (clientChannel) -> EventLoopFuture<Void> in
|
||||
writeWaitAndClose(clientChannel: clientChannel, buffer: buffer)
|
||||
@@ -82,9 +82,9 @@ func run(identifier: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func writeWaitAndClose(clientChannel: Channel, buffer: ByteBuffer) -> EventLoopFuture<Void> {
|
||||
private func writeWaitAndClose(clientChannel: Channel, buffer: ByteBuffer) -> EventLoopFuture<Void> {
|
||||
// Send a byte to make sure everything is really open.
|
||||
return clientChannel.writeAndFlush(buffer).flatMap {
|
||||
clientChannel.writeAndFlush(buffer).flatMap {
|
||||
clientChannel.closeFuture
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
fileprivate final class ServerEchoHandler: ChannelInboundHandler {
|
||||
private final class ServerEchoHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = AddressedEnvelope<ByteBuffer>
|
||||
public typealias OutboundOut = AddressedEnvelope<ByteBuffer>
|
||||
|
||||
@@ -35,19 +35,19 @@ fileprivate final class ServerEchoHandler: ChannelInboundHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class ClientHandler: ChannelInboundHandler {
|
||||
private final class ClientHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = AddressedEnvelope<ByteBuffer>
|
||||
public typealias OutboundOut = AddressedEnvelope<ByteBuffer>
|
||||
|
||||
|
||||
private let remoteAddress: SocketAddress
|
||||
|
||||
|
||||
init(remoteAddress: SocketAddress) {
|
||||
self.remoteAddress = remoteAddress
|
||||
}
|
||||
|
||||
|
||||
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
// If we still have iterations to do send some more data.
|
||||
if (self.iterationsOutstanding > 0) {
|
||||
if self.iterationsOutstanding > 0 {
|
||||
self.iterationsOutstanding -= 1
|
||||
sendBytes(clientChannel: context.channel)
|
||||
} else {
|
||||
@@ -63,10 +63,10 @@ fileprivate final class ClientHandler: ChannelInboundHandler {
|
||||
public func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
|
||||
var iterationsOutstanding = 0
|
||||
var whenDone: EventLoopPromise<Void>? = nil
|
||||
|
||||
|
||||
private func sendBytes(clientChannel: Channel) {
|
||||
var buffer = clientChannel.allocator.buffer(capacity: 1)
|
||||
buffer.writeInteger(3, as: UInt8.self)
|
||||
@@ -75,10 +75,10 @@ fileprivate final class ClientHandler: ChannelInboundHandler {
|
||||
let envelope = AddressedEnvelope<ByteBuffer>(remoteAddress: remoteAddress, data: buffer, metadata: metadata)
|
||||
clientChannel.writeAndFlush(Self.wrapOutboundOut(envelope), promise: nil)
|
||||
}
|
||||
|
||||
|
||||
func sendBytesAndWaitForReply(clientChannel: Channel) -> Int {
|
||||
let numberOfIterations = 1000
|
||||
|
||||
|
||||
// Setup for iteration.
|
||||
self.iterationsOutstanding = numberOfIterations
|
||||
self.whenDone = clientChannel.eventLoop.makePromise()
|
||||
@@ -95,7 +95,7 @@ func run(identifier: String) {
|
||||
.channelOption(ChannelOptions.explicitCongestionNotification, value: true)
|
||||
// Set the handlers that are applied to the bound channel
|
||||
.channelInitializer { channel in
|
||||
return channel.pipeline.addHandler(ServerEchoHandler())
|
||||
channel.pipeline.addHandler(ServerEchoHandler())
|
||||
}
|
||||
.bind(to: localhostPickPort).wait()
|
||||
defer {
|
||||
@@ -114,9 +114,8 @@ func run(identifier: String) {
|
||||
defer {
|
||||
try! clientChannel.close().wait()
|
||||
}
|
||||
|
||||
|
||||
measure(identifier: identifier) {
|
||||
clientHandler.sendBytesAndWaitForReply(clientChannel: clientChannel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
fileprivate final class DoNothingHandler: ChannelInboundHandler {
|
||||
private final class DoNothingHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = ByteBuffer
|
||||
public typealias OutboundOut = ByteBuffer
|
||||
}
|
||||
@@ -23,7 +23,7 @@ fileprivate final class DoNothingHandler: ChannelInboundHandler {
|
||||
func run(identifier: String) {
|
||||
measure(identifier: identifier) {
|
||||
let numberOfIterations = 1000
|
||||
for _ in 0 ..< numberOfIterations {
|
||||
for _ in 0..<numberOfIterations {
|
||||
_ = DatagramBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
channel.pipeline.addHandler(DoNothingHandler())
|
||||
|
||||
+15
-14
@@ -15,22 +15,22 @@
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
fileprivate final class CountReadsHandler: ChannelInboundHandler {
|
||||
private final class CountReadsHandler: ChannelInboundHandler {
|
||||
public typealias InboundIn = ByteBuffer
|
||||
public typealias OutboundOut = ByteBuffer
|
||||
|
||||
|
||||
private var readsRemaining: Int
|
||||
private let completed: EventLoopPromise<Void>
|
||||
|
||||
|
||||
var completionFuture: EventLoopFuture<Void> {
|
||||
return self.completed.futureResult
|
||||
self.completed.futureResult
|
||||
}
|
||||
|
||||
|
||||
init(numberOfReadsExpected: Int, completionPromise: EventLoopPromise<Void>) {
|
||||
self.readsRemaining = numberOfReadsExpected
|
||||
self.completed = completionPromise
|
||||
}
|
||||
|
||||
|
||||
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
self.readsRemaining -= 1
|
||||
if self.readsRemaining <= 0 {
|
||||
@@ -41,13 +41,15 @@ fileprivate final class CountReadsHandler: ChannelInboundHandler {
|
||||
|
||||
func run(identifier: String) {
|
||||
let numberOfIterations = 1000
|
||||
|
||||
let serverHandler = CountReadsHandler(numberOfReadsExpected: numberOfIterations,
|
||||
completionPromise: group.next().makePromise())
|
||||
|
||||
let serverHandler = CountReadsHandler(
|
||||
numberOfReadsExpected: numberOfIterations,
|
||||
completionPromise: group.next().makePromise()
|
||||
)
|
||||
let serverChannel = try! DatagramBootstrap(group: group)
|
||||
// Set the handlers that are applied to the bound channel
|
||||
.channelInitializer { channel in
|
||||
return channel.pipeline.addHandler(serverHandler)
|
||||
channel.pipeline.addHandler(serverHandler)
|
||||
}
|
||||
.bind(to: localhostPickPort).wait()
|
||||
defer {
|
||||
@@ -55,13 +57,13 @@ func run(identifier: String) {
|
||||
}
|
||||
|
||||
let remoteAddress = serverChannel.localAddress!
|
||||
|
||||
|
||||
let clientBootstrap = DatagramBootstrap(group: group)
|
||||
|
||||
measure(identifier: identifier) {
|
||||
let buffer = ByteBuffer(integer: 1, as: UInt8.self)
|
||||
for _ in 0 ..< numberOfIterations {
|
||||
try! clientBootstrap.bind(to: localhostPickPort).flatMap { clientChannel -> EventLoopFuture<Void> in
|
||||
for _ in 0..<numberOfIterations {
|
||||
try! clientBootstrap.bind(to: localhostPickPort).flatMap { clientChannel -> EventLoopFuture<Void> in
|
||||
// Send a byte to make sure everything is really open.
|
||||
let envelope = AddressedEnvelope<ByteBuffer>(remoteAddress: remoteAddress, data: buffer)
|
||||
return clientChannel.writeAndFlush(envelope).flatMap {
|
||||
@@ -73,4 +75,3 @@ func run(identifier: String) {
|
||||
return numberOfIterations
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -25,7 +25,7 @@ func run(identifier: String) {
|
||||
let substring = Substring("A")
|
||||
@inline(never)
|
||||
func doWrites(buffer: inout ByteBuffer, dispatchData: DispatchData, substring: Substring) {
|
||||
/* these ones are zero allocations */
|
||||
// these ones are zero allocations
|
||||
// buffer.writeBytes(foundationData) // see SR-7542
|
||||
buffer.writeBytes([0x41])
|
||||
buffer.writeBytes("A".utf8)
|
||||
@@ -33,15 +33,15 @@ func run(identifier: String) {
|
||||
buffer.writeStaticString("A")
|
||||
buffer.writeInteger(0x41, as: UInt8.self)
|
||||
|
||||
/* those down here should be one allocation each (on Linux) */
|
||||
buffer.writeBytes(dispatchData) // see https://bugs.swift.org/browse/SR-9597
|
||||
// those down here should be one allocation each (on Linux)
|
||||
buffer.writeBytes(dispatchData) // see https://bugs.swift.org/browse/SR-9597
|
||||
|
||||
/* these here are one allocation on all platforms */
|
||||
// these here are one allocation on all platforms
|
||||
buffer.writeSubstring(substring)
|
||||
}
|
||||
@inline(never)
|
||||
func doReads(buffer: inout ByteBuffer) {
|
||||
/* these ones are zero allocations */
|
||||
// these ones are zero allocations
|
||||
let val = buffer.readInteger(as: UInt8.self)
|
||||
precondition(0x41 == val, "\(val!)")
|
||||
var slice = buffer.readSlice(length: 1)
|
||||
@@ -51,13 +51,13 @@ func run(identifier: String) {
|
||||
precondition(ptr[0] == 0x41)
|
||||
}
|
||||
|
||||
/* those down here should be one allocation each */
|
||||
// those down here should be one allocation each
|
||||
let arr = buffer.readBytes(length: 1)
|
||||
precondition([0x41] == arr!, "\(arr!)")
|
||||
let str = buffer.readString(length: 1)
|
||||
precondition("A" == str, "\(str!)")
|
||||
}
|
||||
for _ in 0..<1000 {
|
||||
for _ in 0..<1000 {
|
||||
doWrites(buffer: &buffer, dispatchData: dispatchData, substring: substring)
|
||||
doReads(buffer: &buffer)
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ func run(identifier: String) {
|
||||
let channel = EmbeddedChannel()
|
||||
try! channel.pipeline.addHandler(ByteToMessageHandler(WebSocketFrameDecoder())).wait()
|
||||
try! channel.pipeline.addHandler(UnboxingChannelHandler()).wait()
|
||||
let data = ByteBuffer(bytes: [0x81, 0x00]) // empty websocket
|
||||
let data = ByteBuffer(bytes: [0x81, 0x00]) // empty websocket
|
||||
|
||||
measure(identifier: identifier) {
|
||||
for _ in 0..<1000 {
|
||||
|
||||
+54
-10
@@ -16,7 +16,13 @@ import NIOCore
|
||||
import NIOEmbedded
|
||||
import NIOWebSocket
|
||||
|
||||
func doSendFramesHoldingBuffer(channel: EmbeddedChannel, number numberOfFrameSends: Int, data originalData: [UInt8], spareBytesAtFront: Int, mask: WebSocketMaskingKey? = nil) throws -> Int {
|
||||
func doSendFramesHoldingBuffer(
|
||||
channel: EmbeddedChannel,
|
||||
number numberOfFrameSends: Int,
|
||||
data originalData: [UInt8],
|
||||
spareBytesAtFront: Int,
|
||||
mask: WebSocketMaskingKey? = nil
|
||||
) throws -> Int {
|
||||
var data = channel.allocator.buffer(capacity: originalData.count + spareBytesAtFront)
|
||||
data.moveWriterIndex(forwardBy: spareBytesAtFront)
|
||||
data.moveReaderIndex(forwardBy: spareBytesAtFront)
|
||||
@@ -35,8 +41,13 @@ func doSendFramesHoldingBuffer(channel: EmbeddedChannel, number numberOfFrameSen
|
||||
return numberOfFrameSends
|
||||
}
|
||||
|
||||
|
||||
func doSendFramesNewBuffer(channel: EmbeddedChannel, number numberOfFrameSends: Int, data originalData: [UInt8], spareBytesAtFront: Int, mask: WebSocketMaskingKey? = nil) throws -> Int {
|
||||
func doSendFramesNewBuffer(
|
||||
channel: EmbeddedChannel,
|
||||
number numberOfFrameSends: Int,
|
||||
data originalData: [UInt8],
|
||||
spareBytesAtFront: Int,
|
||||
mask: WebSocketMaskingKey? = nil
|
||||
) throws -> Int {
|
||||
for _ in 0..<numberOfFrameSends {
|
||||
// We need a new allocation every time to drop the original data ref.
|
||||
var data = channel.allocator.buffer(capacity: originalData.count + spareBytesAtFront)
|
||||
@@ -55,7 +66,6 @@ func doSendFramesNewBuffer(channel: EmbeddedChannel, number numberOfFrameSends:
|
||||
return numberOfFrameSends
|
||||
}
|
||||
|
||||
|
||||
func run(identifier: String) {
|
||||
let maskKey: WebSocketMaskingKey = [1, 2, 3, 4]
|
||||
let channel = EmbeddedChannel()
|
||||
@@ -63,25 +73,47 @@ func run(identifier: String) {
|
||||
let data = Array(repeating: UInt8(0), count: 1024)
|
||||
|
||||
measure(identifier: identifier + "_holding_buffer") {
|
||||
let numberDone = try! doSendFramesHoldingBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 0)
|
||||
let numberDone = try! doSendFramesHoldingBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 0
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
|
||||
measure(identifier: identifier + "_holding_buffer_with_space") {
|
||||
let numberDone = try! doSendFramesHoldingBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 8)
|
||||
let numberDone = try! doSendFramesHoldingBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 8
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
|
||||
measure(identifier: identifier + "_holding_buffer_with_mask") {
|
||||
let numberDone = try! doSendFramesHoldingBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 0, mask: maskKey)
|
||||
let numberDone = try! doSendFramesHoldingBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 0,
|
||||
mask: maskKey
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
|
||||
measure(identifier: identifier + "_holding_buffer_with_space_with_mask") {
|
||||
let numberDone = try! doSendFramesHoldingBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 8, mask: maskKey)
|
||||
let numberDone = try! doSendFramesHoldingBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 8,
|
||||
mask: maskKey
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
@@ -99,13 +131,25 @@ func run(identifier: String) {
|
||||
}
|
||||
|
||||
measure(identifier: identifier + "_new_buffer_with_mask") {
|
||||
let numberDone = try! doSendFramesNewBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 0, mask: maskKey)
|
||||
let numberDone = try! doSendFramesNewBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 0,
|
||||
mask: maskKey
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
|
||||
measure(identifier: identifier + "_new_buffer_with_space_with_mask") {
|
||||
let numberDone = try! doSendFramesNewBuffer(channel: channel, number: 1000, data: data, spareBytesAtFront: 8, mask: maskKey)
|
||||
let numberDone = try! doSendFramesNewBuffer(
|
||||
channel: channel,
|
||||
number: 1000,
|
||||
data: data,
|
||||
spareBytesAtFront: 8,
|
||||
mask: maskKey
|
||||
)
|
||||
precondition(numberDone == 1000)
|
||||
return numberDone
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ func run(identifier: String) {
|
||||
}
|
||||
|
||||
let el = EmbeddedEventLoop()
|
||||
for _ in 0..<1000 {
|
||||
for _ in 0..<1000 {
|
||||
doEraseResult(loop: el)
|
||||
}
|
||||
return 1000
|
||||
|
||||
+11
-11
@@ -17,22 +17,22 @@ import NIOEmbedded
|
||||
|
||||
func run(identifier: String) {
|
||||
measure(identifier: identifier) {
|
||||
struct MyError: Error { }
|
||||
struct MyError: Error {}
|
||||
@inline(never)
|
||||
func doThenAndFriends(loop: EventLoop) {
|
||||
let p = loop.makePromise(of: Int.self)
|
||||
let f = p.futureResult.flatMap { (r: Int) -> EventLoopFuture<Int> in
|
||||
// This call allocates a new Future, and
|
||||
// so does flatMap(), so this is two Futures.
|
||||
return loop.makeSucceededFuture(r + 1)
|
||||
loop.makeSucceededFuture(r + 1)
|
||||
}.flatMapThrowing { (r: Int) -> Int in
|
||||
// flatMapThrowing allocates a new Future, and calls `flatMap`
|
||||
// which also allocates, so this is two.
|
||||
return r + 2
|
||||
r + 2
|
||||
}.map { (r: Int) -> Int in
|
||||
// map allocates a new future, and calls `flatMap` which
|
||||
// also allocates, so this is two.
|
||||
return r + 2
|
||||
r + 2
|
||||
}.flatMapThrowing { (r: Int) -> Int in
|
||||
// flatMapThrowing allocates a future on the error path and
|
||||
// calls `flatMap`, which also allocates, so this is two.
|
||||
@@ -40,7 +40,7 @@ func run(identifier: String) {
|
||||
}.flatMapError { (err: Error) -> EventLoopFuture<Int> in
|
||||
// This call allocates a new Future, and so does flatMapError,
|
||||
// so this is two Futures.
|
||||
return loop.makeFailedFuture(err)
|
||||
loop.makeFailedFuture(err)
|
||||
}.flatMapErrorThrowing { (err: Error) -> Int in
|
||||
// flatMapError allocates a new Future, and calls flatMapError,
|
||||
// so this is two Futures
|
||||
@@ -48,7 +48,7 @@ func run(identifier: String) {
|
||||
}.recover { (err: Error) -> Int in
|
||||
// recover allocates a future, and calls flatMapError, so
|
||||
// this is two Futures.
|
||||
return 1
|
||||
1
|
||||
}
|
||||
p.succeed(0)
|
||||
|
||||
@@ -65,10 +65,10 @@ func run(identifier: String) {
|
||||
// and(result:) allocate two.
|
||||
|
||||
let f = p1.futureResult
|
||||
.and(p2.futureResult)
|
||||
.and(p3.futureResult)
|
||||
.and(value: 1)
|
||||
.and(value: 1)
|
||||
.and(p2.futureResult)
|
||||
.and(p3.futureResult)
|
||||
.and(value: 1)
|
||||
.and(value: 1)
|
||||
|
||||
p1.succeed(1)
|
||||
p2.succeed(1)
|
||||
@@ -76,7 +76,7 @@ func run(identifier: String) {
|
||||
_ = try! f.wait()
|
||||
}
|
||||
let el = EmbeddedEventLoop()
|
||||
for _ in 0..<1000 {
|
||||
for _ in 0..<1000 {
|
||||
doThenAndFriends(loop: el)
|
||||
doAnd(loop: el)
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
import NIOCore
|
||||
|
||||
func run(identifier: String) {
|
||||
var buffer = CircularBuffer<Array<Int>>(initialCapacity: 100)
|
||||
var buffer = CircularBuffer<[Int]>(initialCapacity: 100)
|
||||
for _ in 0..<100 {
|
||||
buffer.append([])
|
||||
}
|
||||
|
||||
+8
-5
@@ -35,8 +35,12 @@ private final class PongDecoder: ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
|
||||
return .needMoreData
|
||||
public func decodeLast(
|
||||
context: ChannelHandlerContext,
|
||||
buffer: inout ByteBuffer,
|
||||
seenEOF: Bool
|
||||
) throws -> DecodingState {
|
||||
.needMoreData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +73,8 @@ private final class PingHandler: ChannelInboundHandler {
|
||||
}
|
||||
|
||||
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
var buf = Self.unwrapInboundIn(data)
|
||||
if buf.readableBytes == 1 &&
|
||||
buf.readInteger(as: UInt8.self) == PongHandler.pongCode {
|
||||
var buf = self.unwrapInboundIn(data)
|
||||
if buf.readableBytes == 1 && buf.readInteger(as: UInt8.self) == PongHandler.pongCode {
|
||||
if self.remainingNumberOfRequests > 0 {
|
||||
self.remainingNumberOfRequests -= 1
|
||||
context.writeAndFlush(Self.wrapOutboundOut(self.pingBuffer), promise: nil)
|
||||
|
||||
+22
-16
@@ -12,11 +12,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import Dispatch
|
||||
import Foundation
|
||||
import NIOConcurrencyHelpers
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
import NIOConcurrencyHelpers
|
||||
|
||||
func run(identifier: String) {
|
||||
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
@@ -37,27 +37,33 @@ func run(identifier: String) {
|
||||
var fileBuffer = allocator.buffer(capacity: numberOfChunks)
|
||||
fileBuffer.writeString(String(repeating: "X", count: numberOfChunks))
|
||||
let path = NSTemporaryDirectory() + "/\(UUID())"
|
||||
let fileHandle = try! NIOFileHandle(path: path,
|
||||
mode: [.write, .read],
|
||||
flags: .allowFileCreation(posixMode: 0o600))
|
||||
let fileHandle = try! NIOFileHandle(
|
||||
path: path,
|
||||
mode: [.write, .read],
|
||||
flags: .allowFileCreation(posixMode: 0o600)
|
||||
)
|
||||
defer {
|
||||
unlink(path)
|
||||
}
|
||||
try! fileIO.write(fileHandle: fileHandle,
|
||||
buffer: fileBuffer,
|
||||
eventLoop: loop).wait()
|
||||
try! fileIO.write(
|
||||
fileHandle: fileHandle,
|
||||
buffer: fileBuffer,
|
||||
eventLoop: loop
|
||||
).wait()
|
||||
|
||||
let numberOfBytes = NIOAtomic<Int>.makeAtomic(value: 0)
|
||||
measure(identifier: identifier) {
|
||||
numberOfBytes.store(0)
|
||||
try! fileIO.readChunked(fileHandle: fileHandle,
|
||||
fromOffset: 0,
|
||||
byteCount: numberOfChunks,
|
||||
chunkSize: 1,
|
||||
allocator: allocator,
|
||||
eventLoop: loop) { buffer in
|
||||
numberOfBytes.add(buffer.readableBytes)
|
||||
return loop.makeSucceededFuture(())
|
||||
try! fileIO.readChunked(
|
||||
fileHandle: fileHandle,
|
||||
fromOffset: 0,
|
||||
byteCount: numberOfChunks,
|
||||
chunkSize: 1,
|
||||
allocator: allocator,
|
||||
eventLoop: loop
|
||||
) { buffer in
|
||||
numberOfBytes.add(buffer.readableBytes)
|
||||
return loop.makeSucceededFuture(())
|
||||
}.wait()
|
||||
precondition(numberOfBytes.load() == numberOfChunks, "\(numberOfBytes.load()), \(numberOfChunks)")
|
||||
return numberOfBytes.load()
|
||||
|
||||
+15
-15
@@ -18,9 +18,9 @@ import PackageDescription
|
||||
let swiftAtomics: PackageDescription.Target.Dependency = .product(name: "Atomics", package: "swift-atomics")
|
||||
let swiftCollections: PackageDescription.Target.Dependency = .product(name: "DequeModule", package: "swift-collections")
|
||||
let swiftSystem: PackageDescription.Target.Dependency = .product(
|
||||
name: "SystemPackage",
|
||||
package: "swift-system",
|
||||
condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .linux, .android])
|
||||
name: "SystemPackage",
|
||||
package: "swift-system",
|
||||
condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .linux, .android])
|
||||
)
|
||||
|
||||
// This doesn't work when cross-compiling: the privacy manifest will be included in the Bundle and
|
||||
@@ -121,7 +121,7 @@ let package = Package(
|
||||
name: "CNIOAtomics",
|
||||
dependencies: [],
|
||||
cSettings: [
|
||||
.define("_GNU_SOURCE"),
|
||||
.define("_GNU_SOURCE")
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -132,14 +132,14 @@ let package = Package(
|
||||
name: "CNIOLinux",
|
||||
dependencies: [],
|
||||
cSettings: [
|
||||
.define("_GNU_SOURCE"),
|
||||
.define("_GNU_SOURCE")
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "CNIODarwin",
|
||||
dependencies: [],
|
||||
cSettings: [
|
||||
.define("__APPLE_USE_RFC_3542"),
|
||||
.define("__APPLE_USE_RFC_3542")
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -149,7 +149,7 @@ let package = Package(
|
||||
.target(
|
||||
name: "NIOConcurrencyHelpers",
|
||||
dependencies: [
|
||||
"CNIOAtomics",
|
||||
"CNIOAtomics"
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -159,7 +159,7 @@ let package = Package(
|
||||
"NIOCore",
|
||||
"NIOConcurrencyHelpers",
|
||||
"CNIOLLHTTP",
|
||||
swiftCollections
|
||||
swiftCollections,
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -169,14 +169,14 @@ let package = Package(
|
||||
"NIOCore",
|
||||
"NIOHTTP1",
|
||||
"CNIOSHA1",
|
||||
"_NIOBase64"
|
||||
"_NIOBase64",
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "CNIOLLHTTP",
|
||||
cSettings: [
|
||||
.define("_GNU_SOURCE"),
|
||||
.define("LLHTTP_STRICT_MODE")
|
||||
.define("_GNU_SOURCE"),
|
||||
.define("LLHTTP_STRICT_MODE"),
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -218,14 +218,14 @@ let package = Package(
|
||||
.target(
|
||||
name: "NIOFileSystem",
|
||||
dependencies: [
|
||||
"_NIOFileSystem",
|
||||
"_NIOFileSystem"
|
||||
],
|
||||
path: "Sources/_NIOFileSystemExported"
|
||||
),
|
||||
.target(
|
||||
name: "_NIOFileSystemFoundationCompat",
|
||||
dependencies: [
|
||||
"_NIOFileSystem",
|
||||
"_NIOFileSystem"
|
||||
],
|
||||
path: "Sources/NIOFileSystemFoundationCompat"
|
||||
),
|
||||
@@ -503,7 +503,7 @@ let package = Package(
|
||||
// Contains known files and directory structures used
|
||||
// for the integration tests. Exclude the whole tree from
|
||||
// the build.
|
||||
"Test Data",
|
||||
"Test Data"
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
@@ -512,7 +512,7 @@ let package = Package(
|
||||
"_NIOFileSystem",
|
||||
"_NIOFileSystemFoundationCompat",
|
||||
]
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
// snippet.hide
|
||||
import _NIOFileSystem
|
||||
|
||||
import NIOCore
|
||||
import _NIOFileSystem
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||
func main() async throws
|
||||
{
|
||||
// snippet.show
|
||||
func main() async throws {
|
||||
// snippet.show
|
||||
|
||||
// NIOFileSystem provides access to the local file system via the FileSystem
|
||||
// type which is available as a global shared instance.
|
||||
let fileSystem = FileSystem.shared
|
||||
// NIOFileSystem provides access to the local file system via the FileSystem
|
||||
// type which is available as a global shared instance.
|
||||
let fileSystem = FileSystem.shared
|
||||
|
||||
// Files can be inspected by using 'info':
|
||||
if let info = try await fileSystem.info(forFileAt: "/Users/hal9000/demise-of-dave.txt") {
|
||||
print("demise-of-dave.txt has type '\(info.type)'")
|
||||
} else {
|
||||
print("demise-of-dave.txt doesn't exist")
|
||||
}
|
||||
// Files can be inspected by using 'info':
|
||||
if let info = try await fileSystem.info(forFileAt: "/Users/hal9000/demise-of-dave.txt") {
|
||||
print("demise-of-dave.txt has type '\(info.type)'")
|
||||
} else {
|
||||
print("demise-of-dave.txt doesn't exist")
|
||||
}
|
||||
|
||||
// Let's find out what's in that file.
|
||||
do {
|
||||
// Reading a whole file requires a limit. If the file is larger than the limit
|
||||
// then an error is thrown. This avoids accidentally consuming too much memory
|
||||
// if the file is larger than expected.
|
||||
let plan = try await ByteBuffer(
|
||||
contentsOf: "/Users/hal9000/demise-of-dave.txt",
|
||||
maximumSizeAllowed: .mebibytes(1)
|
||||
)
|
||||
print("Plan for Dave's demise:", String(decoding: plan.readableBytesView, as: UTF8.self))
|
||||
} catch let error as FileSystemError where error.code == .notFound {
|
||||
// All errors thrown by the module have type FileSystemError (or
|
||||
// Swift.CancellationError). It looks like the file doesn't exist. Let's
|
||||
// create it now.
|
||||
// Let's find out what's in that file.
|
||||
do {
|
||||
// Reading a whole file requires a limit. If the file is larger than the limit
|
||||
// then an error is thrown. This avoids accidentally consuming too much memory
|
||||
// if the file is larger than expected.
|
||||
let plan = try await ByteBuffer(
|
||||
contentsOf: "/Users/hal9000/demise-of-dave.txt",
|
||||
maximumSizeAllowed: .mebibytes(1)
|
||||
)
|
||||
print("Plan for Dave's demise:", String(decoding: plan.readableBytesView, as: UTF8.self))
|
||||
} catch let error as FileSystemError where error.code == .notFound {
|
||||
// All errors thrown by the module have type FileSystemError (or
|
||||
// Swift.CancellationError). It looks like the file doesn't exist. Let's
|
||||
// create it now.
|
||||
//
|
||||
// The code above for reading the file is shorthand for opening the file in
|
||||
// read-only mode and then reading its contents. The FileSystemProtocol
|
||||
// has a few different 'withFileHandle' methods for opening a file in different
|
||||
// modes. Let's open a file for writing, creating it at the same time.
|
||||
try await fileSystem.withFileHandle(
|
||||
forWritingAt: "/Users/hal9000/demise-of-dave.txt",
|
||||
options: .newFile(replaceExisting: false)
|
||||
) { file in
|
||||
let plan = ByteBuffer(string: "TODO...")
|
||||
try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Directories can be opened like regular files but they cannot be read from or
|
||||
// written to. However, their contents can be listed:
|
||||
let path: FilePath? = try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Music") { directory in
|
||||
for try await entry in directory.listContents() {
|
||||
if entry.name.extension == "mp3", entry.name.stem.contains("daisy") {
|
||||
// Found it!
|
||||
return entry.path
|
||||
}
|
||||
}
|
||||
// No luck.
|
||||
return nil
|
||||
}
|
||||
|
||||
if let path = path {
|
||||
print("Found file at '\(path)'")
|
||||
}
|
||||
|
||||
// The file system can also be used to perform the following operations on files
|
||||
// and directories:
|
||||
// - copy,
|
||||
// - remove,
|
||||
// - rename, and
|
||||
// - replace.
|
||||
//
|
||||
// The code above for reading the file is shorthand for opening the file in
|
||||
// read-only mode and then reading its contents. The FileSystemProtocol
|
||||
// has a few different 'withFileHandle' methods for opening a file in different
|
||||
// modes. Let's open a file for writing, creating it at the same time.
|
||||
try await fileSystem.withFileHandle(
|
||||
forWritingAt: "/Users/hal9000/demise-of-dave.txt",
|
||||
options: .newFile(replaceExisting: false)
|
||||
) { file in
|
||||
let plan = ByteBuffer(string: "TODO...")
|
||||
try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0)
|
||||
// Here's an example of copying a directory:
|
||||
try await fileSystem.copyItem(at: "/Users/hal9000/Music", to: "/Volumes/Tardis/Music")
|
||||
|
||||
// Symbolic links can also be created (and read with 'destinationOfSymbolicLink(at:)').
|
||||
try await fileSystem.createSymbolicLink(at: "/Users/hal9000/Backup", withDestination: "/Volumes/Tardis")
|
||||
|
||||
// Opening a symbolic link opens its destination so in most cases there's no
|
||||
// need to read the destination of a symbolic link:
|
||||
try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Backup") { directory in
|
||||
// Beyond listing the contents of a directory, the directory handle provides a
|
||||
// number of other functions, many of which are also available on regular file
|
||||
// handles.
|
||||
//
|
||||
// This includes getting information about a file, such as its permissions, last access time,
|
||||
// and last modification time:
|
||||
let info = try await directory.info()
|
||||
print("The directory has permissions '\(info.permissions)'")
|
||||
|
||||
// Where supported, the extended attributes of a file can also be accessed, read, and modified:
|
||||
for attribute in try await directory.attributeNames() {
|
||||
let value = try await directory.valueForAttribute(attribute)
|
||||
print("Extended attribute '\(attribute)' has value '\(value)'")
|
||||
}
|
||||
|
||||
// Once this closure returns the file system will close the directory handle freeing
|
||||
// any resources required to access it such as file descriptors. Handles can also be opened
|
||||
// with the 'openFile' and 'openDirectory' APIs but that places the onus you to close the
|
||||
// handle at an appropriate time to avoid leaking resources.
|
||||
}
|
||||
}
|
||||
|
||||
// Directories can be opened like regular files but they cannot be read from or
|
||||
// written to. However, their contents can be listed:
|
||||
let path: FilePath? = try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Music") { directory in
|
||||
for try await entry in directory.listContents() {
|
||||
if entry.name.extension == "mp3", entry.name.stem.contains("daisy") {
|
||||
// Found it!
|
||||
return entry.path
|
||||
}
|
||||
}
|
||||
// No luck.
|
||||
return nil
|
||||
}
|
||||
|
||||
if let path = path {
|
||||
print("Found file at '\(path)'")
|
||||
}
|
||||
|
||||
// The file system can also be used to perform the following operations on files
|
||||
// and directories:
|
||||
// - copy,
|
||||
// - remove,
|
||||
// - rename, and
|
||||
// - replace.
|
||||
//
|
||||
// Here's an example of copying a directory:
|
||||
try await fileSystem.copyItem(at: "/Users/hal9000/Music", to: "/Volumes/Tardis/Music")
|
||||
|
||||
// Symbolic links can also be created (and read with 'destinationOfSymbolicLink(at:)').
|
||||
try await fileSystem.createSymbolicLink(at: "/Users/hal9000/Backup", withDestination: "/Volumes/Tardis")
|
||||
|
||||
// Opening a symbolic link opens its destination so in most cases there's no
|
||||
// need to read the destination of a symbolic link:
|
||||
try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Backup") { directory in
|
||||
// Beyond listing the contents of a directory, the directory handle provides a
|
||||
// number of other functions, many of which are also available on regular file
|
||||
// handles.
|
||||
//
|
||||
// This includes getting information about a file, such as its permissions, last access time,
|
||||
// and last modification time:
|
||||
let info = try await directory.info()
|
||||
print("The directory has permissions '\(info.permissions)'")
|
||||
|
||||
// Where supported, the extended attributes of a file can also be accessed, read, and modified:
|
||||
for attribute in try await directory.attributeNames() {
|
||||
let value = try await directory.valueForAttribute(attribute)
|
||||
print("Extended attribute '\(attribute)' has value '\(value)'")
|
||||
}
|
||||
|
||||
// Once this closure returns the file system will close the directory handle freeing
|
||||
// any resources required to access it such as file descriptors. Handles can also be opened
|
||||
// with the 'openFile' and 'openDirectory' APIs but that places the onus you to close the
|
||||
// handle at an appropriate time to avoid leaking resources.
|
||||
}
|
||||
// snippet.end
|
||||
// snippet.end
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ struct AsyncChannelIO<Request, Response> {
|
||||
}
|
||||
|
||||
func start() async throws -> AsyncChannelIO<Request, Response> {
|
||||
try await channel.pipeline.addHandler(RequestResponseHandler<HTTPRequestHead, NIOHTTPClientResponseFull>()).get()
|
||||
try await channel.pipeline.addHandler(RequestResponseHandler<HTTPRequestHead, NIOHTTPClientResponseFull>())
|
||||
.get()
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ public final class RequestResponseHandler<Request, Response>: ChannelDuplexHandl
|
||||
private var state: State = .operational
|
||||
private var promiseBuffer: CircularBuffer<EventLoopPromise<Response>>
|
||||
|
||||
|
||||
/// Create a new `RequestResponseHandler`.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -83,7 +82,7 @@ public final class RequestResponseHandler<Request, Response>: ChannelDuplexHandl
|
||||
case .operational:
|
||||
let promiseBuffer = self.promiseBuffer
|
||||
self.promiseBuffer.removeAll()
|
||||
promiseBuffer.forEach { promise in
|
||||
for promise in promiseBuffer {
|
||||
promise.fail(ChannelError.eof)
|
||||
}
|
||||
}
|
||||
@@ -112,8 +111,8 @@ public final class RequestResponseHandler<Request, Response>: ChannelDuplexHandl
|
||||
let promiseBuffer = self.promiseBuffer
|
||||
self.promiseBuffer.removeAll()
|
||||
context.close(promise: nil)
|
||||
promiseBuffer.forEach {
|
||||
$0.fail(error)
|
||||
for promise in promiseBuffer {
|
||||
promise.fail(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
import NIOHTTP1
|
||||
|
||||
import Dispatch
|
||||
import NIOCore
|
||||
import NIOHTTP1
|
||||
import NIOPosix
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
func makeHTTPChannel(host: String, port: Int, group: EventLoopGroup) async throws -> AsyncChannelIO<HTTPRequestHead, NIOHTTPClientResponseFull> {
|
||||
func makeHTTPChannel(
|
||||
host: String,
|
||||
port: Int,
|
||||
group: EventLoopGroup
|
||||
) async throws -> AsyncChannelIO<HTTPRequestHead, NIOHTTPClientResponseFull> {
|
||||
let channel = try await ClientBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
channel.eventLoop.makeCompletedFuture {
|
||||
try channel.pipeline.syncOperations.addHTTPClientHandlers()
|
||||
try channel.pipeline.syncOperations.addHandler(NIOHTTPClientResponseAggregator(maxContentLength: 1_000_000))
|
||||
try channel.pipeline.syncOperations.addHandler(
|
||||
NIOHTTPClientResponseAggregator(maxContentLength: 1_000_000)
|
||||
)
|
||||
try channel.pipeline.syncOperations.addHandler(MakeFullRequestHandler())
|
||||
}
|
||||
}
|
||||
@@ -39,17 +46,25 @@ func main() async {
|
||||
print("OK, connected to \(channel)")
|
||||
|
||||
print("Sending request 1", terminator: "")
|
||||
let response1 = try await channel.sendRequest(HTTPRequestHead(version: .http1_1,
|
||||
method: .GET,
|
||||
uri: "/base64/SGVsbG8gV29ybGQsIGZyb20gSFRUUEJpbiEgCg==",
|
||||
headers: ["host": "httpbin.org"]))
|
||||
let response1 = try await channel.sendRequest(
|
||||
HTTPRequestHead(
|
||||
version: .http1_1,
|
||||
method: .GET,
|
||||
uri: "/base64/SGVsbG8gV29ybGQsIGZyb20gSFRUUEJpbiEgCg==",
|
||||
headers: ["host": "httpbin.org"]
|
||||
)
|
||||
)
|
||||
print(", response:", String(buffer: response1.body ?? ByteBuffer()))
|
||||
|
||||
print("Sending request 2", terminator: "")
|
||||
let response2 = try await channel.sendRequest(HTTPRequestHead(version: .http1_1,
|
||||
method: .GET,
|
||||
uri: "/get",
|
||||
headers: ["host": "httpbin.org"]))
|
||||
let response2 = try await channel.sendRequest(
|
||||
HTTPRequestHead(
|
||||
version: .http1_1,
|
||||
method: .GET,
|
||||
uri: "/get",
|
||||
headers: ["host": "httpbin.org"]
|
||||
)
|
||||
)
|
||||
print(", response:", String(buffer: response2.body ?? ByteBuffer()))
|
||||
|
||||
try await channel.close()
|
||||
|
||||
@@ -20,7 +20,7 @@ private final class ChatHandler: ChannelInboundHandler {
|
||||
|
||||
private func printByte(_ byte: UInt8) {
|
||||
#if os(Android)
|
||||
print(Character(UnicodeScalar(byte)), terminator:"")
|
||||
print(Character(UnicodeScalar(byte)), terminator: "")
|
||||
#else
|
||||
fputc(Int32(byte), stdout)
|
||||
#endif
|
||||
@@ -68,14 +68,14 @@ enum ConnectTo {
|
||||
|
||||
let connectTarget: ConnectTo
|
||||
switch (arg1, arg1.flatMap(Int.init), arg2.flatMap(Int.init)) {
|
||||
case (.some(let h), _ , .some(let p)):
|
||||
/* we got two arguments, let's interpret that as host and port */
|
||||
case (.some(let h), _, .some(let p)):
|
||||
// we got two arguments, let's interpret that as host and port
|
||||
connectTarget = .ip(host: h, port: p)
|
||||
case (.some(let portString), .none, _):
|
||||
/* couldn't parse as number, expecting unix domain socket path */
|
||||
// couldn't parse as number, expecting unix domain socket path
|
||||
connectTarget = .unixDomainSocket(path: portString)
|
||||
case (_, .some(let p), _):
|
||||
/* only one argument --> port */
|
||||
// only one argument --> port
|
||||
connectTarget = .ip(host: defaultHost, port: p)
|
||||
default:
|
||||
connectTarget = .ip(host: defaultHost, port: defaultPort)
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Dispatch
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
import Dispatch
|
||||
|
||||
private let newLine = "\n".utf8.first!
|
||||
|
||||
@@ -51,28 +52,36 @@ final class ChatHandler: ChannelInboundHandler {
|
||||
// All access to channels is guarded by channelsSyncQueue.
|
||||
private let channelsSyncQueue = DispatchQueue(label: "channelsQueue")
|
||||
private var channels: [ObjectIdentifier: Channel] = [:]
|
||||
|
||||
|
||||
public func channelActive(context: ChannelHandlerContext) {
|
||||
let remoteAddress = context.remoteAddress!
|
||||
let channel = context.channel
|
||||
self.channelsSyncQueue.async {
|
||||
// broadcast the message to all the connected clients except the one that just became active.
|
||||
self.writeToAll(channels: self.channels, allocator: channel.allocator, message: "(ChatServer) - New client connected with address: \(remoteAddress)\n")
|
||||
|
||||
self.writeToAll(
|
||||
channels: self.channels,
|
||||
allocator: channel.allocator,
|
||||
message: "(ChatServer) - New client connected with address: \(remoteAddress)\n"
|
||||
)
|
||||
|
||||
self.channels[ObjectIdentifier(channel)] = channel
|
||||
}
|
||||
|
||||
|
||||
var buffer = channel.allocator.buffer(capacity: 64)
|
||||
buffer.writeString("(ChatServer) - Welcome to: \(context.localAddress!)\n")
|
||||
context.writeAndFlush(Self.wrapOutboundOut(buffer), promise: nil)
|
||||
}
|
||||
|
||||
|
||||
public func channelInactive(context: ChannelHandlerContext) {
|
||||
let channel = context.channel
|
||||
self.channelsSyncQueue.async {
|
||||
if self.channels.removeValue(forKey: ObjectIdentifier(channel)) != nil {
|
||||
// Broadcast the message to all the connected clients except the one that just was disconnected.
|
||||
self.writeToAll(channels: self.channels, allocator: channel.allocator, message: "(ChatServer) - Client disconnected\n")
|
||||
self.writeToAll(
|
||||
channels: self.channels,
|
||||
allocator: channel.allocator,
|
||||
message: "(ChatServer) - Client disconnected\n"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,12 +109,14 @@ final class ChatHandler: ChannelInboundHandler {
|
||||
}
|
||||
|
||||
private func writeToAll(channels: [ObjectIdentifier: Channel], allocator: ByteBufferAllocator, message: String) {
|
||||
let buffer = allocator.buffer(string: message)
|
||||
let buffer = allocator.buffer(string: message)
|
||||
self.writeToAll(channels: channels, buffer: buffer)
|
||||
}
|
||||
|
||||
private func writeToAll(channels: [ObjectIdentifier: Channel], buffer: ByteBuffer) {
|
||||
channels.forEach { $0.value.writeAndFlush(buffer, promise: nil) }
|
||||
for channel in channels {
|
||||
channel.value.writeAndFlush(buffer, promise: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,8 +165,8 @@ enum BindTo {
|
||||
|
||||
let bindTarget: BindTo
|
||||
switch (arg1, arg1.flatMap(Int.init), arg2.flatMap(Int.init)) {
|
||||
case (.some(let h), _ , .some(let p)):
|
||||
/* we got two arguments, let's interpret that as host and port */
|
||||
case (.some(let h), _, .some(let p)):
|
||||
// we got two arguments, let's interpret that as host and port
|
||||
bindTarget = .ip(host: h, port: p)
|
||||
|
||||
case (let portString?, .none, _):
|
||||
@@ -180,7 +191,9 @@ let channel = try { () -> Channel in
|
||||
}()
|
||||
|
||||
guard let localAddress = channel.localAddress else {
|
||||
fatalError("Address was unable to bind. Please check that the socket was not closed or that the address family was understood.")
|
||||
fatalError(
|
||||
"Address was unable to bind. Please check that the socket was not closed or that the address family was understood."
|
||||
)
|
||||
}
|
||||
print("Server started and listening on \(localAddress)")
|
||||
|
||||
|
||||
@@ -33,145 +33,153 @@ public protocol NIOAtomicPrimitive {
|
||||
extension Bool: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic__Bool
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic__Bool_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic__Bool_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic__Bool_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic__Bool_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic__Bool_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic__Bool_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic__Bool_store
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic__Bool_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic__Bool_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic__Bool_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic__Bool_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic__Bool_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic__Bool_store
|
||||
}
|
||||
|
||||
extension Int8: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_int_least8_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_int_least8_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least8_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least8_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least8_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least8_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least8_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least8_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_int_least8_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least8_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least8_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least8_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least8_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least8_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least8_t_store
|
||||
}
|
||||
|
||||
extension UInt8: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_uint_least8_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_uint_least8_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least8_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least8_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least8_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least8_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least8_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least8_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_uint_least8_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least8_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least8_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least8_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least8_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least8_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least8_t_store
|
||||
}
|
||||
|
||||
extension Int16: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_int_least16_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_int_least16_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least16_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least16_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least16_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least16_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least16_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least16_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_int_least16_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least16_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least16_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least16_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least16_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least16_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least16_t_store
|
||||
}
|
||||
|
||||
extension UInt16: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_uint_least16_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_uint_least16_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least16_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least16_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least16_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least16_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least16_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least16_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_uint_least16_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least16_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least16_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least16_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least16_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least16_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least16_t_store
|
||||
}
|
||||
|
||||
extension Int32: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_int_least32_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_int_least32_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least32_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least32_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least32_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least32_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least32_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least32_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_int_least32_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_int_least32_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_int_least32_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_int_least32_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_int_least32_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_int_least32_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_int_least32_t_store
|
||||
}
|
||||
|
||||
extension UInt32: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_uint_least32_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_uint_least32_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least32_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least32_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least32_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least32_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least32_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least32_t_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_uint_least32_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uint_least32_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uint_least32_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uint_least32_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uint_least32_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uint_least32_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uint_least32_t_store
|
||||
}
|
||||
|
||||
extension Int64: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_long_long
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_long_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_long_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_long_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_long_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_long_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_long_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_long_long_store
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_long_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_long_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_long_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_long_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_long_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_long_long_store
|
||||
}
|
||||
|
||||
extension UInt64: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_unsigned_long_long
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_unsigned_long_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_unsigned_long_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_unsigned_long_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_unsigned_long_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_unsigned_long_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_unsigned_long_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_unsigned_long_long_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_unsigned_long_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_unsigned_long_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_unsigned_long_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_unsigned_long_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_unsigned_long_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_unsigned_long_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_unsigned_long_long_store
|
||||
}
|
||||
|
||||
#if os(Windows)
|
||||
extension Int: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_intptr_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_intptr_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_intptr_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_intptr_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_intptr_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_intptr_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_intptr_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_intptr_t_store
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_intptr_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_intptr_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_intptr_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_intptr_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_intptr_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_intptr_t_store
|
||||
}
|
||||
|
||||
extension UInt: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_uintptr_t
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_uintptr_t_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uintptr_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uintptr_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uintptr_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uintptr_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uintptr_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uintptr_t_store
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_uintptr_t_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_uintptr_t_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_uintptr_t_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_uintptr_t_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_uintptr_t_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_uintptr_t_store
|
||||
}
|
||||
#else
|
||||
extension Int: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_long
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_long_store
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_long_store
|
||||
}
|
||||
|
||||
extension UInt: NIOAtomicPrimitive {
|
||||
public typealias AtomicWrapper = catmc_nio_atomic_unsigned_long
|
||||
public static let nio_atomic_create_with_existing_storage = catmc_nio_atomic_unsigned_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_unsigned_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_unsigned_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_unsigned_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_unsigned_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_unsigned_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_unsigned_long_store
|
||||
public static let nio_atomic_create_with_existing_storage =
|
||||
catmc_nio_atomic_unsigned_long_create_with_existing_storage
|
||||
public static let nio_atomic_compare_and_exchange = catmc_nio_atomic_unsigned_long_compare_and_exchange
|
||||
public static let nio_atomic_add = catmc_nio_atomic_unsigned_long_add
|
||||
public static let nio_atomic_sub = catmc_nio_atomic_unsigned_long_sub
|
||||
public static let nio_atomic_exchange = catmc_nio_atomic_unsigned_long_exchange
|
||||
public static let nio_atomic_load = catmc_nio_atomic_unsigned_long_load
|
||||
public static let nio_atomic_store = catmc_nio_atomic_unsigned_long_store
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -193,7 +201,7 @@ extension UInt: NIOAtomicPrimitive {
|
||||
/// By necessity, all atomic values are references: after all, it makes no
|
||||
/// sense to talk about managing an atomic value when each time it's modified
|
||||
/// the thread that modified it gets a local copy!
|
||||
@available(*, deprecated, message:"please use ManagedAtomic from https://github.com/apple/swift-atomics instead")
|
||||
@available(*, deprecated, message: "please use ManagedAtomic from https://github.com/apple/swift-atomics instead")
|
||||
public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
@usableFromInline
|
||||
typealias Manager = ManagedBufferPointer<Void, T.AtomicWrapper>
|
||||
@@ -225,8 +233,8 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
/// match the current value and so no exchange occurred.
|
||||
@inlinable
|
||||
public func compareAndExchange(expected: T, desired: T) -> Bool {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_compare_and_exchange($0, expected, desired)
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_compare_and_exchange($0, expected, desired)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,8 +249,8 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
@inlinable
|
||||
@discardableResult
|
||||
public func add(_ rhs: T) -> T {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_add($0, rhs)
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_add($0, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,8 +265,8 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
@inlinable
|
||||
@discardableResult
|
||||
public func sub(_ rhs: T) -> T {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_sub($0, rhs)
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_sub($0, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,8 +280,8 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
/// - Returns: The value previously held by this object.
|
||||
@inlinable
|
||||
public func exchange(with value: T) -> T {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_exchange($0, value)
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_exchange($0, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,8 +294,8 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
/// - Returns: The value of this object
|
||||
@inlinable
|
||||
public func load() -> T {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_load($0)
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_load($0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,9 +307,9 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
|
||||
///
|
||||
/// - Parameter value: The new value to set the object to.
|
||||
@inlinable
|
||||
public func store(_ value: T) -> Void {
|
||||
return Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
return T.nio_atomic_store($0, value)
|
||||
public func store(_ value: T) {
|
||||
Manager(unsafeBufferObject: self).withUnsafeMutablePointerToElements {
|
||||
T.nio_atomic_store($0, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,61 +34,61 @@ typealias LockPrimitive = pthread_mutex_t
|
||||
#endif
|
||||
|
||||
@usableFromInline
|
||||
enum LockOperations { }
|
||||
enum LockOperations {}
|
||||
|
||||
extension LockOperations {
|
||||
@inlinable
|
||||
static func create(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
|
||||
mutex.assertValidAlignment()
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
InitializeSRWLock(mutex)
|
||||
#else
|
||||
#else
|
||||
var attr = pthread_mutexattr_t()
|
||||
pthread_mutexattr_init(&attr)
|
||||
debugOnly {
|
||||
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
|
||||
}
|
||||
|
||||
|
||||
let err = pthread_mutex_init(mutex, &attr)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
static func destroy(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
|
||||
mutex.assertValidAlignment()
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
// SRWLOCK does not need to be free'd
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_destroy(mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
static func lock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
|
||||
mutex.assertValidAlignment()
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
AcquireSRWLockExclusive(mutex)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_lock(mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
static func unlock(_ mutex: UnsafeMutablePointer<LockPrimitive>) {
|
||||
mutex.assertValidAlignment()
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
ReleaseSRWLockExclusive(mutex)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_unlock(mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,49 +122,49 @@ extension LockOperations {
|
||||
// See also: https://github.com/apple/swift/pull/40000
|
||||
@usableFromInline
|
||||
final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
|
||||
|
||||
|
||||
@inlinable
|
||||
static func create(value: Value) -> Self {
|
||||
let buffer = Self.create(minimumCapacity: 1) { _ in
|
||||
return value
|
||||
value
|
||||
}
|
||||
let storage = unsafeDowncast(buffer, to: Self.self)
|
||||
|
||||
|
||||
storage.withUnsafeMutablePointers { _, lockPtr in
|
||||
LockOperations.create(lockPtr)
|
||||
}
|
||||
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
func lock() {
|
||||
self.withUnsafeMutablePointerToElements { lockPtr in
|
||||
LockOperations.lock(lockPtr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
func unlock() {
|
||||
self.withUnsafeMutablePointerToElements { lockPtr in
|
||||
LockOperations.unlock(lockPtr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
deinit {
|
||||
self.withUnsafeMutablePointerToElements { lockPtr in
|
||||
LockOperations.destroy(lockPtr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
func withLockPrimitive<T>(_ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T) rethrows -> T {
|
||||
try self.withUnsafeMutablePointerToElements { lockPtr in
|
||||
return try body(lockPtr)
|
||||
try body(lockPtr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
|
||||
try self.withUnsafeMutablePointers { valuePtr, lockPtr in
|
||||
@@ -175,7 +175,7 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
|
||||
}
|
||||
}
|
||||
|
||||
extension LockStorage: @unchecked Sendable { }
|
||||
extension LockStorage: @unchecked Sendable {}
|
||||
|
||||
/// A threading lock based on `libpthread` instead of `libdispatch`.
|
||||
///
|
||||
@@ -188,7 +188,7 @@ extension LockStorage: @unchecked Sendable { }
|
||||
public struct NIOLock {
|
||||
@usableFromInline
|
||||
internal let _storage: LockStorage<Void>
|
||||
|
||||
|
||||
/// Create a new lock.
|
||||
@inlinable
|
||||
public init() {
|
||||
@@ -215,7 +215,7 @@ public struct NIOLock {
|
||||
|
||||
@inlinable
|
||||
internal func withLockPrimitive<T>(_ body: (UnsafeMutablePointer<LockPrimitive>) throws -> T) rethrows -> T {
|
||||
return try self._storage.withLockPrimitive(body)
|
||||
try self._storage.withLockPrimitive(body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ extension NIOLock {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func withLockVoid(_ body: () throws -> Void) rethrows -> Void {
|
||||
public func withLockVoid(_ body: () throws -> Void) rethrows {
|
||||
try self.withLock(body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
/// acquire/release the lock in the correct place. ``NIOLockedValueBox`` makes
|
||||
/// that much easier.
|
||||
public struct NIOLockedValueBox<Value> {
|
||||
|
||||
|
||||
@usableFromInline
|
||||
internal let _storage: LockStorage<Value>
|
||||
|
||||
@@ -35,7 +35,7 @@ public struct NIOLockedValueBox<Value> {
|
||||
/// Access the `Value`, allowing mutation of it.
|
||||
@inlinable
|
||||
public func withLockedValue<T>(_ mutate: (inout Value) throws -> T) rethrows -> T {
|
||||
return try self._storage.withLockedValue(mutate)
|
||||
try self._storage.withLockedValue(mutate)
|
||||
}
|
||||
|
||||
/// Provides an unsafe view over the lock and its value.
|
||||
@@ -72,7 +72,7 @@ public struct NIOLockedValueBox<Value> {
|
||||
public func withValueAssumingLockIsAcquired<Result>(
|
||||
_ mutate: (_ value: inout Value) throws -> Result
|
||||
) rethrows -> Result {
|
||||
return try self._storage.withUnsafeMutablePointerToHeader { value in
|
||||
try self._storage.withUnsafeMutablePointerToHeader { value in
|
||||
try mutate(&value.pointee)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ import CNIOAtomics
|
||||
|
||||
#if canImport(Darwin)
|
||||
import Darwin
|
||||
fileprivate func sys_sched_yield() {
|
||||
private func sys_sched_yield() {
|
||||
pthread_yield_np()
|
||||
}
|
||||
#elseif os(Windows)
|
||||
import ucrt
|
||||
import WinSDK
|
||||
fileprivate func sys_sched_yield() {
|
||||
Sleep(0)
|
||||
private func sys_sched_yield() {
|
||||
Sleep(0)
|
||||
}
|
||||
#else
|
||||
#if canImport(Glibc)
|
||||
@@ -34,7 +34,7 @@ import Musl
|
||||
#error("The concurrency atomics module was unable to identify your C library.")
|
||||
#endif
|
||||
|
||||
fileprivate func sys_sched_yield() {
|
||||
private func sys_sched_yield() {
|
||||
_ = sched_yield()
|
||||
}
|
||||
#endif
|
||||
@@ -86,7 +86,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
/// match the current value and so no exchange occurred.
|
||||
@inlinable
|
||||
public func compareAndExchange(expected: T, desired: T) -> Bool {
|
||||
return T.atomic_compare_and_exchange(self.value, expected, desired)
|
||||
T.atomic_compare_and_exchange(self.value, expected, desired)
|
||||
}
|
||||
|
||||
/// Atomically adds `rhs` to this object.
|
||||
@@ -100,7 +100,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public func add(_ rhs: T) -> T {
|
||||
return T.atomic_add(self.value, rhs)
|
||||
T.atomic_add(self.value, rhs)
|
||||
}
|
||||
|
||||
/// Atomically subtracts `rhs` from this object.
|
||||
@@ -114,7 +114,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public func sub(_ rhs: T) -> T {
|
||||
return T.atomic_sub(self.value, rhs)
|
||||
T.atomic_sub(self.value, rhs)
|
||||
}
|
||||
|
||||
/// Atomically exchanges `value` for the current value of this object.
|
||||
@@ -127,7 +127,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
/// - Returns: The value previously held by this object.
|
||||
@inlinable
|
||||
public func exchange(with value: T) -> T {
|
||||
return T.atomic_exchange(self.value, value)
|
||||
T.atomic_exchange(self.value, value)
|
||||
}
|
||||
|
||||
/// Atomically loads and returns the value of this object.
|
||||
@@ -139,7 +139,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
/// - Returns: The value of this object
|
||||
@inlinable
|
||||
public func load() -> T {
|
||||
return T.atomic_load(self.value)
|
||||
T.atomic_load(self.value)
|
||||
}
|
||||
|
||||
/// Atomically replaces the value of this object with `value`.
|
||||
@@ -150,7 +150,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
///
|
||||
/// - Parameter value: The new value to set the object to.
|
||||
@inlinable
|
||||
public func store(_ value: T) -> Void {
|
||||
public func store(_ value: T) {
|
||||
T.atomic_store(self.value, value)
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public struct UnsafeEmbeddedAtomic<T: AtomicPrimitive> {
|
||||
/// By necessity, all atomic values are references: after all, it makes no
|
||||
/// sense to talk about managing an atomic value when each time it's modified
|
||||
/// the thread that modified it gets a local copy!
|
||||
@available(*, deprecated, message:"please use ManagedAtomic from https://github.com/apple/swift-atomics instead")
|
||||
@available(*, deprecated, message: "please use ManagedAtomic from https://github.com/apple/swift-atomics instead")
|
||||
public final class Atomic<T: AtomicPrimitive> {
|
||||
@usableFromInline
|
||||
internal let embedded: UnsafeEmbeddedAtomic<T>
|
||||
@@ -209,7 +209,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
/// match the current value and so no exchange occurred.
|
||||
@inlinable
|
||||
public func compareAndExchange(expected: T, desired: T) -> Bool {
|
||||
return self.embedded.compareAndExchange(expected: expected, desired: desired)
|
||||
self.embedded.compareAndExchange(expected: expected, desired: desired)
|
||||
}
|
||||
|
||||
/// Atomically adds `rhs` to this object.
|
||||
@@ -223,7 +223,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public func add(_ rhs: T) -> T {
|
||||
return self.embedded.add(rhs)
|
||||
self.embedded.add(rhs)
|
||||
}
|
||||
|
||||
/// Atomically subtracts `rhs` from this object.
|
||||
@@ -237,7 +237,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public func sub(_ rhs: T) -> T {
|
||||
return self.embedded.sub(rhs)
|
||||
self.embedded.sub(rhs)
|
||||
}
|
||||
|
||||
/// Atomically exchanges `value` for the current value of this object.
|
||||
@@ -250,7 +250,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
/// - Returns: The value previously held by this object.
|
||||
@inlinable
|
||||
public func exchange(with value: T) -> T {
|
||||
return self.embedded.exchange(with: value)
|
||||
self.embedded.exchange(with: value)
|
||||
}
|
||||
|
||||
/// Atomically loads and returns the value of this object.
|
||||
@@ -262,7 +262,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
/// - Returns: The value of this object
|
||||
@inlinable
|
||||
public func load() -> T {
|
||||
return self.embedded.load()
|
||||
self.embedded.load()
|
||||
}
|
||||
|
||||
/// Atomically replaces the value of this object with `value`.
|
||||
@@ -273,7 +273,7 @@ public final class Atomic<T: AtomicPrimitive> {
|
||||
///
|
||||
/// - Parameter value: The new value to set the object to.
|
||||
@inlinable
|
||||
public func store(_ value: T) -> Void {
|
||||
public func store(_ value: T) {
|
||||
self.embedded.store(value)
|
||||
}
|
||||
|
||||
@@ -299,147 +299,147 @@ public protocol AtomicPrimitive {
|
||||
}
|
||||
|
||||
extension Bool: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic__Bool_create
|
||||
public static let atomic_destroy = catmc_atomic__Bool_destroy
|
||||
public static let atomic_create = catmc_atomic__Bool_create
|
||||
public static let atomic_destroy = catmc_atomic__Bool_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic__Bool_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic__Bool_add
|
||||
public static let atomic_sub = catmc_atomic__Bool_sub
|
||||
public static let atomic_exchange = catmc_atomic__Bool_exchange
|
||||
public static let atomic_load = catmc_atomic__Bool_load
|
||||
public static let atomic_store = catmc_atomic__Bool_store
|
||||
public static let atomic_add = catmc_atomic__Bool_add
|
||||
public static let atomic_sub = catmc_atomic__Bool_sub
|
||||
public static let atomic_exchange = catmc_atomic__Bool_exchange
|
||||
public static let atomic_load = catmc_atomic__Bool_load
|
||||
public static let atomic_store = catmc_atomic__Bool_store
|
||||
}
|
||||
|
||||
extension Int8: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_int_least8_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least8_t_destroy
|
||||
public static let atomic_create = catmc_atomic_int_least8_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least8_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_int_least8_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_int_least8_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least8_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least8_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least8_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least8_t_store
|
||||
public static let atomic_add = catmc_atomic_int_least8_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least8_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least8_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least8_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least8_t_store
|
||||
}
|
||||
|
||||
extension UInt8: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_uint_least8_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least8_t_destroy
|
||||
public static let atomic_create = catmc_atomic_uint_least8_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least8_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_uint_least8_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_uint_least8_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least8_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least8_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least8_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least8_t_store
|
||||
public static let atomic_add = catmc_atomic_uint_least8_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least8_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least8_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least8_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least8_t_store
|
||||
}
|
||||
|
||||
extension Int16: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_int_least16_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least16_t_destroy
|
||||
public static let atomic_create = catmc_atomic_int_least16_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least16_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_int_least16_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_int_least16_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least16_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least16_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least16_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least16_t_store
|
||||
public static let atomic_add = catmc_atomic_int_least16_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least16_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least16_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least16_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least16_t_store
|
||||
}
|
||||
|
||||
extension UInt16: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_uint_least16_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least16_t_destroy
|
||||
public static let atomic_create = catmc_atomic_uint_least16_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least16_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_uint_least16_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_uint_least16_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least16_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least16_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least16_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least16_t_store
|
||||
public static let atomic_add = catmc_atomic_uint_least16_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least16_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least16_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least16_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least16_t_store
|
||||
}
|
||||
|
||||
extension Int32: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_int_least32_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least32_t_destroy
|
||||
public static let atomic_create = catmc_atomic_int_least32_t_create
|
||||
public static let atomic_destroy = catmc_atomic_int_least32_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_int_least32_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_int_least32_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least32_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least32_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least32_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least32_t_store
|
||||
public static let atomic_add = catmc_atomic_int_least32_t_add
|
||||
public static let atomic_sub = catmc_atomic_int_least32_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_int_least32_t_exchange
|
||||
public static let atomic_load = catmc_atomic_int_least32_t_load
|
||||
public static let atomic_store = catmc_atomic_int_least32_t_store
|
||||
}
|
||||
|
||||
extension UInt32: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_uint_least32_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least32_t_destroy
|
||||
public static let atomic_create = catmc_atomic_uint_least32_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uint_least32_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_uint_least32_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_uint_least32_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least32_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least32_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least32_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least32_t_store
|
||||
public static let atomic_add = catmc_atomic_uint_least32_t_add
|
||||
public static let atomic_sub = catmc_atomic_uint_least32_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uint_least32_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uint_least32_t_load
|
||||
public static let atomic_store = catmc_atomic_uint_least32_t_store
|
||||
}
|
||||
|
||||
extension Int64: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_long_long_create
|
||||
public static let atomic_destroy = catmc_atomic_long_long_destroy
|
||||
public static let atomic_create = catmc_atomic_long_long_create
|
||||
public static let atomic_destroy = catmc_atomic_long_long_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_long_long_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_long_long_add
|
||||
public static let atomic_sub = catmc_atomic_long_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_long_long_exchange
|
||||
public static let atomic_load = catmc_atomic_long_long_load
|
||||
public static let atomic_store = catmc_atomic_long_long_store
|
||||
public static let atomic_add = catmc_atomic_long_long_add
|
||||
public static let atomic_sub = catmc_atomic_long_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_long_long_exchange
|
||||
public static let atomic_load = catmc_atomic_long_long_load
|
||||
public static let atomic_store = catmc_atomic_long_long_store
|
||||
}
|
||||
|
||||
extension UInt64: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_unsigned_long_long_create
|
||||
public static let atomic_destroy = catmc_atomic_unsigned_long_long_destroy
|
||||
public static let atomic_create = catmc_atomic_unsigned_long_long_create
|
||||
public static let atomic_destroy = catmc_atomic_unsigned_long_long_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_unsigned_long_long_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_unsigned_long_long_add
|
||||
public static let atomic_sub = catmc_atomic_unsigned_long_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_unsigned_long_long_exchange
|
||||
public static let atomic_load = catmc_atomic_unsigned_long_long_load
|
||||
public static let atomic_store = catmc_atomic_unsigned_long_long_store
|
||||
public static let atomic_add = catmc_atomic_unsigned_long_long_add
|
||||
public static let atomic_sub = catmc_atomic_unsigned_long_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_unsigned_long_long_exchange
|
||||
public static let atomic_load = catmc_atomic_unsigned_long_long_load
|
||||
public static let atomic_store = catmc_atomic_unsigned_long_long_store
|
||||
}
|
||||
|
||||
#if os(Windows)
|
||||
extension Int: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_intptr_t_create
|
||||
public static let atomic_destroy = catmc_atomic_intptr_t_destroy
|
||||
public static let atomic_create = catmc_atomic_intptr_t_create
|
||||
public static let atomic_destroy = catmc_atomic_intptr_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_intptr_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_intptr_t_add
|
||||
public static let atomic_sub = catmc_atomic_intptr_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_intptr_t_exchange
|
||||
public static let atomic_load = catmc_atomic_intptr_t_load
|
||||
public static let atomic_store = catmc_atomic_intptr_t_store
|
||||
public static let atomic_add = catmc_atomic_intptr_t_add
|
||||
public static let atomic_sub = catmc_atomic_intptr_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_intptr_t_exchange
|
||||
public static let atomic_load = catmc_atomic_intptr_t_load
|
||||
public static let atomic_store = catmc_atomic_intptr_t_store
|
||||
}
|
||||
|
||||
extension UInt: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_uintptr_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uintptr_t_destroy
|
||||
public static let atomic_create = catmc_atomic_uintptr_t_create
|
||||
public static let atomic_destroy = catmc_atomic_uintptr_t_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_uintptr_t_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_uintptr_t_add
|
||||
public static let atomic_sub = catmc_atomic_uintptr_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uintptr_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uintptr_t_load
|
||||
public static let atomic_store = catmc_atomic_uintptr_t_store
|
||||
public static let atomic_add = catmc_atomic_uintptr_t_add
|
||||
public static let atomic_sub = catmc_atomic_uintptr_t_sub
|
||||
public static let atomic_exchange = catmc_atomic_uintptr_t_exchange
|
||||
public static let atomic_load = catmc_atomic_uintptr_t_load
|
||||
public static let atomic_store = catmc_atomic_uintptr_t_store
|
||||
}
|
||||
#else
|
||||
extension Int: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_long_create
|
||||
public static let atomic_destroy = catmc_atomic_long_destroy
|
||||
public static let atomic_create = catmc_atomic_long_create
|
||||
public static let atomic_destroy = catmc_atomic_long_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_long_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_long_add
|
||||
public static let atomic_sub = catmc_atomic_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_long_exchange
|
||||
public static let atomic_load = catmc_atomic_long_load
|
||||
public static let atomic_store = catmc_atomic_long_store
|
||||
public static let atomic_add = catmc_atomic_long_add
|
||||
public static let atomic_sub = catmc_atomic_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_long_exchange
|
||||
public static let atomic_load = catmc_atomic_long_load
|
||||
public static let atomic_store = catmc_atomic_long_store
|
||||
}
|
||||
|
||||
extension UInt: AtomicPrimitive {
|
||||
public static let atomic_create = catmc_atomic_unsigned_long_create
|
||||
public static let atomic_destroy = catmc_atomic_unsigned_long_destroy
|
||||
public static let atomic_create = catmc_atomic_unsigned_long_create
|
||||
public static let atomic_destroy = catmc_atomic_unsigned_long_destroy
|
||||
public static let atomic_compare_and_exchange = catmc_atomic_unsigned_long_compare_and_exchange
|
||||
public static let atomic_add = catmc_atomic_unsigned_long_add
|
||||
public static let atomic_sub = catmc_atomic_unsigned_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_unsigned_long_exchange
|
||||
public static let atomic_load = catmc_atomic_unsigned_long_load
|
||||
public static let atomic_store = catmc_atomic_unsigned_long_store
|
||||
public static let atomic_add = catmc_atomic_unsigned_long_add
|
||||
public static let atomic_sub = catmc_atomic_unsigned_long_sub
|
||||
public static let atomic_exchange = catmc_atomic_unsigned_long_exchange
|
||||
public static let atomic_load = catmc_atomic_unsigned_long_load
|
||||
public static let atomic_store = catmc_atomic_unsigned_long_store
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -447,7 +447,11 @@ extension UInt: AtomicPrimitive {
|
||||
///
|
||||
/// - warning: The use of `AtomicBox` should be avoided because it requires an implementation of a spin-lock
|
||||
/// (more precisely a CAS loop) to operate correctly.
|
||||
@available(*, deprecated, message: "AtomicBox is deprecated without replacement because the original implementation doesn't work.")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "AtomicBox is deprecated without replacement because the original implementation doesn't work."
|
||||
)
|
||||
public final class AtomicBox<T: AnyObject> {
|
||||
private let storage: NIOAtomic<UInt>
|
||||
|
||||
@@ -482,7 +486,7 @@ public final class AtomicBox<T: AnyObject> {
|
||||
/// - Returns: `True` if the exchange occurred, or `False` if `expected` did not
|
||||
/// match the current value and so no exchange occurred.
|
||||
public func compareAndExchange(expected: T, desired: T) -> Bool {
|
||||
return withExtendedLifetime(desired) {
|
||||
withExtendedLifetime(desired) {
|
||||
let expectedPtr = Unmanaged<T>.passUnretained(expected)
|
||||
let desiredPtr = Unmanaged<T>.passUnretained(desired)
|
||||
let expectedPtrBits = UInt(bitPattern: expectedPtr.toOpaque())
|
||||
@@ -573,7 +577,7 @@ public final class AtomicBox<T: AnyObject> {
|
||||
|
||||
// step 3: Now, let's exchange it back into the store
|
||||
let casWorked = self.storage.compareAndExchange(expected: 0, desired: ptrBits)
|
||||
precondition(casWorked) // this _has_ to work because `0` means we own it exclusively.
|
||||
precondition(casWorked) // this _has_ to work because `0` means we own it exclusively.
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -587,7 +591,7 @@ public final class AtomicBox<T: AnyObject> {
|
||||
/// 100% CPU load.
|
||||
///
|
||||
/// - Parameter value: The new value to set the object to.
|
||||
public func store(_ value: T) -> Void {
|
||||
public func store(_ value: T) {
|
||||
_ = self.exchange(with: value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,19 +33,19 @@ import Musl
|
||||
/// `SRWLOCK` type.
|
||||
@available(*, deprecated, renamed: "NIOLock")
|
||||
public final class Lock {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
|
||||
UnsafeMutablePointer.allocate(capacity: 1)
|
||||
#else
|
||||
#else
|
||||
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
|
||||
UnsafeMutablePointer.allocate(capacity: 1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Create a new lock.
|
||||
public init() {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
InitializeSRWLock(self.mutex)
|
||||
#else
|
||||
#else
|
||||
var attr = pthread_mutexattr_t()
|
||||
pthread_mutexattr_init(&attr)
|
||||
debugOnly {
|
||||
@@ -54,16 +54,16 @@ public final class Lock {
|
||||
|
||||
let err = pthread_mutex_init(self.mutex, &attr)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
// SRWLOCK does not need to be free'd
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_destroy(self.mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
mutex.deallocate()
|
||||
}
|
||||
|
||||
@@ -72,12 +72,12 @@ public final class Lock {
|
||||
/// Whenever possible, consider using `withLock` instead of this method and
|
||||
/// `unlock`, to simplify lock handling.
|
||||
public func lock() {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
AcquireSRWLockExclusive(self.mutex)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_lock(self.mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Release the lock.
|
||||
@@ -85,12 +85,12 @@ public final class Lock {
|
||||
/// Whenever possible, consider using `withLock` instead of this method and
|
||||
/// `lock`, to simplify lock handling.
|
||||
public func unlock() {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
ReleaseSRWLockExclusive(self.mutex)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_mutex_unlock(self.mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Acquire the lock for the duration of the given block.
|
||||
@@ -112,7 +112,7 @@ public final class Lock {
|
||||
|
||||
// specialise Void return (for performance)
|
||||
@inlinable
|
||||
public func withLockVoid(_ body: () throws -> Void) rethrows -> Void {
|
||||
public func withLockVoid(_ body: () throws -> Void) rethrows {
|
||||
try self.withLock(body)
|
||||
}
|
||||
}
|
||||
@@ -124,13 +124,13 @@ public final class Lock {
|
||||
public final class ConditionLock<T: Equatable> {
|
||||
private var _value: T
|
||||
private let mutex: NIOLock
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
private let cond: UnsafeMutablePointer<CONDITION_VARIABLE> =
|
||||
UnsafeMutablePointer.allocate(capacity: 1)
|
||||
#else
|
||||
#else
|
||||
private let cond: UnsafeMutablePointer<pthread_cond_t> =
|
||||
UnsafeMutablePointer.allocate(capacity: 1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Create the lock, and initialize the state variable to `value`.
|
||||
///
|
||||
@@ -138,21 +138,21 @@ public final class ConditionLock<T: Equatable> {
|
||||
public init(value: T) {
|
||||
self._value = value
|
||||
self.mutex = NIOLock()
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
InitializeConditionVariable(self.cond)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_cond_init(self.cond, nil)
|
||||
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
// condition variables do not need to be explicitly destroyed
|
||||
#else
|
||||
#else
|
||||
let err = pthread_cond_destroy(self.cond)
|
||||
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
self.cond.deallocate()
|
||||
}
|
||||
|
||||
@@ -190,13 +190,13 @@ public final class ConditionLock<T: Equatable> {
|
||||
break
|
||||
}
|
||||
self.mutex.withLockPrimitive { mutex in
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
let result = SleepConditionVariableSRW(self.cond, mutex, INFINITE, 0)
|
||||
precondition(result, "\(#function) failed in SleepConditionVariableSRW with error \(GetLastError())")
|
||||
#else
|
||||
#else
|
||||
let err = pthread_cond_wait(self.cond, mutex)
|
||||
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ public final class ConditionLock<T: Equatable> {
|
||||
public func lock(whenValue wantedValue: T, timeoutSeconds: Double) -> Bool {
|
||||
precondition(timeoutSeconds >= 0)
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
var dwMilliseconds: DWORD = DWORD(timeoutSeconds * 1000)
|
||||
|
||||
self.lock()
|
||||
@@ -222,10 +222,14 @@ public final class ConditionLock<T: Equatable> {
|
||||
}
|
||||
|
||||
let dwWaitStart = timeGetTime()
|
||||
if !SleepConditionVariableSRW(self.cond, self.mutex._storage.mutex,
|
||||
dwMilliseconds, 0) {
|
||||
if !SleepConditionVariableSRW(
|
||||
self.cond,
|
||||
self.mutex._storage.mutex,
|
||||
dwMilliseconds,
|
||||
0
|
||||
) {
|
||||
let dwError = GetLastError()
|
||||
if (dwError == ERROR_TIMEOUT) {
|
||||
if dwError == ERROR_TIMEOUT {
|
||||
self.unlock()
|
||||
return false
|
||||
}
|
||||
@@ -235,18 +239,20 @@ public final class ConditionLock<T: Equatable> {
|
||||
// NOTE: this may be a spurious wakeup, adjust the timeout accordingly
|
||||
dwMilliseconds = dwMilliseconds - (timeGetTime() - dwWaitStart)
|
||||
}
|
||||
#else
|
||||
let nsecPerSec: Int64 = 1000000000
|
||||
#else
|
||||
let nsecPerSec: Int64 = 1_000_000_000
|
||||
self.lock()
|
||||
/* the timeout as a (seconds, nano seconds) pair */
|
||||
// the timeout as a (seconds, nano seconds) pair
|
||||
let timeoutNS = Int64(timeoutSeconds * Double(nsecPerSec))
|
||||
|
||||
var curTime = timeval()
|
||||
gettimeofday(&curTime, nil)
|
||||
|
||||
let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000
|
||||
var timeoutAbs = timespec(tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)),
|
||||
tv_nsec: Int(allNSecs % nsecPerSec))
|
||||
var timeoutAbs = timespec(
|
||||
tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)),
|
||||
tv_nsec: Int(allNSecs % nsecPerSec)
|
||||
)
|
||||
assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec))
|
||||
assert(timeoutAbs.tv_sec >= curTime.tv_sec)
|
||||
return self.mutex.withLockPrimitive { mutex -> Bool in
|
||||
@@ -265,7 +271,7 @@ public final class ConditionLock<T: Equatable> {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Release the lock, setting the state variable to `newValue`.
|
||||
@@ -275,12 +281,12 @@ public final class ConditionLock<T: Equatable> {
|
||||
public func unlock(withValue newValue: T) {
|
||||
self._value = newValue
|
||||
self.unlock()
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
WakeAllConditionVariable(self.cond)
|
||||
#else
|
||||
#else
|
||||
let err = pthread_cond_broadcast(self.cond)
|
||||
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +297,12 @@ public final class ConditionLock<T: Equatable> {
|
||||
/// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
|
||||
@inlinable
|
||||
internal func debugOnly(_ body: () -> Void) {
|
||||
assert({ body(); return true }())
|
||||
assert(
|
||||
{
|
||||
body()
|
||||
return true
|
||||
}()
|
||||
)
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
|
||||
@@ -27,19 +27,19 @@ public struct AddressedEnvelope<DataType> {
|
||||
self.remoteAddress = remoteAddress
|
||||
self.data = data
|
||||
}
|
||||
|
||||
|
||||
public init(remoteAddress: SocketAddress, data: DataType, metadata: Metadata?) {
|
||||
self.remoteAddress = remoteAddress
|
||||
self.data = data
|
||||
self.metadata = metadata
|
||||
}
|
||||
|
||||
|
||||
/// Any metadata associated with an `AddressedEnvelope`
|
||||
public struct Metadata: Hashable, Sendable {
|
||||
/// Details of any congestion state.
|
||||
public var ecnState: NIOExplicitCongestionNotificationState
|
||||
public var packetInfo: NIOPacketInfo?
|
||||
|
||||
|
||||
public init(ecnState: NIOExplicitCongestionNotificationState) {
|
||||
self.ecnState = ecnState
|
||||
self.packetInfo = nil
|
||||
@@ -54,7 +54,7 @@ public struct AddressedEnvelope<DataType> {
|
||||
|
||||
extension AddressedEnvelope: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "AddressedEnvelope { remoteAddress: \(self.remoteAddress), data: \(self.data) }"
|
||||
"AddressedEnvelope { remoteAddress: \(self.remoteAddress), data: \(self.data) }"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ extension EventLoopFuture {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@inlinable
|
||||
public func get() async throws -> Value {
|
||||
return try await withUnsafeThrowingContinuation { (cont: UnsafeContinuation<UnsafeTransfer<Value>, Error>) in
|
||||
try await withUnsafeThrowingContinuation { (cont: UnsafeContinuation<UnsafeTransfer<Value>, Error>) in
|
||||
self.whenComplete { result in
|
||||
switch result {
|
||||
case .success(let value):
|
||||
@@ -38,7 +38,7 @@ extension EventLoopGroup {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@inlinable
|
||||
public func shutdownGracefully() async throws {
|
||||
return try await withCheckedThrowingContinuation { cont in
|
||||
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
||||
self.shutdownGracefully { error in
|
||||
if let error = error {
|
||||
cont.resume(throwing: error)
|
||||
@@ -97,7 +97,7 @@ extension Channel {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@inlinable
|
||||
public func getOption<Option: ChannelOption>(_ option: Option) async throws -> Option.Value {
|
||||
return try await self.getOption(option).get()
|
||||
try await self.getOption(option).get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,9 +162,11 @@ extension ChannelOutboundInvoker {
|
||||
extension ChannelPipeline {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@preconcurrency
|
||||
public func addHandler(_ handler: ChannelHandler & Sendable,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last) async throws {
|
||||
public func addHandler(
|
||||
_ handler: ChannelHandler & Sendable,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) async throws {
|
||||
try await self.addHandler(handler, name: name, position: position).get()
|
||||
}
|
||||
|
||||
@@ -185,43 +187,62 @@ extension ChannelPipeline {
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@available(*, deprecated, message: "ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message:
|
||||
"ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop"
|
||||
)
|
||||
@preconcurrency
|
||||
public func context(handler: ChannelHandler & Sendable) async throws -> ChannelHandlerContext {
|
||||
return try await self.context(handler: handler).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
try await self.context(handler: handler).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@available(*, deprecated, message: "ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message:
|
||||
"ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop"
|
||||
)
|
||||
public func context(name: String) async throws -> ChannelHandlerContext {
|
||||
return try await self.context(name: name).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
try await self.context(name: name).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@available(*, deprecated, message: "ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message:
|
||||
"ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop"
|
||||
)
|
||||
@inlinable
|
||||
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) async throws -> ChannelHandlerContext {
|
||||
return try await self.context(handlerType: handlerType).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
try await self.context(handlerType: handlerType).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@preconcurrency
|
||||
public func addHandlers(_ handlers: [ChannelHandler & Sendable],
|
||||
position: ChannelPipeline.Position = .last) async throws {
|
||||
public func addHandlers(
|
||||
_ handlers: [ChannelHandler & Sendable],
|
||||
position: ChannelPipeline.Position = .last
|
||||
) async throws {
|
||||
try await self.addHandlers(handlers, position: position).map { UnsafeTransfer($0) }.get().wrappedValue
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
@preconcurrency
|
||||
public func addHandlers(_ handlers: (ChannelHandler & Sendable)...,
|
||||
position: ChannelPipeline.Position = .last) async throws {
|
||||
public func addHandlers(
|
||||
_ handlers: (ChannelHandler & Sendable)...,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) async throws {
|
||||
try await self.addHandlers(handlers, position: position)
|
||||
}
|
||||
}
|
||||
|
||||
public struct NIOTooManyBytesError: Error, Hashable {
|
||||
public init() {}
|
||||
}
|
||||
public init() {}
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension AsyncSequence where Element: RandomAccessCollection, Element.Element == UInt8 {
|
||||
@@ -246,7 +267,7 @@ extension AsyncSequence where Element: RandomAccessCollection, Element.Element =
|
||||
accumulationBuffer.writeBytes(fragment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Accumulates an `AsyncSequence` of `RandomAccessCollection`s into a single ``ByteBuffer``.
|
||||
/// - Parameters:
|
||||
/// - maxBytes: The maximum number of bytes this method is allowed to accumulate
|
||||
@@ -289,7 +310,7 @@ extension AsyncSequence where Element == ByteBuffer {
|
||||
accumulationBuffer.writeImmutableBuffer(fragment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Accumulates an `AsyncSequence` of ``ByteBuffer``s into a single ``ByteBuffer``.
|
||||
/// - Parameters:
|
||||
/// - maxBytes: The maximum number of bytes this method is allowed to accumulate
|
||||
@@ -309,7 +330,7 @@ extension AsyncSequence where Element == ByteBuffer {
|
||||
guard head.readableBytes <= maxBytes else {
|
||||
throw NIOTooManyBytesError()
|
||||
}
|
||||
|
||||
|
||||
let tail = AsyncSequenceFromIterator(iterator)
|
||||
// it is guaranteed that
|
||||
// `maxBytes >= 0 && head.readableBytes >= 0 && head.readableBytes <= maxBytes`
|
||||
@@ -324,13 +345,13 @@ extension AsyncSequence where Element == ByteBuffer {
|
||||
@usableFromInline
|
||||
struct AsyncSequenceFromIterator<AsyncIterator: AsyncIteratorProtocol>: AsyncSequence {
|
||||
@usableFromInline typealias Element = AsyncIterator.Element
|
||||
|
||||
|
||||
@usableFromInline var iterator: AsyncIterator
|
||||
|
||||
|
||||
@inlinable init(_ iterator: AsyncIterator) {
|
||||
self.iterator = iterator
|
||||
}
|
||||
|
||||
|
||||
@inlinable func makeAsyncIterator() -> AsyncIterator {
|
||||
self.iterator
|
||||
}
|
||||
@@ -339,7 +360,9 @@ struct AsyncSequenceFromIterator<AsyncIterator: AsyncIteratorProtocol>: AsyncSeq
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension EventLoop {
|
||||
@inlinable
|
||||
public func makeFutureWithTask<Return>(_ body: @Sendable @escaping () async throws -> Return) -> EventLoopFuture<Return> {
|
||||
public func makeFutureWithTask<Return>(
|
||||
_ body: @Sendable @escaping () async throws -> Return
|
||||
) -> EventLoopFuture<Return> {
|
||||
let promise = self.makePromise(of: Return.self)
|
||||
promise.completeWithTask(body)
|
||||
return promise.futureResult
|
||||
|
||||
@@ -58,7 +58,10 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
/// - inboundType: The ``NIOAsyncChannel/inbound`` message's type.
|
||||
/// - outboundType: The ``NIOAsyncChannel/outbound`` message's type.
|
||||
public init(
|
||||
backPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark = .init(lowWatermark: 2, highWatermark: 10),
|
||||
backPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark = .init(
|
||||
lowWatermark: 2,
|
||||
highWatermark: 10
|
||||
),
|
||||
isOutboundHalfClosureEnabled: Bool = false,
|
||||
inboundType: Inbound.Type = Inbound.self,
|
||||
outboundType: Outbound.Type = Outbound.self
|
||||
@@ -147,7 +150,12 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
/// - Parameters:
|
||||
/// - channel: The ``Channel`` to wrap.
|
||||
/// - configuration: The ``NIOAsyncChannel``s configuration.
|
||||
@available(*, deprecated, renamed: "init(wrappingChannelSynchronously:configuration:)", message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
renamed: "init(wrappingChannelSynchronously:configuration:)",
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
@inlinable
|
||||
public init(
|
||||
synchronouslyWrapping channel: Channel,
|
||||
@@ -173,7 +181,12 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
/// - channel: The ``Channel`` to wrap.
|
||||
/// - configuration: The ``NIOAsyncChannel``s configuration.
|
||||
@inlinable
|
||||
@available(*, deprecated, renamed: "init(wrappingChannelSynchronously:configuration:)", message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
renamed: "init(wrappingChannelSynchronously:configuration:)",
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
public init(
|
||||
synchronouslyWrapping channel: Channel,
|
||||
configuration: Configuration = .init()
|
||||
@@ -206,7 +219,11 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
///
|
||||
/// - Important: This is not considered stable API and should not be used.
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
public static func _wrapAsyncChannelWithTransformations(
|
||||
synchronouslyWrapping channel: Channel,
|
||||
backPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
|
||||
@@ -214,12 +231,14 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
channelReadTransformation: @Sendable @escaping (Channel) -> EventLoopFuture<Inbound>
|
||||
) throws -> NIOAsyncChannel<Inbound, Outbound> where Outbound == Never {
|
||||
channel.eventLoop.preconditionInEventLoop()
|
||||
let (inboundStream, outboundWriter): (NIOAsyncChannelInboundStream<Inbound>, NIOAsyncChannelOutboundWriter<Outbound>) = try channel._syncAddAsyncHandlersWithTransformations(
|
||||
backPressureStrategy: backPressureStrategy,
|
||||
isOutboundHalfClosureEnabled: isOutboundHalfClosureEnabled,
|
||||
closeOnDeinit: true,
|
||||
channelReadTransformation: channelReadTransformation
|
||||
)
|
||||
let (inboundStream, outboundWriter):
|
||||
(NIOAsyncChannelInboundStream<Inbound>, NIOAsyncChannelOutboundWriter<Outbound>) =
|
||||
try channel._syncAddAsyncHandlersWithTransformations(
|
||||
backPressureStrategy: backPressureStrategy,
|
||||
isOutboundHalfClosureEnabled: isOutboundHalfClosureEnabled,
|
||||
closeOnDeinit: true,
|
||||
channelReadTransformation: channelReadTransformation
|
||||
)
|
||||
|
||||
outboundWriter.finish()
|
||||
|
||||
@@ -242,12 +261,14 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
channelReadTransformation: @Sendable @escaping (Channel) -> EventLoopFuture<Inbound>
|
||||
) throws -> NIOAsyncChannel<Inbound, Outbound> where Outbound == Never {
|
||||
channel.eventLoop.preconditionInEventLoop()
|
||||
let (inboundStream, outboundWriter): (NIOAsyncChannelInboundStream<Inbound>, NIOAsyncChannelOutboundWriter<Outbound>) = try channel._syncAddAsyncHandlersWithTransformations(
|
||||
backPressureStrategy: backPressureStrategy,
|
||||
isOutboundHalfClosureEnabled: isOutboundHalfClosureEnabled,
|
||||
closeOnDeinit: false,
|
||||
channelReadTransformation: channelReadTransformation
|
||||
)
|
||||
let (inboundStream, outboundWriter):
|
||||
(NIOAsyncChannelInboundStream<Inbound>, NIOAsyncChannelOutboundWriter<Outbound>) =
|
||||
try channel._syncAddAsyncHandlersWithTransformations(
|
||||
backPressureStrategy: backPressureStrategy,
|
||||
isOutboundHalfClosureEnabled: isOutboundHalfClosureEnabled,
|
||||
closeOnDeinit: false,
|
||||
channelReadTransformation: channelReadTransformation
|
||||
)
|
||||
|
||||
outboundWriter.finish()
|
||||
|
||||
@@ -264,7 +285,8 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
///
|
||||
/// - Parameter body: A closure that gets scoped access to the inbound and outbound.
|
||||
public func executeThenClose<Result>(
|
||||
_ body: (_ inbound: NIOAsyncChannelInboundStream<Inbound>, _ outbound: NIOAsyncChannelOutboundWriter<Outbound>) async throws -> Result
|
||||
_ body: (_ inbound: NIOAsyncChannelInboundStream<Inbound>, _ outbound: NIOAsyncChannelOutboundWriter<Outbound>)
|
||||
async throws -> Result
|
||||
) async throws -> Result {
|
||||
let result: Result
|
||||
do {
|
||||
@@ -290,7 +312,11 @@ public struct NIOAsyncChannel<Inbound: Sendable, Outbound: Sendable>: Sendable {
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// swift-format-ignore: AmbiguousTrailingClosureOverload
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension NIOAsyncChannel {
|
||||
/// Provides scoped access to the inbound side of the underlying ``Channel``.
|
||||
///
|
||||
/// - Important: After this method returned the underlying ``Channel`` will be closed.
|
||||
|
||||
@@ -147,7 +147,6 @@ extension NIOAsyncChannelHandler: ChannelInboundHandler {
|
||||
context.fireChannelInactive()
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
|
||||
switch event {
|
||||
@@ -298,7 +297,6 @@ extension NIOAsyncChannelHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension NIOAsyncChannelHandler {
|
||||
@inlinable
|
||||
@@ -341,7 +339,9 @@ struct NIOAsyncChannelHandlerProducerDelegate: @unchecked Sendable, NIOAsyncSequ
|
||||
let _produceMore: () -> Void
|
||||
|
||||
@inlinable
|
||||
init<InboundIn, ProducerElement, OutboundOut>(handler: NIOAsyncChannelHandler<InboundIn, ProducerElement, OutboundOut>) {
|
||||
init<InboundIn, ProducerElement, OutboundOut>(
|
||||
handler: NIOAsyncChannelHandler<InboundIn, ProducerElement, OutboundOut>
|
||||
) {
|
||||
self.eventLoop = handler.eventLoop
|
||||
self._didTerminate = handler._didTerminate
|
||||
self._produceMore = handler._produceMore
|
||||
|
||||
@@ -151,7 +151,7 @@ extension NIOAsyncChannelInboundStream: AsyncSequence {
|
||||
|
||||
@inlinable
|
||||
public func makeAsyncIterator() -> AsyncIterator {
|
||||
return AsyncIterator(self._backing)
|
||||
AsyncIterator(self._backing)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,8 @@ public struct NIOAsyncChannelOutboundWriter<OutboundOut: Sendable>: Sendable {
|
||||
///
|
||||
/// This method suspends if the underlying channel is not writable and will resume once the it becomes writable again.
|
||||
@inlinable
|
||||
public func write<Writes: AsyncSequence>(contentsOf sequence: Writes) async throws where Writes.Element == OutboundOut {
|
||||
public func write<Writes: AsyncSequence>(contentsOf sequence: Writes) async throws
|
||||
where Writes.Element == OutboundOut {
|
||||
for try await data in sequence {
|
||||
try await self.write(data)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public struct NIOAsyncSequenceProducer<
|
||||
public let sequence: NIOAsyncSequenceProducer
|
||||
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal init(
|
||||
internal init(
|
||||
source: Source,
|
||||
sequence: NIOAsyncSequenceProducer
|
||||
) {
|
||||
@@ -122,12 +122,13 @@ public struct NIOAsyncSequenceProducer<
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _throwingSequence: NIOThrowingAsyncSequenceProducer<
|
||||
Element,
|
||||
Never,
|
||||
Strategy,
|
||||
Delegate
|
||||
>
|
||||
internal let _throwingSequence:
|
||||
NIOThrowingAsyncSequenceProducer<
|
||||
Element,
|
||||
Never,
|
||||
Strategy,
|
||||
Delegate
|
||||
>
|
||||
|
||||
/// Initializes a new ``NIOAsyncSequenceProducer`` and a ``NIOAsyncSequenceProducer/Source``.
|
||||
///
|
||||
@@ -175,7 +176,12 @@ public struct NIOAsyncSequenceProducer<
|
||||
/// - delegate: The delegate of the sequence
|
||||
/// - Returns: A ``NIOAsyncSequenceProducer/Source`` and a ``NIOAsyncSequenceProducer``.
|
||||
@inlinable
|
||||
@available(*, deprecated, renamed: "makeSequence(elementType:backPressureStrategy:finishOnDeinit:delegate:)", message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
renamed: "makeSequence(elementType:backPressureStrategy:finishOnDeinit:delegate:)",
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
public static func makeSequence(
|
||||
elementType: Element.Type = Element.self,
|
||||
backPressureStrategy: Strategy,
|
||||
@@ -194,7 +200,7 @@ public struct NIOAsyncSequenceProducer<
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* private */ internal init(
|
||||
internal init(
|
||||
throwingSequence: NIOThrowingAsyncSequenceProducer<Element, Never, Strategy, Delegate>
|
||||
) {
|
||||
self._throwingSequence = throwingSequence
|
||||
@@ -212,12 +218,13 @@ extension NIOAsyncSequenceProducer: AsyncSequence {
|
||||
extension NIOAsyncSequenceProducer {
|
||||
public struct AsyncIterator: AsyncIteratorProtocol {
|
||||
@usableFromInline
|
||||
/* private */ internal let _throwingIterator: NIOThrowingAsyncSequenceProducer<
|
||||
Element,
|
||||
Never,
|
||||
Strategy,
|
||||
Delegate
|
||||
>.AsyncIterator
|
||||
internal let _throwingIterator:
|
||||
NIOThrowingAsyncSequenceProducer<
|
||||
Element,
|
||||
Never,
|
||||
Strategy,
|
||||
Delegate
|
||||
>.AsyncIterator
|
||||
|
||||
fileprivate init(
|
||||
throwingIterator: NIOThrowingAsyncSequenceProducer<
|
||||
@@ -233,7 +240,7 @@ extension NIOAsyncSequenceProducer {
|
||||
@inlinable
|
||||
public func next() async -> Element? {
|
||||
// this call will only throw if cancelled and we want to just return nil in that case
|
||||
return try? await self._throwingIterator.next()
|
||||
try? await self._throwingIterator.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,10 +260,10 @@ extension NIOAsyncSequenceProducer {
|
||||
>.Source
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal var _throwingSource: ThrowingSource
|
||||
internal var _throwingSource: ThrowingSource
|
||||
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal init(throwingSource: ThrowingSource) {
|
||||
internal init(throwingSource: ThrowingSource) {
|
||||
self._throwingSource = throwingSource
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public struct NIOAsyncWriterError: Error, Hashable, CustomStringConvertible {
|
||||
|
||||
@inlinable
|
||||
public static func == (lhs: NIOAsyncWriterError, rhs: NIOAsyncWriterError) -> Bool {
|
||||
return lhs._code == rhs._code
|
||||
lhs._code == rhs._code
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -147,7 +147,7 @@ public struct NIOAsyncWriter<
|
||||
public let writer: NIOAsyncWriter
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal init(
|
||||
internal init(
|
||||
sink: Sink,
|
||||
writer: NIOAsyncWriter
|
||||
) {
|
||||
@@ -158,7 +158,7 @@ public struct NIOAsyncWriter<
|
||||
|
||||
/// This class is needed to hook the deinit to observe once all references to the ``NIOAsyncWriter`` are dropped.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal final class InternalClass: Sendable {
|
||||
internal final class InternalClass: Sendable {
|
||||
@usableFromInline
|
||||
internal let _storage: Storage
|
||||
|
||||
@@ -183,10 +183,10 @@ public struct NIOAsyncWriter<
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _internalClass: InternalClass
|
||||
internal let _internalClass: InternalClass
|
||||
|
||||
@inlinable
|
||||
/* private */ internal var _storage: Storage {
|
||||
internal var _storage: Storage {
|
||||
self._internalClass._storage
|
||||
}
|
||||
|
||||
@@ -203,7 +203,12 @@ public struct NIOAsyncWriter<
|
||||
/// - delegate: The delegate of the writer.
|
||||
/// - Returns: A ``NIOAsyncWriter/NewWriter``.
|
||||
@inlinable
|
||||
@available(*, deprecated, renamed: "makeWriter(elementType:isWritable:finishOnDeinit:delegate:)", message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
renamed: "makeWriter(elementType:isWritable:finishOnDeinit:delegate:)",
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
public static func makeWriter(
|
||||
elementType: Element.Type = Element.self,
|
||||
isWritable: Bool,
|
||||
@@ -251,7 +256,7 @@ public struct NIOAsyncWriter<
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* private */ internal init(
|
||||
internal init(
|
||||
isWritable: Bool,
|
||||
finishOnDeinit: Bool,
|
||||
delegate: Delegate
|
||||
@@ -337,9 +342,9 @@ extension NIOAsyncWriter {
|
||||
public struct Sink {
|
||||
/// This class is needed to hook the deinit to observe once all references to the ``NIOAsyncWriter/Sink`` are dropped.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal final class InternalClass: Sendable {
|
||||
internal final class InternalClass: Sendable {
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal let _storage: Storage
|
||||
internal let _storage: Storage
|
||||
|
||||
@usableFromInline
|
||||
internal let _finishOnDeinit: Bool
|
||||
@@ -362,10 +367,10 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _internalClass: InternalClass
|
||||
internal let _internalClass: InternalClass
|
||||
|
||||
@inlinable
|
||||
/* private */ internal var _storage: Storage {
|
||||
internal var _storage: Storage {
|
||||
self._internalClass._storage
|
||||
}
|
||||
|
||||
@@ -414,7 +419,7 @@ extension NIOAsyncWriter {
|
||||
extension NIOAsyncWriter {
|
||||
/// This is the underlying storage of the writer. The goal of this is to synchronize the access to all state.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal struct Storage: Sendable {
|
||||
internal struct Storage: Sendable {
|
||||
/// Internal type to generate unique yield IDs.
|
||||
///
|
||||
/// This type has reference semantics.
|
||||
@@ -424,7 +429,7 @@ extension NIOAsyncWriter {
|
||||
@usableFromInline
|
||||
struct YieldID: Equatable, Sendable {
|
||||
@usableFromInline
|
||||
/* private */ internal var value: UInt64
|
||||
internal var value: UInt64
|
||||
|
||||
@inlinable
|
||||
init(value: UInt64) {
|
||||
@@ -447,10 +452,10 @@ extension NIOAsyncWriter {
|
||||
|
||||
/// The counter used to assign an ID to all our yields.
|
||||
@usableFromInline
|
||||
/* private */ internal let _yieldIDGenerator = YieldIDGenerator()
|
||||
internal let _yieldIDGenerator = YieldIDGenerator()
|
||||
/// The state machine.
|
||||
@usableFromInline
|
||||
/* private */ internal let _state: NIOLockedValueBox<State>
|
||||
internal let _state: NIOLockedValueBox<State>
|
||||
|
||||
@usableFromInline
|
||||
struct State: Sendable {
|
||||
@@ -485,7 +490,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal init(
|
||||
internal init(
|
||||
isWritable: Bool,
|
||||
delegate: Delegate
|
||||
) {
|
||||
@@ -494,7 +499,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func setWritability(to writability: Bool) {
|
||||
internal func setWritability(to writability: Bool) {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
@@ -504,7 +509,9 @@ extension NIOAsyncWriter {
|
||||
|
||||
switch action {
|
||||
case .resumeContinuations(let suspendedYields):
|
||||
suspendedYields.forEach { $0.continuation.resume(returning: .retry) }
|
||||
for yield in suspendedYields {
|
||||
yield .continuation.resume(returning: .retry)
|
||||
}
|
||||
|
||||
case .none:
|
||||
return
|
||||
@@ -512,7 +519,8 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func yield<S: Sequence>(contentsOf sequence: S) async throws where S.Element == Element {
|
||||
internal func yield<S: Sequence>(contentsOf sequence: S) async throws
|
||||
where S.Element == Element {
|
||||
let yieldID = self._yieldIDGenerator.generateUniqueYieldID()
|
||||
while true {
|
||||
switch try await self._yield(contentsOf: sequence, yieldID: yieldID) {
|
||||
@@ -525,7 +533,10 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func _yield<S: Sequence>(contentsOf sequence: S, yieldID: StateMachine.YieldID?) async throws -> StateMachine.YieldResult where S.Element == Element {
|
||||
internal func _yield<S: Sequence>(
|
||||
contentsOf sequence: S,
|
||||
yieldID: StateMachine.YieldID?
|
||||
) async throws -> StateMachine.YieldResult where S.Element == Element {
|
||||
let yieldID = yieldID ?? self._yieldIDGenerator.generateUniqueYieldID()
|
||||
|
||||
return try await withTaskCancellationHandler {
|
||||
@@ -550,7 +561,8 @@ extension NIOAsyncWriter {
|
||||
throw error
|
||||
|
||||
case .suspendTask:
|
||||
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<StateMachine.YieldResult, Error>) in
|
||||
return try await withCheckedThrowingContinuation {
|
||||
(continuation: CheckedContinuation<StateMachine.YieldResult, Error>) in
|
||||
let didSuspend = unsafe.withValueAssumingLockIsAcquired {
|
||||
$0.stateMachine.yield(continuation: continuation, yieldID: yieldID)
|
||||
return $0.didSuspend
|
||||
@@ -579,7 +591,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func yield(element: Element) async throws {
|
||||
internal func yield(element: Element) async throws {
|
||||
let yieldID = self._yieldIDGenerator.generateUniqueYieldID()
|
||||
while true {
|
||||
switch try await self._yield(element: element, yieldID: yieldID) {
|
||||
@@ -592,7 +604,10 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func _yield(element: Element, yieldID: StateMachine.YieldID?) async throws -> StateMachine.YieldResult {
|
||||
internal func _yield(
|
||||
element: Element,
|
||||
yieldID: StateMachine.YieldID?
|
||||
) async throws -> StateMachine.YieldResult {
|
||||
let yieldID = yieldID ?? self._yieldIDGenerator.generateUniqueYieldID()
|
||||
|
||||
return try await withTaskCancellationHandler {
|
||||
@@ -617,7 +632,8 @@ extension NIOAsyncWriter {
|
||||
throw error
|
||||
|
||||
case .suspendTask:
|
||||
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<StateMachine.YieldResult, Error>) in
|
||||
return try await withCheckedThrowingContinuation {
|
||||
(continuation: CheckedContinuation<StateMachine.YieldResult, Error>) in
|
||||
let didSuspend = unsafe.withValueAssumingLockIsAcquired {
|
||||
$0.stateMachine.yield(continuation: continuation, yieldID: yieldID)
|
||||
return $0.didSuspend
|
||||
@@ -645,7 +661,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func writerFinish(error: Error?) {
|
||||
internal func writerFinish(error: Error?) {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
@@ -658,7 +674,9 @@ extension NIOAsyncWriter {
|
||||
delegate.didTerminate(error: error)
|
||||
|
||||
case .resumeContinuations(let suspendedYields):
|
||||
suspendedYields.forEach { $0.continuation.resume(returning: .retry) }
|
||||
for yield in suspendedYields {
|
||||
yield .continuation.resume(returning: .retry)
|
||||
}
|
||||
|
||||
case .none:
|
||||
break
|
||||
@@ -666,7 +684,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func sinkFinish(error: Error?) {
|
||||
internal func sinkFinish(error: Error?) {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
@@ -676,23 +694,26 @@ extension NIOAsyncWriter {
|
||||
|
||||
switch action {
|
||||
case .resumeContinuationsWithError(let suspendedYields, let error):
|
||||
suspendedYields.forEach { $0.continuation.resume(throwing: error) }
|
||||
for yield in suspendedYields {
|
||||
yield .continuation.resume(throwing: error)
|
||||
}
|
||||
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func unbufferQueuedEvents() {
|
||||
while let action = self._state.withLockedValue({ $0.stateMachine.unbufferQueuedEvents()}) {
|
||||
internal func unbufferQueuedEvents() {
|
||||
while let action = self._state.withLockedValue({ $0.stateMachine.unbufferQueuedEvents() }) {
|
||||
switch action {
|
||||
case .callDidTerminate(let delegate, let error):
|
||||
delegate.didTerminate(error: error)
|
||||
|
||||
case .resumeContinuations(let suspendedYields):
|
||||
suspendedYields.forEach { $0.continuation.resume(returning: .retry) }
|
||||
for yield in suspendedYields {
|
||||
yield .continuation.resume(returning: .retry)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -703,12 +724,12 @@ extension NIOAsyncWriter {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension NIOAsyncWriter {
|
||||
@usableFromInline
|
||||
/* private */ internal struct StateMachine: Sendable {
|
||||
internal struct StateMachine: Sendable {
|
||||
@usableFromInline
|
||||
typealias YieldID = Storage.YieldIDGenerator.YieldID
|
||||
/// This is a small helper struct to encapsulate the two different values for a suspended yield.
|
||||
@usableFromInline
|
||||
/* private */ internal struct SuspendedYield: Sendable {
|
||||
internal struct SuspendedYield: Sendable {
|
||||
/// The yield's ID.
|
||||
@usableFromInline
|
||||
var yieldID: YieldID
|
||||
@@ -725,7 +746,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
/// The internal result of a yield.
|
||||
@usableFromInline
|
||||
/* private */ internal enum YieldResult {
|
||||
internal enum YieldResult {
|
||||
/// Indicates that the elements got yielded to the sink.
|
||||
case yielded
|
||||
/// Indicates that the yield should be retried.
|
||||
@@ -734,7 +755,7 @@ extension NIOAsyncWriter {
|
||||
|
||||
/// The current state of our ``NIOAsyncWriter``.
|
||||
@usableFromInline
|
||||
/* private */ internal enum State: Sendable, CustomStringConvertible {
|
||||
internal enum State: Sendable, CustomStringConvertible {
|
||||
/// The initial state before either a call to ``NIOAsyncWriter/yield(contentsOf:)`` or
|
||||
/// ``NIOAsyncWriter/finish(completion:)`` happened.
|
||||
case initial(
|
||||
@@ -778,9 +799,19 @@ extension NIOAsyncWriter {
|
||||
case .initial(let isWritable, _):
|
||||
return "initial(isWritable: \(isWritable))"
|
||||
case .streaming(let isWritable, let inDelegateOutcall, let cancelledYields, let suspendedYields, _):
|
||||
return "streaming(isWritable: \(isWritable), inDelegateOutcall: \(inDelegateOutcall), cancelledYields: \(cancelledYields.count), suspendedYields: \(suspendedYields.count))"
|
||||
case .writerFinished(let isWritable, let inDelegateOutcall, let suspendedYields, let cancelledYields, let bufferedYieldIDs, _, _):
|
||||
return "writerFinished(isWritable: \(isWritable), inDelegateOutcall: \(inDelegateOutcall), suspendedYields: \(suspendedYields.count), cancelledYields: \(cancelledYields.count), bufferedYieldIDs: \(bufferedYieldIDs.count)"
|
||||
return
|
||||
"streaming(isWritable: \(isWritable), inDelegateOutcall: \(inDelegateOutcall), cancelledYields: \(cancelledYields.count), suspendedYields: \(suspendedYields.count))"
|
||||
case .writerFinished(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let suspendedYields,
|
||||
let cancelledYields,
|
||||
let bufferedYieldIDs,
|
||||
_,
|
||||
_
|
||||
):
|
||||
return
|
||||
"writerFinished(isWritable: \(isWritable), inDelegateOutcall: \(inDelegateOutcall), suspendedYields: \(suspendedYields.count), cancelledYields: \(cancelledYields.count), bufferedYieldIDs: \(bufferedYieldIDs.count)"
|
||||
case .finished:
|
||||
return "finished"
|
||||
case .modifying:
|
||||
@@ -791,7 +822,7 @@ extension NIOAsyncWriter {
|
||||
|
||||
/// The state machine's current state.
|
||||
@usableFromInline
|
||||
/* private */ internal var _state: State
|
||||
internal var _state: State
|
||||
|
||||
@inlinable
|
||||
internal var isWriterFinished: Bool {
|
||||
@@ -817,7 +848,6 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
init(
|
||||
isWritable: Bool,
|
||||
@@ -834,7 +864,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func setWritability(to newWritability: Bool) -> SetWritabilityAction? {
|
||||
internal mutating func setWritability(to newWritability: Bool) -> SetWritabilityAction? {
|
||||
switch self._state {
|
||||
case .initial(_, let delegate):
|
||||
// We just need to store the new writability state
|
||||
@@ -842,7 +872,13 @@ extension NIOAsyncWriter {
|
||||
|
||||
return .none
|
||||
|
||||
case .streaming(let isWritable, let inDelegateOutcall, let cancelledYields, let suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let cancelledYields,
|
||||
let suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
if isWritable == newWritability {
|
||||
// The writability didn't change so we can just early exit here
|
||||
return .none
|
||||
@@ -882,7 +918,15 @@ extension NIOAsyncWriter {
|
||||
return .none
|
||||
}
|
||||
|
||||
case .writerFinished(_, let inDelegateOutcall, let suspendedYields, let cancelledYields, let bufferedYieldIDs, let delegate, let error):
|
||||
case .writerFinished(
|
||||
_,
|
||||
let inDelegateOutcall,
|
||||
let suspendedYields,
|
||||
let cancelledYields,
|
||||
let bufferedYieldIDs,
|
||||
let delegate,
|
||||
let error
|
||||
):
|
||||
if !newWritability {
|
||||
// We are not writable so we can't deliver the outstanding elements
|
||||
return .none
|
||||
@@ -958,7 +1002,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func yield(
|
||||
internal mutating func yield(
|
||||
yieldID: YieldID
|
||||
) -> YieldAction {
|
||||
switch self._state {
|
||||
@@ -967,7 +1011,7 @@ extension NIOAsyncWriter {
|
||||
|
||||
self._state = .streaming(
|
||||
isWritable: isWritable,
|
||||
inDelegateOutcall: isWritable, // If we are writable we are going to make an outcall
|
||||
inDelegateOutcall: isWritable, // If we are writable we are going to make an outcall
|
||||
cancelledYields: [],
|
||||
suspendedYields: .init(),
|
||||
delegate: delegate
|
||||
@@ -975,7 +1019,13 @@ extension NIOAsyncWriter {
|
||||
|
||||
return .init(isWritable: isWritable, delegate: delegate)
|
||||
|
||||
case .streaming(let isWritable, let inDelegateOutcall, var cancelledYields, let suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
var cancelledYields,
|
||||
let suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
self._state = .modifying
|
||||
|
||||
if let index = cancelledYields.firstIndex(of: yieldID) {
|
||||
@@ -999,7 +1049,7 @@ extension NIOAsyncWriter {
|
||||
case (true, false):
|
||||
self._state = .streaming(
|
||||
isWritable: isWritable,
|
||||
inDelegateOutcall: true, // We are now making a call to the delegate
|
||||
inDelegateOutcall: true, // We are now making a call to the delegate
|
||||
cancelledYields: cancelledYields,
|
||||
suspendedYields: suspendedYields,
|
||||
delegate: delegate
|
||||
@@ -1018,7 +1068,15 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
}
|
||||
|
||||
case .writerFinished(let isWritable, let inDelegateOutcall, let suspendedYields, var cancelledYields, let bufferedYieldIDs, let delegate, let error):
|
||||
case .writerFinished(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let suspendedYields,
|
||||
var cancelledYields,
|
||||
let bufferedYieldIDs,
|
||||
let delegate,
|
||||
let error
|
||||
):
|
||||
if bufferedYieldIDs.contains(yieldID) {
|
||||
// This yield was buffered before we became finished so we still have to deliver it
|
||||
self._state = .modifying
|
||||
@@ -1046,7 +1104,7 @@ extension NIOAsyncWriter {
|
||||
case (true, false):
|
||||
self._state = .writerFinished(
|
||||
isWritable: isWritable,
|
||||
inDelegateOutcall: true, // We are now making a call to the delegate
|
||||
inDelegateOutcall: true, // We are now making a call to the delegate
|
||||
suspendedYields: suspendedYields,
|
||||
cancelledYields: cancelledYields,
|
||||
bufferedYieldIDs: bufferedYieldIDs,
|
||||
@@ -1084,12 +1142,18 @@ extension NIOAsyncWriter {
|
||||
|
||||
/// This method is called as a result of the above `yield` method if it decided that the task needs to get suspended.
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func yield(
|
||||
internal mutating func yield(
|
||||
continuation: CheckedContinuation<YieldResult, Error>,
|
||||
yieldID: YieldID
|
||||
) {
|
||||
switch self._state {
|
||||
case .streaming(let isWritable, let inDelegateOutcall, let cancelledYields, var suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let cancelledYields,
|
||||
var suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
// We have a suspended yield at this point that hasn't been cancelled yet.
|
||||
// We need to store the yield now.
|
||||
|
||||
@@ -1127,7 +1191,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func cancel(
|
||||
internal mutating func cancel(
|
||||
yieldID: YieldID
|
||||
) -> CancelAction {
|
||||
switch self._state {
|
||||
@@ -1145,7 +1209,13 @@ extension NIOAsyncWriter {
|
||||
|
||||
return .none
|
||||
|
||||
case .streaming(let isWritable, let inDelegateOutcall, var cancelledYields, var suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
var cancelledYields,
|
||||
var suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
if let index = suspendedYields.firstIndex(where: { $0.yieldID == yieldID }) {
|
||||
self._state = .modifying
|
||||
// We have a suspended yield for the id. We need to resume the continuation now.
|
||||
@@ -1185,7 +1255,15 @@ extension NIOAsyncWriter {
|
||||
return .none
|
||||
}
|
||||
|
||||
case .writerFinished(let isWritable, let inDelegateOutcall, var suspendedYields, var cancelledYields, let bufferedYieldIDs, let delegate, let error):
|
||||
case .writerFinished(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
var suspendedYields,
|
||||
var cancelledYields,
|
||||
let bufferedYieldIDs,
|
||||
let delegate,
|
||||
let error
|
||||
):
|
||||
guard bufferedYieldIDs.contains(yieldID) else {
|
||||
return .none
|
||||
}
|
||||
@@ -1253,7 +1331,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func writerFinish(error: Error?) -> WriterFinishAction {
|
||||
internal mutating func writerFinish(error: Error?) -> WriterFinishAction {
|
||||
switch self._state {
|
||||
case .initial(_, let delegate):
|
||||
// Nothing was ever written so we can transition to finished
|
||||
@@ -1261,7 +1339,13 @@ extension NIOAsyncWriter {
|
||||
|
||||
return .callDidTerminate(delegate)
|
||||
|
||||
case .streaming(let isWritable, let inDelegateOutcall, let cancelledYields, let suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let cancelledYields,
|
||||
let suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
// We are currently streaming and the writer got finished.
|
||||
if suspendedYields.isEmpty {
|
||||
if inDelegateOutcall {
|
||||
@@ -1317,7 +1401,7 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func sinkFinish(error: Error?) -> SinkFinishAction {
|
||||
internal mutating func sinkFinish(error: Error?) -> SinkFinishAction {
|
||||
switch self._state {
|
||||
case .initial(_, _):
|
||||
// Nothing was ever written so we can transition to finished
|
||||
@@ -1360,12 +1444,18 @@ extension NIOAsyncWriter {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal mutating func unbufferQueuedEvents() -> UnbufferQueuedEventsAction? {
|
||||
internal mutating func unbufferQueuedEvents() -> UnbufferQueuedEventsAction? {
|
||||
switch self._state {
|
||||
case .initial:
|
||||
preconditionFailure("Invalid state")
|
||||
|
||||
case .streaming(let isWritable, let inDelegateOutcall, let cancelledYields, let suspendedYields, let delegate):
|
||||
case .streaming(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let cancelledYields,
|
||||
let suspendedYields,
|
||||
let delegate
|
||||
):
|
||||
precondition(inDelegateOutcall, "We must be in a delegate outcall when we unbuffer events")
|
||||
// We have to resume the other suspended yields now.
|
||||
|
||||
@@ -1391,7 +1481,15 @@ extension NIOAsyncWriter {
|
||||
return .resumeContinuations(suspendedYields)
|
||||
}
|
||||
|
||||
case .writerFinished(let isWritable, let inDelegateOutcall, let suspendedYields, let cancelledYields, let bufferedYieldIDs, let delegate, let error):
|
||||
case .writerFinished(
|
||||
let isWritable,
|
||||
let inDelegateOutcall,
|
||||
let suspendedYields,
|
||||
let cancelledYields,
|
||||
let bufferedYieldIDs,
|
||||
let delegate,
|
||||
let error
|
||||
):
|
||||
precondition(inDelegateOutcall, "We must be in a delegate outcall when we unbuffer events")
|
||||
if suspendedYields.isEmpty {
|
||||
// We were the last writer task and can now call didTerminate
|
||||
@@ -1401,7 +1499,6 @@ extension NIOAsyncWriter {
|
||||
// There are still other writer tasks that need to be resumed
|
||||
self._state = .modifying
|
||||
|
||||
|
||||
self._state = .writerFinished(
|
||||
isWritable: isWritable,
|
||||
inDelegateOutcall: inDelegateOutcall,
|
||||
|
||||
@@ -49,7 +49,7 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
public let sequence: NIOThrowingAsyncSequenceProducer
|
||||
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal init(
|
||||
internal init(
|
||||
source: Source,
|
||||
sequence: NIOThrowingAsyncSequenceProducer
|
||||
) {
|
||||
@@ -62,7 +62,7 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
///
|
||||
/// If we get move-only types we should be able to drop this class and use the `deinit` of the ``AsyncIterator`` struct itself.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal final class InternalClass: Sendable {
|
||||
internal final class InternalClass: Sendable {
|
||||
@usableFromInline
|
||||
internal let _storage: Storage
|
||||
|
||||
@@ -78,10 +78,10 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _internalClass: InternalClass
|
||||
internal let _internalClass: InternalClass
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal var _storage: Storage {
|
||||
internal var _storage: Storage {
|
||||
self._internalClass._storage
|
||||
}
|
||||
|
||||
@@ -130,7 +130,11 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
/// - backPressureStrategy: The back-pressure strategy of the sequence.
|
||||
/// - delegate: The delegate of the sequence
|
||||
/// - Returns: A ``NIOThrowingAsyncSequenceProducer/Source`` and a ``NIOThrowingAsyncSequenceProducer``.
|
||||
@available(*, deprecated, message: "Support for a generic Failure type is deprecated. Failure type must be `any Swift.Error`.")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "Support for a generic Failure type is deprecated. Failure type must be `any Swift.Error`."
|
||||
)
|
||||
@inlinable
|
||||
public static func makeSequence(
|
||||
elementType: Element.Type = Element.self,
|
||||
@@ -161,7 +165,12 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
/// - delegate: The delegate of the sequence
|
||||
/// - Returns: A ``NIOThrowingAsyncSequenceProducer/Source`` and a ``NIOThrowingAsyncSequenceProducer``.
|
||||
@inlinable
|
||||
@available(*, deprecated, renamed: "makeSequence(elementType:failureType:backPressureStrategy:finishOnDeinit:delegate:)", message: "This method has been deprecated since it defaults to deinit based resource teardown")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
renamed: "makeSequence(elementType:failureType:backPressureStrategy:finishOnDeinit:delegate:)",
|
||||
message: "This method has been deprecated since it defaults to deinit based resource teardown"
|
||||
)
|
||||
public static func makeSequence(
|
||||
elementType: Element.Type = Element.self,
|
||||
failureType: Failure.Type = Error.self,
|
||||
@@ -195,7 +204,7 @@ public struct NIOThrowingAsyncSequenceProducer<
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* private */ internal init(
|
||||
internal init(
|
||||
backPressureStrategy: Strategy,
|
||||
delegate: Delegate
|
||||
) {
|
||||
@@ -221,9 +230,9 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
///
|
||||
/// If we get move-only types we should be able to drop this class and use the `deinit` of the ``AsyncIterator`` struct itself.
|
||||
@usableFromInline
|
||||
/* private */ internal final class InternalClass: Sendable {
|
||||
internal final class InternalClass: Sendable {
|
||||
@usableFromInline
|
||||
/* private */ internal let _storage: Storage
|
||||
internal let _storage: Storage
|
||||
|
||||
fileprivate init(storage: Storage) {
|
||||
self._storage = storage
|
||||
@@ -236,13 +245,13 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func next() async throws -> Element? {
|
||||
internal func next() async throws -> Element? {
|
||||
try await self._storage.next()
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _internalClass: InternalClass
|
||||
internal let _internalClass: InternalClass
|
||||
|
||||
fileprivate init(storage: Storage) {
|
||||
self._internalClass = InternalClass(storage: storage)
|
||||
@@ -268,7 +277,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
///
|
||||
/// - Important: This is safe to be unchecked ``Sendable`` since the `storage` is ``Sendable`` and `immutable`.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal final class InternalClass: Sendable {
|
||||
internal final class InternalClass: Sendable {
|
||||
@usableFromInline
|
||||
internal let _storage: Storage
|
||||
|
||||
@@ -293,15 +302,15 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal let _internalClass: InternalClass
|
||||
internal let _internalClass: InternalClass
|
||||
|
||||
@usableFromInline
|
||||
/* private */ internal var _storage: Storage {
|
||||
internal var _storage: Storage {
|
||||
self._internalClass._storage
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal init(storage: Storage, finishOnDeinit: Bool) {
|
||||
internal init(storage: Storage, finishOnDeinit: Bool) {
|
||||
self._internalClass = .init(storage: storage, finishOnDeinit: finishOnDeinit)
|
||||
}
|
||||
|
||||
@@ -388,7 +397,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
extension NIOThrowingAsyncSequenceProducer {
|
||||
/// This is the underlying storage of the sequence. The goal of this is to synchronize the access to all state.
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal struct Storage: Sendable {
|
||||
internal struct Storage: Sendable {
|
||||
@usableFromInline
|
||||
struct State: Sendable {
|
||||
@usableFromInline
|
||||
@@ -426,7 +435,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
/* fileprivate */ internal init(
|
||||
internal init(
|
||||
backPressureStrategy: Strategy,
|
||||
delegate: Delegate
|
||||
) {
|
||||
@@ -438,7 +447,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func sequenceDeinitialized() {
|
||||
internal func sequenceDeinitialized() {
|
||||
let delegate: Delegate? = self._state.withLockedValue {
|
||||
let action = $0.stateMachine.sequenceDeinitialized()
|
||||
|
||||
@@ -457,14 +466,14 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func iteratorInitialized() {
|
||||
internal func iteratorInitialized() {
|
||||
self._state.withLockedValue {
|
||||
$0.stateMachine.iteratorInitialized()
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func iteratorDeinitialized() {
|
||||
internal func iteratorDeinitialized() {
|
||||
let delegate: Delegate? = self._state.withLockedValue {
|
||||
let action = $0.stateMachine.iteratorDeinitialized()
|
||||
|
||||
@@ -484,7 +493,8 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func yield<S: Sequence>(_ sequence: S) -> Source.YieldResult where S.Element == Element {
|
||||
internal func yield<S: Sequence>(_ sequence: S) -> Source.YieldResult
|
||||
where S.Element == Element {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
@@ -515,23 +525,24 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func finish(_ failure: Failure?) {
|
||||
internal func finish(_ failure: Failure?) {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
let (delegate, action): (Delegate?, NIOThrowingAsyncSequenceProducer.StateMachine.FinishAction) = self._state.withLockedValue {
|
||||
let action = $0.stateMachine.finish(failure)
|
||||
let (delegate, action): (Delegate?, NIOThrowingAsyncSequenceProducer.StateMachine.FinishAction) = self
|
||||
._state.withLockedValue {
|
||||
let action = $0.stateMachine.finish(failure)
|
||||
|
||||
switch action {
|
||||
case .resumeContinuationWithFailureAndCallDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
return (delegate, action)
|
||||
switch action {
|
||||
case .resumeContinuationWithFailureAndCallDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
return (delegate, action)
|
||||
|
||||
case .none:
|
||||
return (nil, action)
|
||||
case .none:
|
||||
return (nil, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch action {
|
||||
case .resumeContinuationWithFailureAndCallDidTerminate(let continuation, let failure):
|
||||
@@ -550,7 +561,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
/* fileprivate */ internal func next() async throws -> Element? {
|
||||
internal func next() async throws -> Element? {
|
||||
try await withTaskCancellationHandler { () async throws -> Element? in
|
||||
let unsafe = self._state.unsafe
|
||||
unsafe.lock()
|
||||
@@ -614,7 +625,8 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
case .suspendTask:
|
||||
// It is safe to hold the lock across this method
|
||||
// since the closure is guaranteed to be run straight away
|
||||
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Element?, any Error>) in
|
||||
return try await withCheckedThrowingContinuation {
|
||||
(continuation: CheckedContinuation<Element?, any Error>) in
|
||||
let (action, callDidSuspend) = unsafe.withValueAssumingLockIsAcquired {
|
||||
let action = $0.stateMachine.next(for: continuation)
|
||||
let callDidSuspend = $0.didSuspend != nil
|
||||
@@ -644,26 +656,27 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
// We must not resume the continuation while holding the lock
|
||||
// because it can deadlock in combination with the underlying ulock
|
||||
// in cases where we race with a cancellation handler
|
||||
let (delegate, action): (Delegate?, NIOThrowingAsyncSequenceProducer.StateMachine.CancelledAction) = self._state.withLockedValue {
|
||||
let action = $0.stateMachine.cancelled()
|
||||
let (delegate, action): (Delegate?, NIOThrowingAsyncSequenceProducer.StateMachine.CancelledAction) =
|
||||
self._state.withLockedValue {
|
||||
let action = $0.stateMachine.cancelled()
|
||||
|
||||
switch action {
|
||||
case .callDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
switch action {
|
||||
case .callDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
|
||||
return (delegate, action)
|
||||
return (delegate, action)
|
||||
|
||||
case .resumeContinuationWithCancellationErrorAndCallDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
case .resumeContinuationWithCancellationErrorAndCallDidTerminate:
|
||||
let delegate = $0.delegate
|
||||
$0.delegate = nil
|
||||
|
||||
return (delegate, action)
|
||||
return (delegate, action)
|
||||
|
||||
case .none:
|
||||
return (nil, action)
|
||||
case .none:
|
||||
return (nil, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch action {
|
||||
case .callDidTerminate:
|
||||
@@ -697,9 +710,9 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||
extension NIOThrowingAsyncSequenceProducer {
|
||||
@usableFromInline
|
||||
/* private */ internal struct StateMachine: Sendable {
|
||||
internal struct StateMachine: Sendable {
|
||||
@usableFromInline
|
||||
/* private */ internal enum State: Sendable {
|
||||
internal enum State: Sendable {
|
||||
/// The initial state before either a call to `yield()` or a call to `next()` happened
|
||||
case initial(
|
||||
backPressureStrategy: Strategy,
|
||||
@@ -736,7 +749,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
|
||||
/// The state machine's current state.
|
||||
@usableFromInline
|
||||
/* private */ internal var _state: State
|
||||
internal var _state: State
|
||||
|
||||
@inlinable
|
||||
var isFinished: Bool {
|
||||
@@ -750,7 +763,6 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Initializes a new `StateMachine`.
|
||||
///
|
||||
/// We are passing and holding the back-pressure strategy here because
|
||||
@@ -778,18 +790,18 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
mutating func sequenceDeinitialized() -> SequenceDeinitializedAction {
|
||||
switch self._state {
|
||||
case .initial(_, iteratorInitialized: false),
|
||||
.streaming(_, _, _, _, iteratorInitialized: false),
|
||||
.sourceFinished(_, iteratorInitialized: false, _),
|
||||
.cancelled(iteratorInitialized: false):
|
||||
.streaming(_, _, _, _, iteratorInitialized: false),
|
||||
.sourceFinished(_, iteratorInitialized: false, _),
|
||||
.cancelled(iteratorInitialized: false):
|
||||
// No iterator was created so we can transition to finished right away.
|
||||
self._state = .finished(iteratorInitialized: false)
|
||||
|
||||
return .callDidTerminate
|
||||
|
||||
case .initial(_, iteratorInitialized: true),
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true):
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true):
|
||||
// An iterator was created and we deinited the sequence.
|
||||
// This is an expected pattern and we just continue on normal.
|
||||
return .none
|
||||
@@ -808,10 +820,10 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
mutating func iteratorInitialized() {
|
||||
switch self._state {
|
||||
case .initial(_, iteratorInitialized: true),
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true),
|
||||
.finished(iteratorInitialized: true):
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true),
|
||||
.finished(iteratorInitialized: true):
|
||||
// Our sequence is a unicast sequence and does not support multiple AsyncIterator's
|
||||
fatalError("NIOThrowingAsyncSequenceProducer allows only a single AsyncIterator to be created")
|
||||
|
||||
@@ -868,16 +880,16 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
mutating func iteratorDeinitialized() -> IteratorDeinitializedAction {
|
||||
switch self._state {
|
||||
case .initial(_, iteratorInitialized: false),
|
||||
.streaming(_, _, _, _, iteratorInitialized: false),
|
||||
.sourceFinished(_, iteratorInitialized: false, _),
|
||||
.cancelled(iteratorInitialized: false):
|
||||
.streaming(_, _, _, _, iteratorInitialized: false),
|
||||
.sourceFinished(_, iteratorInitialized: false, _),
|
||||
.cancelled(iteratorInitialized: false):
|
||||
// An iterator needs to be initialized before it can be deinitialized.
|
||||
preconditionFailure("Internal inconsistency")
|
||||
|
||||
case .initial(_, iteratorInitialized: true),
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true):
|
||||
.streaming(_, _, _, _, iteratorInitialized: true),
|
||||
.sourceFinished(_, iteratorInitialized: true, _),
|
||||
.cancelled(iteratorInitialized: true):
|
||||
// An iterator was created and deinited. Since we only support
|
||||
// a single iterator we can now transition to finish and inform the delegate.
|
||||
self._state = .finished(iteratorInitialized: true)
|
||||
@@ -917,7 +929,10 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
case returnDropped
|
||||
|
||||
@usableFromInline
|
||||
init(shouldProduceMore: Bool, continuationAndElement: (CheckedContinuation<Element?, Error>, Element)? = nil) {
|
||||
init(
|
||||
shouldProduceMore: Bool,
|
||||
continuationAndElement: (CheckedContinuation<Element?, Error>, Element)? = nil
|
||||
) {
|
||||
switch (shouldProduceMore, continuationAndElement) {
|
||||
case (true, .none):
|
||||
self = .returnProduceMore
|
||||
@@ -957,7 +972,13 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
|
||||
return .init(shouldProduceMore: shouldProduceMore)
|
||||
|
||||
case .streaming(var backPressureStrategy, var buffer, .some(let continuation), let hasOutstandingDemand, let iteratorInitialized):
|
||||
case .streaming(
|
||||
var backPressureStrategy,
|
||||
var buffer,
|
||||
.some(let continuation),
|
||||
let hasOutstandingDemand,
|
||||
let iteratorInitialized
|
||||
):
|
||||
// The buffer should always be empty if we hold a continuation
|
||||
precondition(buffer.isEmpty, "Expected an empty buffer")
|
||||
|
||||
@@ -982,7 +1003,7 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
self._state = .streaming(
|
||||
backPressureStrategy: backPressureStrategy,
|
||||
buffer: buffer,
|
||||
continuation: nil, // Setting this to nil since we are resuming the continuation
|
||||
continuation: nil, // Setting this to nil since we are resuming the continuation
|
||||
hasOutstandingDemand: shouldProduceMore,
|
||||
iteratorInitialized: iteratorInitialized
|
||||
)
|
||||
@@ -1167,7 +1188,13 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
// We have multiple AsyncIterators iterating the sequence
|
||||
preconditionFailure("This should never happen since we only allow a single Iterator to be created")
|
||||
|
||||
case .streaming(var backPressureStrategy, var buffer, .none, let hasOutstandingDemand, let iteratorInitialized):
|
||||
case .streaming(
|
||||
var backPressureStrategy,
|
||||
var buffer,
|
||||
.none,
|
||||
let hasOutstandingDemand,
|
||||
let iteratorInitialized
|
||||
):
|
||||
self._state = .modifying
|
||||
|
||||
if let element = buffer.popFirst() {
|
||||
@@ -1252,7 +1279,13 @@ extension NIOThrowingAsyncSequenceProducer {
|
||||
// We are transitioning away from the initial state in `next()`
|
||||
preconditionFailure("Invalid state")
|
||||
|
||||
case .streaming(var backPressureStrategy, let buffer, .none, let hasOutstandingDemand, let iteratorInitialized):
|
||||
case .streaming(
|
||||
var backPressureStrategy,
|
||||
let buffer,
|
||||
.none,
|
||||
let hasOutstandingDemand,
|
||||
let iteratorInitialized
|
||||
):
|
||||
precondition(buffer.isEmpty, "Expected an empty buffer")
|
||||
|
||||
self._state = .modifying
|
||||
|
||||
@@ -69,16 +69,21 @@ import Musl
|
||||
import CNIOLinux
|
||||
|
||||
#if os(Android)
|
||||
private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer, UnsafeMutablePointer<CChar>, socklen_t) -> UnsafePointer<CChar>? = inet_ntop
|
||||
private let sysInet_ntop:
|
||||
@convention(c) (CInt, UnsafeRawPointer, UnsafeMutablePointer<CChar>, socklen_t) -> UnsafePointer<CChar>? = inet_ntop
|
||||
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>, UnsafeMutableRawPointer) -> CInt = inet_pton
|
||||
#else
|
||||
private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? = inet_ntop
|
||||
private let sysInet_ntop:
|
||||
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
|
||||
inet_ntop
|
||||
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
|
||||
#endif
|
||||
#elseif canImport(Darwin)
|
||||
import Darwin
|
||||
|
||||
private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? = inet_ntop
|
||||
private let sysInet_ntop:
|
||||
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
|
||||
inet_ntop
|
||||
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
|
||||
#else
|
||||
#error("The BSD Socket module was unable to identify your C library.")
|
||||
@@ -99,11 +104,11 @@ let SO_RCVTIMEO = CNIOLinux_SO_RCVTIMEO
|
||||
#endif
|
||||
|
||||
public enum NIOBSDSocket {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
public typealias Handle = SOCKET
|
||||
#else
|
||||
#else
|
||||
public typealias Handle = CInt
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
extension NIOBSDSocket {
|
||||
@@ -178,74 +183,73 @@ extension NIOBSDSocket.Option: Hashable {
|
||||
extension NIOBSDSocket.AddressFamily {
|
||||
/// Address for IP version 4.
|
||||
public static let inet: NIOBSDSocket.AddressFamily =
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_INET)
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_INET)
|
||||
|
||||
/// Address for IP version 6.
|
||||
public static let inet6: NIOBSDSocket.AddressFamily =
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_INET6)
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_INET6)
|
||||
|
||||
/// Unix local to host address.
|
||||
public static let unix: NIOBSDSocket.AddressFamily =
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_UNIX)
|
||||
NIOBSDSocket.AddressFamily(rawValue: AF_UNIX)
|
||||
}
|
||||
|
||||
// Protocol Family
|
||||
extension NIOBSDSocket.ProtocolFamily {
|
||||
/// IP network 4 protocol.
|
||||
public static let inet: NIOBSDSocket.ProtocolFamily =
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_INET)
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_INET)
|
||||
|
||||
/// IP network 6 protocol.
|
||||
public static let inet6: NIOBSDSocket.ProtocolFamily =
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_INET6)
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_INET6)
|
||||
|
||||
/// UNIX local to the host.
|
||||
public static let unix: NIOBSDSocket.ProtocolFamily =
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_UNIX)
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_UNIX)
|
||||
}
|
||||
|
||||
#if !os(Windows)
|
||||
extension NIOBSDSocket.ProtocolFamily {
|
||||
/// UNIX local to the host, alias for `PF_UNIX` (`.unix`)
|
||||
public static let local: NIOBSDSocket.ProtocolFamily =
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_LOCAL)
|
||||
}
|
||||
extension NIOBSDSocket.ProtocolFamily {
|
||||
/// UNIX local to the host, alias for `PF_UNIX` (`.unix`)
|
||||
public static let local: NIOBSDSocket.ProtocolFamily =
|
||||
NIOBSDSocket.ProtocolFamily(rawValue: PF_LOCAL)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Option Level
|
||||
extension NIOBSDSocket.OptionLevel {
|
||||
/// Socket options that apply only to IP sockets.
|
||||
#if os(Linux) || os(Android)
|
||||
public static let ip: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_IP))
|
||||
public static let ip: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_IP))
|
||||
#else
|
||||
public static let ip: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IP)
|
||||
public static let ip: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IP)
|
||||
#endif
|
||||
|
||||
/// Socket options that apply only to IPv6 sockets.
|
||||
#if os(Linux) || os(Android)
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_IPV6))
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_IPV6))
|
||||
#elseif os(Windows)
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IPV6.rawValue)
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IPV6.rawValue)
|
||||
#else
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IPV6)
|
||||
public static let ipv6: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_IPV6)
|
||||
#endif
|
||||
|
||||
/// Socket options that apply only to TCP sockets.
|
||||
#if os(Linux) || os(Android)
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_TCP))
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_TCP))
|
||||
#elseif os(Windows)
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_TCP.rawValue)
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_TCP.rawValue)
|
||||
#else
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_TCP)
|
||||
public static let tcp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_TCP)
|
||||
#endif
|
||||
|
||||
/// Socket options that apply to MPTCP sockets.
|
||||
@@ -255,15 +259,15 @@ extension NIOBSDSocket.OptionLevel {
|
||||
|
||||
/// Socket options that apply to all sockets.
|
||||
public static let socket: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: SOL_SOCKET)
|
||||
NIOBSDSocket.OptionLevel(rawValue: SOL_SOCKET)
|
||||
|
||||
/// Socket options that apply only to UDP sockets.
|
||||
#if os(Linux) || os(Android)
|
||||
public static let udp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_UDP))
|
||||
NIOBSDSocket.OptionLevel(rawValue: CInt(IPPROTO_UDP))
|
||||
#else
|
||||
public static let udp: NIOBSDSocket.OptionLevel =
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_UDP)
|
||||
NIOBSDSocket.OptionLevel(rawValue: IPPROTO_UDP)
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -271,72 +275,72 @@ extension NIOBSDSocket.OptionLevel {
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Add a multicast group membership.
|
||||
public static let ip_add_membership: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_ADD_MEMBERSHIP)
|
||||
NIOBSDSocket.Option(rawValue: IP_ADD_MEMBERSHIP)
|
||||
|
||||
/// Drop a multicast group membership.
|
||||
public static let ip_drop_membership: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_DROP_MEMBERSHIP)
|
||||
NIOBSDSocket.Option(rawValue: IP_DROP_MEMBERSHIP)
|
||||
|
||||
/// Set the interface for outgoing multicast packets.
|
||||
public static let ip_multicast_if: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_IF)
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_IF)
|
||||
|
||||
/// Control multicast loopback.
|
||||
public static let ip_multicast_loop: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_LOOP)
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_LOOP)
|
||||
|
||||
/// Control multicast time-to-live.
|
||||
public static let ip_multicast_ttl: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_TTL)
|
||||
NIOBSDSocket.Option(rawValue: IP_MULTICAST_TTL)
|
||||
|
||||
/// The IPv4 layer generates an IP header when sending a packet
|
||||
/// unless the ``ip_hdrincl`` socket option is enabled on the socket.
|
||||
/// When it is enabled, the packet must contain an IP header. For
|
||||
/// receiving, the IP header is always included in the packet.
|
||||
public static let ip_hdrincl: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IP_HDRINCL)
|
||||
NIOBSDSocket.Option(rawValue: IP_HDRINCL)
|
||||
}
|
||||
|
||||
// IPv6 Options
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Add an IPv6 group membership.
|
||||
public static let ipv6_join_group: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_JOIN_GROUP)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_JOIN_GROUP)
|
||||
|
||||
/// Drop an IPv6 group membership.
|
||||
public static let ipv6_leave_group: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_LEAVE_GROUP)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_LEAVE_GROUP)
|
||||
|
||||
/// Specify the maximum number of router hops for an IPv6 packet.
|
||||
public static let ipv6_multicast_hops: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_HOPS)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_HOPS)
|
||||
|
||||
/// Set the interface for outgoing multicast packets.
|
||||
public static let ipv6_multicast_if: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_IF)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_IF)
|
||||
|
||||
/// Control multicast loopback.
|
||||
public static let ipv6_multicast_loop: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_LOOP)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_MULTICAST_LOOP)
|
||||
|
||||
/// Indicates if a socket created for the `AF_INET6` address family is
|
||||
/// restricted to IPv6 only.
|
||||
public static let ipv6_v6only: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: IPV6_V6ONLY)
|
||||
NIOBSDSocket.Option(rawValue: IPV6_V6ONLY)
|
||||
}
|
||||
|
||||
// TCP Options
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Disables the Nagle algorithm for send coalescing.
|
||||
public static let tcp_nodelay: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: TCP_NODELAY)
|
||||
NIOBSDSocket.Option(rawValue: TCP_NODELAY)
|
||||
}
|
||||
|
||||
#if os(Linux) || os(FreeBSD) || os(Android)
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Get information about the TCP connection.
|
||||
public static let tcp_info: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: TCP_INFO)
|
||||
NIOBSDSocket.Option(rawValue: TCP_INFO)
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -344,7 +348,7 @@ extension NIOBSDSocket.Option {
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Get information about the TCP connection.
|
||||
public static let tcp_connection_info: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: TCP_CONNECTION_INFO)
|
||||
NIOBSDSocket.Option(rawValue: TCP_CONNECTION_INFO)
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -398,14 +402,18 @@ extension NIOBSDSocket.Option {
|
||||
extension NIOBSDSocket.Option {
|
||||
/// Indicate when to generate timestamps.
|
||||
public static let so_timestamp: NIOBSDSocket.Option =
|
||||
NIOBSDSocket.Option(rawValue: SO_TIMESTAMP)
|
||||
NIOBSDSocket.Option(rawValue: SO_TIMESTAMP)
|
||||
}
|
||||
#endif
|
||||
|
||||
extension NIOBSDSocket {
|
||||
// Sadly this was defined on BSDSocket, and we need it for SocketAddress.
|
||||
@inline(never)
|
||||
internal static func inet_pton(addressFamily: NIOBSDSocket.AddressFamily, addressDescription: UnsafePointer<CChar>, address: UnsafeMutableRawPointer) throws {
|
||||
internal static func inet_pton(
|
||||
addressFamily: NIOBSDSocket.AddressFamily,
|
||||
addressDescription: UnsafePointer<CChar>,
|
||||
address: UnsafeMutableRawPointer
|
||||
) throws {
|
||||
#if os(Windows)
|
||||
// TODO(compnerd) use `InetPtonW` to ensure that we handle unicode properly
|
||||
switch WinSDK.inet_pton(addressFamily.rawValue, addressDescription, address) {
|
||||
@@ -424,12 +432,22 @@ extension NIOBSDSocket {
|
||||
|
||||
@discardableResult
|
||||
@inline(never)
|
||||
internal static func inet_ntop(addressFamily: NIOBSDSocket.AddressFamily, addressBytes: UnsafeRawPointer, addressDescription: UnsafeMutablePointer<CChar>, addressDescriptionLength: socklen_t) throws -> UnsafePointer<CChar> {
|
||||
internal static func inet_ntop(
|
||||
addressFamily: NIOBSDSocket.AddressFamily,
|
||||
addressBytes: UnsafeRawPointer,
|
||||
addressDescription: UnsafeMutablePointer<CChar>,
|
||||
addressDescriptionLength: socklen_t
|
||||
) throws -> UnsafePointer<CChar> {
|
||||
#if os(Windows)
|
||||
// TODO(compnerd) use `InetNtopW` to ensure that we handle unicode properly
|
||||
guard let result = WinSDK.inet_ntop(addressFamily.rawValue, addressBytes,
|
||||
addressDescription,
|
||||
Int(addressDescriptionLength)) else {
|
||||
guard
|
||||
let result = WinSDK.inet_ntop(
|
||||
addressFamily.rawValue,
|
||||
addressBytes,
|
||||
addressDescription,
|
||||
Int(addressDescriptionLength)
|
||||
)
|
||||
else {
|
||||
throw IOError(windows: GetLastError(), reason: "inet_ntop")
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -36,7 +36,7 @@ extension ByteBuffer {
|
||||
// this is not technically correct because we shouldn't just bind
|
||||
// the memory to `UInt8` but it's not a real issue either and we
|
||||
// need to work around https://bugs.swift.org/browse/SR-9604
|
||||
Array<UInt8>(UnsafeRawBufferPointer(fastRebase: ptr[range]).bindMemory(to: UInt8.self))
|
||||
[UInt8](UnsafeRawBufferPointer(fastRebase: ptr[range]).bindMemory(to: UInt8.self))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,13 @@ extension ByteBuffer {
|
||||
@inlinable
|
||||
public mutating func setStaticString(_ string: StaticString, at index: Int) -> Int {
|
||||
// please do not replace the code below with code that uses `string.withUTF8Buffer { ... }` (see SR-7541)
|
||||
return self.setBytes(UnsafeRawBufferPointer(start: string.utf8Start,
|
||||
count: string.utf8CodeUnitCount), at: index)
|
||||
self.setBytes(
|
||||
UnsafeRawBufferPointer(
|
||||
start: string.utf8Start,
|
||||
count: string.utf8CodeUnitCount
|
||||
),
|
||||
at: index
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: String APIs
|
||||
@@ -96,7 +101,7 @@ extension ByteBuffer {
|
||||
self._moveWriterIndex(forwardBy: written)
|
||||
return written
|
||||
}
|
||||
|
||||
|
||||
/// Write `string` into this `ByteBuffer` null terminated using UTF-8 encoding, moving the writer index forward appropriately.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -144,7 +149,7 @@ extension ByteBuffer {
|
||||
return self._setStringSlowpath(string, at: index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Write `string` null terminated into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -176,7 +181,7 @@ extension ByteBuffer {
|
||||
return String(decoding: UnsafeRawBufferPointer(fastRebase: pointer[range]), as: Unicode.UTF8.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get the string at `index` from this `ByteBuffer` decoding using the UTF-8 encoding. Does not move the reader index.
|
||||
/// The selected bytes must be readable or else `nil` will be returned.
|
||||
///
|
||||
@@ -216,7 +221,7 @@ extension ByteBuffer {
|
||||
self._moveReaderIndex(forwardBy: length)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/// Read a null terminated string off this `ByteBuffer`, decoding it as `String` using the UTF-8 encoding. Move the reader index
|
||||
/// forward by the string's length and its null terminator.
|
||||
///
|
||||
@@ -228,7 +233,7 @@ extension ByteBuffer {
|
||||
return nil
|
||||
}
|
||||
let result = self.readString(length: stringLength)
|
||||
self.moveReaderIndex(forwardBy: 1) // move forward by null terminator
|
||||
self.moveReaderIndex(forwardBy: 1) // move forward by null terminator
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -245,7 +250,7 @@ extension ByteBuffer {
|
||||
self._moveWriterIndex(forwardBy: written)
|
||||
return written
|
||||
}
|
||||
|
||||
|
||||
/// Write `substring` into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -267,7 +272,7 @@ extension ByteBuffer {
|
||||
return self.setString(String(substring), at: index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: DispatchData APIs
|
||||
/// Write `dispatchData` into this `ByteBuffer`, moving the writer index forward appropriately.
|
||||
///
|
||||
@@ -295,7 +300,7 @@ extension ByteBuffer {
|
||||
self.reserveCapacity(index + allBytesCount)
|
||||
self.withVeryUnsafeMutableBytes { destCompleteStorage in
|
||||
assert(destCompleteStorage.count >= index + allBytesCount)
|
||||
let dest = destCompleteStorage[index ..< index + allBytesCount]
|
||||
let dest = destCompleteStorage[index..<index + allBytesCount]
|
||||
dispatchData.copyBytes(to: .init(fastRebase: dest), count: dest.count)
|
||||
}
|
||||
return allBytesCount
|
||||
@@ -315,7 +320,7 @@ extension ByteBuffer {
|
||||
return nil
|
||||
}
|
||||
return self.withUnsafeReadableBytes { pointer in
|
||||
return DispatchData(bytes: UnsafeRawBufferPointer(fastRebase: pointer[range]))
|
||||
DispatchData(bytes: UnsafeRawBufferPointer(fastRebase: pointer[range]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +338,6 @@ extension ByteBuffer {
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// MARK: Other APIs
|
||||
|
||||
/// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index
|
||||
@@ -352,21 +356,6 @@ extension ByteBuffer {
|
||||
return bytesRead
|
||||
}
|
||||
|
||||
/// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index
|
||||
/// by the number of bytes `body` returns in the first tuple component.
|
||||
///
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.
|
||||
/// - returns: The value `body` returned in the second tuple component.
|
||||
@inlinable
|
||||
public mutating func readWithUnsafeReadableBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> (Int, T)) rethrows -> T {
|
||||
let (bytesRead, ret) = try self.withUnsafeReadableBytes({ try body($0) })
|
||||
self._moveReaderIndex(forwardBy: bytesRead)
|
||||
return ret
|
||||
}
|
||||
|
||||
/// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes.
|
||||
/// Will move the reader index by the number of bytes returned by `body` but leave writer index as it was.
|
||||
///
|
||||
@@ -377,27 +366,14 @@ extension ByteBuffer {
|
||||
/// - returns: The number of bytes read.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func readWithUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int {
|
||||
public mutating func readWithUnsafeMutableReadableBytes(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> Int
|
||||
) rethrows -> Int {
|
||||
let bytesRead = try self.withUnsafeMutableReadableBytes({ try body($0) })
|
||||
self._moveReaderIndex(forwardBy: bytesRead)
|
||||
return bytesRead
|
||||
}
|
||||
|
||||
/// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes.
|
||||
/// Will move the reader index by the number of bytes `body` returns in the first tuple component but leave writer index as it was.
|
||||
///
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.
|
||||
/// - returns: The value `body` returned in the second tuple component.
|
||||
@inlinable
|
||||
public mutating func readWithUnsafeMutableReadableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T)) rethrows -> T {
|
||||
let (bytesRead, ret) = try self.withUnsafeMutableReadableBytes({ try body($0) })
|
||||
self._moveReaderIndex(forwardBy: bytesRead)
|
||||
return ret
|
||||
}
|
||||
|
||||
/// Copy `buffer`'s readable bytes into this `ByteBuffer` starting at `index`. Does not move any of the reader or writer indices.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -407,7 +383,7 @@ extension ByteBuffer {
|
||||
@discardableResult
|
||||
@available(*, deprecated, renamed: "setBuffer(_:at:)")
|
||||
public mutating func set(buffer: ByteBuffer, at index: Int) -> Int {
|
||||
return self.setBuffer(buffer, at: index)
|
||||
self.setBuffer(buffer, at: index)
|
||||
}
|
||||
|
||||
/// Copy `buffer`'s readable bytes into this `ByteBuffer` starting at `index`. Does not move any of the reader or writer indices.
|
||||
@@ -419,7 +395,7 @@ extension ByteBuffer {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func setBuffer(_ buffer: ByteBuffer, at index: Int) -> Int {
|
||||
return buffer.withUnsafeReadableBytes{ p in
|
||||
buffer.withUnsafeReadableBytes { p in
|
||||
self.setBytes(p, at: index)
|
||||
}
|
||||
}
|
||||
@@ -464,7 +440,7 @@ extension ByteBuffer {
|
||||
self._moveWriterIndex(forwardBy: written)
|
||||
return written
|
||||
}
|
||||
|
||||
|
||||
/// Writes `byte` `count` times. Moves the writer index forward by the number of bytes written.
|
||||
///
|
||||
/// - parameter byte: The `UInt8` byte to repeat.
|
||||
@@ -477,7 +453,7 @@ extension ByteBuffer {
|
||||
self._moveWriterIndex(forwardBy: written)
|
||||
return written
|
||||
}
|
||||
|
||||
|
||||
/// Sets the given `byte` `count` times at the given `index`. Will reserve more memory if necessary. Does not move the writer index.
|
||||
///
|
||||
/// - parameter byte: The `UInt8` byte to repeat.
|
||||
@@ -489,7 +465,7 @@ extension ByteBuffer {
|
||||
precondition(count >= 0, "Can't write fewer than 0 bytes")
|
||||
self.reserveCapacity(index + count)
|
||||
self.withVeryUnsafeMutableBytes { pointer in
|
||||
let dest = UnsafeMutableRawBufferPointer(fastRebase: pointer[index ..< index+count])
|
||||
let dest = UnsafeMutableRawBufferPointer(fastRebase: pointer[index..<index + count])
|
||||
_ = dest.initializeMemory(as: UInt8.self, repeating: byte)
|
||||
}
|
||||
return count
|
||||
@@ -504,7 +480,8 @@ extension ByteBuffer {
|
||||
/// - returns: A `ByteBuffer` sharing storage containing the readable bytes only.
|
||||
@inlinable
|
||||
public func slice() -> ByteBuffer {
|
||||
return self.getSlice(at: self.readerIndex, length: self.readableBytes)! // must work, bytes definitely in the buffer
|
||||
// must work, bytes definitely in the buffer// must work, bytes definitely in the buffer
|
||||
self.getSlice(at: self.readerIndex, length: self.readableBytes)!
|
||||
}
|
||||
|
||||
/// Slice `length` bytes off this `ByteBuffer` and move the reader index forward by `length`.
|
||||
@@ -536,6 +513,43 @@ extension ByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
// swift-format-ignore: AmbiguousTrailingClosureOverload
|
||||
extension ByteBuffer {
|
||||
/// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes.
|
||||
/// Will move the reader index by the number of bytes `body` returns in the first tuple component but leave writer index as it was.
|
||||
///
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.
|
||||
/// - returns: The value `body` returned in the second tuple component.
|
||||
@inlinable
|
||||
public mutating func readWithUnsafeMutableReadableBytes<T>(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T)
|
||||
) rethrows -> T {
|
||||
let (bytesRead, ret) = try self.withUnsafeMutableReadableBytes({ try body($0) })
|
||||
self._moveReaderIndex(forwardBy: bytesRead)
|
||||
return ret
|
||||
}
|
||||
|
||||
/// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index
|
||||
/// by the number of bytes `body` returns in the first tuple component.
|
||||
///
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.
|
||||
/// - returns: The value `body` returned in the second tuple component.
|
||||
@inlinable
|
||||
public mutating func readWithUnsafeReadableBytes<T>(
|
||||
_ body: (UnsafeRawBufferPointer) throws -> (Int, T)
|
||||
) rethrows -> T {
|
||||
let (bytesRead, ret) = try self.withUnsafeReadableBytes({ try body($0) })
|
||||
self._moveReaderIndex(forwardBy: bytesRead)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBuffer {
|
||||
/// Return an empty `ByteBuffer` allocated with `ByteBufferAllocator()`.
|
||||
///
|
||||
@@ -749,9 +763,11 @@ extension ByteBufferAllocator {
|
||||
///
|
||||
/// - returns: The `ByteBuffer` containing the written bytes.
|
||||
@inlinable
|
||||
public func buffer<I: FixedWidthInteger>(integer: I,
|
||||
endianness: Endianness = .big,
|
||||
as: I.Type = I.self) -> ByteBuffer {
|
||||
public func buffer<I: FixedWidthInteger>(
|
||||
integer: I,
|
||||
endianness: Endianness = .big,
|
||||
as: I.Type = I.self
|
||||
) -> ByteBuffer {
|
||||
var buffer = self.buffer(capacity: MemoryLayout<I>.size)
|
||||
buffer.writeInteger(integer, endianness: endianness, as: `as`)
|
||||
return buffer
|
||||
@@ -799,7 +815,6 @@ extension ByteBufferAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Optional where Wrapped == ByteBuffer {
|
||||
/// If `nil`, replace `self` with `.some(buffer)`. If non-`nil`, write `buffer`'s readable bytes into the
|
||||
/// `ByteBuffer` starting at `writerIndex`.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import Dispatch
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
|
||||
|
||||
/// Creates a `[UInt8]` from the given buffer. The entire readable portion of the buffer will be read.
|
||||
/// - parameter buffer: The buffer to read.
|
||||
@inlinable
|
||||
@@ -23,11 +23,11 @@ extension Array where Element == UInt8 {
|
||||
var buffer = buffer
|
||||
self = buffer.readBytes(length: buffer.readableBytes)!
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
|
||||
/// Creates a `String` from a given `ByteBuffer`. The entire readable portion of the buffer will be read.
|
||||
/// - parameter buffer: The buffer to read.
|
||||
@inlinable
|
||||
@@ -49,7 +49,7 @@ extension String {
|
||||
}
|
||||
|
||||
extension DispatchData {
|
||||
|
||||
|
||||
/// Creates a `DispatchData` from a given `ByteBuffer`. The entire readable portion of the buffer will be read.
|
||||
/// - parameter buffer: The buffer to read.
|
||||
@inlinable
|
||||
@@ -57,5 +57,5 @@ extension DispatchData {
|
||||
var buffer = buffer
|
||||
self = buffer.readDispatchData(length: buffer.readableBytes)!
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import Musl
|
||||
#endif
|
||||
|
||||
@usableFromInline let sysMalloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? = malloc
|
||||
@usableFromInline let sysRealloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer? = realloc
|
||||
@usableFromInline let sysRealloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer? =
|
||||
realloc
|
||||
|
||||
/// Xcode 13 GM shipped with a bug in the SDK that caused `free`'s first argument to be annotated as
|
||||
/// non-nullable. To that end, we define a thunk through to `free` that matches that constraint, as we
|
||||
@@ -42,19 +43,19 @@ struct _ByteBufferSlice: Sendable {
|
||||
@usableFromInline private(set) var upperBound: ByteBuffer._Index
|
||||
@usableFromInline private(set) var _begin: _UInt24
|
||||
@inlinable var lowerBound: ByteBuffer._Index {
|
||||
return UInt32(self._begin)
|
||||
UInt32(self._begin)
|
||||
}
|
||||
@inlinable var count: Int {
|
||||
// Safe: the only constructors that set this enforce that upperBound > lowerBound, so
|
||||
// this cannot underflow.
|
||||
return Int(self.upperBound &- self.lowerBound)
|
||||
Int(self.upperBound &- self.lowerBound)
|
||||
}
|
||||
@inlinable init() {
|
||||
self._begin = .init(0)
|
||||
self.upperBound = .init(0)
|
||||
}
|
||||
@inlinable static var maxSupportedLowerBound: ByteBuffer._Index {
|
||||
return ByteBuffer._Index(_UInt24.max)
|
||||
ByteBuffer._Index(_UInt24.max)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ extension _ByteBufferSlice {
|
||||
extension _ByteBufferSlice: CustomStringConvertible {
|
||||
@usableFromInline
|
||||
var description: String {
|
||||
return "_ByteBufferSlice { \(self.lowerBound)..<\(self.upperBound) }"
|
||||
"_ByteBufferSlice { \(self.lowerBound)..<\(self.upperBound) }"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,17 +83,21 @@ public struct ByteBufferAllocator: Sendable {
|
||||
/// therefore it's recommended to reuse `ByteBufferAllocators` where possible instead of creating fresh ones in
|
||||
/// many places.
|
||||
@inlinable public init() {
|
||||
self.init(hookedMalloc: { sysMalloc($0) },
|
||||
hookedRealloc: { sysRealloc($0, $1) },
|
||||
hookedFree: { sysFree($0) },
|
||||
hookedMemcpy: { $0.copyMemory(from: $1, byteCount: $2) })
|
||||
self.init(
|
||||
hookedMalloc: { sysMalloc($0) },
|
||||
hookedRealloc: { sysRealloc($0, $1) },
|
||||
hookedFree: { sysFree($0) },
|
||||
hookedMemcpy: { $0.copyMemory(from: $1, byteCount: $2) }
|
||||
)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal init(hookedMalloc: @escaping @convention(c) (size_t) -> UnsafeMutableRawPointer?,
|
||||
hookedRealloc: @escaping @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?,
|
||||
hookedFree: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void,
|
||||
hookedMemcpy: @escaping @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void) {
|
||||
internal init(
|
||||
hookedMalloc: @escaping @convention(c) (size_t) -> UnsafeMutableRawPointer?,
|
||||
hookedRealloc: @escaping @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?,
|
||||
hookedFree: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void,
|
||||
hookedMemcpy: @escaping @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void
|
||||
) {
|
||||
self.malloc = hookedMalloc
|
||||
self.realloc = hookedRealloc
|
||||
self.free = hookedFree
|
||||
@@ -118,20 +123,24 @@ public struct ByteBufferAllocator: Sendable {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal static let zeroCapacityWithDefaultAllocator = ByteBuffer(allocator: ByteBufferAllocator(), startingCapacity: 0)
|
||||
internal static let zeroCapacityWithDefaultAllocator = ByteBuffer(
|
||||
allocator: ByteBufferAllocator(),
|
||||
startingCapacity: 0
|
||||
)
|
||||
|
||||
@usableFromInline internal let malloc: @convention(c) (size_t) -> UnsafeMutableRawPointer?
|
||||
@usableFromInline internal let realloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?
|
||||
@usableFromInline internal let realloc:
|
||||
@convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?
|
||||
@usableFromInline internal let free: @convention(c) (UnsafeMutableRawPointer) -> Void
|
||||
@usableFromInline internal let memcpy: @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void
|
||||
}
|
||||
|
||||
@inlinable func _toCapacity(_ value: Int) -> ByteBuffer._Capacity {
|
||||
return ByteBuffer._Capacity(truncatingIfNeeded: value)
|
||||
ByteBuffer._Capacity(truncatingIfNeeded: value)
|
||||
}
|
||||
|
||||
@inlinable func _toIndex(_ value: Int) -> ByteBuffer._Index {
|
||||
return ByteBuffer._Index(truncatingIfNeeded: value)
|
||||
ByteBuffer._Index(truncatingIfNeeded: value)
|
||||
}
|
||||
|
||||
/// `ByteBuffer` stores contiguously allocated raw bytes. It is a random and sequential accessible sequence of zero or
|
||||
@@ -283,28 +292,30 @@ public struct ByteBuffer {
|
||||
|
||||
@inlinable
|
||||
var fullSlice: _ByteBufferSlice {
|
||||
return _ByteBufferSlice(0..<self.capacity)
|
||||
_ByteBufferSlice(0..<self.capacity)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func _allocateAndPrepareRawMemory(bytes: _Capacity, allocator: Allocator) -> UnsafeMutableRawPointer {
|
||||
let ptr = allocator.malloc(size_t(bytes))!
|
||||
/* bind the memory so we can assume it elsewhere to be bound to UInt8 */
|
||||
// bind the memory so we can assume it elsewhere to be bound to UInt8
|
||||
ptr.bindMemory(to: UInt8.self, capacity: Int(bytes))
|
||||
return ptr
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func allocateStorage() -> _Storage {
|
||||
return self.allocateStorage(capacity: self.capacity)
|
||||
self.allocateStorage(capacity: self.capacity)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func allocateStorage(capacity: _Capacity) -> _Storage {
|
||||
let newCapacity = capacity == 0 ? 0 : capacity.nextPowerOf2ClampedToMax()
|
||||
return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: self.allocator),
|
||||
capacity: newCapacity,
|
||||
allocator: self.allocator)
|
||||
return _Storage(
|
||||
bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: self.allocator),
|
||||
capacity: newCapacity,
|
||||
allocator: self.allocator
|
||||
)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -319,7 +330,7 @@ public struct ByteBuffer {
|
||||
func reallocStorage(capacity minimumNeededCapacity: _Capacity) {
|
||||
let newCapacity = minimumNeededCapacity.nextPowerOf2ClampedToMax()
|
||||
let ptr = self.allocator.realloc(self.bytes, size_t(newCapacity))!
|
||||
/* bind the memory so we can assume it elsewhere to be bound to UInt8 */
|
||||
// bind the memory so we can assume it elsewhere to be bound to UInt8
|
||||
ptr.bindMemory(to: UInt8.self, capacity: Int(newCapacity))
|
||||
self.bytes = ptr
|
||||
self.capacity = newCapacity
|
||||
@@ -333,15 +344,17 @@ public struct ByteBuffer {
|
||||
static func reallocated(minimumCapacity: _Capacity, allocator: Allocator) -> _Storage {
|
||||
let newCapacity = minimumCapacity == 0 ? 0 : minimumCapacity.nextPowerOf2ClampedToMax()
|
||||
// TODO: Use realloc if possible
|
||||
return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: allocator),
|
||||
capacity: newCapacity,
|
||||
allocator: allocator)
|
||||
return _Storage(
|
||||
bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: allocator),
|
||||
capacity: newCapacity,
|
||||
allocator: allocator
|
||||
)
|
||||
}
|
||||
|
||||
func dumpBytes(slice: Slice, offset: Int, length: Int) -> String {
|
||||
var desc = "["
|
||||
let bytes = UnsafeRawBufferPointer(start: self.bytes, count: Int(self.capacity))
|
||||
for byte in bytes[Int(slice.lowerBound) + offset ..< Int(slice.lowerBound) + offset + length] {
|
||||
for byte in bytes[Int(slice.lowerBound) + offset..<Int(slice.lowerBound) + offset + length] {
|
||||
let hexByte = String(byte, radix: 16)
|
||||
desc += " \(hexByte.count == 1 ? "0" : "")\(hexByte)"
|
||||
}
|
||||
@@ -393,7 +406,7 @@ public struct ByteBuffer {
|
||||
let storageUpperBound = min(self._slice.upperBound, storageRebaseAmount + capacity)
|
||||
|
||||
// Step 4: Allocate the new buffer and copy the slice of bytes we want to keep.
|
||||
let newSlice = storageRebaseAmount ..< storageUpperBound
|
||||
let newSlice = storageRebaseAmount..<storageUpperBound
|
||||
self._storage = self._storage.reallocSlice(newSlice, capacity: capacity)
|
||||
|
||||
// Step 5: Fixup the indices. These should never trap, but we're going to leave them checked because this method is fiddly.
|
||||
@@ -425,18 +438,23 @@ public struct ByteBuffer {
|
||||
if self._slice.lowerBound == 0 {
|
||||
self._storage.reallocStorage(capacity: newStorageMinCapacity)
|
||||
} else {
|
||||
self._storage = self._storage.reallocSlice(self._slice.lowerBound ..< self._slice.upperBound,
|
||||
capacity: newStorageMinCapacity)
|
||||
self._storage = self._storage.reallocSlice(
|
||||
self._slice.lowerBound..<self._slice.upperBound,
|
||||
capacity: newStorageMinCapacity
|
||||
)
|
||||
}
|
||||
self._slice = self._storage.fullSlice
|
||||
} else {
|
||||
// yes, let's just extend the slice until the end of the buffer
|
||||
self._slice = _ByteBufferSlice(_slice.lowerBound ..< self._storage.capacity)
|
||||
self._slice = _ByteBufferSlice(_slice.lowerBound..<self._storage.capacity)
|
||||
}
|
||||
}
|
||||
assert(self._slice.lowerBound + index + capacity <= self._slice.upperBound)
|
||||
assert(self._slice.lowerBound >= 0, "illegal slice: negative lower bound: \(self._slice.lowerBound)")
|
||||
assert(self._slice.upperBound <= self._storage.capacity, "illegal slice: upper bound (\(self._slice.upperBound)) exceeds capacity: \(self._storage.capacity)")
|
||||
assert(
|
||||
self._slice.upperBound <= self._storage.capacity,
|
||||
"illegal slice: upper bound (\(self._slice.upperBound)) exceeds capacity: \(self._storage.capacity)"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: Internal API
|
||||
@@ -487,10 +505,13 @@ public struct ByteBuffer {
|
||||
@inline(never)
|
||||
@inlinable
|
||||
@_specialize(where Bytes == CircularBuffer<UInt8>)
|
||||
mutating func _setSlowPath<Bytes: Sequence>(bytes: Bytes, at index: _Index) -> _Capacity where Bytes.Element == UInt8 {
|
||||
mutating func _setSlowPath<Bytes: Sequence>(bytes: Bytes, at index: _Index) -> _Capacity
|
||||
where Bytes.Element == UInt8 {
|
||||
func ensureCapacityAndReturnStorageBase(capacity: Int) -> UnsafeMutablePointer<UInt8> {
|
||||
self._ensureAvailableCapacity(_Capacity(capacity), at: index)
|
||||
let newBytesPtr = UnsafeMutableRawBufferPointer(fastRebase: self._slicedStorageBuffer[Int(index) ..< Int(index) + Int(capacity)])
|
||||
let newBytesPtr = UnsafeMutableRawBufferPointer(
|
||||
fastRebase: self._slicedStorageBuffer[Int(index)..<Int(index) + Int(capacity)]
|
||||
)
|
||||
return newBytesPtr.bindMemory(to: UInt8.self).baseAddress!
|
||||
}
|
||||
let underestimatedByteCount = bytes.underestimatedCount
|
||||
@@ -501,7 +522,9 @@ public struct ByteBuffer {
|
||||
}
|
||||
|
||||
var base = ensureCapacityAndReturnStorageBase(capacity: underestimatedByteCount)
|
||||
var (iterator, idx) = UnsafeMutableBufferPointer(start: base, count: underestimatedByteCount).initialize(from: bytes)
|
||||
var (iterator, idx) = UnsafeMutableBufferPointer(start: base, count: underestimatedByteCount).initialize(
|
||||
from: bytes
|
||||
)
|
||||
assert(idx == underestimatedByteCount)
|
||||
while let b = iterator.next() {
|
||||
base = ensureCapacityAndReturnStorageBase(capacity: idx + 1)
|
||||
@@ -512,7 +535,8 @@ public struct ByteBuffer {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func _setBytes<Bytes: Sequence>(_ bytes: Bytes, at index: _Index) -> _Capacity where Bytes.Element == UInt8 {
|
||||
mutating func _setBytes<Bytes: Sequence>(_ bytes: Bytes, at index: _Index) -> _Capacity
|
||||
where Bytes.Element == UInt8 {
|
||||
if let written = bytes.withContiguousStorageIfAvailable({ bytes in
|
||||
self._setBytes(UnsafeRawBufferPointer(bytes), at: index)
|
||||
}) {
|
||||
@@ -537,20 +561,20 @@ public struct ByteBuffer {
|
||||
/// trigger a copy of the bytes.
|
||||
@inlinable public var writableBytes: Int {
|
||||
// this cannot over/overflow because both values are positive and writerIndex<=slice.count, checked on ingestion
|
||||
return Int(_toCapacity(self._slice.count) &- self._writerIndex)
|
||||
Int(_toCapacity(self._slice.count) &- self._writerIndex)
|
||||
}
|
||||
|
||||
/// The number of bytes readable (`readableBytes` = `writerIndex` - `readerIndex`).
|
||||
@inlinable public var readableBytes: Int {
|
||||
// this cannot under/overflow because both are positive and writer >= reader (checked on ingestion of bytes).
|
||||
return Int(self._writerIndex &- self._readerIndex)
|
||||
Int(self._writerIndex &- self._readerIndex)
|
||||
}
|
||||
|
||||
/// The current capacity of the storage of this `ByteBuffer`, this is not constant and does _not_ signify the number
|
||||
/// of bytes that have been written to this `ByteBuffer`.
|
||||
@inlinable
|
||||
public var capacity: Int {
|
||||
return self._slice.count
|
||||
self._slice.count
|
||||
}
|
||||
|
||||
/// The current capacity of the underlying storage of this `ByteBuffer`.
|
||||
@@ -558,7 +582,7 @@ public struct ByteBuffer {
|
||||
/// buffer until new data is written.
|
||||
@inlinable
|
||||
public var storageCapacity: Int {
|
||||
return self._storage.fullSlice.count
|
||||
self._storage.fullSlice.count
|
||||
}
|
||||
|
||||
/// Reserves enough space to store the specified number of bytes.
|
||||
@@ -597,7 +621,7 @@ public struct ByteBuffer {
|
||||
/// - Parameter minimumWritableBytes: The minimum number of writable bytes this buffer must have.
|
||||
@inlinable
|
||||
public mutating func reserveCapacity(minimumWritableBytes: Int) {
|
||||
return self.reserveCapacity(self.writerIndex + minimumWritableBytes)
|
||||
self.reserveCapacity(self.writerIndex + minimumWritableBytes)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -610,8 +634,10 @@ public struct ByteBuffer {
|
||||
|
||||
@inlinable
|
||||
var _slicedStorageBuffer: UnsafeMutableRawBufferPointer {
|
||||
return UnsafeMutableRawBufferPointer(start: self._storage.bytes.advanced(by: Int(self._slice.lowerBound)),
|
||||
count: self._slice.count)
|
||||
UnsafeMutableRawBufferPointer(
|
||||
start: self._storage.bytes.advanced(by: Int(self._slice.lowerBound)),
|
||||
count: self._slice.count
|
||||
)
|
||||
}
|
||||
|
||||
/// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify those bytes.
|
||||
@@ -622,7 +648,9 @@ public struct ByteBuffer {
|
||||
/// - body: The closure that will accept the yielded bytes.
|
||||
/// - returns: The value returned by `body`.
|
||||
@inlinable
|
||||
public mutating func withUnsafeMutableReadableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
|
||||
public mutating func withUnsafeMutableReadableBytes<T>(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> T
|
||||
) rethrows -> T {
|
||||
self._copyStorageAndRebaseIfNeeded()
|
||||
// this is safe because we always know that readerIndex >= writerIndex
|
||||
let range = Range<Int>(uncheckedBounds: (lower: self.readerIndex, upper: self.writerIndex))
|
||||
@@ -640,7 +668,9 @@ public struct ByteBuffer {
|
||||
/// - body: The closure that will accept the yielded bytes and return the number of bytes written.
|
||||
/// - returns: The number of bytes written.
|
||||
@inlinable
|
||||
public mutating func withUnsafeMutableWritableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
|
||||
public mutating func withUnsafeMutableWritableBytes<T>(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> T
|
||||
) rethrows -> T {
|
||||
self._copyStorageAndRebaseIfNeeded()
|
||||
return try body(.init(fastRebase: self._slicedStorageBuffer.dropFirst(self.writerIndex)))
|
||||
}
|
||||
@@ -655,7 +685,10 @@ public struct ByteBuffer {
|
||||
/// - returns: The number of bytes written.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func writeWithUnsafeMutableBytes(minimumWritableBytes: Int, _ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int {
|
||||
public mutating func writeWithUnsafeMutableBytes(
|
||||
minimumWritableBytes: Int,
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> Int
|
||||
) rethrows -> Int {
|
||||
if minimumWritableBytes > 0 {
|
||||
self.reserveCapacity(minimumWritableBytes: minimumWritableBytes)
|
||||
}
|
||||
@@ -664,11 +697,18 @@ public struct ByteBuffer {
|
||||
return bytesWritten
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "please use writeWithUnsafeMutableBytes(minimumWritableBytes:_:) instead to ensure sufficient write capacity.")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message:
|
||||
"please use writeWithUnsafeMutableBytes(minimumWritableBytes:_:) instead to ensure sufficient write capacity."
|
||||
)
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func writeWithUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int {
|
||||
return try self.writeWithUnsafeMutableBytes(minimumWritableBytes: 0, { try body($0) })
|
||||
public mutating func writeWithUnsafeMutableBytes(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> Int
|
||||
) rethrows -> Int {
|
||||
try self.writeWithUnsafeMutableBytes(minimumWritableBytes: 0, { try body($0) })
|
||||
}
|
||||
|
||||
/// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain
|
||||
@@ -677,7 +717,7 @@ public struct ByteBuffer {
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
@inlinable
|
||||
public func withVeryUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T {
|
||||
return try body(.init(self._slicedStorageBuffer))
|
||||
try body(.init(self._slicedStorageBuffer))
|
||||
}
|
||||
|
||||
/// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain
|
||||
@@ -685,8 +725,10 @@ public struct ByteBuffer {
|
||||
///
|
||||
/// - warning: Do not escape the pointer from the closure for later use.
|
||||
@inlinable
|
||||
public mutating func withVeryUnsafeMutableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
|
||||
self._copyStorageAndRebaseIfNeeded() // this will trigger a CoW if necessary
|
||||
public mutating func withVeryUnsafeMutableBytes<T>(
|
||||
_ body: (UnsafeMutableRawBufferPointer) throws -> T
|
||||
) rethrows -> T {
|
||||
self._copyStorageAndRebaseIfNeeded() // this will trigger a CoW if necessary
|
||||
return try body(.init(self._slicedStorageBuffer))
|
||||
}
|
||||
|
||||
@@ -716,7 +758,9 @@ public struct ByteBuffer {
|
||||
/// - body: The closure that will accept the yielded bytes and the `storageManagement`.
|
||||
/// - returns: The value returned by `body`.
|
||||
@inlinable
|
||||
public func withUnsafeReadableBytesWithStorageManagement<T>(_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T) rethrows -> T {
|
||||
public func withUnsafeReadableBytesWithStorageManagement<T>(
|
||||
_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T
|
||||
) rethrows -> T {
|
||||
let storageReference: Unmanaged<AnyObject> = Unmanaged.passUnretained(self._storage)
|
||||
// This is safe, writerIndex >= readerIndex
|
||||
let range = Range<Int>(uncheckedBounds: (lower: self.readerIndex, upper: self.writerIndex))
|
||||
@@ -725,7 +769,9 @@ public struct ByteBuffer {
|
||||
|
||||
/// See `withUnsafeReadableBytesWithStorageManagement` and `withVeryUnsafeBytes`.
|
||||
@inlinable
|
||||
public func withVeryUnsafeBytesWithStorageManagement<T>(_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T) rethrows -> T {
|
||||
public func withVeryUnsafeBytesWithStorageManagement<T>(
|
||||
_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T
|
||||
) rethrows -> T {
|
||||
let storageReference: Unmanaged<AnyObject> = Unmanaged.passUnretained(self._storage)
|
||||
return try body(.init(self._slicedStorageBuffer), storageReference)
|
||||
}
|
||||
@@ -754,13 +800,16 @@ public struct ByteBuffer {
|
||||
/// not readable in the initial `ByteBuffer`.
|
||||
@inlinable
|
||||
public func getSlice(at index: Int, length: Int) -> ByteBuffer? {
|
||||
return self.getSlice_inlineAlways(at: index, length: length)
|
||||
self.getSlice_inlineAlways(at: index, length: length)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@inlinable
|
||||
internal func getSlice_inlineAlways(at index: Int, length: Int) -> ByteBuffer? {
|
||||
guard index >= 0 && length >= 0 && index >= self.readerIndex && length <= self.writerIndex && index <= self.writerIndex &- length else {
|
||||
guard
|
||||
index >= 0 && length >= 0 && index >= self.readerIndex && length <= self.writerIndex
|
||||
&& index <= self.writerIndex &- length
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
let index = _toIndex(index)
|
||||
@@ -789,8 +838,14 @@ public struct ByteBuffer {
|
||||
// 2. `length` <= `self.writerIndex` (see `guard`s)
|
||||
// 3. `sliceStartIndex` + `self._slice.count` is always safe (because that's `self._slice.upperBound`.
|
||||
// - The range construction is safe because `length` >= 0 (see `guard` at the beginning of the function).
|
||||
new._slice = _ByteBufferSlice(Range(uncheckedBounds: (lower: sliceStartIndex,
|
||||
upper: sliceStartIndex &+ length)))
|
||||
new._slice = _ByteBufferSlice(
|
||||
Range(
|
||||
uncheckedBounds: (
|
||||
lower: sliceStartIndex,
|
||||
upper: sliceStartIndex &+ length
|
||||
)
|
||||
)
|
||||
)
|
||||
new._moveReaderIndex(to: 0)
|
||||
new._moveWriterIndex(to: length)
|
||||
return new
|
||||
@@ -815,8 +870,10 @@ public struct ByteBuffer {
|
||||
|
||||
if isKnownUniquelyReferenced(&self._storage) {
|
||||
self._storage.bytes.advanced(by: Int(self._slice.lowerBound))
|
||||
.copyMemory(from: self._storage.bytes.advanced(by: Int(self._slice.lowerBound + self._readerIndex)),
|
||||
byteCount: self.readableBytes)
|
||||
.copyMemory(
|
||||
from: self._storage.bytes.advanced(by: Int(self._slice.lowerBound + self._readerIndex)),
|
||||
byteCount: self.readableBytes
|
||||
)
|
||||
let indexShift = self._readerIndex
|
||||
self._moveReaderIndex(to: 0)
|
||||
self._moveWriterIndex(to: self._writerIndex - indexShift)
|
||||
@@ -830,14 +887,14 @@ public struct ByteBuffer {
|
||||
/// newly allocated `ByteBuffer`.
|
||||
@inlinable
|
||||
public var readerIndex: Int {
|
||||
return Int(self._readerIndex)
|
||||
Int(self._readerIndex)
|
||||
}
|
||||
|
||||
/// The write index or the number of bytes previously written to this `ByteBuffer`. `writerIndex` is `0` for a
|
||||
/// newly allocated `ByteBuffer`.
|
||||
@inlinable
|
||||
public var writerIndex: Int {
|
||||
return Int(self._writerIndex)
|
||||
Int(self._writerIndex)
|
||||
}
|
||||
|
||||
/// Set both reader index and writer index to `0`. This will reset the state of this `ByteBuffer` to the state
|
||||
@@ -869,7 +926,7 @@ public struct ByteBuffer {
|
||||
public mutating func clear(minimumCapacity: UInt32) {
|
||||
self.clear(minimumCapacity: Int(minimumCapacity))
|
||||
}
|
||||
|
||||
|
||||
/// Set both reader index and writer index to `0`. This will reset the state of this `ByteBuffer` to the state
|
||||
/// of a freshly allocated one, if possible without allocations. This is the cheapest way to recycle a `ByteBuffer`
|
||||
/// for a new use-case.
|
||||
@@ -883,7 +940,7 @@ public struct ByteBuffer {
|
||||
public mutating func clear(minimumCapacity: Int) {
|
||||
precondition(minimumCapacity >= 0, "Cannot have a minimum capacity < 0")
|
||||
precondition(minimumCapacity <= _Capacity.max, "Minimum capacity must be <= \(_Capacity.max)")
|
||||
|
||||
|
||||
let minimumCapacity = _Capacity(minimumCapacity)
|
||||
if !isKnownUniquelyReferenced(&self._storage) {
|
||||
self._storage = self._storage.allocateStorage(capacity: minimumCapacity)
|
||||
@@ -906,7 +963,7 @@ extension ByteBuffer: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
///
|
||||
/// - returns: A description of this `ByteBuffer`.
|
||||
public var description: String {
|
||||
return """
|
||||
"""
|
||||
ByteBuffer { \
|
||||
readerIndex: \(self.readerIndex), \
|
||||
writerIndex: \(self.writerIndex), \
|
||||
@@ -928,24 +985,24 @@ extension ByteBuffer: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
///
|
||||
/// - returns: A description of this `ByteBuffer` useful for debugging.
|
||||
public var debugDescription: String {
|
||||
return "\(self.description)\nreadable bytes (max 1k): \(self._storage.dumpBytes(slice: self._slice, offset: self.readerIndex, length: min(1024, self.readableBytes)))"
|
||||
"\(self.description)\nreadable bytes (max 1k): \(self._storage.dumpBytes(slice: self._slice, offset: self.readerIndex, length: min(1024, self.readableBytes)))"
|
||||
}
|
||||
}
|
||||
|
||||
/* change types to the user visible `Int` */
|
||||
// change types to the user visible `Int`
|
||||
extension ByteBuffer {
|
||||
/// Copy the collection of `bytes` into the `ByteBuffer` at `index`. Does not move the writer index.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func setBytes<Bytes: Sequence>(_ bytes: Bytes, at index: Int) -> Int where Bytes.Element == UInt8 {
|
||||
return Int(self._setBytes(bytes, at: _toIndex(index)))
|
||||
Int(self._setBytes(bytes, at: _toIndex(index)))
|
||||
}
|
||||
|
||||
/// Copy `bytes` into the `ByteBuffer` at `index`. Does not move the writer index.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func setBytes(_ bytes: UnsafeRawBufferPointer, at index: Int) -> Int {
|
||||
return Int(self._setBytes(bytes, at: _toIndex(index)))
|
||||
Int(self._setBytes(bytes, at: _toIndex(index)))
|
||||
}
|
||||
|
||||
/// Move the reader index forward by `offset` bytes.
|
||||
@@ -958,7 +1015,10 @@ extension ByteBuffer {
|
||||
@inlinable
|
||||
public mutating func moveReaderIndex(forwardBy offset: Int) {
|
||||
let newIndex = self._readerIndex + _toIndex(offset)
|
||||
precondition(newIndex >= 0 && newIndex <= writerIndex, "new readerIndex: \(newIndex), expected: range(0, \(writerIndex))")
|
||||
precondition(
|
||||
newIndex >= 0 && newIndex <= writerIndex,
|
||||
"new readerIndex: \(newIndex), expected: range(0, \(writerIndex))"
|
||||
)
|
||||
self._moveReaderIndex(to: newIndex)
|
||||
}
|
||||
|
||||
@@ -972,7 +1032,10 @@ extension ByteBuffer {
|
||||
@inlinable
|
||||
public mutating func moveReaderIndex(to offset: Int) {
|
||||
let newIndex = _toIndex(offset)
|
||||
precondition(newIndex >= 0 && newIndex <= writerIndex, "new readerIndex: \(newIndex), expected: range(0, \(writerIndex))")
|
||||
precondition(
|
||||
newIndex >= 0 && newIndex <= writerIndex,
|
||||
"new readerIndex: \(newIndex), expected: range(0, \(writerIndex))"
|
||||
)
|
||||
self._moveReaderIndex(to: newIndex)
|
||||
}
|
||||
|
||||
@@ -986,7 +1049,10 @@ extension ByteBuffer {
|
||||
@inlinable
|
||||
public mutating func moveWriterIndex(forwardBy offset: Int) {
|
||||
let newIndex = self._writerIndex + _toIndex(offset)
|
||||
precondition(newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))")
|
||||
precondition(
|
||||
newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),
|
||||
"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))"
|
||||
)
|
||||
self._moveWriterIndex(to: newIndex)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1066,10 @@ extension ByteBuffer {
|
||||
@inlinable
|
||||
public mutating func moveWriterIndex(to offset: Int) {
|
||||
let newIndex = _toIndex(offset)
|
||||
precondition(newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))")
|
||||
precondition(
|
||||
newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),
|
||||
"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))"
|
||||
)
|
||||
self._moveWriterIndex(to: newIndex)
|
||||
}
|
||||
}
|
||||
@@ -1061,11 +1130,11 @@ extension ByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBuffer.CopyBytesError: Hashable { }
|
||||
extension ByteBuffer.CopyBytesError: Hashable {}
|
||||
|
||||
extension ByteBuffer.CopyBytesError: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
return String(describing: self.baseError)
|
||||
String(describing: self.baseError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1074,7 +1143,7 @@ extension ByteBuffer: Equatable {
|
||||
|
||||
/// Compare two `ByteBuffer` values. Two `ByteBuffer` values are considered equal if the readable bytes are equal.
|
||||
@inlinable
|
||||
public static func ==(lhs: ByteBuffer, rhs: ByteBuffer) -> Bool {
|
||||
public static func == (lhs: ByteBuffer, rhs: ByteBuffer) -> Bool {
|
||||
guard lhs.readableBytes == rhs.readableBytes else {
|
||||
return false
|
||||
}
|
||||
@@ -1143,7 +1212,7 @@ extension ByteBuffer {
|
||||
return nil
|
||||
}
|
||||
|
||||
let upperBound = indexFromReaderIndex &+ length // safe, can't overflow, we checked it above.
|
||||
let upperBound = indexFromReaderIndex &+ length // safe, can't overflow, we checked it above.
|
||||
|
||||
// uncheckedBounds is safe because `length` is >= 0, so the lower bound will always be lower/equal to upper
|
||||
return Range<Int>(uncheckedBounds: (lower: indexFromReaderIndex, upper: upperBound))
|
||||
|
||||
@@ -131,7 +131,7 @@ extension ByteBuffer {
|
||||
result += String(repeating: " ", count: 60 - result.count)
|
||||
|
||||
// Right column renders the 16 bytes line as ASCII characters, or "." if the character is not printable.
|
||||
let printableRange = UInt8(ascii: " ") ..< UInt8(ascii: "~")
|
||||
let printableRange = UInt8(ascii: " ")..<UInt8(ascii: "~")
|
||||
let printableBytes = self.readableBytesView.map {
|
||||
printableRange.contains($0) ? $0 : UInt8(ascii: ".")
|
||||
}
|
||||
@@ -183,7 +183,7 @@ extension ByteBuffer {
|
||||
|
||||
// reserve capacity for the maxBytes dumped, plus the separator line, and buffer length line.
|
||||
var result = ""
|
||||
result.reserveCapacity(maxBytes/16 * 79 + 79 + 8)
|
||||
result.reserveCapacity(maxBytes / 16 * 79 + 79 + 8)
|
||||
|
||||
var buffer = self
|
||||
|
||||
@@ -246,7 +246,7 @@ extension ByteBuffer {
|
||||
/// - parameters:
|
||||
/// - format: ``HexDumpFormat`` to use for the dump.
|
||||
public func hexDump(format: HexDumpFormat) -> String {
|
||||
switch(format.value) {
|
||||
switch format.value {
|
||||
case .plain(let maxBytes):
|
||||
if let maxBytes = maxBytes {
|
||||
return self.hexDumpPlain(maxBytes: maxBytes)
|
||||
@@ -263,4 +263,3 @@ extension ByteBuffer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
extension ByteBuffer {
|
||||
@inlinable
|
||||
func _toEndianness<T: FixedWidthInteger> (value: T, endianness: Endianness) -> T {
|
||||
func _toEndianness<T: FixedWidthInteger>(value: T, endianness: Endianness) -> T {
|
||||
switch endianness {
|
||||
case .little:
|
||||
return value.littleEndian
|
||||
@@ -48,7 +48,11 @@ extension ByteBuffer {
|
||||
/// - returns: An integer value deserialized from this `ByteBuffer` or `nil` if the bytes of interest are not
|
||||
/// readable.
|
||||
@inlinable
|
||||
public func getInteger<T: FixedWidthInteger>(at index: Int, endianness: Endianness = Endianness.big, as: T.Type = T.self) -> T? {
|
||||
public func getInteger<T: FixedWidthInteger>(
|
||||
at index: Int,
|
||||
endianness: Endianness = Endianness.big,
|
||||
as: T.Type = T.self
|
||||
) -> T? {
|
||||
guard let range = self.rangeWithinReadableBytes(index: index, length: MemoryLayout<T>.size) else {
|
||||
return nil
|
||||
}
|
||||
@@ -78,9 +82,11 @@ extension ByteBuffer {
|
||||
/// - returns: The number of bytes written.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func writeInteger<T: FixedWidthInteger>(_ integer: T,
|
||||
endianness: Endianness = .big,
|
||||
as: T.Type = T.self) -> Int {
|
||||
public mutating func writeInteger<T: FixedWidthInteger>(
|
||||
_ integer: T,
|
||||
endianness: Endianness = .big,
|
||||
as: T.Type = T.self
|
||||
) -> Int {
|
||||
let bytesWritten = self.setInteger(integer, at: self.writerIndex, endianness: endianness)
|
||||
self._moveWriterIndex(forwardBy: bytesWritten)
|
||||
return Int(bytesWritten)
|
||||
@@ -96,7 +102,12 @@ extension ByteBuffer {
|
||||
/// - returns: The number of bytes written.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func setInteger<T: FixedWidthInteger>(_ integer: T, at index: Int, endianness: Endianness = .big, as: T.Type = T.self) -> Int {
|
||||
public mutating func setInteger<T: FixedWidthInteger>(
|
||||
_ integer: T,
|
||||
at index: Int,
|
||||
endianness: Endianness = .big,
|
||||
as: T.Type = T.self
|
||||
) -> Int {
|
||||
var value = _toEndianness(value: integer, endianness: endianness)
|
||||
return Swift.withUnsafeBytes(of: &value) { ptr in
|
||||
self.setBytes(ptr, at: index)
|
||||
@@ -166,7 +177,7 @@ public enum Endianness: Sendable {
|
||||
public static let host: Endianness = hostEndianness0()
|
||||
|
||||
private static func hostEndianness0() -> Endianness {
|
||||
let number: UInt32 = 0x12345678
|
||||
let number: UInt32 = 0x1234_5678
|
||||
return number == number.bigEndian ? .big : .little
|
||||
}
|
||||
|
||||
@@ -176,5 +187,3 @@ public enum Endianness: Sendable {
|
||||
/// little endian, the least significant byte (LSB) is at the lowest address
|
||||
case little
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,13 @@ extension ByteBuffer {
|
||||
case messageCouldNotBeReadSuccessfully
|
||||
}
|
||||
private var baseError: BaseError
|
||||
|
||||
public static let messageLengthDoesNotFitExactlyIntoRequiredIntegerFormat: LengthPrefixError = .init(baseError: .messageLengthDoesNotFitExactlyIntoRequiredIntegerFormat)
|
||||
public static let messageCouldNotBeReadSuccessfully: LengthPrefixError = .init(baseError: .messageCouldNotBeReadSuccessfully)
|
||||
|
||||
public static let messageLengthDoesNotFitExactlyIntoRequiredIntegerFormat: LengthPrefixError = .init(
|
||||
baseError: .messageLengthDoesNotFitExactlyIntoRequiredIntegerFormat
|
||||
)
|
||||
public static let messageCouldNotBeReadSuccessfully: LengthPrefixError = .init(
|
||||
baseError: .messageCouldNotBeReadSuccessfully
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,44 +45,44 @@ extension ByteBuffer {
|
||||
writeMessage: (inout ByteBuffer) throws -> Int
|
||||
) throws -> Int where Integer: FixedWidthInteger {
|
||||
var totalBytesWritten = 0
|
||||
|
||||
|
||||
let lengthPrefixIndex = self.writerIndex
|
||||
// Write a zero as a placeholder which will later be overwritten by the actual number of bytes written
|
||||
totalBytesWritten += self.writeInteger(.zero, endianness: endianness, as: Integer.self)
|
||||
|
||||
|
||||
let startWriterIndex = self.writerIndex
|
||||
let messageLength = try writeMessage(&self)
|
||||
let endWriterIndex = self.writerIndex
|
||||
|
||||
|
||||
totalBytesWritten += messageLength
|
||||
|
||||
|
||||
let actualBytesWritten = endWriterIndex - startWriterIndex
|
||||
assert(
|
||||
actualBytesWritten == messageLength,
|
||||
actualBytesWritten == messageLength,
|
||||
"writeMessage returned \(messageLength) bytes, but actually \(actualBytesWritten) bytes were written, but they should be the same"
|
||||
)
|
||||
|
||||
|
||||
guard let lengthPrefix = Integer(exactly: messageLength) else {
|
||||
throw LengthPrefixError.messageLengthDoesNotFitExactlyIntoRequiredIntegerFormat
|
||||
}
|
||||
|
||||
|
||||
self.setInteger(lengthPrefix, at: lengthPrefixIndex, endianness: endianness, as: Integer.self)
|
||||
|
||||
|
||||
return totalBytesWritten
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBuffer {
|
||||
/// Reads an `Integer` from `self`, reads a slice of that length and passes it to `readMessage`.
|
||||
/// Reads an `Integer` from `self`, reads a slice of that length and passes it to `readMessage`.
|
||||
/// It is checked that `readMessage` returns a non-nil value.
|
||||
///
|
||||
///
|
||||
/// The `readerIndex` is **not** moved forward if the length prefix could not be read or `self` does not contain enough bytes. Otherwise `readerIndex` is moved forward even if `readMessage` throws or returns nil.
|
||||
/// - Parameters:
|
||||
/// - endianness: The endianness of the length prefix `Integer` in this `ByteBuffer` (defaults to big endian).
|
||||
/// - integer: the desired `Integer` type used to read the length prefix
|
||||
/// - readMessage: A closure that takes a `ByteBuffer` slice which contains the message after the length prefix
|
||||
/// - Throws: if `readMessage` returns nil
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// the length prefix is negative or
|
||||
/// the buffer does not contain enough bytes to read a message of this length.
|
||||
/// Otherwise the result of `readMessage`.
|
||||
@@ -96,14 +100,14 @@ extension ByteBuffer {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/// Reads an `Integer` from `self` and reads a slice of that length from `self` and returns it.
|
||||
///
|
||||
///
|
||||
/// If nil is returned, `readerIndex` is **not** moved forward.
|
||||
/// - Parameters:
|
||||
/// - endianness: The endianness of the length prefix `Integer` in this `ByteBuffer` (defaults to big endian).
|
||||
/// - integer: the desired `Integer` type used to read the length prefix
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// the length prefix is negative or
|
||||
/// the buffer does not contain enough bytes to read a message of this length.
|
||||
/// Otherwise the message after the length prefix.
|
||||
@@ -112,19 +116,20 @@ extension ByteBuffer {
|
||||
endianness: Endianness = .big,
|
||||
as integer: Integer.Type
|
||||
) -> ByteBuffer? where Integer: FixedWidthInteger {
|
||||
guard let result = self.getLengthPrefixedSlice(at: self.readerIndex, endianness: endianness, as: Integer.self) else {
|
||||
guard let result = self.getLengthPrefixedSlice(at: self.readerIndex, endianness: endianness, as: Integer.self)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
self._moveReaderIndex(forwardBy: MemoryLayout<Integer>.size + result.readableBytes)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/// Gets an `Integer` from `self` and gets a slice of that length from `self` and returns it.
|
||||
///
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - endianness: The endianness of the length prefix `Integer` in this `ByteBuffer` (defaults to big endian).
|
||||
/// - integer: the desired `Integer` type used to get the length prefix
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// - Returns: `nil` if the length prefix could not be read,
|
||||
/// the length prefix is negative or
|
||||
/// the buffer does not contain enough bytes to read a message of this length.
|
||||
/// Otherwise the message after the length prefix.
|
||||
@@ -135,12 +140,12 @@ extension ByteBuffer {
|
||||
as integer: Integer.Type
|
||||
) -> ByteBuffer? where Integer: FixedWidthInteger {
|
||||
guard let lengthPrefix = self.getInteger(at: index, endianness: endianness, as: Integer.self),
|
||||
let messageLength = Int(exactly: lengthPrefix),
|
||||
let messageBuffer = self.getSlice(at: index + MemoryLayout<Integer>.size, length: messageLength)
|
||||
let messageLength = Int(exactly: lengthPrefix),
|
||||
let messageBuffer = self.getSlice(at: index + MemoryLayout<Integer>.size, length: messageLength)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
return messageBuffer
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,8 +21,8 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
public typealias Index = Int
|
||||
public typealias SubSequence = ByteBufferView
|
||||
|
||||
/* private but usableFromInline */ @usableFromInline var _buffer: ByteBuffer
|
||||
/* private but usableFromInline */ @usableFromInline var _range: Range<Index>
|
||||
@usableFromInline var _buffer: ByteBuffer
|
||||
@usableFromInline var _range: Range<Index>
|
||||
|
||||
@inlinable
|
||||
internal init(buffer: ByteBuffer, range: Range<Index>) {
|
||||
@@ -34,37 +34,41 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
/// Creates a `ByteBufferView` from the readable bytes of the given `buffer`.
|
||||
@inlinable
|
||||
public init(_ buffer: ByteBuffer) {
|
||||
self = ByteBufferView(buffer: buffer, range: buffer.readerIndex ..< buffer.writerIndex)
|
||||
self = ByteBufferView(buffer: buffer, range: buffer.readerIndex..<buffer.writerIndex)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
|
||||
return try self._buffer.withVeryUnsafeBytes { ptr in
|
||||
try body(UnsafeRawBufferPointer(start: ptr.baseAddress!.advanced(by: self._range.lowerBound),
|
||||
count: self._range.count))
|
||||
try self._buffer.withVeryUnsafeBytes { ptr in
|
||||
try body(
|
||||
UnsafeRawBufferPointer(
|
||||
start: ptr.baseAddress!.advanced(by: self._range.lowerBound),
|
||||
count: self._range.count
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var startIndex: Index {
|
||||
return self._range.lowerBound
|
||||
self._range.lowerBound
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var endIndex: Index {
|
||||
return self._range.upperBound
|
||||
self._range.upperBound
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func index(after i: Index) -> Index {
|
||||
return i + 1
|
||||
i + 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var count: Int {
|
||||
// Unchecked is safe here: Range enforces that upperBound is strictly greater than
|
||||
// lower bound, and we guarantee that _range.lowerBound >= 0.
|
||||
return self._range.upperBound &- self._range.lowerBound
|
||||
self._range.upperBound &- self._range.lowerBound
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -73,7 +77,7 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
guard position >= self._range.lowerBound && position < self._range.upperBound else {
|
||||
preconditionFailure("index \(position) out of range")
|
||||
}
|
||||
return self._buffer.getInteger(at: position)! // range check above
|
||||
return self._buffer.getInteger(at: position)! // range check above
|
||||
}
|
||||
set {
|
||||
guard position >= self._range.lowerBound && position < self._range.upperBound else {
|
||||
@@ -86,7 +90,7 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
@inlinable
|
||||
public subscript(range: Range<Index>) -> ByteBufferView {
|
||||
get {
|
||||
return ByteBufferView(buffer: self._buffer, range: range)
|
||||
ByteBufferView(buffer: self._buffer, range: range)
|
||||
}
|
||||
set {
|
||||
self.replaceSubrange(range, with: newValue)
|
||||
@@ -95,35 +99,41 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
|
||||
@inlinable
|
||||
public func withContiguousStorageIfAvailable<R>(_ body: (UnsafeBufferPointer<UInt8>) throws -> R) rethrows -> R? {
|
||||
return try self.withUnsafeBytes { bytes in
|
||||
return try body(bytes.bindMemory(to: UInt8.self))
|
||||
try self.withUnsafeBytes { bytes in
|
||||
try body(bytes.bindMemory(to: UInt8.self))
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func _customIndexOfEquatableElement(_ element : Element) -> Index?? {
|
||||
return .some(self.withUnsafeBytes { ptr -> Index? in
|
||||
return ptr.firstIndex(of: element).map { $0 + self._range.lowerBound }
|
||||
})
|
||||
public func _customIndexOfEquatableElement(_ element: Element) -> Index?? {
|
||||
.some(
|
||||
self.withUnsafeBytes { ptr -> Index? in
|
||||
ptr.firstIndex(of: element).map { $0 + self._range.lowerBound }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? {
|
||||
return .some(self.withUnsafeBytes { ptr -> Index? in
|
||||
return ptr.lastIndex(of: element).map { $0 + self._range.lowerBound }
|
||||
})
|
||||
.some(
|
||||
self.withUnsafeBytes { ptr -> Index? in
|
||||
ptr.lastIndex(of: element).map { $0 + self._range.lowerBound }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@inlinable
|
||||
public func _customContainsEquatableElement(_ element: Element) -> Bool? {
|
||||
return .some(self.withUnsafeBytes { ptr -> Bool in
|
||||
return ptr.contains(element)
|
||||
})
|
||||
.some(
|
||||
self.withUnsafeBytes { ptr -> Bool in
|
||||
ptr.contains(element)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func _copyContents(
|
||||
initializing ptr: UnsafeMutableBufferPointer<UInt8>
|
||||
initializing ptr: UnsafeMutableBufferPointer<UInt8>
|
||||
) -> (Iterator, UnsafeMutableBufferPointer<UInt8>.Index) {
|
||||
precondition(ptr.count >= self.count)
|
||||
|
||||
@@ -141,10 +151,10 @@ public struct ByteBufferView: RandomAccessCollection, Sendable {
|
||||
// These are implemented as no-ops for performance reasons.
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {}
|
||||
|
||||
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ index: Index, bounds: ClosedRange<Index>) {}
|
||||
|
||||
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {}
|
||||
}
|
||||
@@ -176,7 +186,7 @@ extension ByteBufferView: RangeReplaceableCollection {
|
||||
// ``CollectionOfOne`` has no witness for
|
||||
// ``Sequence.withContiguousStorageIfAvailable(_:)``. so we do this instead:
|
||||
self._buffer.setInteger(byte, at: self._range.upperBound)
|
||||
self._range = self._range.lowerBound ..< self._range.upperBound.advanced(by: 1)
|
||||
self._range = self._range.lowerBound..<self._range.upperBound.advanced(by: 1)
|
||||
self._buffer.moveWriterIndex(to: self._range.upperBound)
|
||||
}
|
||||
|
||||
@@ -184,14 +194,17 @@ extension ByteBufferView: RangeReplaceableCollection {
|
||||
@inlinable
|
||||
public mutating func append<Bytes: Sequence>(contentsOf bytes: Bytes) where Bytes.Element == UInt8 {
|
||||
let written = self._buffer.setBytes(bytes, at: self._range.upperBound)
|
||||
self._range = self._range.lowerBound ..< self._range.upperBound.advanced(by: written)
|
||||
self._range = self._range.lowerBound..<self._range.upperBound.advanced(by: written)
|
||||
self._buffer.moveWriterIndex(to: self._range.upperBound)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) where ByteBufferView.Element == C.Element {
|
||||
precondition(subrange.startIndex >= self.startIndex && subrange.endIndex <= self.endIndex,
|
||||
"subrange out of bounds")
|
||||
public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C)
|
||||
where ByteBufferView.Element == C.Element {
|
||||
precondition(
|
||||
subrange.startIndex >= self.startIndex && subrange.endIndex <= self.endIndex,
|
||||
"subrange out of bounds"
|
||||
)
|
||||
|
||||
if newElements.count == subrange.count {
|
||||
self._buffer.setBytes(newElements, at: subrange.startIndex)
|
||||
@@ -201,9 +214,11 @@ extension ByteBufferView: RangeReplaceableCollection {
|
||||
|
||||
// Remove the unwanted bytes between the newly copied bytes and the end of the subrange.
|
||||
// try! is fine here: the copied range is within the view and the length can't be negative.
|
||||
try! self._buffer.copyBytes(at: subrange.endIndex,
|
||||
to: subrange.startIndex.advanced(by: newElements.count),
|
||||
length: subrange.endIndex.distance(to: self._buffer.writerIndex))
|
||||
try! self._buffer.copyBytes(
|
||||
at: subrange.endIndex,
|
||||
to: subrange.startIndex.advanced(by: newElements.count),
|
||||
length: subrange.endIndex.distance(to: self._buffer.writerIndex)
|
||||
)
|
||||
|
||||
// Shorten the range.
|
||||
let removedBytes = subrange.count - newElements.count
|
||||
@@ -212,9 +227,11 @@ extension ByteBufferView: RangeReplaceableCollection {
|
||||
} else {
|
||||
// Make space for the new elements.
|
||||
// try! is fine here: the copied range is within the view and the length can't be negative.
|
||||
try! self._buffer.copyBytes(at: subrange.endIndex,
|
||||
to: subrange.startIndex.advanced(by: newElements.count),
|
||||
length: subrange.endIndex.distance(to: self._buffer.writerIndex))
|
||||
try! self._buffer.copyBytes(
|
||||
at: subrange.endIndex,
|
||||
to: subrange.startIndex.advanced(by: newElements.count),
|
||||
length: subrange.endIndex.distance(to: self._buffer.writerIndex)
|
||||
)
|
||||
|
||||
// Replace the bytes.
|
||||
self._buffer.setBytes(newElements, at: subrange.startIndex)
|
||||
@@ -222,7 +239,7 @@ extension ByteBufferView: RangeReplaceableCollection {
|
||||
// Widen the range.
|
||||
let additionalByteCount = newElements.count - subrange.count
|
||||
self._buffer.moveWriterIndex(forwardBy: additionalByteCount)
|
||||
self._range = self._range.startIndex ..< self._range.endIndex.advanced(by: additionalByteCount)
|
||||
self._range = self._range.startIndex..<self._range.endIndex.advanced(by: additionalByteCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,7 +248,7 @@ extension ByteBuffer {
|
||||
/// A view into the readable bytes of the `ByteBuffer`.
|
||||
@inlinable
|
||||
public var readableBytesView: ByteBufferView {
|
||||
return ByteBufferView(self)
|
||||
ByteBufferView(self)
|
||||
}
|
||||
|
||||
/// Returns a view into some portion of the readable bytes of a `ByteBuffer`.
|
||||
@@ -246,7 +263,7 @@ extension ByteBuffer {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ByteBufferView(buffer: self, range: index ..< (index + length))
|
||||
return ByteBufferView(buffer: self, range: index..<(index + length))
|
||||
}
|
||||
|
||||
/// Create a `ByteBuffer` from the given `ByteBufferView`s range.
|
||||
@@ -264,14 +281,14 @@ extension ByteBufferView: Equatable {
|
||||
public static func == (lhs: ByteBufferView, rhs: ByteBufferView) -> Bool {
|
||||
|
||||
guard lhs._range.count == rhs._range.count else {
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
// A well-formed ByteBufferView can never have a range that is out-of-bounds of the backing ByteBuffer.
|
||||
// As a result, these getSlice calls can never fail, and we'd like to know it if they do.
|
||||
let leftBufferSlice = lhs._buffer.getSlice(at: lhs._range.startIndex, length: lhs._range.count)!
|
||||
let rightBufferSlice = rhs._buffer.getSlice(at: rhs._range.startIndex, length: rhs._range.count)!
|
||||
|
||||
|
||||
return leftBufferSlice == rightBufferSlice
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public protocol Channel: AnyObject, ChannelOutboundInvoker, _NIOPreconcurrencySe
|
||||
extension Channel {
|
||||
/// Default implementation: `NIOSynchronousChannelOptions` are not supported.
|
||||
public var syncOptions: NIOSynchronousChannelOptions? {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,6 @@ extension Channel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Provides special extension to make writing data to the `Channel` easier by removing the need to wrap data in `NIOAny` manually.
|
||||
extension Channel {
|
||||
|
||||
@@ -218,7 +217,7 @@ extension Channel {
|
||||
///
|
||||
/// - seealso: `ChannelOutboundInvoker.write`.
|
||||
public func write<T>(_ any: T) -> EventLoopFuture<Void> {
|
||||
return self.write(NIOAny(any))
|
||||
self.write(NIOAny(any))
|
||||
}
|
||||
|
||||
/// Write data into the `Channel`, automatically wrapping with `NIOAny`.
|
||||
@@ -232,10 +231,9 @@ extension Channel {
|
||||
///
|
||||
/// - seealso: `ChannelOutboundInvoker.writeAndFlush`.
|
||||
public func writeAndFlush<T>(_ any: T) -> EventLoopFuture<Void> {
|
||||
return self.writeAndFlush(NIOAny(any))
|
||||
self.writeAndFlush(NIOAny(any))
|
||||
}
|
||||
|
||||
|
||||
/// Write and flush data into the `Channel`, automatically wrapping with `NIOAny`.
|
||||
///
|
||||
/// - seealso: `ChannelOutboundInvoker.writeAndFlush`.
|
||||
@@ -262,7 +260,7 @@ extension ChannelCore {
|
||||
/// - returns: The content of the `NIOAny`.
|
||||
@inlinable
|
||||
public func unwrapData<T>(_ data: NIOAny, as: T.Type = T.self) -> T {
|
||||
return data.forceAs()
|
||||
data.forceAs()
|
||||
}
|
||||
|
||||
/// Attempts to unwrap the given `NIOAny` as a specific concrete type.
|
||||
@@ -284,7 +282,7 @@ extension ChannelCore {
|
||||
/// are doing something _extremely_ unusual.
|
||||
@inlinable
|
||||
public func tryUnwrapData<T>(_ data: NIOAny, as: T.Type = T.self) -> T? {
|
||||
return data.tryAs()
|
||||
data.tryAs()
|
||||
}
|
||||
|
||||
/// Removes the `ChannelHandler`s from the `ChannelPipeline` belonging to `channel`, and
|
||||
@@ -384,7 +382,7 @@ extension ChannelError {
|
||||
static let _unremovableHandler: any Error = ChannelError.unremovableHandler
|
||||
}
|
||||
|
||||
extension ChannelError: Equatable { }
|
||||
extension ChannelError: Equatable {}
|
||||
|
||||
/// The removal of a `ChannelHandler` using `ChannelPipeline.removeHandler` has been attempted more than once.
|
||||
public struct NIOAttemptedToRemoveHandlerMultipleTimesError: Error {}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
/// A `ChannelHandler` that implements a backoff for a `ServerChannel` when accept produces an `IOError`.
|
||||
/// These errors are often recoverable by reducing the rate at which we call accept.
|
||||
public final class AcceptBackoffHandler: ChannelDuplexHandler, RemovableChannelHandler {
|
||||
@@ -28,7 +27,7 @@ public final class AcceptBackoffHandler: ChannelDuplexHandler, RemovableChannelH
|
||||
|
||||
/// Default implementation used as `backoffProvider` which delays accept by 1 second.
|
||||
public static func defaultBackoffProvider(error: IOError) -> TimeAmount? {
|
||||
return .seconds(1)
|
||||
.seconds(1)
|
||||
}
|
||||
|
||||
/// Create a new instance
|
||||
@@ -108,10 +107,8 @@ public final class AcceptBackoffHandler: ChannelDuplexHandler, RemovableChannelH
|
||||
@available(*, unavailable)
|
||||
extension AcceptBackoffHandler: Sendable {}
|
||||
|
||||
/**
|
||||
ChannelHandler implementation which enforces back-pressure by stopping to read from the remote peer when it cannot write back fast enough.
|
||||
It will start reading again once pending data was written.
|
||||
*/
|
||||
/// ChannelHandler implementation which enforces back-pressure by stopping to read from the remote peer when it cannot write back fast enough.
|
||||
/// It will start reading again once pending data was written.
|
||||
public final class BackPressureHandler: ChannelDuplexHandler, RemovableChannelHandler {
|
||||
public typealias OutboundIn = NIOAny
|
||||
public typealias InboundIn = ByteBuffer
|
||||
@@ -121,7 +118,7 @@ public final class BackPressureHandler: ChannelDuplexHandler, RemovableChannelHa
|
||||
private var pendingRead = false
|
||||
private var writable: Bool = true
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
public func read(context: ChannelHandlerContext) {
|
||||
if writable {
|
||||
@@ -218,7 +215,7 @@ public final class IdleStateHandler: ChannelDuplexHandler, RemovableChannelHandl
|
||||
}
|
||||
|
||||
public func channelReadComplete(context: ChannelHandlerContext) {
|
||||
if (readTimeout != nil || allTimeout != nil) && reading {
|
||||
if (readTimeout != nil || allTimeout != nil) && reading {
|
||||
lastReadTime = .now()
|
||||
reading = false
|
||||
}
|
||||
@@ -246,32 +243,41 @@ public final class IdleStateHandler: ChannelDuplexHandler, RemovableChannelHandl
|
||||
}
|
||||
|
||||
private func makeReadTimeoutTask(_ context: ChannelHandlerContext, _ timeout: TimeAmount) -> (() -> Void) {
|
||||
return {
|
||||
guard self.shouldReschedule(context) else {
|
||||
{
|
||||
guard self.shouldReschedule(context) else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.reading {
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(in: timeout, self.makeReadTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
in: timeout,
|
||||
self.makeReadTimeoutTask(context, timeout)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
let diff = .now() - self.lastReadTime
|
||||
if diff >= timeout {
|
||||
// Reader is idle - set a new timeout and trigger an event through the pipeline
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(in: timeout, self.makeReadTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
in: timeout,
|
||||
self.makeReadTimeoutTask(context, timeout)
|
||||
)
|
||||
|
||||
context.fireUserInboundEventTriggered(IdleStateEvent.read)
|
||||
} else {
|
||||
// Read occurred before the timeout - set a new timeout with shorter delay.
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(deadline: self.lastReadTime + timeout, self.makeReadTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
deadline: self.lastReadTime + timeout,
|
||||
self.makeReadTimeoutTask(context, timeout)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeWriteTimeoutTask(_ context: ChannelHandlerContext, _ timeout: TimeAmount) -> (() -> Void) {
|
||||
return {
|
||||
guard self.shouldReschedule(context) else {
|
||||
{
|
||||
guard self.shouldReschedule(context) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -280,24 +286,33 @@ public final class IdleStateHandler: ChannelDuplexHandler, RemovableChannelHandl
|
||||
|
||||
if diff >= timeout {
|
||||
// Writer is idle - set a new timeout and notify the callback.
|
||||
self.scheduledWriterTask = context.eventLoop.assumeIsolated().scheduleTask(in: timeout, self.makeWriteTimeoutTask(context, timeout))
|
||||
self.scheduledWriterTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
in: timeout,
|
||||
self.makeWriteTimeoutTask(context, timeout)
|
||||
)
|
||||
|
||||
context.fireUserInboundEventTriggered(IdleStateEvent.write)
|
||||
} else {
|
||||
// Write occurred before the timeout - set a new timeout with shorter delay.
|
||||
self.scheduledWriterTask = context.eventLoop.assumeIsolated().scheduleTask(deadline: self.lastWriteCompleteTime + timeout, self.makeWriteTimeoutTask(context, timeout))
|
||||
self.scheduledWriterTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
deadline: self.lastWriteCompleteTime + timeout,
|
||||
self.makeWriteTimeoutTask(context, timeout)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeAllTimeoutTask(_ context: ChannelHandlerContext, _ timeout: TimeAmount) -> (() -> Void) {
|
||||
return {
|
||||
guard self.shouldReschedule(context) else {
|
||||
{
|
||||
guard self.shouldReschedule(context) else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.reading {
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(in: timeout, self.makeAllTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
in: timeout,
|
||||
self.makeAllTimeoutTask(context, timeout)
|
||||
)
|
||||
return
|
||||
}
|
||||
let lastRead = self.lastReadTime
|
||||
@@ -307,17 +322,27 @@ public final class IdleStateHandler: ChannelDuplexHandler, RemovableChannelHandl
|
||||
let diff = .now() - latestLast
|
||||
if diff >= timeout {
|
||||
// Reader is idle - set a new timeout and trigger an event through the pipeline
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(in: timeout, self.makeAllTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
in: timeout,
|
||||
self.makeAllTimeoutTask(context, timeout)
|
||||
)
|
||||
|
||||
context.fireUserInboundEventTriggered(IdleStateEvent.all)
|
||||
} else {
|
||||
// Read occurred before the timeout - set a new timeout with shorter delay.
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(deadline: latestLast + timeout, self.makeAllTimeoutTask(context, timeout))
|
||||
self.scheduledReaderTask = context.eventLoop.assumeIsolated().scheduleTask(
|
||||
deadline: latestLast + timeout,
|
||||
self.makeAllTimeoutTask(context, timeout)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func schedule(_ context: ChannelHandlerContext, _ amount: TimeAmount?, _ body: @escaping (ChannelHandlerContext, TimeAmount) -> (() -> Void) ) -> Scheduled<Void>? {
|
||||
private func schedule(
|
||||
_ context: ChannelHandlerContext,
|
||||
_ amount: TimeAmount?,
|
||||
_ body: @escaping (ChannelHandlerContext, TimeAmount) -> (() -> Void)
|
||||
) -> Scheduled<Void>? {
|
||||
if let timeout = amount {
|
||||
return context.eventLoop.assumeIsolated().scheduleTask(in: timeout, body(context, timeout))
|
||||
}
|
||||
|
||||
@@ -103,7 +103,11 @@ extension ChannelOutboundInvoker {
|
||||
/// - parameters:
|
||||
/// - to: the `SocketAddress` to which we should bind the `Channel`.
|
||||
/// - returns: the future which will be notified once the operation completes.
|
||||
public func bind(to address: SocketAddress, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
||||
public func bind(
|
||||
to address: SocketAddress,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<Void> {
|
||||
let promise = makePromise(file: file, line: line)
|
||||
bind(to: address, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -113,7 +117,11 @@ extension ChannelOutboundInvoker {
|
||||
/// - parameters:
|
||||
/// - to: the `SocketAddress` to which we should connect the `Channel`.
|
||||
/// - returns: the future which will be notified once the operation completes.
|
||||
public func connect(to address: SocketAddress, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
||||
public func connect(
|
||||
to address: SocketAddress,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<Void> {
|
||||
let promise = makePromise(file: file, line: line)
|
||||
connect(to: address, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -138,7 +146,8 @@ extension ChannelOutboundInvoker {
|
||||
/// - parameters:
|
||||
/// - data: the data to write
|
||||
/// - returns: the future which will be notified once the `write` operation completes.
|
||||
public func writeAndFlush(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
||||
public func writeAndFlush(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void>
|
||||
{
|
||||
let promise = makePromise(file: file, line: line)
|
||||
writeAndFlush(data, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -149,7 +158,8 @@ extension ChannelOutboundInvoker {
|
||||
/// - parameters:
|
||||
/// - mode: the `CloseMode` that is used
|
||||
/// - returns: the future which will be notified once the operation completes.
|
||||
public func close(mode: CloseMode = .all, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
||||
public func close(mode: CloseMode = .all, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void>
|
||||
{
|
||||
let promise = makePromise(file: file, line: line)
|
||||
close(mode: mode, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -160,14 +170,18 @@ extension ChannelOutboundInvoker {
|
||||
/// - parameters:
|
||||
/// - event: the event itself.
|
||||
/// - returns: the future which will be notified once the operation completes.
|
||||
public func triggerUserOutboundEvent(_ event: Any, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
||||
public func triggerUserOutboundEvent(
|
||||
_ event: Any,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<Void> {
|
||||
let promise = makePromise(file: file, line: line)
|
||||
triggerUserOutboundEvent(event, promise: promise)
|
||||
return promise.futureResult
|
||||
}
|
||||
|
||||
private func makePromise(file: StaticString = #fileID, line: UInt = #line) -> EventLoopPromise<Void> {
|
||||
return eventLoop.makePromise(file: file, line: line)
|
||||
eventLoop.makePromise(file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +242,7 @@ public protocol ChannelInboundInvoker {
|
||||
}
|
||||
|
||||
/// A protocol that signals that outbound and inbound events are triggered by this invoker.
|
||||
public protocol ChannelInvoker: ChannelOutboundInvoker, ChannelInboundInvoker { }
|
||||
public protocol ChannelInvoker: ChannelOutboundInvoker, ChannelInboundInvoker {}
|
||||
|
||||
/// Specify what kind of close operation is requested.
|
||||
public enum CloseMode: Sendable {
|
||||
|
||||
@@ -20,11 +20,11 @@ public protocol ChannelOption: Equatable, _NIOPreconcurrencySendable {
|
||||
|
||||
public typealias SocketOptionName = Int32
|
||||
#if (os(Linux) || os(Android)) && !canImport(Musl)
|
||||
public typealias SocketOptionLevel = Int
|
||||
public typealias SocketOptionValue = Int
|
||||
public typealias SocketOptionLevel = Int
|
||||
public typealias SocketOptionValue = Int
|
||||
#else
|
||||
public typealias SocketOptionLevel = CInt
|
||||
public typealias SocketOptionValue = CInt
|
||||
public typealias SocketOptionLevel = CInt
|
||||
public typealias SocketOptionValue = CInt
|
||||
#endif
|
||||
|
||||
@available(*, deprecated, renamed: "ChannelOptions.Types.SocketOption")
|
||||
@@ -77,7 +77,7 @@ extension ChannelOptions {
|
||||
|
||||
public var level: SocketOptionLevel {
|
||||
get {
|
||||
return SocketOptionLevel(optionLevel.rawValue)
|
||||
SocketOptionLevel(optionLevel.rawValue)
|
||||
}
|
||||
set {
|
||||
self.optionLevel = NIOBSDSocket.OptionLevel(rawValue: CInt(newValue))
|
||||
@@ -85,7 +85,7 @@ extension ChannelOptions {
|
||||
}
|
||||
public var name: SocketOptionName {
|
||||
get {
|
||||
return SocketOptionName(optionName.rawValue)
|
||||
SocketOptionName(optionName.rawValue)
|
||||
}
|
||||
set {
|
||||
self.optionName = NIOBSDSocket.Option(rawValue: CInt(newValue))
|
||||
@@ -93,15 +93,15 @@ extension ChannelOptions {
|
||||
}
|
||||
|
||||
#if !os(Windows)
|
||||
/// Create a new `SocketOption`.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - level: The level for the option as defined in `man setsockopt`, e.g. SO_SOCKET.
|
||||
/// - name: The name of the option as defined in `man setsockopt`, e.g. `SO_REUSEADDR`.
|
||||
public init(level: SocketOptionLevel, name: SocketOptionName) {
|
||||
self.optionLevel = NIOBSDSocket.OptionLevel(rawValue: CInt(level))
|
||||
self.optionName = NIOBSDSocket.Option(rawValue: CInt(name))
|
||||
}
|
||||
/// Create a new `SocketOption`.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - level: The level for the option as defined in `man setsockopt`, e.g. SO_SOCKET.
|
||||
/// - name: The name of the option as defined in `man setsockopt`, e.g. `SO_REUSEADDR`.
|
||||
public init(level: SocketOptionLevel, name: SocketOptionName) {
|
||||
self.optionLevel = NIOBSDSocket.OptionLevel(rawValue: CInt(level))
|
||||
self.optionName = NIOBSDSocket.Option(rawValue: CInt(name))
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Create a new `SocketOption`.
|
||||
@@ -188,7 +188,7 @@ extension ChannelOptions {
|
||||
public struct DatagramVectorReadMessageCountOption: ChannelOption, Sendable {
|
||||
public typealias Value = Int
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// ``DatagramSegmentSize`` controls the `UDP_SEGMENT` socket option (sometimes reffered to as 'GSO') which allows for
|
||||
@@ -201,7 +201,7 @@ extension ChannelOptions {
|
||||
/// Setting this option to zero disables segmentation offload.
|
||||
public struct DatagramSegmentSize: ChannelOption, Sendable {
|
||||
public typealias Value = CInt
|
||||
public init() { }
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// ``DatagramReceiveOffload`` sets the `UDP_GRO` socket option which allows for datagrams to be accumulated
|
||||
@@ -214,7 +214,7 @@ extension ChannelOptions {
|
||||
/// The default allocator for datagram channels uses fixed sized buffers of 2048 bytes.
|
||||
public struct DatagramReceiveOffload: ChannelOption, Sendable {
|
||||
public typealias Value = Bool
|
||||
public init() { }
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// When set to true IP level ECN information will be reported through `AddressedEnvelope.Metadata`
|
||||
@@ -293,23 +293,27 @@ extension ChannelOptions {
|
||||
/// Provides `ChannelOption`s to be used with a `Channel`, `Bootstrap` or `ServerBootstrap`.
|
||||
public struct ChannelOptions: Sendable {
|
||||
#if !os(Windows)
|
||||
public static let socket: @Sendable (SocketOptionLevel, SocketOptionName) -> ChannelOptions.Types.SocketOption = { (level: SocketOptionLevel, name: SocketOptionName) -> Types.SocketOption in
|
||||
.init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name)))
|
||||
}
|
||||
public static let socket: @Sendable (SocketOptionLevel, SocketOptionName) -> ChannelOptions.Types.SocketOption = {
|
||||
(level: SocketOptionLevel, name: SocketOptionName) -> Types.SocketOption in
|
||||
.init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name)))
|
||||
}
|
||||
#endif
|
||||
|
||||
/// - seealso: `SocketOption`.
|
||||
public static let socketOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
public static let socketOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = {
|
||||
(name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
.init(level: .socket, name: name)
|
||||
}
|
||||
|
||||
/// - seealso: `SocketOption`.
|
||||
public static let ipOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
public static let ipOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = {
|
||||
(name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
.init(level: .ip, name: name)
|
||||
}
|
||||
|
||||
/// - seealso: `SocketOption`.
|
||||
public static let tcpOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
public static let tcpOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = {
|
||||
(name: NIOBSDSocket.Option) -> Types.SocketOption in
|
||||
.init(level: .tcp, name: name)
|
||||
}
|
||||
|
||||
@@ -361,7 +365,11 @@ extension ChannelOptions {
|
||||
/// `Channel` that needs to store `ChannelOption`s.
|
||||
public struct Storage: Sendable {
|
||||
@usableFromInline
|
||||
internal var _storage: [(any ChannelOption, (any Sendable, @Sendable (Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void>))]
|
||||
internal var _storage:
|
||||
[(
|
||||
any ChannelOption,
|
||||
(any Sendable, @Sendable (Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void>)
|
||||
)]
|
||||
|
||||
public init() {
|
||||
self._storage = []
|
||||
@@ -377,8 +385,8 @@ extension ChannelOptions {
|
||||
public mutating func append<Option: ChannelOption>(key newKey: Option, value newValue: Option.Value) {
|
||||
@Sendable
|
||||
func applier(_ t: Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void> {
|
||||
return { (option, value) in
|
||||
return t.setOption(option as! Option, value: value as! Option.Value)
|
||||
{ (option, value) in
|
||||
t.setOption(option as! Option, value: value as! Option.Value)
|
||||
}
|
||||
}
|
||||
var hasSet = false
|
||||
@@ -407,7 +415,17 @@ extension ChannelOptions {
|
||||
let it = self._storage.makeIterator()
|
||||
|
||||
@Sendable
|
||||
func applyNext(iterator: IndexingIterator<[(any ChannelOption, (any Sendable, @Sendable (any Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void>))]>) {
|
||||
func applyNext(
|
||||
iterator: IndexingIterator<
|
||||
[(
|
||||
any ChannelOption,
|
||||
(
|
||||
any Sendable,
|
||||
@Sendable (any Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void>
|
||||
)
|
||||
)]
|
||||
>
|
||||
) {
|
||||
var iterator = iterator
|
||||
guard let (key, (value, applier)) = iterator.next() else {
|
||||
// If we reached the end, everything is applied.
|
||||
|
||||
@@ -167,9 +167,11 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// - handler: the `ChannelHandler` to add
|
||||
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
||||
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was added.
|
||||
public func addHandler(_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
||||
public func addHandler(
|
||||
_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) -> EventLoopFuture<Void> {
|
||||
let future: EventLoopFuture<Void>
|
||||
|
||||
if self.eventLoop.inEventLoop {
|
||||
@@ -192,9 +194,11 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// - name: the name to use for the `ChannelHandler` when it's added. If none is specified a name will be generated.
|
||||
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
||||
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
||||
fileprivate func addHandlerSync(_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last) -> Result<Void, Error> {
|
||||
fileprivate func addHandlerSync(
|
||||
_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) -> Result<Void, Error> {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
if self.destroyed {
|
||||
@@ -203,25 +207,33 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
|
||||
switch position {
|
||||
case .first:
|
||||
return self.add0(name: name,
|
||||
handler: handler,
|
||||
relativeContext: head!,
|
||||
operation: self.add0(context:after:))
|
||||
return self.add0(
|
||||
name: name,
|
||||
handler: handler,
|
||||
relativeContext: head!,
|
||||
operation: self.add0(context:after:)
|
||||
)
|
||||
case .last:
|
||||
return self.add0(name: name,
|
||||
handler: handler,
|
||||
relativeContext: tail!,
|
||||
operation: self.add0(context:before:))
|
||||
return self.add0(
|
||||
name: name,
|
||||
handler: handler,
|
||||
relativeContext: tail!,
|
||||
operation: self.add0(context:before:)
|
||||
)
|
||||
case .before(let beforeHandler):
|
||||
return self.add0(name: name,
|
||||
handler: handler,
|
||||
relativeHandler: beforeHandler,
|
||||
operation: self.add0(context:before:))
|
||||
return self.add0(
|
||||
name: name,
|
||||
handler: handler,
|
||||
relativeHandler: beforeHandler,
|
||||
operation: self.add0(context:before:)
|
||||
)
|
||||
case .after(let afterHandler):
|
||||
return self.add0(name: name,
|
||||
handler: handler,
|
||||
relativeHandler: afterHandler,
|
||||
operation: self.add0(context:after:))
|
||||
return self.add0(
|
||||
name: name,
|
||||
handler: handler,
|
||||
relativeHandler: afterHandler,
|
||||
operation: self.add0(context:after:)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +253,12 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// inserted relative to.
|
||||
/// - operation: A callback that will insert `handler` relative to `relativeHandler`.
|
||||
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
||||
private func add0(name: String?,
|
||||
handler: ChannelHandler,
|
||||
relativeHandler: ChannelHandler,
|
||||
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
|
||||
private func add0(
|
||||
name: String?,
|
||||
handler: ChannelHandler,
|
||||
relativeHandler: ChannelHandler,
|
||||
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void
|
||||
) -> Result<Void, Error> {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
if self.destroyed {
|
||||
return .failure(ChannelError._ioOnClosedChannel)
|
||||
@@ -273,10 +287,12 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// inserted relative to.
|
||||
/// - operation: A callback that will insert `handler` relative to `relativeHandler`.
|
||||
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
||||
private func add0(name: String?,
|
||||
handler: ChannelHandler,
|
||||
relativeContext: ChannelHandlerContext,
|
||||
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
|
||||
private func add0(
|
||||
name: String?,
|
||||
handler: ChannelHandler,
|
||||
relativeContext: ChannelHandlerContext,
|
||||
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void
|
||||
) -> Result<Void, Error> {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
if self.destroyed {
|
||||
@@ -458,7 +474,7 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// - handler: the `ChannelHandler` for which the `ChannelHandlerContext` should be returned
|
||||
/// - returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
||||
fileprivate func contextSync(handler: ChannelHandler) -> Result<ChannelHandlerContext, Error> {
|
||||
return self._contextSync({ $0.handler === handler })
|
||||
self._contextSync({ $0.handler === handler })
|
||||
}
|
||||
|
||||
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
|
||||
@@ -487,7 +503,7 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// - Parameter name: The name of the `ChannelHandler` to find.
|
||||
/// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
||||
fileprivate func contextSync(name: String) -> Result<ChannelHandlerContext, Error> {
|
||||
return self._contextSync({ $0.name == name })
|
||||
self._contextSync({ $0.name == name })
|
||||
}
|
||||
|
||||
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler` of the given type.
|
||||
@@ -527,7 +543,7 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// Returns if the ``ChannelHandler`` of the given type is contained in the pipeline.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - name: The name of the handler.
|
||||
/// - name: The name of the handler.
|
||||
/// - Returns: An ``EventLoopFuture`` that is succeeded if a handler of the given type is contained in the pipeline. Otherwise
|
||||
/// the future will be failed with an error.
|
||||
@inlinable
|
||||
@@ -541,14 +557,16 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
/// - Important: This must be called on the `EventLoop`.
|
||||
/// - Parameter handlerType: The type of handler to search for.
|
||||
/// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
||||
@inlinable // should be fileprivate
|
||||
internal func _contextSync<Handler: ChannelHandler>(handlerType: Handler.Type) -> Result<ChannelHandlerContext, Error> {
|
||||
return self._contextSync({ $0.handler is Handler })
|
||||
@inlinable // should be fileprivate
|
||||
internal func _contextSync<Handler: ChannelHandler>(
|
||||
handlerType: Handler.Type
|
||||
) -> Result<ChannelHandlerContext, Error> {
|
||||
self._contextSync({ $0.handler is Handler })
|
||||
}
|
||||
|
||||
/// Synchronously finds a `ChannelHandlerContext` in the `ChannelPipeline`.
|
||||
/// - Important: This must be called on the `EventLoop`.
|
||||
@usableFromInline // should be fileprivate
|
||||
@usableFromInline // should be fileprivate
|
||||
internal func _contextSync(_ body: (ChannelHandlerContext) -> Bool) -> Result<ChannelHandlerContext, Error> {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
@@ -815,11 +833,11 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
// These methods are expected to only be called from within the EventLoop
|
||||
|
||||
private var firstOutboundCtx: ChannelHandlerContext? {
|
||||
return self.tail?.prev
|
||||
self.tail?.prev
|
||||
}
|
||||
|
||||
private var firstInboundCtx: ChannelHandlerContext? {
|
||||
return self.head?.next
|
||||
self.head?.next
|
||||
}
|
||||
|
||||
private func close0(mode: CloseMode, promise: EventLoopPromise<Void>?) {
|
||||
@@ -946,7 +964,7 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
}
|
||||
|
||||
private var inEventLoop: Bool {
|
||||
return eventLoop.inEventLoop
|
||||
eventLoop.inEventLoop
|
||||
}
|
||||
|
||||
/// Create `ChannelPipeline` for a given `Channel`. This method should never be called by the end-user
|
||||
@@ -958,11 +976,19 @@ public final class ChannelPipeline: ChannelInvoker {
|
||||
public init(channel: Channel) {
|
||||
self._channel = channel
|
||||
self.eventLoop = channel.eventLoop
|
||||
self.head = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
||||
self.tail = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
||||
self.head = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
||||
self.tail = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
||||
|
||||
self.head = ChannelHandlerContext(name: HeadChannelHandler.name, handler: HeadChannelHandler.sharedInstance, pipeline: self)
|
||||
self.tail = ChannelHandlerContext(name: TailChannelHandler.name, handler: TailChannelHandler.sharedInstance, pipeline: self)
|
||||
self.head = ChannelHandlerContext(
|
||||
name: HeadChannelHandler.name,
|
||||
handler: HeadChannelHandler.sharedInstance,
|
||||
pipeline: self
|
||||
)
|
||||
self.tail = ChannelHandlerContext(
|
||||
name: TailChannelHandler.name,
|
||||
handler: TailChannelHandler.sharedInstance,
|
||||
pipeline: self
|
||||
)
|
||||
self.head?.next = self.tail
|
||||
self.tail?.prev = self.head
|
||||
}
|
||||
@@ -979,8 +1005,10 @@ extension ChannelPipeline {
|
||||
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
||||
///
|
||||
/// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
|
||||
public func addHandlers(_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
||||
public func addHandlers(
|
||||
_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position = .last
|
||||
) -> EventLoopFuture<Void> {
|
||||
let future: EventLoopFuture<Void>
|
||||
|
||||
if self.eventLoop.inEventLoop {
|
||||
@@ -1002,9 +1030,11 @@ extension ChannelPipeline {
|
||||
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
||||
///
|
||||
/// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
|
||||
public func addHandlers(_ handlers: ChannelHandler...,
|
||||
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
||||
return self.addHandlers(handlers, position: position)
|
||||
public func addHandlers(
|
||||
_ handlers: ChannelHandler...,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) -> EventLoopFuture<Void> {
|
||||
self.addHandlers(handlers, position: position)
|
||||
}
|
||||
|
||||
/// Synchronously adds the provided `ChannelHandler`s to the pipeline in the order given, taking
|
||||
@@ -1015,8 +1045,10 @@ extension ChannelPipeline {
|
||||
/// - handlers: The array of `ChannelHandler`s to add.
|
||||
/// - position: The position in the `ChannelPipeline` to add the handlers.
|
||||
/// - Returns: A result representing whether the handlers were added or not.
|
||||
fileprivate func addHandlersSync(_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position) -> Result<Void, Error> {
|
||||
fileprivate func addHandlersSync(
|
||||
_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position
|
||||
) -> Result<Void, Error> {
|
||||
switch position {
|
||||
case .first, .after:
|
||||
return self._addHandlersSync(handlers.reversed(), position: position)
|
||||
@@ -1032,8 +1064,10 @@ extension ChannelPipeline {
|
||||
/// - handlers: A sequence of handlers to add.
|
||||
/// - position: The position in the `ChannelPipeline` to add the handlers.
|
||||
/// - Returns: A result representing whether the handlers were added or not.
|
||||
private func _addHandlersSync<Handlers: Sequence>(_ handlers: Handlers,
|
||||
position: ChannelPipeline.Position) -> Result<Void, Error> where Handlers.Element == ChannelHandler {
|
||||
private func _addHandlersSync<Handlers: Sequence>(
|
||||
_ handlers: Handlers,
|
||||
position: ChannelPipeline.Position
|
||||
) -> Result<Void, Error> where Handlers.Element == ChannelHandler {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
for handler in handlers {
|
||||
@@ -1066,7 +1100,7 @@ extension ChannelPipeline {
|
||||
|
||||
/// The `EventLoop` of the `Channel` this synchronous operations view corresponds to.
|
||||
public var eventLoop: EventLoop {
|
||||
return self._pipeline.eventLoop
|
||||
self._pipeline.eventLoop
|
||||
}
|
||||
|
||||
/// Add a handler to the pipeline.
|
||||
@@ -1076,9 +1110,11 @@ extension ChannelPipeline {
|
||||
/// - handler: The handler to add.
|
||||
/// - name: The name to use for the `ChannelHandler` when it's added. If no name is specified the one will be generated.
|
||||
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
||||
public func addHandler(_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last) throws {
|
||||
public func addHandler(
|
||||
_ handler: ChannelHandler,
|
||||
name: String? = nil,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) throws {
|
||||
try self._pipeline.addHandlerSync(handler, name: name, position: position).get()
|
||||
}
|
||||
|
||||
@@ -1088,8 +1124,10 @@ extension ChannelPipeline {
|
||||
/// - Parameters:
|
||||
/// - handlers: The handlers to add.
|
||||
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
||||
public func addHandlers(_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position = .last) throws {
|
||||
public func addHandlers(
|
||||
_ handlers: [ChannelHandler],
|
||||
position: ChannelPipeline.Position = .last
|
||||
) throws {
|
||||
try self._pipeline.addHandlersSync(handlers, position: position).get()
|
||||
}
|
||||
|
||||
@@ -1099,8 +1137,10 @@ extension ChannelPipeline {
|
||||
/// - Parameters:
|
||||
/// - handlers: The handlers to add.
|
||||
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
||||
public func addHandlers(_ handlers: ChannelHandler...,
|
||||
position: ChannelPipeline.Position = .last) throws {
|
||||
public func addHandlers(
|
||||
_ handlers: ChannelHandler...,
|
||||
position: ChannelPipeline.Position = .last
|
||||
) throws {
|
||||
try self._pipeline.addHandlersSync(handlers, position: position).get()
|
||||
}
|
||||
|
||||
@@ -1128,7 +1168,7 @@ extension ChannelPipeline {
|
||||
/// - Parameter handler: The handler belonging to the context to fetch.
|
||||
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
||||
public func context(handler: ChannelHandler) throws -> ChannelHandlerContext {
|
||||
return try self._pipeline._contextSync({ $0.handler === handler }).get()
|
||||
try self._pipeline._contextSync({ $0.handler === handler }).get()
|
||||
}
|
||||
|
||||
/// Returns the `ChannelHandlerContext` for the handler with the given name, if one exists.
|
||||
@@ -1137,7 +1177,7 @@ extension ChannelPipeline {
|
||||
/// - Parameter name: The name of the handler whose context is being fetched.
|
||||
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
||||
public func context(name: String) throws -> ChannelHandlerContext {
|
||||
return try self._pipeline.contextSync(name: name).get()
|
||||
try self._pipeline.contextSync(name: name).get()
|
||||
}
|
||||
|
||||
/// Returns the `ChannelHandlerContext` for the handler of given type, if one exists.
|
||||
@@ -1147,7 +1187,7 @@ extension ChannelPipeline {
|
||||
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
||||
@inlinable
|
||||
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) throws -> ChannelHandlerContext {
|
||||
return try self._pipeline._contextSync(handlerType: handlerType).get()
|
||||
try self._pipeline._contextSync(handlerType: handlerType).get()
|
||||
}
|
||||
|
||||
/// Returns the `ChannelHandler` of the given type from the `ChannelPipeline`, if it exists.
|
||||
@@ -1156,7 +1196,7 @@ extension ChannelPipeline {
|
||||
/// - Returns: A `ChannelHandler` of the given type if one exists in the `ChannelPipeline`.
|
||||
@inlinable
|
||||
public func handler<Handler: ChannelHandler>(type _: Handler.Type) throws -> Handler {
|
||||
return try self._pipeline._handlerSync(type: Handler.self).get()
|
||||
try self._pipeline._handlerSync(type: Handler.self).get()
|
||||
}
|
||||
|
||||
/// Fires `channelRegistered` from the head to the tail.
|
||||
@@ -1307,7 +1347,7 @@ extension ChannelPipeline {
|
||||
/// Returns a view of operations which can be performed synchronously on this pipeline. All
|
||||
/// operations **must** be called on the event loop.
|
||||
public var syncOperations: SynchronousOperations {
|
||||
return SynchronousOperations(pipeline: self)
|
||||
SynchronousOperations(pipeline: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1335,12 +1375,12 @@ extension ChannelPipeline {
|
||||
extension ChannelPipeline.Position: Sendable {}
|
||||
|
||||
/// Special `ChannelHandler` that forwards all events to the `Channel.Unsafe` implementation.
|
||||
/* private but tests */ final class HeadChannelHandler: _ChannelOutboundHandler {
|
||||
final class HeadChannelHandler: _ChannelOutboundHandler {
|
||||
|
||||
static let name = "head"
|
||||
static let sharedInstance = HeadChannelHandler()
|
||||
|
||||
private init() { }
|
||||
private init() {}
|
||||
|
||||
func register(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
|
||||
context.channel._channelCore.register0(promise: promise)
|
||||
@@ -1376,9 +1416,9 @@ extension ChannelPipeline.Position: Sendable {}
|
||||
|
||||
}
|
||||
|
||||
private extension CloseMode {
|
||||
extension CloseMode {
|
||||
/// Returns the error to fail outstanding operations writes with.
|
||||
var error: any Error {
|
||||
fileprivate var error: any Error {
|
||||
switch self {
|
||||
case .all:
|
||||
return ChannelError._ioOnClosedChannel
|
||||
@@ -1391,12 +1431,12 @@ private extension CloseMode {
|
||||
}
|
||||
|
||||
/// Special `ChannelInboundHandler` which will consume all inbound events.
|
||||
/* private but tests */ final class TailChannelHandler: _ChannelInboundHandler {
|
||||
final class TailChannelHandler: _ChannelInboundHandler {
|
||||
|
||||
static let name = "tail"
|
||||
static let sharedInstance = TailChannelHandler()
|
||||
|
||||
private init() { }
|
||||
private init() {}
|
||||
|
||||
func channelRegistered(context: ChannelHandlerContext) {
|
||||
// Discard
|
||||
@@ -1461,11 +1501,11 @@ public final class ChannelHandlerContext: ChannelInvoker {
|
||||
public let pipeline: ChannelPipeline
|
||||
|
||||
public var channel: Channel {
|
||||
return self.pipeline.channel
|
||||
self.pipeline.channel
|
||||
}
|
||||
|
||||
public var handler: ChannelHandler {
|
||||
return self.inboundHandler ?? self.outboundHandler!
|
||||
self.inboundHandler ?? self.outboundHandler!
|
||||
}
|
||||
|
||||
public var remoteAddress: SocketAddress? {
|
||||
@@ -1495,7 +1535,7 @@ public final class ChannelHandlerContext: ChannelInvoker {
|
||||
}
|
||||
|
||||
public var eventLoop: EventLoop {
|
||||
return self.pipeline.eventLoop
|
||||
self.pipeline.eventLoop
|
||||
}
|
||||
|
||||
public let name: String
|
||||
@@ -1512,7 +1552,10 @@ public final class ChannelHandlerContext: ChannelInvoker {
|
||||
self.outboundHandler = handler as? _ChannelOutboundHandler
|
||||
self.next = nil
|
||||
self.prev = nil
|
||||
precondition(self.inboundHandler != nil || self.outboundHandler != nil, "ChannelHandlers need to either be inbound or outbound")
|
||||
precondition(
|
||||
self.inboundHandler != nil || self.outboundHandler != nil,
|
||||
"ChannelHandlers need to either be inbound or outbound"
|
||||
)
|
||||
}
|
||||
|
||||
/// Send a `channelRegistered` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
||||
@@ -1788,7 +1831,7 @@ public final class ChannelHandlerContext: ChannelInvoker {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func invokeBind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
||||
fileprivate func invokeBind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
if let outboundHandler = self.outboundHandler {
|
||||
@@ -1916,8 +1959,10 @@ extension ChannelHandlerContext {
|
||||
return
|
||||
}
|
||||
self.userTriggeredRemovalStarted = true
|
||||
(self.handler as! RemovableChannelHandler).removeHandler(context: self,
|
||||
removalToken: .init(promise: promise))
|
||||
(self.handler as! RemovableChannelHandler).removeHandler(
|
||||
context: self,
|
||||
removalToken: .init(promise: promise)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1933,15 +1978,17 @@ extension ChannelPipeline: CustomDebugStringConvertible {
|
||||
//
|
||||
var desc = ["ChannelPipeline[\(ObjectIdentifier(self))]:"]
|
||||
let debugInfos = self.collectHandlerDebugInfos()
|
||||
let maxIncomingTypeNameCount = debugInfos.filter { $0.isIncoming }
|
||||
let maxIncomingTypeNameCount =
|
||||
debugInfos.filter { $0.isIncoming }
|
||||
.map { $0.typeName.count }
|
||||
.max() ?? 0
|
||||
let maxOutgoingTypeNameCount = debugInfos.filter { $0.isOutgoing }
|
||||
let maxOutgoingTypeNameCount =
|
||||
debugInfos.filter { $0.isOutgoing }
|
||||
.map { $0.typeName.count }
|
||||
.max() ?? 0
|
||||
|
||||
func whitespace(count: Int) -> String {
|
||||
return String(repeating: " ", count: count)
|
||||
String(repeating: " ", count: count)
|
||||
}
|
||||
|
||||
if debugInfos.isEmpty {
|
||||
@@ -1979,9 +2026,11 @@ extension ChannelPipeline: CustomDebugStringConvertible {
|
||||
/// - type: the type of `ChannelHandler` to return.
|
||||
@inlinable
|
||||
public func handler<Handler: ChannelHandler>(type _: Handler.Type) -> EventLoopFuture<Handler> {
|
||||
return self.context(handlerType: Handler.self).map { context in
|
||||
self.context(handlerType: Handler.self).map { context in
|
||||
guard let typedContext = context.handler as? Handler else {
|
||||
preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
|
||||
preconditionFailure(
|
||||
"Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead."
|
||||
)
|
||||
}
|
||||
|
||||
return typedContext
|
||||
@@ -1993,11 +2042,13 @@ extension ChannelPipeline: CustomDebugStringConvertible {
|
||||
/// - Important: This must be called on the `EventLoop`.
|
||||
/// - Parameters:
|
||||
/// - type: the type of `ChannelHandler` to return.
|
||||
@inlinable // should be fileprivate
|
||||
@inlinable // should be fileprivate
|
||||
internal func _handlerSync<Handler: ChannelHandler>(type _: Handler.Type) -> Result<Handler, Error> {
|
||||
return self._contextSync(handlerType: Handler.self).map { context in
|
||||
self._contextSync(handlerType: Handler.self).map { context in
|
||||
guard let typedContext = context.handler as? Handler else {
|
||||
preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
|
||||
preconditionFailure(
|
||||
"Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead."
|
||||
)
|
||||
}
|
||||
return typedContext
|
||||
}
|
||||
@@ -2007,13 +2058,13 @@ extension ChannelPipeline: CustomDebugStringConvertible {
|
||||
let handler: ChannelHandler
|
||||
let name: String
|
||||
var isIncoming: Bool {
|
||||
return self.handler is _ChannelInboundHandler
|
||||
self.handler is _ChannelInboundHandler
|
||||
}
|
||||
var isOutgoing: Bool {
|
||||
return self.handler is _ChannelOutboundHandler
|
||||
self.handler is _ChannelOutboundHandler
|
||||
}
|
||||
var typeName: String {
|
||||
return "\(type(of: self.handler))"
|
||||
"\(type(of: self.handler))"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public struct CircularBuffer<Element>: CustomStringConvertible {
|
||||
|
||||
@inlinable
|
||||
internal var mask: Int {
|
||||
return self._buffer.count &- 1
|
||||
self._buffer.count &- 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -42,17 +42,17 @@ public struct CircularBuffer<Element>: CustomStringConvertible {
|
||||
|
||||
@inlinable
|
||||
internal func indexBeforeHeadIdx() -> Int {
|
||||
return self.indexAdvanced(index: self.headBackingIndex, by: -1)
|
||||
self.indexAdvanced(index: self.headBackingIndex, by: -1)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func indexBeforeTailIdx() -> Int {
|
||||
return self.indexAdvanced(index: self.tailBackingIndex, by: -1)
|
||||
self.indexAdvanced(index: self.tailBackingIndex, by: -1)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func indexAdvanced(index: Int, by: Int) -> Int {
|
||||
return (index &+ by) & self.mask
|
||||
(index &+ by) & self.mask
|
||||
}
|
||||
|
||||
/// An opaque `CircularBuffer` index.
|
||||
@@ -69,7 +69,7 @@ public struct CircularBuffer<Element>: CustomStringConvertible {
|
||||
|
||||
@inlinable
|
||||
internal var backingIndex: Int {
|
||||
return Int(self._backingIndex)
|
||||
Int(self._backingIndex)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -85,9 +85,8 @@ public struct CircularBuffer<Element>: CustomStringConvertible {
|
||||
|
||||
@inlinable
|
||||
public static func == (lhs: Index, rhs: Index) -> Bool {
|
||||
return lhs._backingIndex == rhs._backingIndex &&
|
||||
lhs._backingCheck == rhs._backingCheck &&
|
||||
lhs.isIndexGEQHeadIndex == rhs.isIndexGEQHeadIndex
|
||||
lhs._backingIndex == rhs._backingIndex && lhs._backingCheck == rhs._backingCheck
|
||||
&& lhs.isIndexGEQHeadIndex == rhs.isIndexGEQHeadIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -105,8 +104,8 @@ public struct CircularBuffer<Element>: CustomStringConvertible {
|
||||
|
||||
@usableFromInline
|
||||
internal func isValidIndex(for ring: CircularBuffer<Element>) -> Bool {
|
||||
return self._backingCheck == _UInt24.max || Int(self._backingCheck) == ring.count
|
||||
}
|
||||
self._backingCheck == _UInt24.max || Int(self._backingCheck) == ring.count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +127,13 @@ extension CircularBuffer: Collection, MutableCollection {
|
||||
/// - Returns: The index value immediately after `i`.
|
||||
@inlinable
|
||||
public func index(after: Index) -> Index {
|
||||
return self.index(after, offsetBy: 1)
|
||||
self.index(after, offsetBy: 1)
|
||||
}
|
||||
|
||||
/// Returns the index before `index`.
|
||||
@inlinable
|
||||
public func index(before: Index) -> Index {
|
||||
return self.index(before, offsetBy: -1)
|
||||
self.index(before, offsetBy: -1)
|
||||
}
|
||||
|
||||
/// Accesses the element at the specified index.
|
||||
@@ -152,15 +151,19 @@ extension CircularBuffer: Collection, MutableCollection {
|
||||
@inlinable
|
||||
public subscript(position: Index) -> Element {
|
||||
get {
|
||||
assert(position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
|
||||
"but actual count is \(self.count)")
|
||||
assert(
|
||||
position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), "
|
||||
+ "but actual count is \(self.count)"
|
||||
)
|
||||
return self._buffer[position.backingIndex]!
|
||||
}
|
||||
set {
|
||||
assert(position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
|
||||
"but actual count is \(self.count)")
|
||||
assert(
|
||||
position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), "
|
||||
+ "but actual count is \(self.count)"
|
||||
)
|
||||
self._buffer[position.backingIndex] = newValue
|
||||
}
|
||||
}
|
||||
@@ -170,9 +173,11 @@ extension CircularBuffer: Collection, MutableCollection {
|
||||
/// If the `CircularBuffer` is empty, `startIndex` is equal to `endIndex`.
|
||||
@inlinable
|
||||
public var startIndex: Index {
|
||||
return .init(backingIndex: self.headBackingIndex,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex)
|
||||
.init(
|
||||
backingIndex: self.headBackingIndex,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex
|
||||
)
|
||||
}
|
||||
|
||||
/// The `CircularBuffer`'s "past the end" position---that is, the position one
|
||||
@@ -186,9 +191,11 @@ extension CircularBuffer: Collection, MutableCollection {
|
||||
/// If the `CircularBuffer` is empty, `endIndex` is equal to `startIndex`.
|
||||
@inlinable
|
||||
public var endIndex: Index {
|
||||
return .init(backingIndex: self.tailBackingIndex,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex)
|
||||
.init(
|
||||
backingIndex: self.tailBackingIndex,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the distance between two indices.
|
||||
@@ -251,14 +258,14 @@ extension CircularBuffer: Collection, MutableCollection {
|
||||
|
||||
return (self[self.endIndex..<self.endIndex].makeIterator(), self.count)
|
||||
}
|
||||
|
||||
|
||||
// These are implemented as no-ops for performance reasons.
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {}
|
||||
|
||||
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ index: Index, bounds: ClosedRange<Index>) {}
|
||||
|
||||
|
||||
@inlinable
|
||||
public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {}
|
||||
}
|
||||
@@ -294,9 +301,11 @@ extension CircularBuffer: RandomAccessCollection {
|
||||
/// value of `distance`.
|
||||
@inlinable
|
||||
public func index(_ i: Index, offsetBy distance: Int) -> Index {
|
||||
return .init(backingIndex: (i.backingIndex &+ distance) & self.mask,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex)
|
||||
.init(
|
||||
backingIndex: (i.backingIndex &+ distance) & self.mask,
|
||||
backingCount: self.count,
|
||||
backingIndexOfHead: self.headBackingIndex
|
||||
)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -345,7 +354,7 @@ extension CircularBuffer {
|
||||
public mutating func append(_ value: Element) {
|
||||
self._buffer[self.tailBackingIndex] = value
|
||||
self.advanceTailIdx(by: 1)
|
||||
|
||||
|
||||
if self.headBackingIndex == self.tailBackingIndex {
|
||||
// No more room left for another append so grow the buffer now.
|
||||
self._doubleCapacity()
|
||||
@@ -424,24 +433,24 @@ extension CircularBuffer {
|
||||
self._buffer = newBacking
|
||||
assert(self.verifyInvariants())
|
||||
}
|
||||
|
||||
|
||||
/// Return element `offset` from first element.
|
||||
///
|
||||
/// *O(1)*
|
||||
@inlinable
|
||||
public subscript(offset offset: Int) -> Element {
|
||||
get {
|
||||
return self[self.index(self.startIndex, offsetBy: offset)]
|
||||
self[self.index(self.startIndex, offsetBy: offset)]
|
||||
}
|
||||
set {
|
||||
self[self.index(self.startIndex, offsetBy: offset)] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns whether the ring is empty.
|
||||
@inlinable
|
||||
public var isEmpty: Bool {
|
||||
return self.headBackingIndex == self.tailBackingIndex
|
||||
self.headBackingIndex == self.tailBackingIndex
|
||||
}
|
||||
|
||||
/// Returns the number of element in the ring.
|
||||
@@ -457,7 +466,7 @@ extension CircularBuffer {
|
||||
/// The total number of elements that the ring can contain without allocating new storage.
|
||||
@inlinable
|
||||
public var capacity: Int {
|
||||
return self._buffer.count
|
||||
self._buffer.count
|
||||
}
|
||||
|
||||
/// Removes all members from the circular buffer whist keeping the capacity.
|
||||
@@ -474,7 +483,6 @@ extension CircularBuffer {
|
||||
assert(self.verifyInvariants())
|
||||
}
|
||||
|
||||
|
||||
/// Modify the element at `index`.
|
||||
///
|
||||
/// This function exists to provide a method of modifying the element in its underlying backing storage, instead
|
||||
@@ -491,10 +499,13 @@ extension CircularBuffer {
|
||||
/// - index: The index of the object that should be modified. If this index is invalid this function will trap.
|
||||
/// - modifyFunc: The function to apply to the modified object.
|
||||
@inlinable
|
||||
public mutating func modify<Result>(_ index: Index, _ modifyFunc: (inout Element) throws -> Result) rethrows -> Result {
|
||||
return try modifyFunc(&self._buffer[index.backingIndex]!)
|
||||
public mutating func modify<Result>(
|
||||
_ index: Index,
|
||||
_ modifyFunc: (inout Element) throws -> Result
|
||||
) rethrows -> Result {
|
||||
try modifyFunc(&self._buffer[index.backingIndex]!)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomStringConvertible implementation
|
||||
/// Returns a human readable description of the ring.
|
||||
public var description: String {
|
||||
@@ -572,14 +583,13 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
public mutating func removeLast(_ k: Int) {
|
||||
precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.")
|
||||
var idx = self.tailBackingIndex
|
||||
for _ in 0 ..< k {
|
||||
for _ in 0..<k {
|
||||
idx = self.indexAdvanced(index: idx, by: -1)
|
||||
self._buffer[idx] = nil
|
||||
}
|
||||
self.tailBackingIndex = idx
|
||||
}
|
||||
|
||||
|
||||
/// Removes the specified number of elements from the beginning of the
|
||||
/// `CircularBuffer`.
|
||||
///
|
||||
@@ -595,7 +605,7 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
public mutating func removeFirst(_ k: Int) {
|
||||
precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.")
|
||||
var idx = self.headBackingIndex
|
||||
for _ in 0 ..< k {
|
||||
for _ in 0..<k {
|
||||
self._buffer[idx] = nil
|
||||
idx = self.indexAdvanced(index: idx, by: 1)
|
||||
}
|
||||
@@ -620,7 +630,7 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
}
|
||||
return self.first!
|
||||
}
|
||||
|
||||
|
||||
/// Removes and returns the last element of the `CircularBuffer`.
|
||||
///
|
||||
/// The `CircularBuffer` must not be empty.
|
||||
@@ -652,15 +662,22 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
///
|
||||
/// *O(m)* where _m_ is the combined length of the collection and _newElements_
|
||||
@inlinable
|
||||
public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) where Element == C.Element {
|
||||
precondition(subrange.lowerBound >= self.startIndex && subrange.upperBound <= self.endIndex,
|
||||
"Subrange out of bounds")
|
||||
assert(subrange.lowerBound.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(subrange.lowerBound._backingCheck), " +
|
||||
"but actual count is \(self.count)")
|
||||
assert(subrange.upperBound.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(subrange.upperBound._backingCheck), " +
|
||||
"but actual count is \(self.count)")
|
||||
public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C)
|
||||
where Element == C.Element {
|
||||
precondition(
|
||||
subrange.lowerBound >= self.startIndex && subrange.upperBound <= self.endIndex,
|
||||
"Subrange out of bounds"
|
||||
)
|
||||
assert(
|
||||
subrange.lowerBound.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(subrange.lowerBound._backingCheck), "
|
||||
+ "but actual count is \(self.count)"
|
||||
)
|
||||
assert(
|
||||
subrange.upperBound.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(subrange.upperBound._backingCheck), "
|
||||
+ "but actual count is \(self.count)"
|
||||
)
|
||||
|
||||
let subrangeCount = self.distance(from: subrange.lowerBound, to: subrange.upperBound)
|
||||
|
||||
@@ -674,14 +691,14 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
self.removeSubrange(subrange)
|
||||
} else {
|
||||
var newBuffer: ContiguousArray<Element?> = []
|
||||
let neededNewCapacity = self.count + newElements.count - subrangeCount + 1 /* always one spare */
|
||||
let neededNewCapacity = self.count + newElements.count - subrangeCount + 1 // always one spare
|
||||
let newCapacity = Swift.max(self.capacity, neededNewCapacity.nextPowerOf2())
|
||||
newBuffer.reserveCapacity(newCapacity)
|
||||
|
||||
// This mapping is required due to an inconsistent ability to append sequences of non-optional
|
||||
// to optional sequences.
|
||||
// https://bugs.swift.org/browse/SR-7921
|
||||
newBuffer.append(contentsOf: self[self.startIndex ..< subrange.lowerBound].lazy.map { $0 })
|
||||
newBuffer.append(contentsOf: self[self.startIndex..<subrange.lowerBound].lazy.map { $0 })
|
||||
newBuffer.append(contentsOf: newElements.lazy.map { $0 })
|
||||
newBuffer.append(contentsOf: self[subrange.upperBound..<self.endIndex].lazy.map { $0 })
|
||||
|
||||
@@ -725,9 +742,11 @@ extension CircularBuffer: RangeReplaceableCollection {
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func remove(at position: Index) -> Element {
|
||||
assert(position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
|
||||
"but actual count is \(self.count)")
|
||||
assert(
|
||||
position.isValidIndex(for: self),
|
||||
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), "
|
||||
+ "but actual count is \(self.count)"
|
||||
)
|
||||
defer {
|
||||
assert(self.verifyInvariants())
|
||||
}
|
||||
@@ -807,13 +826,13 @@ extension CircularBuffer {
|
||||
}
|
||||
|
||||
internal func testOnly_verifyInvariantsForNonSlices() -> Bool {
|
||||
return self.verifyInvariants() && self.unreachableAreNil()
|
||||
self.verifyInvariants() && self.unreachableAreNil()
|
||||
}
|
||||
}
|
||||
|
||||
extension CircularBuffer: Equatable where Element: Equatable {
|
||||
public static func ==(lhs: CircularBuffer, rhs: CircularBuffer) -> Bool {
|
||||
return lhs.count == rhs.count && zip(lhs, rhs).allSatisfy(==)
|
||||
public static func == (lhs: CircularBuffer, rhs: CircularBuffer) -> Bool {
|
||||
lhs.count == rhs.count && zip(lhs, rhs).allSatisfy(==)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-36
@@ -12,7 +12,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/// State of the current decoding process.
|
||||
public enum DecodingState: Sendable {
|
||||
/// Continue decoding.
|
||||
@@ -43,7 +42,6 @@ extension ByteToMessageDecoderError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// `ByteToMessageDecoder`s decode bytes in a stream-like fashion from `ByteBuffer` to another message type.
|
||||
///
|
||||
/// ### Purpose
|
||||
@@ -63,7 +61,7 @@ extension ByteToMessageDecoderError {
|
||||
/// ### Implementing ByteToMessageDecoder
|
||||
///
|
||||
/// A type that implements `ByteToMessageDecoder` may implement two methods: decode and decodeLast. Implementations
|
||||
/// must implement decode: if they do not implement decodeLast, a default implementation will be used that
|
||||
/// must implement decode: if they do not implement decodeLast, a default implementation will be used that
|
||||
/// simply calls decode.
|
||||
///
|
||||
/// `decode` is the main decoding method, and is the one that will be called most often. `decode` is invoked
|
||||
@@ -176,7 +174,11 @@ public protocol ByteToMessageDecoder {
|
||||
/// - seenEOF: `true` if EOF has been seen. Usually if this is `false` the handler has been removed.
|
||||
/// - returns: `DecodingState.continue` if we should continue calling this method or `DecodingState.needMoreData` if it should be called
|
||||
/// again when more data is present in the `ByteBuffer`.
|
||||
mutating func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState
|
||||
mutating func decodeLast(
|
||||
context: ChannelHandlerContext,
|
||||
buffer: inout ByteBuffer,
|
||||
seenEOF: Bool
|
||||
) throws -> DecodingState
|
||||
|
||||
/// Called once this `ByteToMessageDecoder` is removed from the `ChannelPipeline`.
|
||||
///
|
||||
@@ -237,15 +239,19 @@ extension ByteToMessageDecoder {
|
||||
|
||||
@inlinable
|
||||
public func wrapInboundOut(_ value: InboundOut) -> NIOAny {
|
||||
return NIOAny(value)
|
||||
NIOAny(value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func wrapInboundOut(_ value: InboundOut) -> NIOAny {
|
||||
return NIOAny(value)
|
||||
NIOAny(value)
|
||||
}
|
||||
|
||||
public mutating func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
|
||||
public mutating func decodeLast(
|
||||
context: ChannelHandlerContext,
|
||||
buffer: inout ByteBuffer,
|
||||
seenEOF: Bool
|
||||
) throws -> DecodingState {
|
||||
while try self.decode(context: context, buffer: &buffer) == .continue {}
|
||||
return .needMoreData
|
||||
}
|
||||
@@ -297,7 +303,7 @@ extension B2MDBuffer {
|
||||
case .ready where self.buffers.count > 0:
|
||||
var buffer = self.buffers.removeFirst()
|
||||
buffer.writeBuffers(self.buffers)
|
||||
self.buffers.removeAll(keepingCapacity: self.buffers.capacity < 16) // don't grow too much
|
||||
self.buffers.removeAll(keepingCapacity: self.buffers.capacity < 16) // don't grow too much
|
||||
if buffer.readableBytes > 0 || allowEmptyBuffer {
|
||||
self.state = .processingInProgress
|
||||
return .available(buffer)
|
||||
@@ -314,7 +320,7 @@ extension B2MDBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func finishProcessing(remainder buffer: inout ByteBuffer) -> Void {
|
||||
mutating func finishProcessing(remainder buffer: inout ByteBuffer) {
|
||||
assert(self.state == .processingInProgress)
|
||||
self.state = .ready
|
||||
if buffer.readableBytes == 0 && self.buffers.isEmpty {
|
||||
@@ -326,7 +332,8 @@ extension B2MDBuffer {
|
||||
} else {
|
||||
buffer.discardReadBytes()
|
||||
buffer.writeBuffers(self.buffers)
|
||||
self.buffers.removeAll(keepingCapacity: self.buffers.capacity < 16) // don't grow too much
|
||||
// don't grow too much
|
||||
self.buffers.removeAll(keepingCapacity: self.buffers.capacity < 16)
|
||||
self.buffers.append(buffer)
|
||||
}
|
||||
}
|
||||
@@ -339,8 +346,8 @@ extension B2MDBuffer {
|
||||
}
|
||||
|
||||
// MARK: B2MDBuffer Helpers
|
||||
private extension ByteBuffer {
|
||||
mutating func writeBuffers(_ buffers: CircularBuffer<ByteBuffer>) {
|
||||
extension ByteBuffer {
|
||||
fileprivate mutating func writeBuffers(_ buffers: CircularBuffer<ByteBuffer>) {
|
||||
guard buffers.count > 0 else {
|
||||
return
|
||||
}
|
||||
@@ -355,8 +362,8 @@ private extension ByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
private extension B2MDBuffer {
|
||||
func _testOnlyOneBuffer() -> ByteBuffer? {
|
||||
extension B2MDBuffer {
|
||||
fileprivate func _testOnlyOneBuffer() -> ByteBuffer? {
|
||||
switch self.buffers.count {
|
||||
case 0:
|
||||
return nil
|
||||
@@ -452,12 +459,15 @@ public final class ByteToMessageHandler<Decoder: ByteToMessageDecoder> {
|
||||
}
|
||||
}
|
||||
|
||||
internal private(set) var decoder: Decoder? // only `nil` if we're already decoding (ie. we're re-entered)
|
||||
// only `nil` if we're already decoding (ie. we're re-entered)
|
||||
internal private(set) var decoder: Decoder?
|
||||
private let maximumBufferSize: Int?
|
||||
private var queuedWrites = CircularBuffer<NIOAny>(initialCapacity: 1) // queues writes received whilst we're already decoding (re-entrant write)
|
||||
// queues writes received whilst we're already decoding (re-entrant write)
|
||||
private var queuedWrites = CircularBuffer<NIOAny>(initialCapacity: 1)
|
||||
private var state: State = .active {
|
||||
willSet {
|
||||
assert(!self.state.isFinalState, "illegal state on state set: \(self.state)") // we can never leave final states
|
||||
// we can never leave final states
|
||||
assert(!self.state.isFinalState, "illegal state on state set: \(self.state)")
|
||||
}
|
||||
}
|
||||
private var removalState: RemovalState = .notAddedToPipeline
|
||||
@@ -484,8 +494,10 @@ public final class ByteToMessageHandler<Decoder: ByteToMessageDecoder> {
|
||||
deinit {
|
||||
if self.removalState != .notAddedToPipeline {
|
||||
// we have been added to the pipeline, if not, we don't need to check our state.
|
||||
assert(self.removalState == .handlerRemovedCalled,
|
||||
"illegal state in deinit: removalState = \(self.removalState)")
|
||||
assert(
|
||||
self.removalState == .handlerRemovedCalled,
|
||||
"illegal state in deinit: removalState = \(self.removalState)"
|
||||
)
|
||||
assert(self.state.isFinalState, "illegal state in deinit: state = \(self.state)")
|
||||
}
|
||||
}
|
||||
@@ -497,7 +509,7 @@ extension ByteToMessageHandler: Sendable {}
|
||||
// MARK: ByteToMessageHandler: Test Helpers
|
||||
extension ByteToMessageHandler {
|
||||
internal var cumulationBuffer: ByteBuffer? {
|
||||
return self.buffer._testOnlyOneBuffer()
|
||||
self.buffer._testOnlyOneBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,11 +526,13 @@ extension ByteToMessageHandler: CanDequeueWrites where Decoder: WriteObservingBy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: ByteToMessageHandler's Main API
|
||||
extension ByteToMessageHandler {
|
||||
@inline(__always) // allocations otherwise (reconsider with Swift 5.1)
|
||||
private func withNextBuffer(allowEmptyBuffer: Bool, _ body: (inout Decoder, inout ByteBuffer) throws -> DecodingState) rethrows -> B2MDBuffer.BufferProcessingResult {
|
||||
@inline(__always) // allocations otherwise (reconsider with Swift 5.1)
|
||||
private func withNextBuffer(
|
||||
allowEmptyBuffer: Bool,
|
||||
_ body: (inout Decoder, inout ByteBuffer) throws -> DecodingState
|
||||
) rethrows -> B2MDBuffer.BufferProcessingResult {
|
||||
switch self.buffer.startProcessing(allowEmptyBuffer: allowEmptyBuffer) {
|
||||
case .bufferAlreadyBeingProcessed:
|
||||
return .cannotProcessReentrantly
|
||||
@@ -528,7 +542,8 @@ extension ByteToMessageHandler {
|
||||
var possiblyReclaimBytes = false
|
||||
var decoder: Decoder? = nil
|
||||
swap(&decoder, &self.decoder)
|
||||
assert(decoder != nil) // self.decoder only `nil` if we're being re-entered, but .available means we're not
|
||||
// self.decoder only `nil` if we're being re-entered, but .available means we're not
|
||||
assert(decoder != nil)
|
||||
defer {
|
||||
swap(&decoder, &self.decoder)
|
||||
if buffer.readableBytes > 0 && possiblyReclaimBytes {
|
||||
@@ -575,7 +590,10 @@ extension ByteToMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private func decodeLoop(context: ChannelHandlerContext, decodeMode: DecodeMode) throws -> B2MDBuffer.BufferProcessingResult {
|
||||
private func decodeLoop(
|
||||
context: ChannelHandlerContext,
|
||||
decodeMode: DecodeMode
|
||||
) throws -> B2MDBuffer.BufferProcessingResult {
|
||||
assert(!self.state.isError)
|
||||
var allowEmptyBuffer = decodeMode == .last
|
||||
while (self.state.isActive && self.removalState == .notBeingRemoved) || decodeMode == .last {
|
||||
@@ -588,7 +606,9 @@ extension ByteToMessageHandler {
|
||||
allowEmptyBuffer = false
|
||||
decoderResult = try decoder.decodeLast(context: context, buffer: &buffer, seenEOF: self.seenEOF)
|
||||
}
|
||||
if decoderResult == .needMoreData, let maximumBufferSize = self.maximumBufferSize, buffer.readableBytes > maximumBufferSize {
|
||||
if decoderResult == .needMoreData, let maximumBufferSize = self.maximumBufferSize,
|
||||
buffer.readableBytes > maximumBufferSize
|
||||
{
|
||||
throw ByteToMessageDecoderError.PayloadTooLargeError()
|
||||
}
|
||||
return decoderResult
|
||||
@@ -600,7 +620,7 @@ extension ByteToMessageHandler {
|
||||
case .didProcess(.needMoreData):
|
||||
if self.queuedWrites.count > 0 {
|
||||
self.tryDecodeWrites()
|
||||
continue // we might have received more, so let's spin once more
|
||||
continue // we might have received more, so let's spin once more
|
||||
} else {
|
||||
return .didProcess(.needMoreData)
|
||||
}
|
||||
@@ -612,7 +632,6 @@ extension ByteToMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: ByteToMessageHandler: ChannelInboundHandler
|
||||
extension ByteToMessageHandler: ChannelInboundHandler {
|
||||
|
||||
@@ -623,11 +642,10 @@ extension ByteToMessageHandler: ChannelInboundHandler {
|
||||
self.removalState = .notBeingRemoved
|
||||
self.buffer = B2MDBuffer(emptyByteBuffer: context.channel.allocator.buffer(capacity: 0))
|
||||
// here we can force it because we know that the decoder isn't in use if we're just adding this handler
|
||||
self.selfAsCanDequeueWrites = self as? CanDequeueWrites // we need to cache this as it allocates.
|
||||
self.selfAsCanDequeueWrites = self as? CanDequeueWrites // we need to cache this as it allocates.
|
||||
self.decoder!.decoderAdded(context: context)
|
||||
}
|
||||
|
||||
|
||||
public func handlerRemoved(context: ChannelHandlerContext) {
|
||||
// very likely, the removal state is `.notBeingRemoved` or `.removalCompleted` here but we can't assert it
|
||||
// because the pipeline might be torn down during the formal removal process.
|
||||
@@ -656,14 +674,14 @@ extension ByteToMessageHandler: ChannelInboundHandler {
|
||||
case .didProcess:
|
||||
switch self.state {
|
||||
case .active:
|
||||
() // cool, all normal
|
||||
() // cool, all normal
|
||||
case .done, .error:
|
||||
() // fair, all done already
|
||||
() // fair, all done already
|
||||
case .leftoversNeedProcessing:
|
||||
// seems like we received a `channelInactive` or `handlerRemoved` whilst we were processing a read
|
||||
switch try self.decodeLoop(context: context, decodeMode: .last) {
|
||||
case .didProcess:
|
||||
() // expected and cool
|
||||
() // expected and cool
|
||||
case .cannotProcessReentrantly:
|
||||
preconditionFailure("bug in NIO: non-reentrant decode loop couldn't run \(self), \(self.state)")
|
||||
}
|
||||
@@ -698,7 +716,8 @@ extension ByteToMessageHandler: ChannelInboundHandler {
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteToMessageHandler: ChannelOutboundHandler, _ChannelOutboundHandler where Decoder: WriteObservingByteToMessageDecoder {
|
||||
extension ByteToMessageHandler: ChannelOutboundHandler, _ChannelOutboundHandler
|
||||
where Decoder: WriteObservingByteToMessageDecoder {
|
||||
public typealias OutboundIn = Decoder.OutboundIn
|
||||
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
||||
if self.decoder != nil {
|
||||
@@ -785,8 +804,10 @@ extension MessageToByteHandler: Sendable {}
|
||||
|
||||
extension MessageToByteHandler {
|
||||
public func handlerAdded(context: ChannelHandlerContext) {
|
||||
precondition(self.state.readyToBeAddedToChannel,
|
||||
"illegal state when adding to Channel: \(self.state)")
|
||||
precondition(
|
||||
self.state.readyToBeAddedToChannel,
|
||||
"illegal state when adding to Channel: \(self.state)"
|
||||
)
|
||||
self.state = .operational
|
||||
self.buffer = context.channel.allocator.buffer(capacity: 256)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ extension NIOClientTCPBootstrapProtocol {
|
||||
/// - returns: The updated bootstrap with and options applied.
|
||||
public func _applyChannelConvenienceOptions(_ options: inout ChannelOptions.TCPConvenienceOptions) -> Self {
|
||||
// Default is to consume no options and not update self.
|
||||
return self
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,10 @@ extension NIOClientTCPBootstrap {
|
||||
var optionsRemaining = options
|
||||
// First give the underlying a chance to consume options.
|
||||
let withUnderlyingOverrides =
|
||||
NIOClientTCPBootstrap(self,
|
||||
updating: underlyingBootstrap._applyChannelConvenienceOptions(&optionsRemaining))
|
||||
NIOClientTCPBootstrap(
|
||||
self,
|
||||
updating: underlyingBootstrap._applyChannelConvenienceOptions(&optionsRemaining)
|
||||
)
|
||||
// Default apply any remaining options.
|
||||
return optionsRemaining.applyFallbackMapping(withUnderlyingOverrides)
|
||||
}
|
||||
@@ -84,11 +86,11 @@ extension ChannelOptions {
|
||||
/// A TCP channel option which can be applied to a bootstrap using convenience notation.
|
||||
public struct TCPConvenienceOption: Hashable, Sendable {
|
||||
fileprivate var data: ConvenienceOption
|
||||
|
||||
|
||||
private init(_ data: ConvenienceOption) {
|
||||
self.data = data
|
||||
}
|
||||
|
||||
|
||||
fileprivate enum ConvenienceOption: Hashable {
|
||||
case allowLocalEndpointReuse
|
||||
case disableAutoRead
|
||||
@@ -101,17 +103,17 @@ extension ChannelOptions {
|
||||
extension ChannelOptions.TCPConvenienceOption {
|
||||
/// Allow immediately reusing a local address.
|
||||
public static let allowLocalEndpointReuse = ChannelOptions.TCPConvenienceOption(.allowLocalEndpointReuse)
|
||||
|
||||
|
||||
/// The user will manually call `Channel.read` once all the data is read from the transport.
|
||||
public static let disableAutoRead = ChannelOptions.TCPConvenienceOption(.disableAutoRead)
|
||||
|
||||
|
||||
/// Allows users to configure whether the `Channel` will close itself when its remote
|
||||
/// peer shuts down its send stream, or whether it will remain open. If set to `false` (the default), the `Channel`
|
||||
/// will be closed automatically if the remote peer shuts down its send stream. If set to true, the `Channel` will
|
||||
/// not be closed: instead, a `ChannelEvent.inboundClosed` user event will be sent on the `ChannelPipeline`,
|
||||
/// and no more data will be received.
|
||||
public static let allowRemoteHalfClosure =
|
||||
ChannelOptions.TCPConvenienceOption(.allowRemoteHalfClosure)
|
||||
ChannelOptions.TCPConvenienceOption(.allowRemoteHalfClosure)
|
||||
}
|
||||
|
||||
extension ChannelOptions {
|
||||
@@ -120,7 +122,7 @@ extension ChannelOptions {
|
||||
var allowLocalEndpointReuse = false
|
||||
var disableAutoRead = false
|
||||
var allowRemoteHalfClosure = false
|
||||
|
||||
|
||||
/// Construct from an array literal.
|
||||
@inlinable
|
||||
public init(arrayLiteral elements: TCPConvenienceOption...) {
|
||||
@@ -128,7 +130,7 @@ extension ChannelOptions {
|
||||
self.add(element)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@usableFromInline
|
||||
mutating func add(_ element: TCPConvenienceOption) {
|
||||
switch element.data {
|
||||
@@ -140,7 +142,7 @@ extension ChannelOptions {
|
||||
self.disableAutoRead = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Caller is consuming the knowledge that `allowLocalEndpointReuse` was set or not.
|
||||
/// The setting will nolonger be set after this call.
|
||||
/// - Returns: If `allowLocalEndpointReuse` was set.
|
||||
@@ -150,7 +152,7 @@ extension ChannelOptions {
|
||||
}
|
||||
return Types.ConvenienceOptionValue<Void>(flag: self.allowLocalEndpointReuse)
|
||||
}
|
||||
|
||||
|
||||
/// Caller is consuming the knowledge that disableAutoRead was set or not.
|
||||
/// The setting will nolonger be set after this call.
|
||||
/// - Returns: If disableAutoRead was set.
|
||||
@@ -160,7 +162,7 @@ extension ChannelOptions {
|
||||
}
|
||||
return Types.ConvenienceOptionValue<Void>(flag: self.disableAutoRead)
|
||||
}
|
||||
|
||||
|
||||
/// Caller is consuming the knowledge that allowRemoteHalfClosure was set or not.
|
||||
/// The setting will nolonger be set after this call.
|
||||
/// - Returns: If allowRemoteHalfClosure was set.
|
||||
@@ -170,7 +172,7 @@ extension ChannelOptions {
|
||||
}
|
||||
return Types.ConvenienceOptionValue<Void>(flag: self.allowRemoteHalfClosure)
|
||||
}
|
||||
|
||||
|
||||
mutating func applyFallbackMapping(_ universalBootstrap: NIOClientTCPBootstrap) -> NIOClientTCPBootstrap {
|
||||
var result = universalBootstrap
|
||||
if self.consumeAllowLocalEndpointReuse().isSet {
|
||||
|
||||
@@ -80,7 +80,7 @@ internal final class DeadChannel: Channel, @unchecked Sendable {
|
||||
let pipeline: ChannelPipeline
|
||||
|
||||
public var closeFuture: EventLoopFuture<Void> {
|
||||
return self.eventLoop.makeSucceededFuture(())
|
||||
self.eventLoop.makeSucceededFuture(())
|
||||
}
|
||||
|
||||
internal init(pipeline: ChannelPipeline) {
|
||||
@@ -90,25 +90,25 @@ internal final class DeadChannel: Channel, @unchecked Sendable {
|
||||
|
||||
// This is `Channel` API so must be thread-safe.
|
||||
var allocator: ByteBufferAllocator {
|
||||
return ByteBufferAllocator()
|
||||
ByteBufferAllocator()
|
||||
}
|
||||
|
||||
var localAddress: SocketAddress? {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
|
||||
var remoteAddress: SocketAddress? {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
|
||||
let parent: Channel? = nil
|
||||
|
||||
func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> EventLoopFuture<Void> {
|
||||
return self.pipeline.eventLoop.makeFailedFuture(ChannelError._ioOnClosedChannel)
|
||||
self.pipeline.eventLoop.makeFailedFuture(ChannelError._ioOnClosedChannel)
|
||||
}
|
||||
|
||||
func getOption<Option: ChannelOption>(_ option: Option) -> EventLoopFuture<Option.Value> {
|
||||
return eventLoop.makeFailedFuture(ChannelError._ioOnClosedChannel)
|
||||
eventLoop.makeFailedFuture(ChannelError._ioOnClosedChannel)
|
||||
}
|
||||
|
||||
let isWritable = false
|
||||
|
||||
@@ -15,13 +15,21 @@
|
||||
extension EventLoop {
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func makeFailedFuture<T>(_ error: Error, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<T> {
|
||||
return self.makeFailedFuture(error)
|
||||
public func makeFailedFuture<T>(
|
||||
_ error: Error,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<T> {
|
||||
self.makeFailedFuture(error)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func makeSucceededFuture<Success>(_ value: Success, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Success> {
|
||||
return self.makeSucceededFuture(value)
|
||||
public func makeSucceededFuture<Success>(
|
||||
_ value: Success,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<Success> {
|
||||
self.makeSucceededFuture(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
/// Implementers of `EventLoop` should consider conforming to this protocol as
|
||||
/// well on Swift 5.9 and later.
|
||||
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
|
||||
public protocol NIOSerialEventLoopExecutor: EventLoop, SerialExecutor { }
|
||||
public protocol NIOSerialEventLoopExecutor: EventLoop, SerialExecutor {}
|
||||
|
||||
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
|
||||
extension NIOSerialEventLoopExecutor {
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import NIOConcurrencyHelpers
|
||||
import Dispatch
|
||||
import NIOConcurrencyHelpers
|
||||
|
||||
#if os(Linux)
|
||||
import CNIOLinux
|
||||
#endif // os(Linux)
|
||||
#endif // os(Linux)
|
||||
|
||||
/// Returned once a task was scheduled on the `EventLoop` for later execution.
|
||||
///
|
||||
@@ -24,8 +25,8 @@ import CNIOLinux
|
||||
/// will be notified once the execution is complete.
|
||||
public struct Scheduled<T> {
|
||||
@usableFromInline typealias CancelationCallback = @Sendable () -> Void
|
||||
/* private but usableFromInline */ @usableFromInline let _promise: EventLoopPromise<T>
|
||||
/* private but usableFromInline */ @usableFromInline let _cancellationTask: CancelationCallback
|
||||
@usableFromInline let _promise: EventLoopPromise<T>
|
||||
@usableFromInline let _cancellationTask: CancelationCallback
|
||||
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
@@ -47,7 +48,7 @@ public struct Scheduled<T> {
|
||||
/// Returns the `EventLoopFuture` which will be notified once the execution of the scheduled task completes.
|
||||
@inlinable
|
||||
public var futureResult: EventLoopFuture<T> {
|
||||
return self._promise.futureResult
|
||||
self._promise.futureResult
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +203,7 @@ public struct EventLoopIterator: Sequence, IteratorProtocol {
|
||||
///
|
||||
/// - returns: The next `EventLoop` if a next element exists; otherwise, `nil`.
|
||||
public mutating func next() -> EventLoop? {
|
||||
return self.eventLoops.next()
|
||||
self.eventLoops.next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ public protocol EventLoop: EventLoopGroup {
|
||||
///
|
||||
/// If it is necessary for correctness to confirm that you're on an event loop, prefer ``preconditionInEventLoop(file:line:)-7ukrq``.
|
||||
var inEventLoop: Bool { get }
|
||||
|
||||
|
||||
/// Submit a given task to be executed by the `EventLoop`
|
||||
@preconcurrency
|
||||
func execute(_ task: @escaping @Sendable () -> Void)
|
||||
@@ -361,7 +362,7 @@ public protocol EventLoop: EventLoopGroup {
|
||||
extension EventLoop {
|
||||
/// Default implementation of `makeSucceededVoidFuture`: Return a fresh future (which will allocate).
|
||||
public func makeSucceededVoidFuture() -> EventLoopFuture<Void> {
|
||||
return EventLoopFuture(eventLoop: self, value: ())
|
||||
EventLoopFuture(eventLoop: self, value: ())
|
||||
}
|
||||
|
||||
public func _preconditionSafeToWait(file: StaticString, line: UInt) {
|
||||
@@ -374,8 +375,9 @@ extension EventLoop {
|
||||
}
|
||||
|
||||
/// Default implementation of `_promiseCompleted`: does nothing.
|
||||
public func _promiseCompleted(futureIdentifier: _NIOEventLoopFutureIdentifier) -> (file: StaticString, line: UInt)? {
|
||||
return nil
|
||||
public func _promiseCompleted(futureIdentifier: _NIOEventLoopFutureIdentifier) -> (file: StaticString, line: UInt)?
|
||||
{
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +404,7 @@ extension EventLoop {
|
||||
|
||||
extension EventLoopGroup {
|
||||
public var description: String {
|
||||
return String(describing: self)
|
||||
String(describing: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +418,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// The nanoseconds representation of the `TimeAmount`.
|
||||
public let nanoseconds: Int64
|
||||
|
||||
/* private but */ @inlinable
|
||||
@inlinable
|
||||
init(_ nanoseconds: Int64) {
|
||||
self.nanoseconds = nanoseconds
|
||||
}
|
||||
@@ -428,7 +430,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - returns: the `TimeAmount` for the given amount.
|
||||
@inlinable
|
||||
public static func nanoseconds(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(amount)
|
||||
TimeAmount(amount)
|
||||
}
|
||||
|
||||
/// Creates a new `TimeAmount` for the given amount of microseconds.
|
||||
@@ -440,7 +442,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - note: returns `TimeAmount(.max)` if the amount overflows when converted to nanoseconds and `TimeAmount(.min)` if it underflows.
|
||||
@inlinable
|
||||
public static func microseconds(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000))
|
||||
TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000))
|
||||
}
|
||||
|
||||
/// Creates a new `TimeAmount` for the given amount of milliseconds.
|
||||
@@ -452,7 +454,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - note: returns `TimeAmount(.max)` if the amount overflows when converted to nanoseconds and `TimeAmount(.min)` if it underflows.
|
||||
@inlinable
|
||||
public static func milliseconds(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000))
|
||||
TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000))
|
||||
}
|
||||
|
||||
/// Creates a new `TimeAmount` for the given amount of seconds.
|
||||
@@ -464,7 +466,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - note: returns `TimeAmount(.max)` if the amount overflows when converted to nanoseconds and `TimeAmount(.min)` if it underflows.
|
||||
@inlinable
|
||||
public static func seconds(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000))
|
||||
TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000))
|
||||
}
|
||||
|
||||
/// Creates a new `TimeAmount` for the given amount of minutes.
|
||||
@@ -476,7 +478,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - note: returns `TimeAmount(.max)` if the amount overflows when converted to nanoseconds and `TimeAmount(.min)` if it underflows.
|
||||
@inlinable
|
||||
public static func minutes(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000 * 60))
|
||||
TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000 * 60))
|
||||
}
|
||||
|
||||
/// Creates a new `TimeAmount` for the given amount of hours.
|
||||
@@ -488,9 +490,9 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
/// - note: returns `TimeAmount(.max)` if the amount overflows when converted to nanoseconds and `TimeAmount(.min)` if it underflows.
|
||||
@inlinable
|
||||
public static func hours(_ amount: Int64) -> TimeAmount {
|
||||
return TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000 * 60 * 60))
|
||||
TimeAmount(_cappedNanoseconds(amount: amount, multiplier: 1000 * 1000 * 1000 * 60 * 60))
|
||||
}
|
||||
|
||||
|
||||
/// Converts `amount` to nanoseconds multiplying it by `multiplier`. The return value is capped to `Int64.max` if the multiplication overflows and `Int64.min` if it underflows.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -511,7 +513,7 @@ public struct TimeAmount: Hashable, Sendable {
|
||||
extension TimeAmount: Comparable {
|
||||
@inlinable
|
||||
public static func < (lhs: TimeAmount, rhs: TimeAmount) -> Bool {
|
||||
return lhs.nanoseconds < rhs.nanoseconds
|
||||
lhs.nanoseconds < rhs.nanoseconds
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,37 +521,37 @@ extension TimeAmount: AdditiveArithmetic {
|
||||
/// The zero value for `TimeAmount`.
|
||||
@inlinable
|
||||
public static var zero: TimeAmount {
|
||||
return TimeAmount.nanoseconds(0)
|
||||
TimeAmount.nanoseconds(0)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func + (lhs: TimeAmount, rhs: TimeAmount) -> TimeAmount {
|
||||
return TimeAmount(lhs.nanoseconds + rhs.nanoseconds)
|
||||
TimeAmount(lhs.nanoseconds + rhs.nanoseconds)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func +=(lhs: inout TimeAmount, rhs: TimeAmount) {
|
||||
public static func += (lhs: inout TimeAmount, rhs: TimeAmount) {
|
||||
lhs = lhs + rhs
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func - (lhs: TimeAmount, rhs: TimeAmount) -> TimeAmount {
|
||||
return TimeAmount(lhs.nanoseconds - rhs.nanoseconds)
|
||||
TimeAmount(lhs.nanoseconds - rhs.nanoseconds)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func -=(lhs: inout TimeAmount, rhs: TimeAmount) {
|
||||
public static func -= (lhs: inout TimeAmount, rhs: TimeAmount) {
|
||||
lhs = lhs - rhs
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func * <T: BinaryInteger>(lhs: T, rhs: TimeAmount) -> TimeAmount {
|
||||
return TimeAmount(Int64(lhs) * rhs.nanoseconds)
|
||||
TimeAmount(Int64(lhs) * rhs.nanoseconds)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func * <T: BinaryInteger>(lhs: TimeAmount, rhs: T) -> TimeAmount {
|
||||
return TimeAmount(lhs.nanoseconds * Int64(rhs))
|
||||
TimeAmount(lhs.nanoseconds * Int64(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +577,7 @@ public struct NIODeadline: Equatable, Hashable, Sendable {
|
||||
public typealias Value = UInt64
|
||||
|
||||
// This really should be an UInt63 but we model it as Int64 with >=0 assert
|
||||
/* private but */ @usableFromInline var _uptimeNanoseconds: Int64 {
|
||||
@usableFromInline var _uptimeNanoseconds: Int64 {
|
||||
didSet {
|
||||
assert(self._uptimeNanoseconds >= 0)
|
||||
}
|
||||
@@ -584,18 +586,17 @@ public struct NIODeadline: Equatable, Hashable, Sendable {
|
||||
/// The nanoseconds since boot representation of the `NIODeadline`.
|
||||
@inlinable
|
||||
public var uptimeNanoseconds: UInt64 {
|
||||
return .init(self._uptimeNanoseconds)
|
||||
.init(self._uptimeNanoseconds)
|
||||
}
|
||||
|
||||
public static let distantPast = NIODeadline(0)
|
||||
public static let distantFuture = NIODeadline(.init(Int64.max))
|
||||
|
||||
/* private but */ @inlinable init(_ nanoseconds: Int64) {
|
||||
@inlinable init(_ nanoseconds: Int64) {
|
||||
precondition(nanoseconds >= 0)
|
||||
self._uptimeNanoseconds = nanoseconds
|
||||
}
|
||||
|
||||
|
||||
/// Getting the time is a very common operation so it warrants optimization.
|
||||
///
|
||||
/// Prior to this function, NIO relied on `DispatchTime.now()`, on all platforms. In addition to
|
||||
@@ -608,31 +609,31 @@ public struct NIODeadline: Equatable, Hashable, Sendable {
|
||||
/// - TODO: Investigate optimizing the call to `DispatchTime.now()` away on other platforms too.
|
||||
@inlinable
|
||||
static func timeNow() -> UInt64 {
|
||||
#if os(Linux)
|
||||
#if os(Linux)
|
||||
var ts = timespec()
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts)
|
||||
/// We use unsafe arithmetic here because `UInt64.max` nanoseconds is more than 580 years,
|
||||
/// and the odds that this code will still be running 530 years from now is very, very low,
|
||||
/// so as a practical matter this will never overflow.
|
||||
return UInt64(ts.tv_sec) &* 1_000_000_000 &+ UInt64(ts.tv_nsec)
|
||||
#else // os(Linux)
|
||||
#else // os(Linux)
|
||||
return DispatchTime.now().uptimeNanoseconds
|
||||
#endif // os(Linux)
|
||||
#endif // os(Linux)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func now() -> NIODeadline {
|
||||
return NIODeadline.uptimeNanoseconds(timeNow())
|
||||
NIODeadline.uptimeNanoseconds(timeNow())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func uptimeNanoseconds(_ nanoseconds: UInt64) -> NIODeadline {
|
||||
return NIODeadline(Int64(min(UInt64(Int64.max), nanoseconds)))
|
||||
NIODeadline(Int64(min(UInt64(Int64.max), nanoseconds)))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func == (lhs: NIODeadline, rhs: NIODeadline) -> Bool {
|
||||
return lhs.uptimeNanoseconds == rhs.uptimeNanoseconds
|
||||
lhs.uptimeNanoseconds == rhs.uptimeNanoseconds
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -644,19 +645,19 @@ public struct NIODeadline: Equatable, Hashable, Sendable {
|
||||
extension NIODeadline: Comparable {
|
||||
@inlinable
|
||||
public static func < (lhs: NIODeadline, rhs: NIODeadline) -> Bool {
|
||||
return lhs.uptimeNanoseconds < rhs.uptimeNanoseconds
|
||||
lhs.uptimeNanoseconds < rhs.uptimeNanoseconds
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func > (lhs: NIODeadline, rhs: NIODeadline) -> Bool {
|
||||
return lhs.uptimeNanoseconds > rhs.uptimeNanoseconds
|
||||
lhs.uptimeNanoseconds > rhs.uptimeNanoseconds
|
||||
}
|
||||
}
|
||||
|
||||
extension NIODeadline: CustomStringConvertible {
|
||||
@inlinable
|
||||
public var description: String {
|
||||
return self.uptimeNanoseconds.description
|
||||
self.uptimeNanoseconds.description
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +666,7 @@ extension NIODeadline {
|
||||
public static func - (lhs: NIODeadline, rhs: NIODeadline) -> TimeAmount {
|
||||
// This won't ever crash, NIODeadlines are guaranteed to be within 0 ..< 2^63-1 nanoseconds so the result can
|
||||
// definitely be stored in a TimeAmount (which is an Int64).
|
||||
return .nanoseconds(Int64(lhs.uptimeNanoseconds) - Int64(rhs.uptimeNanoseconds))
|
||||
.nanoseconds(Int64(lhs.uptimeNanoseconds) - Int64(rhs.uptimeNanoseconds))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -674,7 +675,7 @@ extension NIODeadline {
|
||||
let overflow: Bool
|
||||
(partial, overflow) = Int64(lhs.uptimeNanoseconds).addingReportingOverflow(rhs.nanoseconds)
|
||||
if overflow {
|
||||
assert(rhs.nanoseconds > 0) // this certainly must have overflowed towards +infinity
|
||||
assert(rhs.nanoseconds > 0) // this certainly must have overflowed towards +infinity
|
||||
return NIODeadline.distantFuture
|
||||
}
|
||||
guard partial >= 0 else {
|
||||
@@ -807,7 +808,7 @@ extension EventLoop {
|
||||
) -> Scheduled<T> {
|
||||
self._flatScheduleTask(in: delay, file: file, line: line, task)
|
||||
}
|
||||
|
||||
|
||||
@usableFromInline typealias FlatScheduleTaskDelayCallback<T> = @Sendable () throws -> EventLoopFuture<T>
|
||||
|
||||
@inlinable
|
||||
@@ -826,8 +827,12 @@ extension EventLoop {
|
||||
|
||||
/// Creates and returns a new `EventLoopPromise` that will be notified using this `EventLoop` as execution `NIOThread`.
|
||||
@inlinable
|
||||
public func makePromise<T>(of type: T.Type = T.self, file: StaticString = #fileID, line: UInt = #line) -> EventLoopPromise<T> {
|
||||
return EventLoopPromise<T>(eventLoop: self, file: file, line: line)
|
||||
public func makePromise<T>(
|
||||
of type: T.Type = T.self,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopPromise<T> {
|
||||
EventLoopPromise<T>(eventLoop: self, file: file, line: line)
|
||||
}
|
||||
|
||||
/// Creates and returns a new `EventLoopFuture` that is already marked as failed. Notifications will be done using this `EventLoop` as execution `NIOThread`.
|
||||
@@ -837,7 +842,7 @@ extension EventLoop {
|
||||
/// - returns: a failed `EventLoopFuture`.
|
||||
@inlinable
|
||||
public func makeFailedFuture<T>(_ error: Error) -> EventLoopFuture<T> {
|
||||
return EventLoopFuture<T>(eventLoop: self, error: error)
|
||||
EventLoopFuture<T>(eventLoop: self, error: error)
|
||||
}
|
||||
|
||||
/// Creates and returns a new `EventLoopFuture` that is already marked as success. Notifications will be done using this `EventLoop` as execution `NIOThread`.
|
||||
@@ -885,21 +890,20 @@ extension EventLoop {
|
||||
///
|
||||
/// - returns: Itself, because an `EventLoop` forms a singular `EventLoopGroup`.
|
||||
public func next() -> EventLoop {
|
||||
return self
|
||||
self
|
||||
}
|
||||
|
||||
/// An `EventLoop` forms a singular `EventLoopGroup`, returning itself as 'any' `EventLoop`.
|
||||
///
|
||||
/// - returns: Itself, because an `EventLoop` forms a singular `EventLoopGroup`.
|
||||
public func any() -> EventLoop {
|
||||
return self
|
||||
self
|
||||
}
|
||||
|
||||
/// Close this `EventLoop`.
|
||||
public func close() throws {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
/// Schedule a repeated task to be executed by the `EventLoop` with a fixed delay between the end and start of each
|
||||
/// task.
|
||||
@@ -920,7 +924,7 @@ extension EventLoop {
|
||||
) -> RepeatedTask {
|
||||
self._scheduleRepeatedTask(initialDelay: initialDelay, delay: delay, notifying: promise, task)
|
||||
}
|
||||
|
||||
|
||||
/// Schedule a repeated task to be executed by the `EventLoop` with a fixed delay between the end and start of each
|
||||
/// task.
|
||||
///
|
||||
@@ -939,9 +943,17 @@ extension EventLoop {
|
||||
notifying promise: EventLoopPromise<Void>? = nil,
|
||||
_ task: @escaping @Sendable (RepeatedTask) throws -> Void
|
||||
) -> RepeatedTask {
|
||||
let jitteredInitialDelay = Self._getJitteredDelay(delay: initialDelay, maximumAllowableJitter: maximumAllowableJitter)
|
||||
let jitteredInitialDelay = Self._getJitteredDelay(
|
||||
delay: initialDelay,
|
||||
maximumAllowableJitter: maximumAllowableJitter
|
||||
)
|
||||
let jitteredDelay = Self._getJitteredDelay(delay: delay, maximumAllowableJitter: maximumAllowableJitter)
|
||||
return self.scheduleRepeatedTask(initialDelay: jitteredInitialDelay, delay: jitteredDelay, notifying: promise, task)
|
||||
return self.scheduleRepeatedTask(
|
||||
initialDelay: jitteredInitialDelay,
|
||||
delay: jitteredDelay,
|
||||
notifying: promise,
|
||||
task
|
||||
)
|
||||
}
|
||||
typealias ScheduleRepeatedTaskCallback = @Sendable (RepeatedTask) throws -> Void
|
||||
|
||||
@@ -961,7 +973,7 @@ extension EventLoop {
|
||||
}
|
||||
return self.scheduleRepeatedAsyncTask(initialDelay: initialDelay, delay: delay, notifying: promise, futureTask)
|
||||
}
|
||||
|
||||
|
||||
/// Schedule a repeated asynchronous task to be executed by the `EventLoop` with a fixed delay between the end and
|
||||
/// start of each task.
|
||||
///
|
||||
@@ -988,7 +1000,7 @@ extension EventLoop {
|
||||
) -> RepeatedTask {
|
||||
self._scheduleRepeatedAsyncTask(initialDelay: initialDelay, delay: delay, notifying: promise, task)
|
||||
}
|
||||
|
||||
|
||||
/// Schedule a repeated asynchronous task to be executed by the `EventLoop` with a fixed delay between the end and
|
||||
/// start of each task.
|
||||
///
|
||||
@@ -1014,9 +1026,17 @@ extension EventLoop {
|
||||
notifying promise: EventLoopPromise<Void>? = nil,
|
||||
_ task: @escaping @Sendable (RepeatedTask) -> EventLoopFuture<Void>
|
||||
) -> RepeatedTask {
|
||||
let jitteredInitialDelay = Self._getJitteredDelay(delay: initialDelay, maximumAllowableJitter: maximumAllowableJitter)
|
||||
let jitteredInitialDelay = Self._getJitteredDelay(
|
||||
delay: initialDelay,
|
||||
maximumAllowableJitter: maximumAllowableJitter
|
||||
)
|
||||
let jitteredDelay = Self._getJitteredDelay(delay: delay, maximumAllowableJitter: maximumAllowableJitter)
|
||||
return self._scheduleRepeatedAsyncTask(initialDelay: jitteredInitialDelay, delay: jitteredDelay, notifying: promise, task)
|
||||
return self._scheduleRepeatedAsyncTask(
|
||||
initialDelay: jitteredInitialDelay,
|
||||
delay: jitteredDelay,
|
||||
notifying: promise,
|
||||
task
|
||||
)
|
||||
}
|
||||
typealias ScheduleRepeatedAsyncTaskCallback = @Sendable (RepeatedTask) -> EventLoopFuture<Void>
|
||||
|
||||
@@ -1043,14 +1063,14 @@ extension EventLoop {
|
||||
maximumAllowableJitter: TimeAmount
|
||||
) -> TimeAmount {
|
||||
let jitter = TimeAmount.nanoseconds(Int64.random(in: .zero..<maximumAllowableJitter.nanoseconds))
|
||||
return delay + jitter;
|
||||
return delay + jitter
|
||||
}
|
||||
|
||||
/// Returns an `EventLoopIterator` over this `EventLoop`.
|
||||
///
|
||||
/// - returns: `EventLoopIterator`
|
||||
public func makeIterator() -> EventLoopIterator {
|
||||
return EventLoopIterator([self])
|
||||
EventLoopIterator([self])
|
||||
}
|
||||
|
||||
/// Asserts that the current thread is the one tied to this `EventLoop`.
|
||||
@@ -1127,7 +1147,7 @@ public protocol EventLoopGroup: AnyObject, _NIOPreconcurrencySendable {
|
||||
/// The rule of thumb is: If you are trying to do _load balancing_, use `next()`. If you just want to create a new
|
||||
/// future or kick off some operation, use `any()`.
|
||||
func any() -> EventLoop
|
||||
|
||||
|
||||
/// Shuts down the eventloop gracefully. This function is clearly an outlier in that it uses a completion
|
||||
/// callback instead of an EventLoopFuture. The reason for that is that NIO's EventLoopFutures will call back on an event loop.
|
||||
/// The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue
|
||||
@@ -1150,7 +1170,7 @@ extension EventLoopGroup {
|
||||
/// The default implementation of `any()` just returns the `next()` EventLoop but it's highly recommended to
|
||||
/// override this and return the current `EventLoop` if possible.
|
||||
public func any() -> EventLoop {
|
||||
return self.next()
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ struct IsolatedEventLoop {
|
||||
/// Returns the wrapped event loop.
|
||||
@inlinable
|
||||
func nonisolated() -> any EventLoop {
|
||||
return self._wrapped
|
||||
self._wrapped
|
||||
}
|
||||
}
|
||||
extension EventLoop {
|
||||
@@ -421,8 +421,8 @@ extension EventLoopFuture {
|
||||
@inlinable
|
||||
func unwrap<NewValue>(
|
||||
orReplace replacement: NewValue
|
||||
) -> EventLoopFuture<NewValue>.Isolated where Value == Optional<NewValue> {
|
||||
return self.map { (value) -> NewValue in
|
||||
) -> EventLoopFuture<NewValue>.Isolated where Value == NewValue? {
|
||||
self.map { (value) -> NewValue in
|
||||
guard let value = value else {
|
||||
return replacement
|
||||
}
|
||||
@@ -447,8 +447,8 @@ extension EventLoopFuture {
|
||||
@inlinable
|
||||
func unwrap<NewValue>(
|
||||
orElse callback: @escaping () -> NewValue
|
||||
) -> EventLoopFuture<NewValue>.Isolated where Value == Optional<NewValue> {
|
||||
return self.map { (value) -> NewValue in
|
||||
) -> EventLoopFuture<NewValue>.Isolated where Value == NewValue? {
|
||||
self.map { (value) -> NewValue in
|
||||
guard let value = value else {
|
||||
return callback()
|
||||
}
|
||||
@@ -459,7 +459,7 @@ extension EventLoopFuture {
|
||||
/// Returns the wrapped event loop future.
|
||||
@inlinable
|
||||
func nonisolated() -> EventLoopFuture<Value> {
|
||||
return self._wrapped
|
||||
self._wrapped
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +471,6 @@ extension EventLoopFuture {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension EventLoopPromise {
|
||||
/// A struct wrapping an ``EventLoopPromise`` that ensures all calls to any method on this struct
|
||||
/// are coming from the event loop of the promise.
|
||||
@@ -480,7 +479,6 @@ extension EventLoopPromise {
|
||||
@usableFromInline
|
||||
let _wrapped: EventLoopPromise<Value>
|
||||
|
||||
|
||||
/// Deliver a successful result to the associated `EventLoopFuture<Value>` object.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -514,7 +512,7 @@ extension EventLoopPromise {
|
||||
/// Returns the wrapped event loop promise.
|
||||
@inlinable
|
||||
func nonisolated() -> EventLoopPromise<Value> {
|
||||
return self._wrapped
|
||||
self._wrapped
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,4 +523,3 @@ extension EventLoopPromise {
|
||||
return Isolated(_wrapped: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,63 +15,91 @@
|
||||
extension EventLoopFuture {
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func flatMap<NewValue>(file: StaticString = #fileID, line: UInt = #line, _ callback: @escaping (Value) -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
|
||||
return self.flatMap(callback)
|
||||
public func flatMap<NewValue>(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Value) -> EventLoopFuture<NewValue>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self.flatMap(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func flatMapThrowing<NewValue>(file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Value) throws -> NewValue) -> EventLoopFuture<NewValue> {
|
||||
return self.flatMapThrowing(callback)
|
||||
public func flatMapThrowing<NewValue>(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Value) throws -> NewValue
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self.flatMapThrowing(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func flatMapErrorThrowing(file: StaticString = #fileID, line: UInt = #line, _ callback: @escaping (Error) throws -> Value) -> EventLoopFuture<Value> {
|
||||
return self.flatMapErrorThrowing(callback)
|
||||
public func flatMapErrorThrowing(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Error) throws -> Value
|
||||
) -> EventLoopFuture<Value> {
|
||||
self.flatMapErrorThrowing(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func map<NewValue>(file: StaticString = #fileID, line: UInt = #line, _ callback: @escaping (Value) -> (NewValue)) -> EventLoopFuture<NewValue> {
|
||||
return self.map(callback)
|
||||
public func map<NewValue>(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Value) -> (NewValue)
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self.map(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func flatMapError(file: StaticString = #fileID, line: UInt = #line, _ callback: @escaping (Error) -> EventLoopFuture<Value>) -> EventLoopFuture<Value> {
|
||||
return self.flatMapError(callback)
|
||||
public func flatMapError(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Error) -> EventLoopFuture<Value>
|
||||
) -> EventLoopFuture<Value> {
|
||||
self.flatMapError(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func flatMapResult<NewValue, SomeError: Error>(file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ body: @escaping (Value) -> Result<NewValue, SomeError>) -> EventLoopFuture<NewValue> {
|
||||
return self.flatMapResult(body)
|
||||
public func flatMapResult<NewValue, SomeError: Error>(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ body: @escaping (Value) -> Result<NewValue, SomeError>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self.flatMapResult(body)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func recover(file: StaticString = #fileID, line: UInt = #line, _ callback: @escaping (Error) -> Value) -> EventLoopFuture<Value> {
|
||||
return self.recover(callback)
|
||||
public func recover(
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line,
|
||||
_ callback: @escaping (Error) -> Value
|
||||
) -> EventLoopFuture<Value> {
|
||||
self.recover(callback)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func and<OtherValue>(_ other: EventLoopFuture<OtherValue>,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
return self.and(other)
|
||||
public func and<OtherValue>(
|
||||
_ other: EventLoopFuture<OtherValue>,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
self.and(other)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please don't pass file:line:, there's no point.")
|
||||
public func and<OtherValue>(value: OtherValue,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
return self.and(value: value)
|
||||
public func and<OtherValue>(
|
||||
value: OtherValue,
|
||||
file: StaticString = #fileID,
|
||||
line: UInt = #line
|
||||
) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
self.and(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the eventual value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapWithEventLoop<NewValue>(_ callback: @escaping @Sendable (Value, EventLoop) -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
|
||||
public func flatMapWithEventLoop<NewValue>(
|
||||
_ callback: @escaping @Sendable (Value, EventLoop) -> EventLoopFuture<NewValue>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
let next = EventLoopPromise<NewValue>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
self._whenComplete { [eventLoop = self.eventLoop] in
|
||||
switch self._value! {
|
||||
@@ -61,7 +63,7 @@ extension EventLoopFuture {
|
||||
}
|
||||
return next.futureResult
|
||||
}
|
||||
|
||||
|
||||
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
|
||||
/// may recover from the error by returning an `EventLoopFuture<NewValue>`. The callback is intended to potentially
|
||||
/// recover from the error by returning a new `EventLoopFuture` that will eventually contain the recovered
|
||||
@@ -75,7 +77,9 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the recovered value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapErrorWithEventLoop(_ callback: @escaping @Sendable (Error, EventLoop) -> EventLoopFuture<Value>) -> EventLoopFuture<Value> {
|
||||
public func flatMapErrorWithEventLoop(
|
||||
_ callback: @escaping @Sendable (Error, EventLoop) -> EventLoopFuture<Value>
|
||||
) -> EventLoopFuture<Value> {
|
||||
let next = EventLoopPromise<Value>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
self._whenComplete { [eventLoop = self.eventLoop] in
|
||||
switch self._value! {
|
||||
@@ -119,7 +123,8 @@ extension EventLoopFuture {
|
||||
with combiningFunction: @escaping @Sendable (Value, OtherValue, EventLoop) -> EventLoopFuture<Value>
|
||||
) -> EventLoopFuture<Value> {
|
||||
func fold0(eventLoop: EventLoop) -> EventLoopFuture<Value> {
|
||||
let body = futures.reduce(self) { (f1: EventLoopFuture<Value>, f2: EventLoopFuture<OtherValue>) -> EventLoopFuture<Value> in
|
||||
let body = futures.reduce(self) {
|
||||
(f1: EventLoopFuture<Value>, f2: EventLoopFuture<OtherValue>) -> EventLoopFuture<Value> in
|
||||
let newFuture = f1.and(f2).flatMap { (args: (Value, OtherValue)) -> EventLoopFuture<Value> in
|
||||
let (f1Value, f2Value) = args
|
||||
self.eventLoop.assertInEventLoop()
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import NIOConcurrencyHelpers
|
||||
import Dispatch
|
||||
import NIOConcurrencyHelpers
|
||||
|
||||
/// Internal list of callbacks.
|
||||
///
|
||||
@@ -158,9 +158,11 @@ public struct EventLoopPromise<Value> {
|
||||
|
||||
@inlinable
|
||||
internal static func makeUnleakablePromise(eventLoop: EventLoop, line: UInt = #line) -> EventLoopPromise<Value> {
|
||||
return EventLoopPromise<Value>(eventLoop: eventLoop,
|
||||
file: "BUG in SwiftNIO (please report), unleakable promise leaked.",
|
||||
line: line)
|
||||
EventLoopPromise<Value>(
|
||||
eventLoop: eventLoop,
|
||||
file: "BUG in SwiftNIO (please report), unleakable promise leaked.",
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
/// General initializer
|
||||
@@ -196,7 +198,7 @@ public struct EventLoopPromise<Value> {
|
||||
///
|
||||
/// This method is equivalent to invoking `future.cascade(to: promise)`,
|
||||
/// but sometimes may read better than its cascade counterpart.
|
||||
///
|
||||
///
|
||||
/// - parameters:
|
||||
/// - future: The future whose value will be used to succeed or fail this promise.
|
||||
/// - seealso: `EventLoopFuture.cascade(to:)`
|
||||
@@ -250,7 +252,7 @@ public struct EventLoopPromise<Value> {
|
||||
/// - returns: The callback list to run.
|
||||
@inlinable
|
||||
internal func _setValue(value: Result<Value, Error>) -> CallbackList {
|
||||
return self.futureResult._setValue(value: value)
|
||||
self.futureResult._setValue(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,8 +434,8 @@ public final class EventLoopFuture<Value> {
|
||||
}
|
||||
|
||||
extension EventLoopFuture: Equatable {
|
||||
public static func ==(lhs: EventLoopFuture, rhs: EventLoopFuture) -> Bool {
|
||||
return lhs === rhs
|
||||
public static func == (lhs: EventLoopFuture, rhs: EventLoopFuture) -> Bool {
|
||||
lhs === rhs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +472,9 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the eventual value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMap<NewValue>(_ callback: @escaping @Sendable (Value) -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
|
||||
public func flatMap<NewValue>(
|
||||
_ callback: @escaping @Sendable (Value) -> EventLoopFuture<NewValue>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self._flatMap(callback)
|
||||
}
|
||||
@usableFromInline typealias FlatMapCallback<NewValue> = @Sendable (Value) -> EventLoopFuture<NewValue>
|
||||
@@ -513,13 +517,17 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the eventual value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapThrowing<NewValue>(_ callback: @escaping @Sendable (Value) throws -> NewValue) -> EventLoopFuture<NewValue> {
|
||||
public func flatMapThrowing<NewValue>(
|
||||
_ callback: @escaping @Sendable (Value) throws -> NewValue
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self._flatMapThrowing(callback)
|
||||
}
|
||||
@usableFromInline typealias FlatMapThrowingCallback<NewValue> = @Sendable (Value) throws -> NewValue
|
||||
|
||||
@inlinable
|
||||
func _flatMapThrowing<NewValue>(_ callback: @escaping FlatMapThrowingCallback<NewValue>) -> EventLoopFuture<NewValue> {
|
||||
func _flatMapThrowing<NewValue>(
|
||||
_ callback: @escaping FlatMapThrowingCallback<NewValue>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
let next = EventLoopPromise<NewValue>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
self._whenComplete {
|
||||
switch self._value! {
|
||||
@@ -553,7 +561,8 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the eventual value or a rethrown error.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapErrorThrowing(_ callback: @escaping @Sendable (Error) throws -> Value) -> EventLoopFuture<Value> {
|
||||
public func flatMapErrorThrowing(_ callback: @escaping @Sendable (Error) throws -> Value) -> EventLoopFuture<Value>
|
||||
{
|
||||
self._flatMapErrorThrowing(callback)
|
||||
}
|
||||
@usableFromInline typealias FlatMapErrorThrowingCallback = @Sendable (Error) throws -> Value
|
||||
@@ -618,7 +627,7 @@ extension EventLoopFuture {
|
||||
} else {
|
||||
let next = EventLoopPromise<NewValue>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
self._whenComplete {
|
||||
return next._setValue(value: self._value!.map(callback))
|
||||
next._setValue(value: self._value!.map(callback))
|
||||
}
|
||||
return next.futureResult
|
||||
}
|
||||
@@ -637,7 +646,9 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the recovered value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapError(_ callback: @escaping @Sendable (Error) -> EventLoopFuture<Value>) -> EventLoopFuture<Value> {
|
||||
public func flatMapError(
|
||||
_ callback: @escaping @Sendable (Error) -> EventLoopFuture<Value>
|
||||
) -> EventLoopFuture<Value> {
|
||||
self._flatMapError(callback)
|
||||
}
|
||||
@usableFromInline typealias FlatMapErrorCallback = @Sendable (Error) -> EventLoopFuture<Value>
|
||||
@@ -679,13 +690,19 @@ extension EventLoopFuture {
|
||||
/// - returns: A future that will receive the eventual value.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func flatMapResult<NewValue, SomeError: Error>(_ body: @escaping @Sendable (Value) -> Result<NewValue, SomeError>) -> EventLoopFuture<NewValue> {
|
||||
public func flatMapResult<NewValue, SomeError: Error>(
|
||||
_ body: @escaping @Sendable (Value) -> Result<NewValue, SomeError>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
self._flatMapResult(body)
|
||||
}
|
||||
@usableFromInline typealias FlatMapResultCallback<NewValue, SomeError: Error> = @Sendable (Value) -> Result<NewValue, SomeError>
|
||||
@usableFromInline typealias FlatMapResultCallback<NewValue, SomeError: Error> = @Sendable (Value) -> Result<
|
||||
NewValue, SomeError
|
||||
>
|
||||
|
||||
@inlinable
|
||||
func _flatMapResult<NewValue, SomeError: Error>(_ body: @escaping FlatMapResultCallback<NewValue, SomeError>) -> EventLoopFuture<NewValue> {
|
||||
func _flatMapResult<NewValue, SomeError: Error>(
|
||||
_ body: @escaping FlatMapResultCallback<NewValue, SomeError>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
let next = EventLoopPromise<NewValue>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
self._whenComplete {
|
||||
switch self._value! {
|
||||
@@ -750,7 +767,8 @@ extension EventLoopFuture {
|
||||
|
||||
/// Add a callback. If there's already a value, run as much of the chain as we can.
|
||||
@inlinable
|
||||
@preconcurrency // TODO: We want to remove @preconcurrency but it results in more allocations in 1000_udpconnections
|
||||
// TODO: We want to remove @preconcurrency but it results in more allocations in 1000_udpconnections
|
||||
@preconcurrency
|
||||
internal func _whenComplete(_ callback: @escaping @Sendable () -> CallbackList) {
|
||||
self._internalWhenComplete(callback)
|
||||
}
|
||||
@@ -865,7 +883,7 @@ extension EventLoopFuture {
|
||||
@inlinable
|
||||
public func and<OtherValue>(_ other: EventLoopFuture<OtherValue>) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
let promise = EventLoopPromise<(Value, OtherValue)>.makeUnleakablePromise(eventLoop: self.eventLoop)
|
||||
let box: UnsafeMutableTransferBox<(t:Value?, u: OtherValue?)> = .init((nil, nil))
|
||||
let box: UnsafeMutableTransferBox<(t: Value?, u: OtherValue?)> = .init((nil, nil))
|
||||
|
||||
assert(self.eventLoop === promise.futureResult.eventLoop)
|
||||
self._whenComplete { () -> CallbackList in
|
||||
@@ -905,7 +923,7 @@ extension EventLoopFuture {
|
||||
/// This is just syntactic sugar for `future.and(loop.makeSucceedFuture(value))`.
|
||||
@inlinable
|
||||
public func and<OtherValue>(value: OtherValue) -> EventLoopFuture<(Value, OtherValue)> {
|
||||
return self.and(EventLoopFuture<OtherValue>(eventLoop: self.eventLoop, value: value))
|
||||
self.and(EventLoopFuture<OtherValue>(eventLoop: self.eventLoop, value: value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,7 +1012,7 @@ extension EventLoopFuture {
|
||||
@available(*, noasync, message: "wait() can block indefinitely, prefer get()", renamed: "get()")
|
||||
@inlinable
|
||||
public func wait(file: StaticString = #file, line: UInt = #line) throws -> Value {
|
||||
return try self._wait(file: file, line: line)
|
||||
try self._wait(file: file, line: line)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@@ -1012,7 +1030,7 @@ extension EventLoopFuture {
|
||||
lock.lock(whenValue: 1)
|
||||
lock.unlock()
|
||||
|
||||
switch(v.wrappedValue!) {
|
||||
switch v.wrappedValue! {
|
||||
case .success(let result):
|
||||
return result
|
||||
case .failure(let error):
|
||||
@@ -1056,7 +1074,8 @@ extension EventLoopFuture {
|
||||
with combiningFunction: @escaping FoldCallback<OtherValue>
|
||||
) -> EventLoopFuture<Value> {
|
||||
func fold0() -> EventLoopFuture<Value> {
|
||||
let body = futures.reduce(self) { (f1: EventLoopFuture<Value>, f2: EventLoopFuture<OtherValue>) -> EventLoopFuture<Value> in
|
||||
let body = futures.reduce(self) {
|
||||
(f1: EventLoopFuture<Value>, f2: EventLoopFuture<OtherValue>) -> EventLoopFuture<Value> in
|
||||
let newFuture = f1.and(f2).flatMap { (args: (Value, OtherValue)) -> EventLoopFuture<Value> in
|
||||
let (f1Value, f2Value) = args
|
||||
self.eventLoop.assertInEventLoop()
|
||||
@@ -1106,9 +1125,9 @@ extension EventLoopFuture {
|
||||
@inlinable
|
||||
public static func reduce<InputValue>(
|
||||
_ initialResult: Value,
|
||||
_ futures: [EventLoopFuture<InputValue>],
|
||||
on eventLoop: EventLoop,
|
||||
_ nextPartialResult: @escaping @Sendable (Value, InputValue) -> Value
|
||||
_ futures: [EventLoopFuture<InputValue>],
|
||||
on eventLoop: EventLoop,
|
||||
_ nextPartialResult: @escaping @Sendable (Value, InputValue) -> Value
|
||||
) -> EventLoopFuture<Value> {
|
||||
Self._reduce(initialResult, futures, on: eventLoop, nextPartialResult)
|
||||
}
|
||||
@@ -1203,7 +1222,10 @@ extension EventLoopFuture {
|
||||
/// - on: The `EventLoop` on which the new `EventLoopFuture` callbacks will execute on.
|
||||
/// - Returns: A new `EventLoopFuture` that waits for the other futures to succeed.
|
||||
@inlinable
|
||||
public static func andAllSucceed(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<Void> {
|
||||
public static func andAllSucceed(
|
||||
_ futures: [EventLoopFuture<Value>],
|
||||
on eventLoop: EventLoop
|
||||
) -> EventLoopFuture<Void> {
|
||||
let promise = eventLoop.makePromise(of: Void.self)
|
||||
EventLoopFuture.andAllSucceed(futures, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -1238,7 +1260,10 @@ extension EventLoopFuture {
|
||||
/// - futures: An array of homogenous `EventLoopFuture`s to wait on for fulfilled values.
|
||||
/// - on: The `EventLoop` on which the new `EventLoopFuture` callbacks will fire.
|
||||
/// - Returns: A new `EventLoopFuture` with all of the values fulfilled by the provided futures.
|
||||
public static func whenAllSucceed(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<[Value]> {
|
||||
public static func whenAllSucceed(
|
||||
_ futures: [EventLoopFuture<Value>],
|
||||
on eventLoop: EventLoop
|
||||
) -> EventLoopFuture<[Value]> {
|
||||
let promise = eventLoop.makePromise(of: [Value].self)
|
||||
EventLoopFuture.whenAllSucceed(futures, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -1272,7 +1297,7 @@ extension EventLoopFuture {
|
||||
reduced.futureResult.whenComplete { result in
|
||||
switch result {
|
||||
case .success:
|
||||
// verify that all operations have been completed
|
||||
// verify that all operations have been completed
|
||||
assert(!results.wrappedValue.contains(where: { $0 == nil }))
|
||||
promise.succeed(results.wrappedValue.map { $0! })
|
||||
case .failure(let error):
|
||||
@@ -1321,7 +1346,8 @@ extension EventLoopFuture {
|
||||
// in the "futures" to pass their result to the caller
|
||||
for (index, future) in futures.enumerated() {
|
||||
if future.eventLoop.inEventLoop,
|
||||
let result = future._value {
|
||||
let result = future._value
|
||||
{
|
||||
// Fast-track already-fulfilled results without the overhead of calling `whenComplete`. This can yield a
|
||||
// ~20% performance improvement in the case of large arrays where all elements are already fulfilled.
|
||||
processResult(index, result)
|
||||
@@ -1350,7 +1376,10 @@ extension EventLoopFuture {
|
||||
/// - on: The `EventLoop` on which the new `EventLoopFuture` callbacks will execute on.
|
||||
/// - Returns: A new `EventLoopFuture` that succeeds after all futures complete.
|
||||
@inlinable
|
||||
public static func andAllComplete(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<Void> {
|
||||
public static func andAllComplete(
|
||||
_ futures: [EventLoopFuture<Value>],
|
||||
on eventLoop: EventLoop
|
||||
) -> EventLoopFuture<Void> {
|
||||
let promise = eventLoop.makePromise(of: Void.self)
|
||||
EventLoopFuture.andAllComplete(futures, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -1390,8 +1419,10 @@ extension EventLoopFuture {
|
||||
/// - on: The `EventLoop` on which the new `EventLoopFuture` callbacks will fire.
|
||||
/// - Returns: A new `EventLoopFuture` with all the results of the provided futures.
|
||||
@inlinable
|
||||
public static func whenAllComplete(_ futures: [EventLoopFuture<Value>],
|
||||
on eventLoop: EventLoop) -> EventLoopFuture<[Result<Value, Error>]> {
|
||||
public static func whenAllComplete(
|
||||
_ futures: [EventLoopFuture<Value>],
|
||||
on eventLoop: EventLoop
|
||||
) -> EventLoopFuture<[Result<Value, Error>]> {
|
||||
let promise = eventLoop.makePromise(of: [Result<Value, Error>].self)
|
||||
EventLoopFuture.whenAllComplete(futures, promise: promise)
|
||||
return promise.futureResult
|
||||
@@ -1405,12 +1436,16 @@ extension EventLoopFuture {
|
||||
/// - futures: An array of homogenous `EventLoopFuture`s to gather results from.
|
||||
/// - promise: The `EventLoopPromise` to complete with the result of the futures.
|
||||
@inlinable
|
||||
public static func whenAllComplete(_ futures: [EventLoopFuture<Value>],
|
||||
promise: EventLoopPromise<[Result<Value, Error>]>) {
|
||||
public static func whenAllComplete(
|
||||
_ futures: [EventLoopFuture<Value>],
|
||||
promise: EventLoopPromise<[Result<Value, Error>]>
|
||||
) {
|
||||
let eventLoop = promise.futureResult.eventLoop
|
||||
let reduced = eventLoop.makePromise(of: Void.self)
|
||||
|
||||
let results: UnsafeMutableTransferBox<[Result<Value, Error>]> = .init(.init(repeating: .failure(OperationPlaceholderError()), count: futures.count))
|
||||
let results: UnsafeMutableTransferBox<[Result<Value, Error>]> = .init(
|
||||
.init(repeating: .failure(OperationPlaceholderError()), count: futures.count)
|
||||
)
|
||||
let callback = { @Sendable (index: Int, result: Result<Value, Error>) in
|
||||
results.wrappedValue[index] = result
|
||||
}
|
||||
@@ -1427,10 +1462,12 @@ extension EventLoopFuture {
|
||||
switch result {
|
||||
case .success:
|
||||
// verify that all operations have been completed
|
||||
assert(!results.wrappedValue.contains(where: {
|
||||
guard case let .failure(error) = $0 else { return false }
|
||||
return error is OperationPlaceholderError
|
||||
}))
|
||||
assert(
|
||||
!results.wrappedValue.contains(where: {
|
||||
guard case let .failure(error) = $0 else { return false }
|
||||
return error is OperationPlaceholderError
|
||||
})
|
||||
)
|
||||
promise.succeed(results.wrappedValue)
|
||||
|
||||
case .failure(let error):
|
||||
@@ -1474,7 +1511,8 @@ extension EventLoopFuture {
|
||||
// in the "futures" to pass their result to the caller
|
||||
for (index, future) in futures.enumerated() {
|
||||
if future.eventLoop.inEventLoop,
|
||||
let result = future._value {
|
||||
let result = future._value
|
||||
{
|
||||
// Fast-track already-fulfilled results without the overhead of calling `whenComplete`. This can yield a
|
||||
// ~30% performance improvement in the case of large arrays where all elements are already fulfilled.
|
||||
processResult(index, result)
|
||||
@@ -1519,7 +1557,7 @@ extension EventLoopFuture {
|
||||
/// `EventLoopFuture` has any result.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - callback: the callback that is called when the `EventLoopFuture` is fulfilled.
|
||||
/// - callback: the callback that is called when the `EventLoopFuture` is fulfilled.
|
||||
/// - returns: the current `EventLoopFuture`
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
@@ -1557,8 +1595,8 @@ extension EventLoopFuture {
|
||||
/// future.
|
||||
/// - throws: the `Error` passed in the `orError` parameter when the resolved future's value is `Optional.none`.
|
||||
@inlinable
|
||||
public func unwrap<NewValue>(orError error: Error) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||
return self.flatMapThrowing { (value) throws -> NewValue in
|
||||
public func unwrap<NewValue>(orError error: Error) -> EventLoopFuture<NewValue> where Value == NewValue? {
|
||||
self.flatMapThrowing { (value) throws -> NewValue in
|
||||
guard let value = value else {
|
||||
throw error
|
||||
}
|
||||
@@ -1578,12 +1616,13 @@ extension EventLoopFuture {
|
||||
/// - orReplace: the value of the returned `EventLoopFuture` when then resolved future's value is `Optional.some()`.
|
||||
/// - returns: an new `EventLoopFuture` with new type parameter `NewValue` and the value passed in the `orReplace` parameter.
|
||||
@inlinable
|
||||
public func unwrap<NewValue>(orReplace replacement: NewValue) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||
return self.map { (value) -> NewValue in
|
||||
public func unwrap<NewValue>(orReplace replacement: NewValue) -> EventLoopFuture<NewValue>
|
||||
where Value == NewValue? {
|
||||
self.map { (value) -> NewValue in
|
||||
guard let value = value else {
|
||||
return replacement
|
||||
}
|
||||
return value
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1605,7 +1644,7 @@ extension EventLoopFuture {
|
||||
@preconcurrency
|
||||
public func unwrap<NewValue>(
|
||||
orElse callback: @escaping @Sendable () -> NewValue
|
||||
) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||
) -> EventLoopFuture<NewValue> where Value == NewValue? {
|
||||
self._unwrap(orElse: callback)
|
||||
}
|
||||
@usableFromInline typealias UnwrapCallback<NewValue> = @Sendable () -> NewValue
|
||||
@@ -1613,8 +1652,8 @@ extension EventLoopFuture {
|
||||
@inlinable
|
||||
func _unwrap<NewValue>(
|
||||
orElse callback: @escaping UnwrapCallback<NewValue>
|
||||
) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||
return self.map { (value) -> NewValue in
|
||||
) -> EventLoopFuture<NewValue> where Value == NewValue? {
|
||||
self.map { (value) -> NewValue in
|
||||
guard let value = value else {
|
||||
return callback()
|
||||
}
|
||||
@@ -1623,7 +1662,7 @@ extension EventLoopFuture {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: may block
|
||||
// MARK: may block
|
||||
|
||||
extension EventLoopFuture {
|
||||
/// Chain an `EventLoopFuture<NewValue>` providing the result of a IO / task that may block. For example:
|
||||
@@ -1651,7 +1690,7 @@ extension EventLoopFuture {
|
||||
onto queue: DispatchQueue,
|
||||
_ callbackMayBlock: @escaping FlatMapBlockingCallback<NewValue>
|
||||
) -> EventLoopFuture<NewValue> {
|
||||
return self.flatMap { result in
|
||||
self.flatMap { result in
|
||||
queue.asyncWithFuture(eventLoop: self.eventLoop) { try callbackMayBlock(result) }
|
||||
}
|
||||
}
|
||||
@@ -1687,7 +1726,8 @@ extension EventLoopFuture {
|
||||
/// - callbackMayBlock: The callback that is called with the failed result of the `EventLoopFuture`.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func whenFailureBlocking(onto queue: DispatchQueue, _ callbackMayBlock: @escaping @Sendable (Error) -> Void) {
|
||||
public func whenFailureBlocking(onto queue: DispatchQueue, _ callbackMayBlock: @escaping @Sendable (Error) -> Void)
|
||||
{
|
||||
self._whenFailureBlocking(onto: queue, callbackMayBlock)
|
||||
}
|
||||
@usableFromInline typealias WhenFailureBlockingCallback = @Sendable (Error) -> Void
|
||||
@@ -1707,7 +1747,10 @@ extension EventLoopFuture {
|
||||
/// - callbackMayBlock: The callback that is called when the `EventLoopFuture` is fulfilled.
|
||||
@inlinable
|
||||
@preconcurrency
|
||||
public func whenCompleteBlocking(onto queue: DispatchQueue, _ callbackMayBlock: @escaping @Sendable (Result<Value, Error>) -> Void) {
|
||||
public func whenCompleteBlocking(
|
||||
onto queue: DispatchQueue,
|
||||
_ callbackMayBlock: @escaping @Sendable (Result<Value, Error>) -> Void
|
||||
) {
|
||||
self._whenCompleteBlocking(onto: queue, callbackMayBlock)
|
||||
}
|
||||
@usableFromInline typealias WhenCompleteBlocking = @Sendable (Result<Value, Error>) -> Void
|
||||
@@ -1733,7 +1776,7 @@ extension EventLoopFuture {
|
||||
/// - line: The line this function was called on, for debugging purposes.
|
||||
@inlinable
|
||||
public func assertSuccess(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
|
||||
return self.always { result in
|
||||
self.always { result in
|
||||
switch result {
|
||||
case .success:
|
||||
()
|
||||
@@ -1752,7 +1795,7 @@ extension EventLoopFuture {
|
||||
/// - line: The line this function was called on, for debugging purposes.
|
||||
@inlinable
|
||||
public func assertFailure(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
|
||||
return self.always { result in
|
||||
self.always { result in
|
||||
switch result {
|
||||
case .success(let value):
|
||||
assertionFailure("Expected failure, but got success: \(value)", file: file, line: line)
|
||||
@@ -1772,7 +1815,7 @@ extension EventLoopFuture {
|
||||
/// - line: The line this function was called on, for debugging purposes.
|
||||
@inlinable
|
||||
public func preconditionSuccess(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
|
||||
return self.always { result in
|
||||
self.always { result in
|
||||
switch result {
|
||||
case .success:
|
||||
()
|
||||
@@ -1792,7 +1835,7 @@ extension EventLoopFuture {
|
||||
/// - line: The line this function was called on, for debugging purposes.
|
||||
@inlinable
|
||||
public func preconditionFailure(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
|
||||
return self.always { result in
|
||||
self.always { result in
|
||||
switch result {
|
||||
case .success(let value):
|
||||
Swift.preconditionFailure("Expected failure, but got success: \(value)", file: file, line: line)
|
||||
@@ -1821,17 +1864,17 @@ public struct _NIOEventLoopFutureIdentifier: Hashable, Sendable {
|
||||
// 1. 0xbf15ca5d is randomly picked such that it fits into both 32 and 64 bit address spaces
|
||||
// 2. XOR with 0xbf15ca5d so that Memory Graph Debugger and other memory debugging tools
|
||||
// won't see it as a reference.
|
||||
return UInt(bitPattern: ObjectIdentifier(future)) ^ 0xbf15ca5d
|
||||
UInt(bitPattern: ObjectIdentifier(future)) ^ 0xbf15_ca5d
|
||||
}
|
||||
}
|
||||
|
||||
// EventLoopPromise is a reference type, but by its very nature is Sendable (if its Value is).
|
||||
extension EventLoopPromise: Sendable where Value: Sendable { }
|
||||
extension EventLoopPromise: Sendable where Value: Sendable {}
|
||||
|
||||
// EventLoopFuture is a reference type, but it is Sendable (if its Value is). However, we enforce
|
||||
// that by way of the guarantees of the EventLoop protocol, so the compiler cannot
|
||||
// check it.
|
||||
extension EventLoopFuture: @unchecked Sendable where Value: Sendable { }
|
||||
extension EventLoopFuture: @unchecked Sendable where Value: Sendable {}
|
||||
|
||||
extension EventLoopPromise where Value == Void {
|
||||
// Deliver a successful result to the associated `EventLoopFuture<Void>` object.
|
||||
@@ -1849,7 +1892,8 @@ extension Optional {
|
||||
/// to `promise`.
|
||||
///
|
||||
/// - Parameter promise: The promise to set or cascade to.
|
||||
public mutating func setOrCascade<Value>(to promise: EventLoopPromise<Value>?) where Wrapped == EventLoopPromise<Value> {
|
||||
public mutating func setOrCascade<Value>(to promise: EventLoopPromise<Value>?)
|
||||
where Wrapped == EventLoopPromise<Value> {
|
||||
guard let promise = promise else { return }
|
||||
|
||||
switch self {
|
||||
|
||||
@@ -31,4 +31,3 @@ public protocol FileDescriptor {
|
||||
/// Close this `FileDescriptor`.
|
||||
func close() throws
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,10 @@ public final class NIOFileHandle: FileDescriptor {
|
||||
}
|
||||
|
||||
deinit {
|
||||
assert(!self.isOpen, "leaked open NIOFileHandle(descriptor: \(self.descriptor)). Call `close()` to close or `takeDescriptorOwnership()` to take ownership and close by some other means.")
|
||||
assert(
|
||||
!self.isOpen,
|
||||
"leaked open NIOFileHandle(descriptor: \(self.descriptor)). Call `close()` to close or `takeDescriptorOwnership()` to take ownership and close by some other means."
|
||||
)
|
||||
}
|
||||
|
||||
/// Duplicates this `NIOFileHandle`. This means that a new `NIOFileHandle` object with a new underlying file descriptor
|
||||
@@ -61,7 +64,7 @@ public final class NIOFileHandle: FileDescriptor {
|
||||
///
|
||||
/// - returns: A new `NIOFileHandle` with a fresh underlying file descriptor but shared seek pointer.
|
||||
public func duplicate() throws -> NIOFileHandle {
|
||||
return try withUnsafeFileDescriptor { fd in
|
||||
try withUnsafeFileDescriptor { fd in
|
||||
NIOFileHandle(descriptor: try SystemCalls.dup(descriptor: fd))
|
||||
}
|
||||
}
|
||||
@@ -132,18 +135,18 @@ extension NIOFileHandle {
|
||||
|
||||
public static let `default` = Flags(posixMode: 0, posixFlags: 0)
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
public static let defaultPermissions = _S_IREAD | _S_IWRITE
|
||||
#else
|
||||
#else
|
||||
public static let defaultPermissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Allows file creation when opening file for writing. File owner is set to the effective user ID of the process.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - posixMode: `file mode` applied when file is created. Default permissions are: read and write for fileowner, read for owners group and others.
|
||||
public static func allowFileCreation(posixMode: NIOPOSIXFileMode = defaultPermissions) -> Flags {
|
||||
return Flags(posixMode: posixMode, posixFlags: O_CREAT)
|
||||
Flags(posixMode: posixMode, posixFlags: O_CREAT)
|
||||
}
|
||||
|
||||
/// Allows the specification of POSIX flags (e.g. `O_TRUNC`) and mode (e.g. `S_IWUSR`)
|
||||
@@ -153,7 +156,7 @@ extension NIOFileHandle {
|
||||
/// - mode: The POSIX mode (the third parameter for `open(2)`).
|
||||
/// - returns: A `NIOFileHandle.Mode` equivalent to the given POSIX flags and mode.
|
||||
public static func posix(flags: CInt, mode: NIOPOSIXFileMode) -> Flags {
|
||||
return Flags(posixMode: mode, posixFlags: flags)
|
||||
Flags(posixMode: mode, posixFlags: flags)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,11 +167,11 @@ extension NIOFileHandle {
|
||||
/// - mode: Access mode. Default mode is `.read`.
|
||||
/// - flags: Additional POSIX flags.
|
||||
public convenience init(path: String, mode: Mode = .read, flags: Flags = .default) throws {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
let fl = mode.posixFlags | flags.posixFlags | _O_NOINHERIT
|
||||
#else
|
||||
#else
|
||||
let fl = mode.posixFlags | flags.posixFlags | O_CLOEXEC
|
||||
#endif
|
||||
#endif
|
||||
let fd = try SystemCalls.open(file: path, oFlag: fl, mode: flags.posixMode)
|
||||
self.init(descriptor: fd)
|
||||
}
|
||||
@@ -186,6 +189,6 @@ extension NIOFileHandle {
|
||||
|
||||
extension NIOFileHandle: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "FileHandle { descriptor: \(self.descriptor) }"
|
||||
"FileHandle { descriptor: \(self.descriptor) }"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import Musl
|
||||
#error("The File Region module was unable to identify your C library.")
|
||||
#endif
|
||||
|
||||
|
||||
/// A `FileRegion` represent a readable portion usually created to be sent over the network.
|
||||
///
|
||||
/// Usually a `FileRegion` will allow the underlying transport to use `sendfile` to transfer its content and so allows transferring
|
||||
@@ -47,7 +46,7 @@ public struct FileRegion {
|
||||
/// The current reader index of this `FileRegion`
|
||||
private(set) public var readerIndex: Int {
|
||||
get {
|
||||
return Int(self._readerIndex)
|
||||
Int(self._readerIndex)
|
||||
}
|
||||
set {
|
||||
self._readerIndex = _UInt56(newValue)
|
||||
@@ -56,7 +55,7 @@ public struct FileRegion {
|
||||
|
||||
/// The end index of this `FileRegion`.
|
||||
public var endIndex: Int {
|
||||
return Int(self._endIndex)
|
||||
Int(self._endIndex)
|
||||
}
|
||||
|
||||
/// Create a new `FileRegion` from an open `NIOFileHandle`.
|
||||
@@ -75,7 +74,7 @@ public struct FileRegion {
|
||||
|
||||
/// The number of readable bytes within this FileRegion (taking the `readerIndex` and `endIndex` into account).
|
||||
public var readableBytes: Int {
|
||||
return endIndex - readerIndex
|
||||
endIndex - readerIndex
|
||||
}
|
||||
|
||||
/// Move the readerIndex forward by `offset`.
|
||||
@@ -106,13 +105,13 @@ extension FileRegion {
|
||||
}
|
||||
|
||||
extension FileRegion: Equatable {
|
||||
public static func ==(lhs: FileRegion, rhs: FileRegion) -> Bool {
|
||||
return lhs.fileHandle === rhs.fileHandle && lhs.readerIndex == rhs.readerIndex && lhs.endIndex == rhs.endIndex
|
||||
public static func == (lhs: FileRegion, rhs: FileRegion) -> Bool {
|
||||
lhs.fileHandle === rhs.fileHandle && lhs.readerIndex == rhs.readerIndex && lhs.endIndex == rhs.endIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension FileRegion: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "FileRegion { handle: \(self.fileHandle), readerIndex: \(self.readerIndex), endIndex: \(self.endIndex) }"
|
||||
"FileRegion { handle: \(self.fileHandle), readerIndex: \(self.readerIndex), endIndex: \(self.endIndex) }"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Atomics
|
||||
|
||||
#if canImport(Darwin)
|
||||
import Darwin
|
||||
#elseif os(Windows)
|
||||
@@ -49,8 +50,10 @@ extension NIOSingletons {
|
||||
}
|
||||
|
||||
get {
|
||||
return Self.getTrustworthyThreadCount(rawStorage: globalRawSuggestedLoopCount,
|
||||
environmentVariable: "NIO_SINGLETON_GROUP_LOOP_COUNT")
|
||||
Self.getTrustworthyThreadCount(
|
||||
rawStorage: globalRawSuggestedLoopCount,
|
||||
environmentVariable: "NIO_SINGLETON_GROUP_LOOP_COUNT"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +70,10 @@ extension NIOSingletons {
|
||||
}
|
||||
|
||||
get {
|
||||
return Self.getTrustworthyThreadCount(rawStorage: globalRawSuggestedBlockingThreadCount,
|
||||
environmentVariable: "NIO_SINGLETON_BLOCKING_POOL_THREAD_COUNT")
|
||||
Self.getTrustworthyThreadCount(
|
||||
rawStorage: globalRawSuggestedBlockingThreadCount,
|
||||
environmentVariable: "NIO_SINGLETON_BLOCKING_POOL_THREAD_COUNT"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +84,11 @@ extension NIOSingletons {
|
||||
/// - note: This value must be set _before_ any singletons are used and must only be set once.
|
||||
public static var singletonsEnabledSuggestion: Bool {
|
||||
get {
|
||||
let (exchanged, original) = globalRawSingletonsEnabled.compareExchange(expected: 0,
|
||||
desired: 1,
|
||||
ordering: .relaxed)
|
||||
let (exchanged, original) = globalRawSingletonsEnabled.compareExchange(
|
||||
expected: 0,
|
||||
desired: 1,
|
||||
ordering: .relaxed
|
||||
)
|
||||
if exchanged {
|
||||
// Never been set, we're committing to the default (enabled).
|
||||
assert(original == 0)
|
||||
@@ -96,15 +103,19 @@ extension NIOSingletons {
|
||||
|
||||
set {
|
||||
let intRepresentation = newValue ? 1 : -1
|
||||
let (exchanged, _) = globalRawSingletonsEnabled.compareExchange(expected: 0,
|
||||
desired: intRepresentation,
|
||||
ordering: .relaxed)
|
||||
let (exchanged, _) = globalRawSingletonsEnabled.compareExchange(
|
||||
expected: 0,
|
||||
desired: intRepresentation,
|
||||
ordering: .relaxed
|
||||
)
|
||||
guard exchanged else {
|
||||
fatalError("""
|
||||
Bug in user code: Global singleton enabled suggestion has been changed after \
|
||||
user or has been changed more than once. Either is an error, you must set this value very \
|
||||
early and only once.
|
||||
""")
|
||||
fatalError(
|
||||
"""
|
||||
Bug in user code: Global singleton enabled suggestion has been changed after \
|
||||
user or has been changed more than once. Either is an error, you must set this value very \
|
||||
early and only once.
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,19 +135,25 @@ extension NIOSingletons {
|
||||
// to 5.
|
||||
let (exchanged, _) = rawStorage.compareExchange(expected: 0, desired: -userValue, ordering: .relaxed)
|
||||
guard exchanged else {
|
||||
fatalError("""
|
||||
Bug in user code: Global singleton suggested loop/thread count has been changed after \
|
||||
user or has been changed more than once. Either is an error, you must set this value very early \
|
||||
and only once.
|
||||
""")
|
||||
fatalError(
|
||||
"""
|
||||
Bug in user code: Global singleton suggested loop/thread count has been changed after \
|
||||
user or has been changed more than once. Either is an error, you must set this value very early \
|
||||
and only once.
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private static func validateTrustedThreadCount(_ threadCount: Int) {
|
||||
assert(threadCount > 0,
|
||||
"BUG IN NIO, please report: negative suggested loop/thread count: \(threadCount)")
|
||||
assert(threadCount <= 1024,
|
||||
"BUG IN NIO, please report: overly big suggested loop/thread count: \(threadCount)")
|
||||
assert(
|
||||
threadCount > 0,
|
||||
"BUG IN NIO, please report: negative suggested loop/thread count: \(threadCount)"
|
||||
)
|
||||
assert(
|
||||
threadCount <= 1024,
|
||||
"BUG IN NIO, please report: overly big suggested loop/thread count: \(threadCount)"
|
||||
)
|
||||
}
|
||||
|
||||
private static func getTrustworthyThreadCount(rawStorage: ManagedAtomic<Int>, environmentVariable: String) -> Int {
|
||||
@@ -144,15 +161,15 @@ extension NIOSingletons {
|
||||
|
||||
let rawSuggestion = rawStorage.load(ordering: .relaxed)
|
||||
switch rawSuggestion {
|
||||
case 0: // == 0
|
||||
case 0: // == 0
|
||||
// Not set by user, not yet finalised, let's try to get it from the env var and fall back to
|
||||
// `System.coreCount`.
|
||||
let envVarString = getenv(environmentVariable).map { String(cString: $0) }
|
||||
returnedValueUnchecked = envVarString.flatMap(Int.init) ?? System.coreCount
|
||||
case .min ..< 0: // < 0
|
||||
case .min..<0: // < 0
|
||||
// Untrusted and unchecked user value. Let's invert and then sanitise/check.
|
||||
returnedValueUnchecked = -rawSuggestion
|
||||
case 1 ... .max: // > 0
|
||||
case 1 ... .max: // > 0
|
||||
// Trustworthy value that has been evaluated and sanitised before.
|
||||
let returnValue = rawSuggestion
|
||||
Self.validateTrustedThreadCount(returnValue)
|
||||
@@ -167,9 +184,11 @@ extension NIOSingletons {
|
||||
Self.validateTrustedThreadCount(returnValue)
|
||||
|
||||
// Store it for next time.
|
||||
let (exchanged, _) = rawStorage.compareExchange(expected: rawSuggestion,
|
||||
desired: returnValue,
|
||||
ordering: .relaxed)
|
||||
let (exchanged, _) = rawStorage.compareExchange(
|
||||
expected: rawSuggestion,
|
||||
desired: returnValue,
|
||||
ordering: .relaxed
|
||||
)
|
||||
if !exchanged {
|
||||
// We lost the race, this must mean it has been concurrently set correctly so we can safely recurse
|
||||
// and try again.
|
||||
|
||||
+31
-21
@@ -26,7 +26,7 @@ import typealias WinSDK.WCHAR
|
||||
import typealias WinSDK.WORD
|
||||
|
||||
internal func MAKELANGID(_ p: WORD, _ s: WORD) -> DWORD {
|
||||
return DWORD((s << 10) | p)
|
||||
DWORD((s << 10) | p)
|
||||
}
|
||||
#elseif canImport(Glibc)
|
||||
import Glibc
|
||||
@@ -49,15 +49,19 @@ public struct IOError: Swift.Error {
|
||||
/// The actual reason (in an human-readable form) for this `IOError`.
|
||||
private var failureDescription: String
|
||||
|
||||
@available(*, deprecated, message: "NIO no longer uses FailureDescription, use IOError.description for a human-readable error description")
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "NIO no longer uses FailureDescription, use IOError.description for a human-readable error description"
|
||||
)
|
||||
public var reason: FailureDescription {
|
||||
return .reason(self.failureDescription)
|
||||
.reason(self.failureDescription)
|
||||
}
|
||||
|
||||
private enum Error {
|
||||
#if os(Windows)
|
||||
case windows(DWORD)
|
||||
case winsock(CInt)
|
||||
case windows(DWORD)
|
||||
case winsock(CInt)
|
||||
#endif
|
||||
case errno(CInt)
|
||||
}
|
||||
@@ -70,13 +74,13 @@ public struct IOError: Swift.Error {
|
||||
case .errno(let code):
|
||||
return code
|
||||
#if os(Windows)
|
||||
default:
|
||||
fatalError("IOError domain is not `errno`")
|
||||
default:
|
||||
fatalError("IOError domain is not `errno`")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
public init(windows code: DWORD, reason: String) {
|
||||
self.error = .windows(code)
|
||||
self.failureDescription = reason
|
||||
@@ -86,7 +90,7 @@ public struct IOError: Swift.Error {
|
||||
self.error = .winsock(code)
|
||||
self.failureDescription = reason
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Creates a new `IOError``
|
||||
///
|
||||
@@ -126,9 +130,10 @@ private func reasonForError(errnoCode: CInt, reason: String) -> String {
|
||||
|
||||
#if os(Windows)
|
||||
private func reasonForWinError(_ code: DWORD) -> String {
|
||||
let dwFlags: DWORD = DWORD(FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
||||
| DWORD(FORMAT_MESSAGE_FROM_SYSTEM)
|
||||
| DWORD(FORMAT_MESSAGE_IGNORE_INSERTS)
|
||||
let dwFlags: DWORD =
|
||||
DWORD(FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
||||
| DWORD(FORMAT_MESSAGE_FROM_SYSTEM)
|
||||
| DWORD(FORMAT_MESSAGE_IGNORE_INSERTS)
|
||||
|
||||
var buffer: UnsafeMutablePointer<WCHAR>?
|
||||
// We use `FORMAT_MESSAGE_ALLOCATE_BUFFER` in flags which means that the
|
||||
@@ -136,9 +141,15 @@ private func reasonForWinError(_ code: DWORD) -> String {
|
||||
// expects a `LPWSTR` and expects the user to type-pun in this case.
|
||||
let dwResult: DWORD = withUnsafeMutablePointer(to: &buffer) {
|
||||
$0.withMemoryRebound(to: WCHAR.self, capacity: 2) {
|
||||
FormatMessageW(dwFlags, nil, code,
|
||||
MAKELANGID(WORD(LANG_NEUTRAL), WORD(SUBLANG_DEFAULT)),
|
||||
$0, 0, nil)
|
||||
FormatMessageW(
|
||||
dwFlags,
|
||||
nil,
|
||||
code,
|
||||
MAKELANGID(WORD(LANG_NEUTRAL), WORD(SUBLANG_DEFAULT)),
|
||||
$0,
|
||||
0,
|
||||
nil
|
||||
)
|
||||
}
|
||||
}
|
||||
guard dwResult > 0, let message = buffer else {
|
||||
@@ -151,11 +162,11 @@ private func reasonForWinError(_ code: DWORD) -> String {
|
||||
|
||||
extension IOError: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return self.localizedDescription
|
||||
self.localizedDescription
|
||||
}
|
||||
|
||||
public var localizedDescription: String {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
switch self.error {
|
||||
case .errno(let errno):
|
||||
return reasonForError(errnoCode: errno, reason: self.failureDescription)
|
||||
@@ -164,9 +175,9 @@ extension IOError: CustomStringConvertible {
|
||||
case .winsock(let code):
|
||||
return reasonForWinError(DWORD(code))
|
||||
}
|
||||
#else
|
||||
#else
|
||||
return reasonForError(errnoCode: self.errnoCode, reason: self.failureDescription)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +192,7 @@ enum CoreIOResult<T: Equatable>: Equatable {
|
||||
case processed(T)
|
||||
}
|
||||
|
||||
internal extension CoreIOResult where T: FixedWidthInteger {
|
||||
extension CoreIOResult where T: FixedWidthInteger {
|
||||
var result: T {
|
||||
switch self {
|
||||
case .processed(let value):
|
||||
@@ -191,4 +202,3 @@ internal extension CoreIOResult where T: FixedWidthInteger {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
public struct NIOIPProtocol: RawRepresentable, Hashable, Sendable {
|
||||
public typealias RawValue = UInt8
|
||||
public var rawValue: RawValue
|
||||
|
||||
|
||||
@inlinable
|
||||
public init(rawValue: RawValue) {
|
||||
self.rawValue = rawValue
|
||||
@@ -169,7 +169,7 @@ extension NIOIPProtocol: CustomStringConvertible {
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var description: String {
|
||||
let name = self.name ?? "Unknown Protocol"
|
||||
return "\(name) - \(rawValue)"
|
||||
|
||||
@@ -19,11 +19,15 @@ enum _IntegerBitPacking {}
|
||||
|
||||
extension _IntegerBitPacking {
|
||||
@inlinable
|
||||
static func packUU<Left: FixedWidthInteger & UnsignedInteger,
|
||||
Right: FixedWidthInteger & UnsignedInteger,
|
||||
Result: FixedWidthInteger & UnsignedInteger>(_ left: Left,
|
||||
_ right: Right,
|
||||
type: Result.Type = Result.self) -> Result {
|
||||
static func packUU<
|
||||
Left: FixedWidthInteger & UnsignedInteger,
|
||||
Right: FixedWidthInteger & UnsignedInteger,
|
||||
Result: FixedWidthInteger & UnsignedInteger
|
||||
>(
|
||||
_ left: Left,
|
||||
_ right: Right,
|
||||
type: Result.Type = Result.self
|
||||
) -> Result {
|
||||
assert(MemoryLayout<Left>.size + MemoryLayout<Right>.size <= MemoryLayout<Result>.size)
|
||||
|
||||
let resultLeft = Result(left)
|
||||
@@ -34,11 +38,15 @@ extension _IntegerBitPacking {
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func unpackUU<Input: FixedWidthInteger & UnsignedInteger,
|
||||
Left: FixedWidthInteger & UnsignedInteger,
|
||||
Right: FixedWidthInteger & UnsignedInteger>(_ input: Input,
|
||||
leftType: Left.Type = Left.self,
|
||||
rightType: Right.Type = Right.self) -> (Left, Right) {
|
||||
static func unpackUU<
|
||||
Input: FixedWidthInteger & UnsignedInteger,
|
||||
Left: FixedWidthInteger & UnsignedInteger,
|
||||
Right: FixedWidthInteger & UnsignedInteger
|
||||
>(
|
||||
_ input: Input,
|
||||
leftType: Left.Type = Left.self,
|
||||
rightType: Right.Type = Right.self
|
||||
) -> (Left, Right) {
|
||||
assert(MemoryLayout<Left>.size + MemoryLayout<Right>.size <= MemoryLayout<Input>.size)
|
||||
|
||||
let leftMask = Input(Left.max)
|
||||
@@ -57,7 +65,7 @@ enum IntegerBitPacking {}
|
||||
extension IntegerBitPacking {
|
||||
@inlinable
|
||||
static func packUInt32UInt16UInt8(_ left: UInt32, _ middle: UInt16, _ right: UInt8) -> UInt64 {
|
||||
return _IntegerBitPacking.packUU(
|
||||
_IntegerBitPacking.packUU(
|
||||
_IntegerBitPacking.packUU(right, middle, type: UInt32.self),
|
||||
left
|
||||
)
|
||||
@@ -72,27 +80,27 @@ extension IntegerBitPacking {
|
||||
|
||||
@inlinable
|
||||
static func packUInt8UInt8(_ left: UInt8, _ right: UInt8) -> UInt16 {
|
||||
return _IntegerBitPacking.packUU(left, right)
|
||||
_IntegerBitPacking.packUU(left, right)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func unpackUInt8UInt8(_ value: UInt16) -> (UInt8, UInt8) {
|
||||
return _IntegerBitPacking.unpackUU(value)
|
||||
_IntegerBitPacking.unpackUU(value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func packUInt16UInt8(_ left: UInt16, _ right: UInt8) -> UInt32 {
|
||||
return _IntegerBitPacking.packUU(left, right)
|
||||
_IntegerBitPacking.packUU(left, right)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func unpackUInt16UInt8(_ value: UInt32) -> (UInt16, UInt8) {
|
||||
return _IntegerBitPacking.unpackUU(value)
|
||||
_IntegerBitPacking.unpackUU(value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
static func packUInt32CInt(_ left: UInt32, _ right: CInt) -> UInt64 {
|
||||
return _IntegerBitPacking.packUU(left, UInt32(truncatingIfNeeded: right))
|
||||
_IntegerBitPacking.packUU(left, UInt32(truncatingIfNeeded: right))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
|
||||
@@ -50,18 +50,17 @@ extension Int {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension _UInt24: Equatable {
|
||||
@inlinable
|
||||
public static func ==(lhs: _UInt24, rhs: _UInt24) -> Bool {
|
||||
return lhs._backing == rhs._backing
|
||||
public static func == (lhs: _UInt24, rhs: _UInt24) -> Bool {
|
||||
lhs._backing == rhs._backing
|
||||
}
|
||||
}
|
||||
|
||||
extension _UInt24: CustomStringConvertible {
|
||||
@usableFromInline
|
||||
var description: String {
|
||||
return UInt32(self).description
|
||||
UInt32(self).description
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ struct _UInt56: Sendable {
|
||||
|
||||
static let bitWidth: Int = 56
|
||||
|
||||
private static let initializeUInt64 : UInt64 = (1 << 56) - 1
|
||||
private static let initializeUInt64: UInt64 = (1 << 56) - 1
|
||||
static let max: _UInt56 = .init(initializeUInt64)
|
||||
static let min: _UInt56 = .init(0)
|
||||
}
|
||||
@@ -90,9 +89,11 @@ extension _UInt56 {
|
||||
|
||||
extension UInt64 {
|
||||
init(_ value: _UInt56) {
|
||||
self = IntegerBitPacking.packUInt32UInt16UInt8(value._backing.0,
|
||||
value._backing.1,
|
||||
value._backing.2)
|
||||
self = IntegerBitPacking.packUInt32UInt16UInt8(
|
||||
value._backing.0,
|
||||
value._backing.1,
|
||||
value._backing.2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,13 +105,13 @@ extension Int {
|
||||
|
||||
extension _UInt56: Equatable {
|
||||
@inlinable
|
||||
public static func ==(lhs: _UInt56, rhs: _UInt56) -> Bool {
|
||||
return lhs._backing == rhs._backing
|
||||
public static func == (lhs: _UInt56, rhs: _UInt56) -> Bool {
|
||||
lhs._backing == rhs._backing
|
||||
}
|
||||
}
|
||||
|
||||
extension _UInt56: CustomStringConvertible {
|
||||
var description: String {
|
||||
return UInt64(self).description
|
||||
UInt64(self).description
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ import typealias WinSDK.UINT8
|
||||
#endif
|
||||
|
||||
#if !os(Windows)
|
||||
private extension ifaddrs {
|
||||
var dstaddr: UnsafeMutablePointer<sockaddr>? {
|
||||
extension ifaddrs {
|
||||
fileprivate var dstaddr: UnsafeMutablePointer<sockaddr>? {
|
||||
#if os(Linux) || os(Android)
|
||||
return self.ifa_ifu.ifu_dstaddr
|
||||
#elseif canImport(Darwin)
|
||||
@@ -52,7 +52,7 @@ private extension ifaddrs {
|
||||
#endif
|
||||
}
|
||||
|
||||
var broadaddr: UnsafeMutablePointer<sockaddr>? {
|
||||
fileprivate var broadaddr: UnsafeMutablePointer<sockaddr>? {
|
||||
#if os(Linux) || os(Android)
|
||||
return self.ifa_ifu.ifu_broadaddr
|
||||
#elseif canImport(Darwin)
|
||||
@@ -93,11 +93,15 @@ public final class NIONetworkInterface: Sendable {
|
||||
/// The index of the interface, as provided by `if_nametoindex`.
|
||||
public let interfaceIndex: Int
|
||||
|
||||
#if os(Windows)
|
||||
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
|
||||
self.name = String(decodingCString: pAdapter.pointee.FriendlyName,
|
||||
as: UTF16.self)
|
||||
#if os(Windows)
|
||||
internal init?(
|
||||
_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>
|
||||
) {
|
||||
self.name = String(
|
||||
decodingCString: pAdapter.pointee.FriendlyName,
|
||||
as: UTF16.self
|
||||
)
|
||||
guard let address = pAddress.pointee.Address.lpSockaddr.convert() else {
|
||||
return nil
|
||||
}
|
||||
@@ -121,7 +125,7 @@ public final class NIONetworkInterface: Sendable {
|
||||
self.pointToPointDestinationAddress = nil
|
||||
self.multicastSupported = false
|
||||
}
|
||||
#else
|
||||
#else
|
||||
internal init?(_ caddr: ifaddrs) {
|
||||
self.name = String(cString: caddr.ifa_name!)
|
||||
|
||||
@@ -163,7 +167,7 @@ public final class NIONetworkInterface: Sendable {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "NIONetworkDevice")
|
||||
@@ -177,13 +181,11 @@ extension NIONetworkInterface: CustomDebugStringConvertible {
|
||||
|
||||
@available(*, deprecated, renamed: "NIONetworkDevice")
|
||||
extension NIONetworkInterface: Equatable {
|
||||
public static func ==(lhs: NIONetworkInterface, rhs: NIONetworkInterface) -> Bool {
|
||||
return lhs.name == rhs.name &&
|
||||
lhs.address == rhs.address &&
|
||||
lhs.netmask == rhs.netmask &&
|
||||
lhs.broadcastAddress == rhs.broadcastAddress &&
|
||||
lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress &&
|
||||
lhs.interfaceIndex == rhs.interfaceIndex
|
||||
public static func == (lhs: NIONetworkInterface, rhs: NIONetworkInterface) -> Bool {
|
||||
lhs.name == rhs.name && lhs.address == rhs.address && lhs.netmask == rhs.netmask
|
||||
&& lhs.broadcastAddress == rhs.broadcastAddress
|
||||
&& lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress
|
||||
&& lhs.interfaceIndex == rhs.interfaceIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +214,7 @@ public struct NIONetworkDevice {
|
||||
/// The name of the network device.
|
||||
public var name: String {
|
||||
get {
|
||||
return self.backing.name
|
||||
self.backing.name
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -223,7 +225,7 @@ public struct NIONetworkDevice {
|
||||
/// The address associated with the given network device.
|
||||
public var address: SocketAddress? {
|
||||
get {
|
||||
return self.backing.address
|
||||
self.backing.address
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -234,7 +236,7 @@ public struct NIONetworkDevice {
|
||||
/// The netmask associated with this address, if any.
|
||||
public var netmask: SocketAddress? {
|
||||
get {
|
||||
return self.backing.netmask
|
||||
self.backing.netmask
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -246,7 +248,7 @@ public struct NIONetworkDevice {
|
||||
/// interfaces do not, especially those that have a `pointToPointDestinationAddress`.
|
||||
public var broadcastAddress: SocketAddress? {
|
||||
get {
|
||||
return self.backing.broadcastAddress
|
||||
self.backing.broadcastAddress
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -259,7 +261,7 @@ public struct NIONetworkDevice {
|
||||
/// instead.
|
||||
public var pointToPointDestinationAddress: SocketAddress? {
|
||||
get {
|
||||
return self.backing.pointToPointDestinationAddress
|
||||
self.backing.pointToPointDestinationAddress
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -270,7 +272,7 @@ public struct NIONetworkDevice {
|
||||
/// If the Interface supports Multicast
|
||||
public var multicastSupported: Bool {
|
||||
get {
|
||||
return self.backing.multicastSupported
|
||||
self.backing.multicastSupported
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -281,7 +283,7 @@ public struct NIONetworkDevice {
|
||||
/// The index of the interface, as provided by `if_nametoindex`.
|
||||
public var interfaceIndex: Int {
|
||||
get {
|
||||
return self.backing.interfaceIndex
|
||||
self.backing.interfaceIndex
|
||||
}
|
||||
set {
|
||||
self.uniquifyIfNeeded()
|
||||
@@ -294,15 +296,17 @@ public struct NIONetworkDevice {
|
||||
/// This constructor will fail if NIO does not understand the format of the underlying
|
||||
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
|
||||
/// addressed interfaces on most platforms, which NIO does not currently understand.
|
||||
#if os(Windows)
|
||||
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
|
||||
#if os(Windows)
|
||||
internal init?(
|
||||
_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>
|
||||
) {
|
||||
guard let backing = Backing(pAdapter, pAddress) else {
|
||||
return nil
|
||||
}
|
||||
self.backing = backing
|
||||
}
|
||||
#else
|
||||
#else
|
||||
internal init?(_ caddr: ifaddrs) {
|
||||
guard let backing = Backing(caddr) else {
|
||||
return nil
|
||||
@@ -310,9 +314,9 @@ public struct NIONetworkDevice {
|
||||
|
||||
self.backing = backing
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !os(Windows)
|
||||
#if !os(Windows)
|
||||
/// Convert a `NIONetworkInterface` to a `NIONetworkDevice`. As `NIONetworkDevice`s are a superset of `NIONetworkInterface`s,
|
||||
/// it is always possible to perform this conversion.
|
||||
@available(*, deprecated, message: "This is a compatibility helper, and will be removed in a future release")
|
||||
@@ -327,15 +331,17 @@ public struct NIONetworkDevice {
|
||||
interfaceIndex: interface.interfaceIndex
|
||||
)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public init(name: String,
|
||||
address: SocketAddress?,
|
||||
netmask: SocketAddress?,
|
||||
broadcastAddress: SocketAddress?,
|
||||
pointToPointDestinationAddress: SocketAddress,
|
||||
multicastSupported: Bool,
|
||||
interfaceIndex: Int) {
|
||||
public init(
|
||||
name: String,
|
||||
address: SocketAddress?,
|
||||
netmask: SocketAddress?,
|
||||
broadcastAddress: SocketAddress?,
|
||||
pointToPointDestinationAddress: SocketAddress,
|
||||
multicastSupported: Bool,
|
||||
interfaceIndex: Int
|
||||
) {
|
||||
self.backing = Backing(
|
||||
name: name,
|
||||
address: address,
|
||||
@@ -387,11 +393,15 @@ extension NIONetworkDevice {
|
||||
/// This constructor will fail if NIO does not understand the format of the underlying
|
||||
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
|
||||
/// addressed interfaces on most platforms, which NIO does not currently understand.
|
||||
#if os(Windows)
|
||||
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
|
||||
self.name = String(decodingCString: pAdapter.pointee.FriendlyName,
|
||||
as: UTF16.self)
|
||||
#if os(Windows)
|
||||
internal init?(
|
||||
_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
|
||||
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>
|
||||
) {
|
||||
self.name = String(
|
||||
decodingCString: pAdapter.pointee.FriendlyName,
|
||||
as: UTF16.self
|
||||
)
|
||||
self.address = pAddress.pointee.Address.lpSockaddr.convert()
|
||||
|
||||
switch pAddress.pointee.Address.lpSockaddr.pointee.sa_family {
|
||||
@@ -412,7 +422,7 @@ extension NIONetworkDevice {
|
||||
self.pointToPointDestinationAddress = nil
|
||||
self.multicastSupported = false
|
||||
}
|
||||
#else
|
||||
#else
|
||||
internal init?(_ caddr: ifaddrs) {
|
||||
self.name = String(cString: caddr.ifa_name!)
|
||||
self.address = caddr.ifa_addr.flatMap { $0.convert() }
|
||||
@@ -436,7 +446,7 @@ extension NIONetworkDevice {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
init(copying original: Backing) {
|
||||
self.name = original.name
|
||||
@@ -448,13 +458,15 @@ extension NIONetworkDevice {
|
||||
self.interfaceIndex = original.interfaceIndex
|
||||
}
|
||||
|
||||
init(name: String,
|
||||
address: SocketAddress?,
|
||||
netmask: SocketAddress?,
|
||||
broadcastAddress: SocketAddress?,
|
||||
pointToPointDestinationAddress: SocketAddress?,
|
||||
multicastSupported: Bool,
|
||||
interfaceIndex: Int) {
|
||||
init(
|
||||
name: String,
|
||||
address: SocketAddress?,
|
||||
netmask: SocketAddress?,
|
||||
broadcastAddress: SocketAddress?,
|
||||
pointToPointDestinationAddress: SocketAddress?,
|
||||
multicastSupported: Bool,
|
||||
interfaceIndex: Int
|
||||
) {
|
||||
self.name = name
|
||||
self.address = address
|
||||
self.netmask = netmask
|
||||
@@ -476,13 +488,11 @@ extension NIONetworkDevice: CustomDebugStringConvertible {
|
||||
|
||||
// Sadly, as this is class-backed we cannot synthesise the implementation.
|
||||
extension NIONetworkDevice: Equatable {
|
||||
public static func ==(lhs: NIONetworkDevice, rhs: NIONetworkDevice) -> Bool {
|
||||
return lhs.name == rhs.name &&
|
||||
lhs.address == rhs.address &&
|
||||
lhs.netmask == rhs.netmask &&
|
||||
lhs.broadcastAddress == rhs.broadcastAddress &&
|
||||
lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress &&
|
||||
lhs.interfaceIndex == rhs.interfaceIndex
|
||||
public static func == (lhs: NIONetworkDevice, rhs: NIONetworkDevice) -> Bool {
|
||||
lhs.name == rhs.name && lhs.address == rhs.address && lhs.netmask == rhs.netmask
|
||||
&& lhs.broadcastAddress == rhs.broadcastAddress
|
||||
&& lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress
|
||||
&& lhs.interfaceIndex == rhs.interfaceIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,4 +506,3 @@ extension NIONetworkDevice: Hashable {
|
||||
hasher.combine(self.interfaceIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ enum Linux {
|
||||
var buf = ByteBufferAllocator().buffer(capacity: 1024)
|
||||
try buf.writeWithUnsafeMutableBytes(minimumWritableBytes: buf.capacity) { ptr in
|
||||
let res = try fh.withUnsafeFileDescriptor { fd -> CoreIOResult<ssize_t> in
|
||||
return try SystemCalls.read(descriptor: fd, pointer: ptr.baseAddress!, size: ptr.count)
|
||||
try SystemCalls.read(descriptor: fd, pointer: ptr.baseAddress!, size: ptr.count)
|
||||
}
|
||||
switch res {
|
||||
case .processed(let n):
|
||||
@@ -62,8 +62,10 @@ enum Linux {
|
||||
|
||||
/// Get the available core count according to cgroup1 restrictions.
|
||||
/// Round up to the next whole number.
|
||||
static func coreCountCgroup1Restriction(quota quotaPath: String = Linux.cfsQuotaPath,
|
||||
period periodPath: String = Linux.cfsPeriodPath) -> Int? {
|
||||
static func coreCountCgroup1Restriction(
|
||||
quota quotaPath: String = Linux.cfsQuotaPath,
|
||||
period periodPath: String = Linux.cfsPeriodPath
|
||||
) -> Int? {
|
||||
guard
|
||||
let quota = try? Int(firstLineOfFile(path: quotaPath)),
|
||||
quota > 0
|
||||
@@ -72,18 +74,18 @@ enum Linux {
|
||||
let period = try? Int(firstLineOfFile(path: periodPath)),
|
||||
period > 0
|
||||
else { return nil }
|
||||
return (quota - 1 + period) / period // always round up if fractional CPU quota requested
|
||||
return (quota - 1 + period) / period // always round up if fractional CPU quota requested
|
||||
}
|
||||
|
||||
/// Get the available core count according to cgroup2 restrictions.
|
||||
/// Round up to the next whole number.
|
||||
static func coreCountCgroup2Restriction(cpuMaxPath: String = Linux.cfsCpuMaxPath) -> Int? {
|
||||
guard let maxDetails = try? firstLineOfFile(path: cpuMaxPath),
|
||||
let spaceIndex = maxDetails.firstIndex(of: " "),
|
||||
let quota = Int(maxDetails[maxDetails.startIndex ..< spaceIndex]),
|
||||
let period = Int(maxDetails[maxDetails.index(after: spaceIndex) ..< maxDetails.endIndex])
|
||||
let spaceIndex = maxDetails.firstIndex(of: " "),
|
||||
let quota = Int(maxDetails[maxDetails.startIndex..<spaceIndex]),
|
||||
let period = Int(maxDetails[maxDetails.index(after: spaceIndex)..<maxDetails.endIndex])
|
||||
else { return nil }
|
||||
return (quota - 1 + period) / period // always round up if fractional CPU quota requested
|
||||
return (quota - 1 + period) / period // always round up if fractional CPU quota requested
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
/// safe to write.
|
||||
public struct MarkedCircularBuffer<Element>: CustomStringConvertible {
|
||||
@usableFromInline internal var _buffer: CircularBuffer<Element>
|
||||
@usableFromInline internal var _markedIndexOffset: Int? /* nil: nothing marked */
|
||||
@usableFromInline internal var _markedIndexOffset: Int? // nil: nothing marked
|
||||
|
||||
/// Create a new instance.
|
||||
///
|
||||
@@ -60,24 +60,24 @@ public struct MarkedCircularBuffer<Element>: CustomStringConvertible {
|
||||
/// The first element in the buffer.
|
||||
@inlinable
|
||||
public var first: Element? {
|
||||
return self._buffer.first
|
||||
self._buffer.first
|
||||
}
|
||||
|
||||
/// If the buffer is empty.
|
||||
@inlinable
|
||||
public var isEmpty: Bool {
|
||||
return self._buffer.isEmpty
|
||||
self._buffer.isEmpty
|
||||
}
|
||||
|
||||
/// The number of elements in the buffer.
|
||||
@inlinable
|
||||
public var count: Int {
|
||||
return self._buffer.count
|
||||
self._buffer.count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var description: String {
|
||||
return self._buffer.description
|
||||
self._buffer.description
|
||||
}
|
||||
|
||||
// MARK: Marking
|
||||
@@ -119,13 +119,13 @@ public struct MarkedCircularBuffer<Element>: CustomStringConvertible {
|
||||
/// Returns the marked element.
|
||||
@inlinable
|
||||
public var markedElement: Element? {
|
||||
return self.markedElementIndex.map { self._buffer[$0] }
|
||||
self.markedElementIndex.map { self._buffer[$0] }
|
||||
}
|
||||
|
||||
/// Returns true if the buffer has been marked at all.
|
||||
@inlinable
|
||||
public var hasMark: Bool {
|
||||
return self._markedIndexOffset != nil
|
||||
self._markedIndexOffset != nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,20 +136,20 @@ extension MarkedCircularBuffer: Collection, MutableCollection {
|
||||
|
||||
@inlinable
|
||||
public func index(after i: Index) -> Index {
|
||||
return self._buffer.index(after: i)
|
||||
self._buffer.index(after: i)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var startIndex: Index { return self._buffer.startIndex }
|
||||
public var startIndex: Index { self._buffer.startIndex }
|
||||
|
||||
@inlinable
|
||||
public var endIndex: Index { return self._buffer.endIndex }
|
||||
public var endIndex: Index { self._buffer.endIndex }
|
||||
|
||||
/// Retrieves the element at the given index from the buffer, without removing it.
|
||||
@inlinable
|
||||
public subscript(index: Index) -> Element {
|
||||
get {
|
||||
return self._buffer[index]
|
||||
self._buffer[index]
|
||||
}
|
||||
set {
|
||||
self._buffer[index] = newValue
|
||||
@@ -159,7 +159,7 @@ extension MarkedCircularBuffer: Collection, MutableCollection {
|
||||
@inlinable
|
||||
public subscript(bounds: Range<Index>) -> SubSequence {
|
||||
get {
|
||||
return self._buffer[bounds]
|
||||
self._buffer[bounds]
|
||||
}
|
||||
set {
|
||||
var index = bounds.lowerBound
|
||||
@@ -176,17 +176,17 @@ extension MarkedCircularBuffer: Collection, MutableCollection {
|
||||
extension MarkedCircularBuffer: RandomAccessCollection {
|
||||
@inlinable
|
||||
public func index(_ i: Index, offsetBy distance: Int) -> Index {
|
||||
return self._buffer.index(i, offsetBy: distance)
|
||||
self._buffer.index(i, offsetBy: distance)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func distance(from start: Index, to end: Index) -> Int {
|
||||
return self._buffer.distance(from: start, to: end)
|
||||
self._buffer.distance(from: start, to: end)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func index(before i: Index) -> Index {
|
||||
return self._buffer.index(before: i)
|
||||
self._buffer.index(before: i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public protocol MulticastChannel: Channel {
|
||||
/// `nil` if you are not interested in the result of the operation.
|
||||
func joinGroup(_ group: SocketAddress, promise: EventLoopPromise<Void>?)
|
||||
|
||||
#if !os(Windows)
|
||||
#if !os(Windows)
|
||||
/// Request that the `MulticastChannel` join the multicast group given by `group` on the interface
|
||||
/// given by `interface`.
|
||||
///
|
||||
@@ -36,7 +36,7 @@ public protocol MulticastChannel: Channel {
|
||||
/// `nil` if you are not interested in the result of the operation.
|
||||
@available(*, deprecated, renamed: "joinGroup(_:device:promise:)")
|
||||
func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?, promise: EventLoopPromise<Void>?)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Request that the `MulticastChannel` join the multicast group given by `group` on the device
|
||||
/// given by `device`.
|
||||
@@ -56,7 +56,7 @@ public protocol MulticastChannel: Channel {
|
||||
/// `nil` if you are not interested in the result of the operation.
|
||||
func leaveGroup(_ group: SocketAddress, promise: EventLoopPromise<Void>?)
|
||||
|
||||
#if !os(Windows)
|
||||
#if !os(Windows)
|
||||
/// Request that the `MulticastChannel` leave the multicast group given by `group` on the interface
|
||||
/// given by `interface`.
|
||||
///
|
||||
@@ -67,7 +67,7 @@ public protocol MulticastChannel: Channel {
|
||||
/// `nil` if you are not interested in the result of the operation.
|
||||
@available(*, deprecated, renamed: "leaveGroup(_:device:promise:)")
|
||||
func leaveGroup(_ group: SocketAddress, interface: NIONetworkInterface?, promise: EventLoopPromise<Void>?)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Request that the `MulticastChannel` leave the multicast group given by `group` on the device
|
||||
/// given by `device`.
|
||||
@@ -80,7 +80,6 @@ public protocol MulticastChannel: Channel {
|
||||
func leaveGroup(_ group: SocketAddress, device: NIONetworkDevice?, promise: EventLoopPromise<Void>?)
|
||||
}
|
||||
|
||||
|
||||
// MARK:- Default implementations for MulticastChannel
|
||||
extension MulticastChannel {
|
||||
public func joinGroup(_ group: SocketAddress, promise: EventLoopPromise<Void>?) {
|
||||
@@ -93,14 +92,14 @@ extension MulticastChannel {
|
||||
return promise.futureResult
|
||||
}
|
||||
|
||||
#if !os(Windows)
|
||||
#if !os(Windows)
|
||||
@available(*, deprecated, renamed: "joinGroup(_:device:)")
|
||||
public func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture<Void> {
|
||||
let promise = self.eventLoop.makePromise(of: Void.self)
|
||||
self.joinGroup(group, interface: interface, promise: promise)
|
||||
return promise.futureResult
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public func joinGroup(_ group: SocketAddress, device: NIONetworkDevice?) -> EventLoopFuture<Void> {
|
||||
let promise = self.eventLoop.makePromise(of: Void.self)
|
||||
@@ -118,14 +117,14 @@ extension MulticastChannel {
|
||||
return promise.futureResult
|
||||
}
|
||||
|
||||
#if !os(Windows)
|
||||
#if !os(Windows)
|
||||
@available(*, deprecated, renamed: "leaveGroup(_:device:)")
|
||||
public func leaveGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture<Void> {
|
||||
let promise = self.eventLoop.makePromise(of: Void.self)
|
||||
self.leaveGroup(group, interface: interface, promise: promise)
|
||||
return promise.futureResult
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public func leaveGroup(_ group: SocketAddress, device: NIONetworkDevice?) -> EventLoopFuture<Void> {
|
||||
let promise = self.eventLoop.makePromise(of: Void.self)
|
||||
@@ -176,4 +175,3 @@ public struct NIOMulticastNotSupportedError: Error {
|
||||
public struct NIOMulticastNotImplementedError: Error {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
/// }
|
||||
public struct NIOAny {
|
||||
@usableFromInline
|
||||
/* private but _versioned */ let _storage: _NIOAny
|
||||
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
|
||||
@@ -98,7 +98,9 @@ public struct NIOAny {
|
||||
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)")
|
||||
fatalError(
|
||||
"tried to decode as type \(ByteBuffer.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +124,9 @@ public struct NIOAny {
|
||||
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)")
|
||||
fatalError(
|
||||
"tried to decode as type \(IOData.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +150,9 @@ public struct NIOAny {
|
||||
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)")
|
||||
fatalError(
|
||||
"tried to decode as type \(FileRegion.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +176,9 @@ public struct NIOAny {
|
||||
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)")
|
||||
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)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +205,9 @@ public struct NIOAny {
|
||||
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)")
|
||||
fatalError(
|
||||
"tried to decode as type \(T.self) but found \(Mirror(reflecting: Mirror(reflecting: self._storage).children.first!.value).subjectType) with contents \(self._storage)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +272,6 @@ extension NIOAny: Sendable {}
|
||||
|
||||
extension NIOAny: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "NIOAny { \(self.asAny()) }"
|
||||
"NIOAny { \(self.asAny()) }"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,14 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/// A `ChannelInboundHandler` that closes the channel when an error is caught
|
||||
public final class NIOCloseOnErrorHandler: ChannelInboundHandler, Sendable {
|
||||
|
||||
public typealias InboundIn = NIOAny
|
||||
|
||||
|
||||
/// Initialize a `NIOCloseOnErrorHandler`
|
||||
public init() {}
|
||||
|
||||
|
||||
public func errorCaught(context: ChannelHandlerContext, error: Error) {
|
||||
context.fireErrorCaught(error)
|
||||
context.close(promise: nil)
|
||||
|
||||
@@ -28,7 +28,7 @@ public struct NIOLoopBound<Value>: @unchecked Sendable {
|
||||
public let _eventLoop: EventLoop
|
||||
|
||||
@usableFromInline
|
||||
/* private */ var _value: Value
|
||||
var _value: Value
|
||||
|
||||
/// Initialise a ``NIOLoopBound`` to `value` with the precondition that the code is running on `eventLoop`.
|
||||
@inlinable
|
||||
@@ -75,7 +75,7 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
|
||||
public let _eventLoop: EventLoop
|
||||
|
||||
@usableFromInline
|
||||
/* private */var _value: Value
|
||||
var _value: Value
|
||||
|
||||
@inlinable
|
||||
internal init(_value value: Value, uncheckedEventLoop eventLoop: EventLoop) {
|
||||
@@ -96,7 +96,7 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
|
||||
public static func makeEmptyBox<NonOptionalValue>(
|
||||
valueType: NonOptionalValue.Type = NonOptionalValue.self,
|
||||
eventLoop: EventLoop
|
||||
) -> NIOLoopBoundBox<Value> where Optional<NonOptionalValue> == Value {
|
||||
) -> NIOLoopBoundBox<Value> where NonOptionalValue? == Value {
|
||||
// Here, we -- possibly surprisingly -- do not precondition being on the EventLoop. This is okay for a few
|
||||
// reasons:
|
||||
// - We write the `Optional.none` value which we know is _not_ a value of the potentially non-Sendable type
|
||||
@@ -104,7 +104,7 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
|
||||
// - Because of Swift's Definitive Initialisation (DI), we know that we did write `self._value` before `init`
|
||||
// returns.
|
||||
// - The only way to ever write (or read indeed) `self._value` is by proving to be inside the `EventLoop`.
|
||||
return .init(_value: nil, uncheckedEventLoop: eventLoop)
|
||||
.init(_value: nil, uncheckedEventLoop: eventLoop)
|
||||
}
|
||||
|
||||
/// Initialise a ``NIOLoopBoundBox`` by sending a `Sendable` value, validly callable off `eventLoop`.
|
||||
@@ -124,7 +124,7 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
|
||||
// - Because of Swift's Definitive Initialisation (DI), we know that we did write `self._value` before `init`
|
||||
// returns.
|
||||
// - The only way to ever write (or read indeed) `self._value` is by proving to be inside the `EventLoop`.
|
||||
return .init(_value: value, uncheckedEventLoop: eventLoop)
|
||||
.init(_value: value, uncheckedEventLoop: eventLoop)
|
||||
}
|
||||
|
||||
/// Access the `value` with the precondition that the code is running on `eventLoop`.
|
||||
@@ -142,4 +142,3 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public typealias NIOPreconcurrencySendable = _NIOPreconcurrencySendable
|
||||
struct UnsafeTransfer<Wrapped> {
|
||||
@usableFromInline
|
||||
var wrappedValue: Wrapped
|
||||
|
||||
|
||||
@inlinable
|
||||
init(_ wrappedValue: Wrapped) {
|
||||
self.wrappedValue = wrappedValue
|
||||
@@ -46,7 +46,7 @@ extension UnsafeTransfer: Hashable where Wrapped: Hashable {}
|
||||
final class UnsafeMutableTransferBox<Wrapped> {
|
||||
@usableFromInline
|
||||
var wrappedValue: Wrapped
|
||||
|
||||
|
||||
@inlinable
|
||||
init(_ wrappedValue: Wrapped) {
|
||||
self.wrappedValue = wrappedValue
|
||||
@@ -54,4 +54,3 @@ final class UnsafeMutableTransferBox<Wrapped> {
|
||||
}
|
||||
|
||||
extension UnsafeMutableTransferBox: @unchecked Sendable {}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public protocol RecvByteBufferAllocator: _NIOPreconcurrencySendable {
|
||||
extension RecvByteBufferAllocator {
|
||||
// Default implementation to maintain API compatability.
|
||||
public func nextBufferSize() -> Int? {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,17 +46,17 @@ public struct FixedSizeRecvByteBufferAllocator: RecvByteBufferAllocator {
|
||||
|
||||
public mutating func record(actualReadBytes: Int) -> Bool {
|
||||
// Returns false as we always allocate the same size of buffers.
|
||||
return false
|
||||
false
|
||||
}
|
||||
|
||||
public func buffer(allocator: ByteBufferAllocator) -> ByteBuffer {
|
||||
return allocator.buffer(capacity: self.capacity)
|
||||
allocator.buffer(capacity: self.capacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension FixedSizeRecvByteBufferAllocator {
|
||||
public func nextBufferSize() -> Int? {
|
||||
return self.capacity
|
||||
self.capacity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public struct AdaptiveRecvByteBufferAllocator: RecvByteBufferAllocator {
|
||||
}
|
||||
|
||||
public func buffer(allocator: ByteBufferAllocator) -> ByteBuffer {
|
||||
return allocator.buffer(capacity: self.nextReceiveBufferSize)
|
||||
allocator.buffer(capacity: self.nextReceiveBufferSize)
|
||||
}
|
||||
|
||||
public mutating func record(actualReadBytes: Int) -> Bool {
|
||||
@@ -116,8 +116,9 @@ public struct AdaptiveRecvByteBufferAllocator: RecvByteBufferAllocator {
|
||||
} else {
|
||||
self.decreaseNow = true
|
||||
}
|
||||
} else if actualReadBytes >= self.nextReceiveBufferSize && upperBound <= self.maximum &&
|
||||
self.nextReceiveBufferSize != upperBound {
|
||||
} else if actualReadBytes >= self.nextReceiveBufferSize && upperBound <= self.maximum
|
||||
&& self.nextReceiveBufferSize != upperBound
|
||||
{
|
||||
self.nextReceiveBufferSize = upperBound
|
||||
self.decreaseNow = false
|
||||
mayGrow = true
|
||||
@@ -131,6 +132,6 @@ public struct AdaptiveRecvByteBufferAllocator: RecvByteBufferAllocator {
|
||||
|
||||
extension AdaptiveRecvByteBufferAllocator {
|
||||
public func nextBufferSize() -> Int? {
|
||||
return self.nextReceiveBufferSize
|
||||
self.nextReceiveBufferSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/// A simplified version of `ByteToMessageDecoder` that can generate zero or one messages for each invocation of `decode` or `decodeLast`.
|
||||
/// Having `decode` and `decodeLast` return an optional message avoids re-entrancy problems, since the functions relinquish exclusive access
|
||||
/// to the `ByteBuffer` when returning. This allows for greatly simplified processing.
|
||||
@@ -51,7 +50,6 @@ public protocol NIOSingleStepByteToMessageDecoder: ByteToMessageDecoder {
|
||||
mutating func decodeLast(buffer: inout ByteBuffer, seenEOF: Bool) throws -> InboundOut?
|
||||
}
|
||||
|
||||
|
||||
// MARK: NIOSingleStepByteToMessageDecoder: ByteToMessageDecoder
|
||||
extension NIOSingleStepByteToMessageDecoder {
|
||||
public mutating func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
|
||||
@@ -63,7 +61,11 @@ extension NIOSingleStepByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
|
||||
public mutating func decodeLast(
|
||||
context: ChannelHandlerContext,
|
||||
buffer: inout ByteBuffer,
|
||||
seenEOF: Bool
|
||||
) throws -> DecodingState {
|
||||
if let message = try self.decodeLast(buffer: &buffer, seenEOF: seenEOF) {
|
||||
context.fireChannelRead(Self.wrapInboundOut(message))
|
||||
return .continue
|
||||
@@ -73,7 +75,6 @@ extension NIOSingleStepByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// `NIOSingleStepByteToMessageProcessor` uses a `NIOSingleStepByteToMessageDecoder` to produce messages
|
||||
/// from a stream of incoming bytes. It works like `ByteToMessageHandler` but may be used outside of the channel pipeline. This allows
|
||||
/// processing of wrapped protocols in a general way.
|
||||
@@ -238,7 +239,11 @@ public final class NIOSingleStepByteToMessageProcessor<Decoder: NIOSingleStepByt
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func _decodeLoop(decodeMode: DecodeMode, seenEOF: Bool = false, _ messageReceiver: (Decoder.InboundOut) throws -> Void) throws {
|
||||
func _decodeLoop(
|
||||
decodeMode: DecodeMode,
|
||||
seenEOF: Bool = false,
|
||||
_ messageReceiver: (Decoder.InboundOut) throws -> Void
|
||||
) throws {
|
||||
// we want to call decodeLast once with an empty buffer if we have nothing
|
||||
if decodeMode == .last && (self._buffer == nil || self._buffer!.readableBytes == 0) {
|
||||
var emptyBuffer = self._buffer == nil ? ByteBuffer() : self._buffer!
|
||||
|
||||
@@ -37,10 +37,10 @@ import struct WinSDK.sockaddr_un
|
||||
|
||||
import typealias WinSDK.u_short
|
||||
|
||||
fileprivate typealias in_addr = WinSDK.IN_ADDR
|
||||
fileprivate typealias in6_addr = WinSDK.IN6_ADDR
|
||||
fileprivate typealias in_port_t = WinSDK.u_short
|
||||
fileprivate typealias sa_family_t = WinSDK.ADDRESS_FAMILY
|
||||
private typealias in_addr = WinSDK.IN_ADDR
|
||||
private typealias in6_addr = WinSDK.IN6_ADDR
|
||||
private typealias in_port_t = WinSDK.u_short
|
||||
private typealias sa_family_t = WinSDK.ADDRESS_FAMILY
|
||||
#elseif canImport(Darwin)
|
||||
import Darwin
|
||||
#elseif os(Linux) || os(FreeBSD) || os(Android)
|
||||
@@ -70,7 +70,7 @@ extension SocketAddressError {
|
||||
/// Unable to parse a given IP ByteBuffer
|
||||
public struct FailedToParseIPByteBuffer: Error, Hashable {
|
||||
public var address: ByteBuffer
|
||||
|
||||
|
||||
public init(address: ByteBuffer) {
|
||||
self.address = address
|
||||
}
|
||||
@@ -85,10 +85,10 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
private let _storage: Box<(address: sockaddr_in, host: String)>
|
||||
|
||||
/// The libc socket address for an IPv4 address.
|
||||
public var address: sockaddr_in { return _storage.value.address }
|
||||
public var address: sockaddr_in { _storage.value.address }
|
||||
|
||||
/// The host this address is for, if known.
|
||||
public var host: String { return _storage.value.host }
|
||||
public var host: String { _storage.value.host }
|
||||
|
||||
fileprivate init(address: sockaddr_in, host: String) {
|
||||
self._storage = Box((address: address, host: host))
|
||||
@@ -100,10 +100,10 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
private let _storage: Box<(address: sockaddr_in6, host: String)>
|
||||
|
||||
/// The libc socket address for an IPv6 address.
|
||||
public var address: sockaddr_in6 { return _storage.value.address }
|
||||
public var address: sockaddr_in6 { _storage.value.address }
|
||||
|
||||
/// The host this address is for, if known.
|
||||
public var host: String { return _storage.value.host }
|
||||
public var host: String { _storage.value.host }
|
||||
|
||||
fileprivate init(address: sockaddr_in6, host: String) {
|
||||
self._storage = Box((address: address, host: host))
|
||||
@@ -115,7 +115,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
private let _storage: Box<sockaddr_un>
|
||||
|
||||
/// The libc socket address for a Unix Domain Socket.
|
||||
public var address: sockaddr_un { return _storage.value }
|
||||
public var address: sockaddr_un { _storage.value }
|
||||
|
||||
fileprivate init(address: sockaddr_un) {
|
||||
self._storage = Box(address)
|
||||
@@ -165,7 +165,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
|
||||
@available(*, deprecated, renamed: "SocketAddress.protocol")
|
||||
public var protocolFamily: Int32 {
|
||||
return Int32(self.protocol.rawValue)
|
||||
Int32(self.protocol.rawValue)
|
||||
}
|
||||
|
||||
/// Returns the protocol family as defined in `man 2 socket` of this `SocketAddress`.
|
||||
@@ -228,7 +228,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get the pathname of a UNIX domain socket as a string
|
||||
public var pathname: String? {
|
||||
switch self {
|
||||
@@ -364,14 +364,14 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
addr.sin6_scope_id = 0
|
||||
return .v6(.init(address: addr, host: ""))
|
||||
} catch {
|
||||
// If `inet_pton` fails as an IPv6 address (and has failed as an
|
||||
// IPv4 address above), we will throw an error below.
|
||||
// If `inet_pton` fails as an IPv6 address (and has failed as an
|
||||
// IPv4 address above), we will throw an error below.
|
||||
}
|
||||
|
||||
throw SocketAddressError.failedToParseIPString(ipAddress)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Create a new `SocketAddress` for an IP address in ByteBuffer form.
|
||||
///
|
||||
/// - parameters:
|
||||
@@ -381,7 +381,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
/// - throws: may throw `SocketAddressError.failedToParseIPByteBuffer` if the IP address cannot be parsed.
|
||||
public init(packedIPAddress: ByteBuffer, port: Int) throws {
|
||||
let packed = packedIPAddress.readableBytesView
|
||||
|
||||
|
||||
switch packedIPAddress.readableBytes {
|
||||
case 4:
|
||||
var ipv4Addr = sockaddr_in()
|
||||
@@ -411,7 +411,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
internal init(ipv4MaskForPrefix prefix: Int) {
|
||||
precondition((0...32).contains(prefix))
|
||||
|
||||
let packedAddress = (UInt32(0xFFFFFFFF) << (32 - prefix)).bigEndian
|
||||
let packedAddress = (UInt32(0xFFFF_FFFF) << (32 - prefix)).bigEndian
|
||||
var ipv4Addr = sockaddr_in()
|
||||
ipv4Addr.sin_family = sa_family_t(AF_INET)
|
||||
ipv4Addr.sin_port = 0
|
||||
@@ -433,9 +433,9 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
// This defends against the possibility of a greater-than-/64 subnet, which would produce a negative shift
|
||||
// operand which is absolutely not what we want.
|
||||
let highShift = min(prefix, 64)
|
||||
let packedAddressHigh = (UInt64(0xFFFFFFFFFFFFFFFF) << (64 - highShift)).bigEndian
|
||||
let packedAddressHigh = (UInt64(0xFFFF_FFFF_FFFF_FFFF) << (64 - highShift)).bigEndian
|
||||
|
||||
let packedAddressLow = (UInt64(0xFFFFFFFFFFFFFFFF) << (128 - prefix)).bigEndian
|
||||
let packedAddressLow = (UInt64(0xFFFF_FFFF_FFFF_FFFF) << (128 - prefix)).bigEndian
|
||||
let packedAddress = (packedAddressHigh, packedAddressLow)
|
||||
|
||||
var ipv6Addr = sockaddr_in6()
|
||||
@@ -455,9 +455,9 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
/// - returns: the `SocketAddress` for the host / port pair.
|
||||
/// - throws: a `SocketAddressError.unknown` if we could not resolve the `host`, or `SocketAddressError.unsupported` if the address itself is not supported (yet).
|
||||
public static func makeAddressResolvingHost(_ host: String, port: Int) throws -> SocketAddress {
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
return try host.withCString(encodedAs: UTF16.self) { wszHost in
|
||||
return try String(port).withCString(encodedAs: UTF16.self) { wszPort in
|
||||
try String(port).withCString(encodedAs: UTF16.self) { wszPort in
|
||||
var pResult: UnsafeMutablePointer<ADDRINFOW>?
|
||||
|
||||
guard GetAddrInfoW(wszHost, wszPort, nil, &pResult) == 0 else {
|
||||
@@ -482,10 +482,10 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
throw SocketAddressError.unsupported
|
||||
}
|
||||
}
|
||||
#else
|
||||
#else
|
||||
var info: UnsafeMutablePointer<addrinfo>?
|
||||
|
||||
/* FIXME: this is blocking! */
|
||||
// FIXME: this is blocking!
|
||||
if getaddrinfo(host, String(port), nil, &info) != 0 {
|
||||
throw SocketAddressError.unknown(host: host, port: port)
|
||||
}
|
||||
@@ -507,34 +507,36 @@ public enum SocketAddress: CustomStringConvertible, Sendable {
|
||||
throw SocketAddressError.unsupported
|
||||
}
|
||||
} else {
|
||||
/* this is odd, getaddrinfo returned NULL */
|
||||
// this is odd, getaddrinfo returned NULL
|
||||
throw SocketAddressError.unsupported
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// We define an extension on `SocketAddress` that gives it an elementwise equatable conformance, using
|
||||
/// only the elements defined on the structure in their man pages (excluding lengths).
|
||||
extension SocketAddress: Equatable {
|
||||
public static func ==(lhs: SocketAddress, rhs: SocketAddress) -> Bool {
|
||||
public static func == (lhs: SocketAddress, rhs: SocketAddress) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.v4(let addr1), .v4(let addr2)):
|
||||
#if os(Windows)
|
||||
return addr1.address.sin_family == addr2.address.sin_family &&
|
||||
addr1.address.sin_port == addr2.address.sin_port &&
|
||||
addr1.address.sin_addr.S_un.S_addr == addr2.address.sin_addr.S_un.S_addr
|
||||
#else
|
||||
return addr1.address.sin_family == addr2.address.sin_family &&
|
||||
addr1.address.sin_port == addr2.address.sin_port &&
|
||||
addr1.address.sin_addr.s_addr == addr2.address.sin_addr.s_addr
|
||||
#endif
|
||||
#if os(Windows)
|
||||
return addr1.address.sin_family == addr2.address.sin_family
|
||||
&& addr1.address.sin_port == addr2.address.sin_port
|
||||
&& addr1.address.sin_addr.S_un.S_addr == addr2.address.sin_addr.S_un.S_addr
|
||||
#else
|
||||
return addr1.address.sin_family == addr2.address.sin_family
|
||||
&& addr1.address.sin_port == addr2.address.sin_port
|
||||
&& addr1.address.sin_addr.s_addr == addr2.address.sin_addr.s_addr
|
||||
#endif
|
||||
case (.v6(let addr1), .v6(let addr2)):
|
||||
guard addr1.address.sin6_family == addr2.address.sin6_family &&
|
||||
addr1.address.sin6_port == addr2.address.sin6_port &&
|
||||
addr1.address.sin6_flowinfo == addr2.address.sin6_flowinfo &&
|
||||
addr1.address.sin6_scope_id == addr2.address.sin6_scope_id else {
|
||||
return false
|
||||
guard
|
||||
addr1.address.sin6_family == addr2.address.sin6_family
|
||||
&& addr1.address.sin6_port == addr2.address.sin6_port
|
||||
&& addr1.address.sin6_flowinfo == addr2.address.sin6_flowinfo
|
||||
&& addr1.address.sin6_scope_id == addr2.address.sin6_scope_id
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
var s6addr1 = addr1.address.sin6_addr
|
||||
@@ -547,14 +549,13 @@ extension SocketAddress: Equatable {
|
||||
|
||||
let bufferSize = MemoryLayout.size(ofValue: addr1.address.sun_path)
|
||||
|
||||
|
||||
// Swift implicitly binds the memory for homogeneous tuples to both the tuple type and the element type.
|
||||
// This allows us to use assumingMemoryBound(to:) for managing the types. However, we add a static assertion here to validate
|
||||
// that the element type _really is_ what we're assuming it to be.
|
||||
assert(Swift.type(of: addr1.address.sun_path.0) == CChar.self)
|
||||
assert(Swift.type(of: addr2.address.sun_path.0) == CChar.self)
|
||||
return withUnsafePointer(to: addr1.address.sun_path) { sunpath1 in
|
||||
return withUnsafePointer(to: addr2.address.sun_path) { sunpath2 in
|
||||
withUnsafePointer(to: addr2.address.sun_path) { sunpath2 in
|
||||
let typedSunpath1 = UnsafeRawPointer(sunpath1).assumingMemoryBound(to: CChar.self)
|
||||
let typedSunpath2 = UnsafeRawPointer(sunpath2).assumingMemoryBound(to: CChar.self)
|
||||
return strncmp(typedSunpath1, typedSunpath2, bufferSize) == 0
|
||||
@@ -594,11 +595,11 @@ extension SocketAddress: Hashable {
|
||||
hasher.combine(1)
|
||||
hasher.combine(v4Addr.address.sin_family)
|
||||
hasher.combine(v4Addr.address.sin_port)
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
hasher.combine(v4Addr.address.sin_addr.S_un.S_addr)
|
||||
#else
|
||||
#else
|
||||
hasher.combine(v4Addr.address.sin_addr.s_addr)
|
||||
#endif
|
||||
#endif
|
||||
case .v6(let v6Addr):
|
||||
hasher.combine(2)
|
||||
hasher.combine(v6Addr.address.sin6_family)
|
||||
@@ -612,7 +613,6 @@ extension SocketAddress: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension SocketAddress {
|
||||
/// Whether this `SocketAddress` corresponds to a multicast address.
|
||||
public var isMulticast: Bool {
|
||||
@@ -624,15 +624,15 @@ extension SocketAddress {
|
||||
// For IPv4 a multicast address is in the range 224.0.0.0/4.
|
||||
// The easy way to check if this is the case is to just mask off
|
||||
// the address.
|
||||
#if os(Windows)
|
||||
#if os(Windows)
|
||||
let v4WireAddress = v4Addr.address.sin_addr.S_un.S_addr
|
||||
let mask = UInt32(0xF000_0000).bigEndian
|
||||
let subnet = UInt32(0xE000_0000).bigEndian
|
||||
#else
|
||||
#else
|
||||
let v4WireAddress = v4Addr.address.sin_addr.s_addr
|
||||
let mask = in_addr_t(0xF000_0000 as UInt32).bigEndian
|
||||
let subnet = in_addr_t(0xE000_0000 as UInt32).bigEndian
|
||||
#endif
|
||||
#endif
|
||||
return v4WireAddress & mask == subnet
|
||||
case .v6(let v6Addr):
|
||||
// For IPv6 a multicast address is in the range ff00::/8.
|
||||
@@ -649,13 +649,22 @@ protocol SockAddrProtocol {
|
||||
}
|
||||
|
||||
/// Returns a description for the given address.
|
||||
internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String {
|
||||
internal func descriptionForAddress(
|
||||
family: NIOBSDSocket.AddressFamily,
|
||||
bytes: UnsafeRawPointer,
|
||||
length byteCount: Int
|
||||
) throws -> String {
|
||||
var addressBytes: [Int8] = Array(repeating: 0, count: byteCount)
|
||||
return try addressBytes.withUnsafeMutableBufferPointer { (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
|
||||
try NIOBSDSocket.inet_ntop(addressFamily: family, addressBytes: bytes,
|
||||
addressDescription: addressBytesPtr.baseAddress!,
|
||||
addressDescriptionLength: socklen_t(byteCount))
|
||||
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { addressBytesPtr -> String in
|
||||
return try addressBytes.withUnsafeMutableBufferPointer {
|
||||
(addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
|
||||
try NIOBSDSocket.inet_ntop(
|
||||
addressFamily: family,
|
||||
addressBytes: bytes,
|
||||
addressDescription: addressBytesPtr.baseAddress!,
|
||||
addressDescriptionLength: socklen_t(byteCount)
|
||||
)
|
||||
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) {
|
||||
addressBytesPtr -> String in
|
||||
String(cString: addressBytesPtr)
|
||||
}
|
||||
}
|
||||
@@ -663,14 +672,14 @@ internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: U
|
||||
|
||||
extension sockaddr_in: SockAddrProtocol {
|
||||
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
||||
return try withUnsafeBytes(of: self) { p in
|
||||
try withUnsafeBytes(of: self) { p in
|
||||
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a description of the `sockaddr_in`.
|
||||
func addressDescription() -> String {
|
||||
return withUnsafePointer(to: self.sin_addr) { addrPtr in
|
||||
withUnsafePointer(to: self.sin_addr) { addrPtr in
|
||||
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||
try! descriptionForAddress(family: .inet, bytes: addrPtr, length: Int(INET_ADDRSTRLEN))
|
||||
}
|
||||
@@ -679,14 +688,14 @@ extension sockaddr_in: SockAddrProtocol {
|
||||
|
||||
extension sockaddr_in6: SockAddrProtocol {
|
||||
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
||||
return try withUnsafeBytes(of: self) { p in
|
||||
try withUnsafeBytes(of: self) { p in
|
||||
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a description of the `sockaddr_in6`.
|
||||
func addressDescription() -> String {
|
||||
return withUnsafePointer(to: self.sin6_addr) { addrPtr in
|
||||
withUnsafePointer(to: self.sin6_addr) { addrPtr in
|
||||
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||
try! descriptionForAddress(family: .inet6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN))
|
||||
}
|
||||
@@ -695,7 +704,7 @@ extension sockaddr_in6: SockAddrProtocol {
|
||||
|
||||
extension sockaddr_un: SockAddrProtocol {
|
||||
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
||||
return try withUnsafeBytes(of: self) { p in
|
||||
try withUnsafeBytes(of: self) { p in
|
||||
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
||||
}
|
||||
}
|
||||
@@ -703,7 +712,7 @@ extension sockaddr_un: SockAddrProtocol {
|
||||
|
||||
extension sockaddr_storage: SockAddrProtocol {
|
||||
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
||||
return try withUnsafeBytes(of: self) { p in
|
||||
try withUnsafeBytes(of: self) { p in
|
||||
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
||||
}
|
||||
}
|
||||
@@ -714,21 +723,23 @@ extension sockaddr_storage: SockAddrProtocol {
|
||||
// the compiler falls over when we try to access them from test code. As these functions
|
||||
// exist purely to make the behaviours accessible from test code, we name them truly awfully.
|
||||
func __testOnly_addressDescription(_ addr: sockaddr_in) -> String {
|
||||
return addr.addressDescription()
|
||||
addr.addressDescription()
|
||||
}
|
||||
|
||||
func __testOnly_addressDescription(_ addr: sockaddr_in6) -> String {
|
||||
return addr.addressDescription()
|
||||
addr.addressDescription()
|
||||
}
|
||||
|
||||
func __testOnly_withSockAddr<ReturnType>(
|
||||
_ addr: sockaddr_in, _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
||||
_ addr: sockaddr_in,
|
||||
_ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
||||
) rethrows -> ReturnType {
|
||||
return try addr.withSockAddr(body)
|
||||
try addr.withSockAddr(body)
|
||||
}
|
||||
|
||||
func __testOnly_withSockAddr<ReturnType>(
|
||||
_ addr: sockaddr_in6, _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
||||
_ addr: sockaddr_in6,
|
||||
_ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
||||
) rethrows -> ReturnType {
|
||||
return try addr.withSockAddr(body)
|
||||
try addr.withSockAddr(body)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user