mirror of
https://github.com/apple/swift-nio.git
synced 2026-05-20 20:30:36 +00:00
Fixes all warnings when `-require-explicit-sendable` flag is enabled and enables the flag on macOS CI. ### Motivation: We want to ensure our public API is either explicitly marked as `Sendable` or not. ### Modifications: Marked appropriate public types as `Sendable`, or explicitly defined their conformance to the `Sendable` protocol as unavailable. ### Result: We can now enable `-require-explicit-sendable` compiler flag in our codebase.
146 lines
6.7 KiB
Swift
146 lines
6.7 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2024 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
extension ByteBuffer {
|
|
/// A ``NIOBinaryIntegerEncodingStrategy`` which encodes bytes as defined in RFC 9000 § 16
|
|
public struct QUICBinaryEncodingStrategy: NIOBinaryIntegerEncodingStrategy, Sendable {
|
|
/// All possible values for how many bytes a QUIC encoded integer can be
|
|
public enum IntegerLength: Int, Sendable {
|
|
case one = 1
|
|
case two = 2
|
|
case four = 4
|
|
case eight = 8
|
|
}
|
|
/// An estimate of the bytes required to write integers using this strategy
|
|
public var requiredBytesHint: Int
|
|
|
|
/// Note: Prefer to use the APIs directly on ByteBuffer such as ``ByteBuffer/writeEncodedInteger(_:strategy:)`` and pass `.quic` rather than directly initialising an instance of this strategy
|
|
/// - Parameter requiredBytesHint: An estimate of the bytes required to write integers using this strategy. This parameter is only relevant if calling ``ByteBuffer/writeLengthPrefixed(strategy:writeData:)``
|
|
@inlinable
|
|
public init(requiredBytesHint: IntegerLength) {
|
|
self.requiredBytesHint = requiredBytesHint.rawValue
|
|
}
|
|
|
|
@inlinable
|
|
public func readInteger<IntegerType: FixedWidthInteger>(
|
|
as: IntegerType.Type,
|
|
from buffer: inout ByteBuffer
|
|
) -> IntegerType? {
|
|
guard let firstByte = buffer.getInteger(at: buffer.readerIndex, as: UInt8.self) else {
|
|
return nil
|
|
}
|
|
|
|
// Look at the first two bits to work out the length, then read that, mask off the top two bits, and
|
|
// extend to integer.
|
|
switch firstByte & 0xC0 {
|
|
case 0x00:
|
|
// Easy case.
|
|
buffer.moveReaderIndex(forwardBy: 1)
|
|
return IntegerType(firstByte & ~0xC0)
|
|
case 0x40:
|
|
// Length is two bytes long, read the next one.
|
|
return buffer.readInteger(as: UInt16.self).map { IntegerType($0 & ~(0xC0 << 8)) }
|
|
case 0x80:
|
|
// Length is 4 bytes long.
|
|
return buffer.readInteger(as: UInt32.self).map { IntegerType($0 & ~(0xC0 << 24)) }
|
|
case 0xC0:
|
|
// Length is 8 bytes long.
|
|
return buffer.readInteger(as: UInt64.self).map { IntegerType($0 & ~(0xC0 << 56)) }
|
|
default:
|
|
fatalError("Unreachable")
|
|
}
|
|
}
|
|
|
|
/// Calculates the minimum number of bytes needed to encode an integer using this strategy
|
|
/// - Parameter integer: The integer to be encoded
|
|
/// - Returns: The number of bytes needed to encode it
|
|
public static func bytesNeededForInteger<IntegerType: FixedWidthInteger>(_ integer: IntegerType) -> Int {
|
|
// We must cast the integer to UInt64 here
|
|
// Otherwise, an integer can fall through to the default case
|
|
// E.g., if someone calls this function with UInt8.max (which is 255), they would not hit the first case (0..<63)
|
|
// The second case cannot be represented at all in UInt8, because 16383 is too big
|
|
// Swift will end up creating the 16383 literal as 0, and thus we will fall all the way through to the default
|
|
switch UInt64(integer) {
|
|
case 0..<63:
|
|
return 1
|
|
case 0..<16383:
|
|
return 2
|
|
case 0..<1_073_741_823:
|
|
return 4
|
|
case 0..<4_611_686_018_427_387_903:
|
|
return 8
|
|
default:
|
|
fatalError("QUIC variable-length integer outside of valid range")
|
|
}
|
|
}
|
|
|
|
@inlinable
|
|
public func writeInteger<IntegerType: FixedWidthInteger>(
|
|
_ integer: IntegerType,
|
|
to buffer: inout ByteBuffer
|
|
) -> Int {
|
|
self.writeInteger(integer, reservedCapacity: 0, to: &buffer)
|
|
}
|
|
|
|
@inlinable
|
|
public func writeInteger<IntegerType: FixedWidthInteger>(
|
|
_ integer: IntegerType,
|
|
reservedCapacity: Int,
|
|
to buffer: inout ByteBuffer
|
|
) -> Int {
|
|
if reservedCapacity > 8 {
|
|
fatalError("Reserved space for QUIC encoded integer must be at most 8 bytes")
|
|
}
|
|
// Use more space than necessary in order to fill the reserved space
|
|
// This will avoid a memmove
|
|
// If the needed space is more than the reserved, we can't avoid the move
|
|
switch max(reservedCapacity, Self.bytesNeededForInteger(integer)) {
|
|
case 1:
|
|
// Easy, store the value. The top two bits are 0 so we don't need to do any masking.
|
|
return buffer.writeInteger(UInt8(truncatingIfNeeded: integer))
|
|
case 2:
|
|
// Set the top two bit mask, then write the value.
|
|
let value = UInt16(truncatingIfNeeded: integer) | (0x40 << 8)
|
|
return buffer.writeInteger(value)
|
|
case 4:
|
|
// Set the top two bit mask, then write the value.
|
|
let value = UInt32(truncatingIfNeeded: integer) | (0x80 << 24)
|
|
return buffer.writeInteger(value)
|
|
case 8:
|
|
// Set the top two bit mask, then write the value.
|
|
let value = UInt64(truncatingIfNeeded: integer) | (0xC0 << 56)
|
|
return buffer.writeInteger(value)
|
|
default:
|
|
fatalError("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NIOBinaryIntegerEncodingStrategy where Self == ByteBuffer.QUICBinaryEncodingStrategy {
|
|
@inlinable
|
|
/// Encodes bytes as defined in RFC 9000 § 16
|
|
/// - Parameter requiredBytesHint: An estimate of the bytes required to write integers using this strategy. This parameter is only relevant if calling ``ByteBuffer/writeLengthPrefixed(strategy:writeData:)``
|
|
/// - Returns: An instance of ``ByteBuffer/QUICBinaryEncodingStrategy``
|
|
public static func quic(
|
|
requiredBytesHint: ByteBuffer.QUICBinaryEncodingStrategy.IntegerLength
|
|
) -> ByteBuffer.QUICBinaryEncodingStrategy {
|
|
ByteBuffer.QUICBinaryEncodingStrategy(requiredBytesHint: requiredBytesHint)
|
|
}
|
|
|
|
@inlinable
|
|
/// Encodes bytes as defined in RFC 9000 § 16
|
|
public static var quic: ByteBuffer.QUICBinaryEncodingStrategy { .quic(requiredBytesHint: .four) }
|
|
}
|