swift-nio/Sources/NIOCore/ByteBuffer-aux.swift

824 lines
39 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Dispatch
extension ByteBuffer {
// MARK: Bytes ([UInt8]) APIs
/// Get `length` bytes starting at `index` and return the result as `[UInt8]`. This will not change the reader index.
/// The selected bytes must be readable or else `nil` will be returned.
///
/// - parameters:
/// - index: The starting index of the bytes of interest into the `ByteBuffer`.
/// - length: The number of bytes of interest.
/// - returns: A `[UInt8]` value containing the bytes of interest or `nil` if the bytes `ByteBuffer` are not readable.
@inlinable
public func getBytes(at index: Int, length: Int) -> [UInt8]? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil
}
return self.withUnsafeReadableBytes { ptr in
// 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))
}
}
/// Read `length` bytes off this `ByteBuffer`, move the reader index forward by `length` bytes and return the result
/// as `[UInt8]`.
///
/// - parameters:
/// - length: The number of bytes to be read from this `ByteBuffer`.
/// - returns: A `[UInt8]` value containing `length` bytes or `nil` if there aren't at least `length` bytes readable.
@inlinable
public mutating func readBytes(length: Int) -> [UInt8]? {
guard let result = self.getBytes(at: self.readerIndex, length: length) else {
return nil
}
self._moveReaderIndex(forwardBy: length)
return result
}
// MARK: StaticString APIs
/// Write the static `string` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately.
///
/// - parameters:
/// - string: The string to write.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeStaticString(_ string: StaticString) -> Int {
let written = self.setStaticString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
/// Write the static `string` into this `ByteBuffer` at `index` using UTF-8 encoding, moving the writer index forward appropriately.
///
/// - parameters:
/// - string: The string to write.
/// - index: The index for the first serialized byte.
/// - returns: The number of bytes written.
@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)
}
// MARK: String APIs
/// Write `string` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately.
///
/// - parameters:
/// - string: The string to write.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeString(_ string: String) -> Int {
let written = self.setString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
/// Write `string` into this `ByteBuffer` null terminated using UTF-8 encoding, moving the writer index forward appropriately.
///
/// - parameters:
/// - string: The string to write.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeNullTerminatedString(_ string: String) -> Int {
let written = self.setNullTerminatedString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
@inline(never)
@inlinable
mutating func _setStringSlowpath(_ string: String, at index: Int) -> Int {
// slow path, let's try to force the string to be native
if let written = (string + "").utf8.withContiguousStorageIfAvailable({ utf8Bytes in
self.setBytes(utf8Bytes, at: index)
}) {
return written
} else {
return self.setBytes(string.utf8, at: index)
}
}
/// Write `string` into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index.
///
/// - parameters:
/// - string: The string to write.
/// - index: The index for the first serialized byte.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func setString(_ string: String, at index: Int) -> Int {
// Do not implement setString via setSubstring. Before Swift version 5.3,
// Substring.UTF8View did not implement withContiguousStorageIfAvailable
// and therefore had no fast access to the backing storage.
if let written = string.utf8.withContiguousStorageIfAvailable({ utf8Bytes in
self.setBytes(utf8Bytes, at: index)
}) {
// fast path, directly available
return written
} else {
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:
/// - string: The string to write.
/// - index: The index for the first serialized byte.
/// - returns: The number of bytes written.
@inlinable
public mutating func setNullTerminatedString(_ string: String, at index: Int) -> Int {
let length = self.setString(string, at: index)
self.setInteger(UInt8(0), at: index &+ length)
return length &+ 1
}
/// 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.
///
/// - parameters:
/// - index: The starting index into `ByteBuffer` containing the string of interest.
/// - length: The number of bytes making up the string.
/// - returns: A `String` value containing the UTF-8 decoded selected bytes from this `ByteBuffer` or `nil` if
/// the requested bytes are not readable.
@inlinable
public func getString(at index: Int, length: Int) -> String? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil
}
return self.withUnsafeReadableBytes { pointer in
assert(range.lowerBound >= 0 && (range.upperBound - range.lowerBound) <= pointer.count)
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.
///
/// - parameters:
/// - index: The starting index into `ByteBuffer` containing the null terminated string of interest.
/// - returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there isn't a complete null-terminated string,
/// including null-terminator, in the readable bytes after `index` in the buffer
@inlinable
public func getNullTerminatedString(at index: Int) -> String? {
guard let stringLength = self._getNullTerminatedStringLength(at: index) else {
return nil
}
return self.getString(at: index, length: stringLength)
}
@inlinable
func _getNullTerminatedStringLength(at index: Int) -> Int? {
guard self.readerIndex <= index && index < self.writerIndex else {
return nil
}
guard let endIndex = self.readableBytesView[index...].firstIndex(of: 0) else {
return nil
}
return endIndex &- index
}
/// Read `length` bytes off this `ByteBuffer`, decoding it as `String` using the UTF-8 encoding. Move the reader index forward by `length`.
///
/// - parameters:
/// - length: The number of bytes making up the string.
/// - returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there aren't at least `length` bytes readable.
@inlinable
public mutating func readString(length: Int) -> String? {
guard let result = self.getString(at: self.readerIndex, length: length) else {
return nil
}
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.
///
/// - returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there isn't a complete null-terminated string,
/// including null-terminator, in the readable bytes of the buffer
@inlinable
public mutating func readNullTerminatedString() -> String? {
guard let stringLength = self._getNullTerminatedStringLength(at: self.readerIndex) else {
return nil
}
let result = self.readString(length: stringLength)
self.moveReaderIndex(forwardBy: 1) // move forward by null terminator
return result
}
// MARK: Substring APIs
/// Write `substring` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately.
///
/// - parameters:
/// - substring: The substring to write.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeSubstring(_ substring: Substring) -> Int {
let written = self.setSubstring(substring, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
/// Write `substring` into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index.
///
/// - parameters:
/// - substring: The substring to write.
/// - index: The index for the first serialized byte.
/// - returns: The number of bytes written
@discardableResult
@inlinable
public mutating func setSubstring(_ substring: Substring, at index: Int) -> Int {
// Substring.UTF8View implements withContiguousStorageIfAvailable starting with
// Swift version 5.3.
if let written = substring.utf8.withContiguousStorageIfAvailable({ utf8Bytes in
self.setBytes(utf8Bytes, at: index)
}) {
// fast path, directly available
return written
} else {
// slow path, convert to string
return self.setString(String(substring), at: index)
}
}
// MARK: DispatchData APIs
/// Write `dispatchData` into this `ByteBuffer`, moving the writer index forward appropriately.
///
/// - parameters:
/// - dispatchData: The `DispatchData` instance to write to the `ByteBuffer`.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeDispatchData(_ dispatchData: DispatchData) -> Int {
let written = self.setDispatchData(dispatchData, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
/// Write `dispatchData` into this `ByteBuffer` at `index`. Does not move the writer index.
///
/// - parameters:
/// - dispatchData: The `DispatchData` to write.
/// - index: The index for the first serialized byte.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func setDispatchData(_ dispatchData: DispatchData, at index: Int) -> Int {
let allBytesCount = dispatchData.count
self.reserveCapacity(index + allBytesCount)
self.withVeryUnsafeMutableBytes { destCompleteStorage in
assert(destCompleteStorage.count >= index + allBytesCount)
let dest = destCompleteStorage[index ..< index + allBytesCount]
dispatchData.copyBytes(to: .init(fastRebase: dest), count: dest.count)
}
return allBytesCount
}
/// Get the bytes at `index` from this `ByteBuffer` as a `DispatchData`. Does not move the reader index.
/// The selected bytes must be readable or else `nil` will be returned.
///
/// - parameters:
/// - index: The starting index into `ByteBuffer` containing the string of interest.
/// - length: The number of bytes.
/// - returns: A `DispatchData` value deserialized from this `ByteBuffer` or `nil` if the requested bytes
/// are not readable.
@inlinable
public func getDispatchData(at index: Int, length: Int) -> DispatchData? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil
}
return self.withUnsafeReadableBytes { pointer in
return DispatchData(bytes: UnsafeRawBufferPointer(fastRebase: pointer[range]))
}
}
/// Read `length` bytes off this `ByteBuffer` and return them as a `DispatchData`. Move the reader index forward by `length`.
///
/// - parameters:
/// - length: The number of bytes.
/// - returns: A `DispatchData` value containing the bytes from this `ByteBuffer` or `nil` if there aren't at least `length` bytes readable.
@inlinable
public mutating func readDispatchData(length: Int) -> DispatchData? {
guard let result = self.getDispatchData(at: self.readerIndex, length: length) else {
return nil
}
self._moveReaderIndex(forwardBy: length)
return result
}
// MARK: Other APIs
/// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index
/// by the number of bytes returned by `body`.
///
/// - 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.
/// - returns: The number of bytes read.
@discardableResult
@inlinable
public mutating func readWithUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> Int) rethrows -> Int {
let bytesRead = try self.withUnsafeReadableBytes({ try body($0) })
self._moveReaderIndex(forwardBy: bytesRead)
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.
///
/// - 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.
/// - returns: The number of bytes read.
@discardableResult
@inlinable
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:
/// - buffer: The `ByteBuffer` to copy.
/// - index: The index for the first byte.
/// - returns: The number of bytes written.
@discardableResult
@available(*, deprecated, renamed: "setBuffer(_:at:)")
public mutating func set(buffer: ByteBuffer, at index: Int) -> Int {
return 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.
///
/// - parameters:
/// - buffer: The `ByteBuffer` to copy.
/// - index: The index for the first byte.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func setBuffer(_ buffer: ByteBuffer, at index: Int) -> Int {
return buffer.withUnsafeReadableBytes{ p in
self.setBytes(p, at: index)
}
}
/// Write `buffer`'s readable bytes into this `ByteBuffer` starting at `writerIndex`. This will move both this
/// `ByteBuffer`'s writer index as well as `buffer`'s reader index by the number of bytes readable in `buffer`.
///
/// - parameters:
/// - buffer: The `ByteBuffer` to write.
/// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`.
@discardableResult
@inlinable
public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int {
let written = self.setBuffer(buffer, at: writerIndex)
self._moveWriterIndex(forwardBy: written)
buffer._moveReaderIndex(forwardBy: written)
return written
}
/// Write `bytes`, a `Sequence` of `UInt8` into this `ByteBuffer`. Moves the writer index forward by the number of bytes written.
///
/// - parameters:
/// - bytes: A `Collection` of `UInt8` to be written.
/// - returns: The number of bytes written or `bytes.count`.
@discardableResult
@inlinable
public mutating func writeBytes<Bytes: Sequence>(_ bytes: Bytes) -> Int where Bytes.Element == UInt8 {
let written = self.setBytes(bytes, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written)
return written
}
/// Write `bytes` into this `ByteBuffer`. Moves the writer index forward by the number of bytes written.
///
/// - parameters:
/// - bytes: An `UnsafeRawBufferPointer`
/// - returns: The number of bytes written or `bytes.count`.
@discardableResult
@inlinable
public mutating func writeBytes(_ bytes: UnsafeRawBufferPointer) -> Int {
let written = self.setBytes(bytes, at: self.writerIndex)
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.
/// - parameter count: How many times to repeat the given `byte`
/// - returns: How many bytes were written.
@discardableResult
@inlinable
public mutating func writeRepeatingByte(_ byte: UInt8, count: Int) -> Int {
let written = self.setRepeatingByte(byte, count: count, at: self.writerIndex)
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.
/// - parameter count: How many times to repeat the given `byte`
/// - returns: How many bytes were written.
@discardableResult
@inlinable
public mutating func setRepeatingByte(_ byte: UInt8, count: Int, at index: Int) -> Int {
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])
_ = dest.initializeMemory(as: UInt8.self, repeating: byte)
}
return count
}
/// Slice the readable bytes off this `ByteBuffer` without modifying the reader index. This method will return a
/// `ByteBuffer` sharing the underlying storage with the `ByteBuffer` the method was invoked on. The returned
/// `ByteBuffer` will contain the bytes in the range `readerIndex..<writerIndex` of the original `ByteBuffer`.
///
/// - note: Because `ByteBuffer` implements copy-on-write a copy of the storage will be automatically triggered when either of the `ByteBuffer`s sharing storage is written to.
///
/// - 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
}
/// Slice `length` bytes off this `ByteBuffer` and move the reader index forward by `length`.
/// If enough bytes are readable the `ByteBuffer` returned by this method will share the underlying storage with
/// the `ByteBuffer` the method was invoked on.
/// The returned `ByteBuffer` will contain the bytes in the range `readerIndex..<(readerIndex + length)` of the
/// original `ByteBuffer`.
/// The `readerIndex` of the returned `ByteBuffer` will be `0`, the `writerIndex` will be `length`.
///
/// - note: Because `ByteBuffer` implements copy-on-write a copy of the storage will be automatically triggered when either of the `ByteBuffer`s sharing storage is written to.
///
/// - parameters:
/// - length: The number of bytes to slice off.
/// - returns: A `ByteBuffer` sharing storage containing `length` bytes or `nil` if the not enough bytes were readable.
@inlinable
public mutating func readSlice(length: Int) -> ByteBuffer? {
guard let result = self.getSlice_inlineAlways(at: self.readerIndex, length: length) else {
return nil
}
self._moveReaderIndex(forwardBy: length)
return result
}
@discardableResult
@inlinable
public mutating func writeImmutableBuffer(_ buffer: ByteBuffer) -> Int {
var mutable = buffer
return self.writeBuffer(&mutable)
}
}
extension ByteBuffer {
/// Return an empty `ByteBuffer` allocated with `ByteBufferAllocator()`.
///
/// Calling this constructor will not allocate because it will return a `ByteBuffer` that wraps a shared storage
/// object. As soon as you write to the constructed buffer however, you will incur an allocation because a
/// copy-on-write will happen.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` it is
/// recommended using `channel.allocator.buffer(capacity: 0)`. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init() {
self = ByteBufferAllocator.zeroCapacityWithDefaultAllocator
}
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using
/// the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(string:)`. Or if you want to write multiple items into the
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeString` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(string: String) {
self = ByteBufferAllocator().buffer(string: string)
}
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using
/// the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(substring:)`. Or if you want to write multiple items into
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeSubstring` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(substring string: Substring) {
self = ByteBufferAllocator().buffer(substring: string)
}
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using
/// the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(staticString:)`. Or if you want to write multiple items into
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeStaticString` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(staticString string: StaticString) {
self = ByteBufferAllocator().buffer(staticString: string)
}
/// Create a fresh `ByteBuffer` containing the `bytes`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `bytes` and potentially some extra space using
/// the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(bytes:)`. Or if you want to write multiple items into the
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeBytes` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init<Bytes: Sequence>(bytes: Bytes) where Bytes.Element == UInt8 {
self = ByteBufferAllocator().buffer(bytes: bytes)
}
/// Create a fresh `ByteBuffer` containing the bytes of the byte representation in the given `endianness` of
/// `integer`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `integer` and potentially some extra space using
/// the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(integer:)`. Or if you want to write multiple items into the
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeInteger` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init<I: FixedWidthInteger>(integer: I, endianness: Endianness = .big, as: I.Type = I.self) {
self = ByteBufferAllocator().buffer(integer: integer, endianness: endianness, as: `as`)
}
/// Create a fresh `ByteBuffer` containing `count` repetitions of `byte`.
///
/// This will allocate a new `ByteBuffer` with at least `count` bytes.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(repeating:count:)`. Or if you want to write multiple items
/// into the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeRepeatingByte` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(repeating byte: UInt8, count: Int) {
self = ByteBufferAllocator().buffer(repeating: byte, count: count)
}
/// Create a fresh `ByteBuffer` containing the readable bytes of `buffer`.
///
/// This may allocate a new `ByteBuffer` with enough space to fit `buffer` and potentially some extra space using
/// the default allocator.
///
/// - note: Use this method only if you deliberately want to reallocate a potentially smaller `ByteBuffer` than the
/// one you already have. Given that `ByteBuffer` is a value type, defensive copies are not necessary. If
/// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(buffer:)`. Or if you want to write multiple items into the
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeImmutableBuffer` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(buffer: ByteBuffer) {
self = ByteBufferAllocator().buffer(buffer: buffer)
}
/// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially
/// some extra space using the default allocator.
///
/// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we
/// recommend using `channel.allocator.buffer(dispatchData:)`. Or if you want to write multiple items into
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right
/// size followed by a `writeDispatchData` instead of using this method. This allows SwiftNIO to do
/// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(dispatchData: DispatchData) {
self = ByteBufferAllocator().buffer(dispatchData: dispatchData)
}
}
extension ByteBufferAllocator {
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(string: String) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8.count)
buffer.writeString(string)
return buffer
}
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(substring string: Substring) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8.count)
buffer.writeSubstring(string)
return buffer
}
/// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(staticString string: StaticString) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8CodeUnitCount)
buffer.writeStaticString(string)
return buffer
}
/// Create a fresh `ByteBuffer` containing the `bytes`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `bytes` and potentially some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer<Bytes: Sequence>(bytes: Bytes) -> ByteBuffer where Bytes.Element == UInt8 {
var buffer = self.buffer(capacity: bytes.underestimatedCount)
buffer.writeBytes(bytes)
return buffer
}
/// Create a fresh `ByteBuffer` containing the bytes of the byte representation in the given `endianness` of
/// `integer`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit `integer` and potentially some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
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
}
/// Create a fresh `ByteBuffer` containing `count` repetitions of `byte`.
///
/// This will allocate a new `ByteBuffer` with at least `count` bytes.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(repeating byte: UInt8, count: Int) -> ByteBuffer {
var buffer = self.buffer(capacity: count)
buffer.writeRepeatingByte(byte, count: count)
return buffer
}
/// Create a fresh `ByteBuffer` containing the readable bytes of `buffer`.
///
/// This may allocate a new `ByteBuffer` with enough space to fit `buffer` and potentially some extra space.
///
/// - note: Use this method only if you deliberately want to reallocate a potentially smaller `ByteBuffer` than the
/// one you already have. Given that `ByteBuffer` is a value type, defensive copies are not necessary. If
/// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(buffer: ByteBuffer) -> ByteBuffer {
var newBuffer = self.buffer(capacity: buffer.readableBytes)
newBuffer.writeImmutableBuffer(buffer)
return newBuffer
}
/// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`.
///
/// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially
/// some extra space.
///
/// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(dispatchData: DispatchData) -> ByteBuffer {
var buffer = self.buffer(capacity: dispatchData.count)
buffer.writeDispatchData(dispatchData)
return buffer
}
}
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`.
///
/// This method will not modify `buffer`, meaning its `readerIndex` and `writerIndex` stays intact.
///
/// - parameters:
/// - buffer: The `ByteBuffer` to write.
/// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of `readableBytes` in
/// `buffer`.
@discardableResult
@inlinable
public mutating func setOrWriteImmutableBuffer(_ buffer: ByteBuffer) -> Int {
var mutable = buffer
return self.setOrWriteBuffer(&mutable)
}
/// If `nil`, replace `self` with `.some(buffer)`. If non-`nil`, write `buffer`'s readable bytes into the
/// `ByteBuffer` starting at `writerIndex`.
///
/// This will move both this `ByteBuffer`'s writer index as well as `buffer`'s reader index by the number of bytes
/// readable in `buffer`.
///
/// - parameters:
/// - buffer: The `ByteBuffer` to write.
/// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`.
@discardableResult
@inlinable
public mutating func setOrWriteBuffer(_ buffer: inout ByteBuffer) -> Int {
if self == nil {
let readableBytes = buffer.readableBytes
self = buffer
buffer.moveReaderIndex(to: buffer.writerIndex)
return readableBytes
} else {
return self!.writeBuffer(&buffer)
}
}
}