Make all ByteBuffer methods inlinable (#2050)

Motivation:

We've held off on doing this for a while on the theory that function
calls are cheap, and we're happy to make them where there is no
particular need to specialize. Unfortunately, while this is true in
general, Swift is capable of optimizing ARC and exclusivity checking
when it can better understand what a method actually does.

Additionally, we'd hoped that cross-module-optimization would be a
useful addition here. Sadly, right now this still hasn't rolled out as a
default, and even if it had, it tends to be limited to smaller methods
and generic functions, all of which we've already annotated.

Modifications:

- Add @inlinable to essentially everything.

Result:

Better codegen around ByteBuffer.
This commit is contained in:
Cory Benfield 2022-02-21 11:38:38 +00:00 committed by GitHub
parent c74c3bbabf
commit 74cba26b6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 36 deletions

View File

@ -25,6 +25,7 @@ extension ByteBuffer {
/// - index: The starting index of the bytes of interest into the `ByteBuffer`. /// - index: The starting index of the bytes of interest into the `ByteBuffer`.
/// - length: The number of bytes of interest. /// - 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. /// - 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]? { public func getBytes(at index: Int, length: Int) -> [UInt8]? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil return nil
@ -44,6 +45,7 @@ extension ByteBuffer {
/// - parameters: /// - parameters:
/// - length: The number of bytes to be read from this `ByteBuffer`. /// - 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. /// - 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]? { public mutating func readBytes(length: Int) -> [UInt8]? {
guard let result = self.getBytes(at: self.readerIndex, length: length) else { guard let result = self.getBytes(at: self.readerIndex, length: length) else {
return nil return nil
@ -60,6 +62,7 @@ extension ByteBuffer {
/// - string: The string to write. /// - string: The string to write.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func writeStaticString(_ string: StaticString) -> Int { public mutating func writeStaticString(_ string: StaticString) -> Int {
let written = self.setStaticString(string, at: self.writerIndex) let written = self.setStaticString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -72,6 +75,7 @@ extension ByteBuffer {
/// - string: The string to write. /// - string: The string to write.
/// - index: The index for the first serialized byte. /// - index: The index for the first serialized byte.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@inlinable
public mutating func setStaticString(_ string: StaticString, at index: Int) -> Int { 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) // please do not replace the code below with code that uses `string.withUTF8Buffer { ... }` (see SR-7541)
return self.setBytes(UnsafeRawBufferPointer(start: string.utf8Start, return self.setBytes(UnsafeRawBufferPointer(start: string.utf8Start,
@ -85,6 +89,7 @@ extension ByteBuffer {
/// - string: The string to write. /// - string: The string to write.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func writeString(_ string: String) -> Int { public mutating func writeString(_ string: String) -> Int {
let written = self.setString(string, at: self.writerIndex) let written = self.setString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -97,6 +102,7 @@ extension ByteBuffer {
/// - string: The string to write. /// - string: The string to write.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func writeNullTerminatedString(_ string: String) -> Int { public mutating func writeNullTerminatedString(_ string: String) -> Int {
let written = self.setNullTerminatedString(string, at: self.writerIndex) let written = self.setNullTerminatedString(string, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -104,7 +110,7 @@ extension ByteBuffer {
} }
@inline(never) @inline(never)
@usableFromInline @inlinable
mutating func _setStringSlowpath(_ string: String, at index: Int) -> Int { mutating func _setStringSlowpath(_ string: String, at index: Int) -> Int {
// slow path, let's try to force the string to be native // slow path, let's try to force the string to be native
if let written = (string + "").utf8.withContiguousStorageIfAvailable({ utf8Bytes in if let written = (string + "").utf8.withContiguousStorageIfAvailable({ utf8Bytes in
@ -144,6 +150,7 @@ extension ByteBuffer {
/// - string: The string to write. /// - string: The string to write.
/// - index: The index for the first serialized byte. /// - index: The index for the first serialized byte.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@inlinable
public mutating func setNullTerminatedString(_ string: String, at index: Int) -> Int { public mutating func setNullTerminatedString(_ string: String, at index: Int) -> Int {
let length = self.setString(string, at: index) let length = self.setString(string, at: index)
self.setInteger(UInt8(0), at: index &+ length) self.setInteger(UInt8(0), at: index &+ length)
@ -158,6 +165,7 @@ extension ByteBuffer {
/// - length: The number of bytes making up the string. /// - 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 /// - returns: A `String` value containing the UTF-8 decoded selected bytes from this `ByteBuffer` or `nil` if
/// the requested bytes are not readable. /// the requested bytes are not readable.
@inlinable
public func getString(at index: Int, length: Int) -> String? { public func getString(at index: Int, length: Int) -> String? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil return nil
@ -175,14 +183,16 @@ extension ByteBuffer {
/// - index: The starting index into `ByteBuffer` containing the null terminated string of interest. /// - 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, /// - 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 /// including null-terminator, in the readable bytes after `index` in the buffer
@inlinable
public func getNullTerminatedString(at index: Int) -> String? { public func getNullTerminatedString(at index: Int) -> String? {
guard let stringLength = self.getNullTerminatedStringLength(at: index) else { guard let stringLength = self._getNullTerminatedStringLength(at: index) else {
return nil return nil
} }
return self.getString(at: index, length: stringLength) return self.getString(at: index, length: stringLength)
} }
private func getNullTerminatedStringLength(at index: Int) -> Int? { @inlinable
func _getNullTerminatedStringLength(at index: Int) -> Int? {
guard self.readerIndex <= index && index < self.writerIndex else { guard self.readerIndex <= index && index < self.writerIndex else {
return nil return nil
} }
@ -197,6 +207,7 @@ extension ByteBuffer {
/// - parameters: /// - parameters:
/// - length: The number of bytes making up the string. /// - 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. /// - 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? { public mutating func readString(length: Int) -> String? {
guard let result = self.getString(at: self.readerIndex, length: length) else { guard let result = self.getString(at: self.readerIndex, length: length) else {
return nil return nil
@ -210,8 +221,9 @@ extension ByteBuffer {
/// ///
/// - returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there isn't a complete null-terminated string, /// - 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 /// including null-terminator, in the readable bytes of the buffer
@inlinable
public mutating func readNullTerminatedString() -> String? { public mutating func readNullTerminatedString() -> String? {
guard let stringLength = self.getNullTerminatedStringLength(at: self.readerIndex) else { guard let stringLength = self._getNullTerminatedStringLength(at: self.readerIndex) else {
return nil return nil
} }
let result = self.readString(length: stringLength) let result = self.readString(length: stringLength)
@ -226,6 +238,7 @@ extension ByteBuffer {
/// - substring: The substring to write. /// - substring: The substring to write.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func writeSubstring(_ substring: Substring) -> Int { public mutating func writeSubstring(_ substring: Substring) -> Int {
let written = self.setSubstring(substring, at: self.writerIndex) let written = self.setSubstring(substring, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -261,6 +274,7 @@ extension ByteBuffer {
/// - dispatchData: The `DispatchData` instance to write to the `ByteBuffer`. /// - dispatchData: The `DispatchData` instance to write to the `ByteBuffer`.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func writeDispatchData(_ dispatchData: DispatchData) -> Int { public mutating func writeDispatchData(_ dispatchData: DispatchData) -> Int {
let written = self.setDispatchData(dispatchData, at: self.writerIndex) let written = self.setDispatchData(dispatchData, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -274,6 +288,7 @@ extension ByteBuffer {
/// - index: The index for the first serialized byte. /// - index: The index for the first serialized byte.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func setDispatchData(_ dispatchData: DispatchData, at index: Int) -> Int { public mutating func setDispatchData(_ dispatchData: DispatchData, at index: Int) -> Int {
let allBytesCount = dispatchData.count let allBytesCount = dispatchData.count
self.reserveCapacity(index + allBytesCount) self.reserveCapacity(index + allBytesCount)
@ -293,6 +308,7 @@ extension ByteBuffer {
/// - length: The number of bytes. /// - length: The number of bytes.
/// - returns: A `DispatchData` value deserialized from this `ByteBuffer` or `nil` if the requested bytes /// - returns: A `DispatchData` value deserialized from this `ByteBuffer` or `nil` if the requested bytes
/// are not readable. /// are not readable.
@inlinable
public func getDispatchData(at index: Int, length: Int) -> DispatchData? { public func getDispatchData(at index: Int, length: Int) -> DispatchData? {
guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { guard let range = self.rangeWithinReadableBytes(index: index, length: length) else {
return nil return nil
@ -307,6 +323,7 @@ extension ByteBuffer {
/// - parameters: /// - parameters:
/// - length: The number of bytes. /// - 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. /// - 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? { public mutating func readDispatchData(length: Int) -> DispatchData? {
guard let result = self.getDispatchData(at: self.readerIndex, length: length) else { guard let result = self.getDispatchData(at: self.readerIndex, length: length) else {
return nil return nil
@ -399,6 +416,7 @@ extension ByteBuffer {
/// - index: The index for the first byte. /// - index: The index for the first byte.
/// - returns: The number of bytes written. /// - returns: The number of bytes written.
@discardableResult @discardableResult
@inlinable
public mutating func setBuffer(_ buffer: ByteBuffer, at index: Int) -> Int { public mutating func setBuffer(_ buffer: ByteBuffer, at index: Int) -> Int {
return buffer.withUnsafeReadableBytes{ p in return buffer.withUnsafeReadableBytes{ p in
self.setBytes(p, at: index) self.setBytes(p, at: index)
@ -412,6 +430,7 @@ extension ByteBuffer {
/// - buffer: The `ByteBuffer` to write. /// - 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`. /// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`.
@discardableResult @discardableResult
@inlinable
public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int { public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int {
let written = self.setBuffer(buffer, at: writerIndex) let written = self.setBuffer(buffer, at: writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -451,6 +470,7 @@ extension ByteBuffer {
/// - parameter count: How many times to repeat the given `byte` /// - parameter count: How many times to repeat the given `byte`
/// - returns: How many bytes were written. /// - returns: How many bytes were written.
@discardableResult @discardableResult
@inlinable
public mutating func writeRepeatingByte(_ byte: UInt8, count: Int) -> Int { public mutating func writeRepeatingByte(_ byte: UInt8, count: Int) -> Int {
let written = self.setRepeatingByte(byte, count: count, at: self.writerIndex) let written = self.setRepeatingByte(byte, count: count, at: self.writerIndex)
self._moveWriterIndex(forwardBy: written) self._moveWriterIndex(forwardBy: written)
@ -463,6 +483,7 @@ extension ByteBuffer {
/// - parameter count: How many times to repeat the given `byte` /// - parameter count: How many times to repeat the given `byte`
/// - returns: How many bytes were written. /// - returns: How many bytes were written.
@discardableResult @discardableResult
@inlinable
public mutating func setRepeatingByte(_ byte: UInt8, count: Int, at index: Int) -> Int { public mutating func setRepeatingByte(_ byte: UInt8, count: Int, at index: Int) -> Int {
precondition(count >= 0, "Can't write fewer than 0 bytes") precondition(count >= 0, "Can't write fewer than 0 bytes")
self.reserveCapacity(index + count) self.reserveCapacity(index + count)
@ -480,6 +501,7 @@ extension 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. /// - 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. /// - returns: A `ByteBuffer` sharing storage containing the readable bytes only.
@inlinable
public func slice() -> ByteBuffer { public func slice() -> ByteBuffer {
return self.getSlice(at: self.readerIndex, length: self.readableBytes)! // must work, bytes definitely in the buffer return self.getSlice(at: self.readerIndex, length: self.readableBytes)! // must work, bytes definitely in the buffer
} }
@ -496,6 +518,7 @@ extension ByteBuffer {
/// - parameters: /// - parameters:
/// - length: The number of bytes to slice off. /// - 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. /// - 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? { public mutating func readSlice(length: Int) -> ByteBuffer? {
guard let result = self.getSlice_inlineAlways(at: self.readerIndex, length: length) else { guard let result = self.getSlice_inlineAlways(at: self.readerIndex, length: length) else {
return nil return nil
@ -505,6 +528,7 @@ extension ByteBuffer {
} }
@discardableResult @discardableResult
@inlinable
public mutating func writeImmutableBuffer(_ buffer: ByteBuffer) -> Int { public mutating func writeImmutableBuffer(_ buffer: ByteBuffer) -> Int {
var mutable = buffer var mutable = buffer
return self.writeBuffer(&mutable) return self.writeBuffer(&mutable)
@ -536,6 +560,7 @@ extension ByteBuffer {
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(string: String) { public init(string: String) {
self = ByteBufferAllocator().buffer(string: string) self = ByteBufferAllocator().buffer(string: string)
} }
@ -550,6 +575,7 @@ extension ByteBuffer {
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(substring string: Substring) { public init(substring string: Substring) {
self = ByteBufferAllocator().buffer(substring: string) self = ByteBufferAllocator().buffer(substring: string)
} }
@ -564,6 +590,7 @@ extension ByteBuffer {
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(staticString string: StaticString) { public init(staticString string: StaticString) {
self = ByteBufferAllocator().buffer(staticString: string) self = ByteBufferAllocator().buffer(staticString: string)
} }
@ -608,6 +635,7 @@ extension ByteBuffer {
/// into the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(repeating byte: UInt8, count: Int) { public init(repeating byte: UInt8, count: Int) {
self = ByteBufferAllocator().buffer(repeating: byte, count: count) self = ByteBufferAllocator().buffer(repeating: byte, count: count)
} }
@ -626,6 +654,7 @@ extension ByteBuffer {
/// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(buffer: ByteBuffer) { public init(buffer: ByteBuffer) {
self = ByteBufferAllocator().buffer(buffer: buffer) self = ByteBufferAllocator().buffer(buffer: buffer)
} }
@ -640,6 +669,7 @@ extension ByteBuffer {
/// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right /// 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 /// 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. /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future.
@inlinable
public init(dispatchData: DispatchData) { public init(dispatchData: DispatchData) {
self = ByteBufferAllocator().buffer(dispatchData: dispatchData) self = ByteBufferAllocator().buffer(dispatchData: dispatchData)
} }
@ -651,6 +681,7 @@ extension ByteBufferAllocator {
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(string: String) -> ByteBuffer { public func buffer(string: String) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8.count) var buffer = self.buffer(capacity: string.utf8.count)
buffer.writeString(string) buffer.writeString(string)
@ -662,6 +693,7 @@ extension ByteBufferAllocator {
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(substring string: Substring) -> ByteBuffer { public func buffer(substring string: Substring) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8.count) var buffer = self.buffer(capacity: string.utf8.count)
buffer.writeSubstring(string) buffer.writeSubstring(string)
@ -673,6 +705,7 @@ extension ByteBufferAllocator {
/// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(staticString string: StaticString) -> ByteBuffer { public func buffer(staticString string: StaticString) -> ByteBuffer {
var buffer = self.buffer(capacity: string.utf8CodeUnitCount) var buffer = self.buffer(capacity: string.utf8CodeUnitCount)
buffer.writeStaticString(string) buffer.writeStaticString(string)
@ -711,6 +744,7 @@ extension ByteBufferAllocator {
/// This will allocate a new `ByteBuffer` with at least `count` bytes. /// This will allocate a new `ByteBuffer` with at least `count` bytes.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(repeating byte: UInt8, count: Int) -> ByteBuffer { public func buffer(repeating byte: UInt8, count: Int) -> ByteBuffer {
var buffer = self.buffer(capacity: count) var buffer = self.buffer(capacity: count)
buffer.writeRepeatingByte(byte, count: count) buffer.writeRepeatingByte(byte, count: count)
@ -726,6 +760,7 @@ extension ByteBufferAllocator {
/// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead. /// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(buffer: ByteBuffer) -> ByteBuffer { public func buffer(buffer: ByteBuffer) -> ByteBuffer {
var newBuffer = self.buffer(capacity: buffer.readableBytes) var newBuffer = self.buffer(capacity: buffer.readableBytes)
newBuffer.writeImmutableBuffer(buffer) newBuffer.writeImmutableBuffer(buffer)
@ -738,6 +773,7 @@ extension ByteBufferAllocator {
/// some extra space. /// some extra space.
/// ///
/// - returns: The `ByteBuffer` containing the written bytes. /// - returns: The `ByteBuffer` containing the written bytes.
@inlinable
public func buffer(dispatchData: DispatchData) -> ByteBuffer { public func buffer(dispatchData: DispatchData) -> ByteBuffer {
var buffer = self.buffer(capacity: dispatchData.count) var buffer = self.buffer(capacity: dispatchData.count)
buffer.writeDispatchData(dispatchData) buffer.writeDispatchData(dispatchData)
@ -757,6 +793,7 @@ extension Optional where Wrapped == ByteBuffer {
/// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of `readableBytes` in /// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of `readableBytes` in
/// `buffer`. /// `buffer`.
@discardableResult @discardableResult
@inlinable
public mutating func setOrWriteImmutableBuffer(_ buffer: ByteBuffer) -> Int { public mutating func setOrWriteImmutableBuffer(_ buffer: ByteBuffer) -> Int {
var mutable = buffer var mutable = buffer
return self.setOrWriteBuffer(&mutable) return self.setOrWriteBuffer(&mutable)
@ -772,6 +809,7 @@ extension Optional where Wrapped == ByteBuffer {
/// - buffer: The `ByteBuffer` to write. /// - 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`. /// - returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`.
@discardableResult @discardableResult
@inlinable
public mutating func setOrWriteBuffer(_ buffer: inout ByteBuffer) -> Int { public mutating func setOrWriteBuffer(_ buffer: inout ByteBuffer) -> Int {
if self == nil { if self == nil {
let readableBytes = buffer.readableBytes let readableBytes = buffer.readableBytes

View File

@ -18,6 +18,7 @@ extension Array where Element == UInt8 {
/// Creates a `[UInt8]` from the given buffer. The entire readable portion of the buffer will be read. /// Creates a `[UInt8]` from the given buffer. The entire readable portion of the buffer will be read.
/// - parameter buffer: The buffer to read. /// - parameter buffer: The buffer to read.
@inlinable
public init(buffer: ByteBuffer) { public init(buffer: ByteBuffer) {
var buffer = buffer var buffer = buffer
self = buffer.readBytes(length: buffer.readableBytes)! self = buffer.readBytes(length: buffer.readableBytes)!
@ -29,6 +30,7 @@ extension String {
/// Creates a `String` from a given `ByteBuffer`. The entire readable portion of the buffer will be read. /// Creates a `String` from a given `ByteBuffer`. The entire readable portion of the buffer will be read.
/// - parameter buffer: The buffer to read. /// - parameter buffer: The buffer to read.
@inlinable
public init(buffer: ByteBuffer) { public init(buffer: ByteBuffer) {
var buffer = buffer var buffer = buffer
self = buffer.readString(length: buffer.readableBytes)! self = buffer.readString(length: buffer.readableBytes)!
@ -40,6 +42,7 @@ extension DispatchData {
/// Creates a `DispatchData` from a given `ByteBuffer`. The entire readable portion of the buffer will be read. /// Creates a `DispatchData` from a given `ByteBuffer`. The entire readable portion of the buffer will be read.
/// - parameter buffer: The buffer to read. /// - parameter buffer: The buffer to read.
@inlinable
public init(buffer: ByteBuffer) { public init(buffer: ByteBuffer) {
var buffer = buffer var buffer = buffer
self = buffer.readDispatchData(length: buffer.readableBytes)! self = buffer.readDispatchData(length: buffer.readableBytes)!

View File

@ -20,13 +20,13 @@ import Darwin
import Glibc import Glibc
#endif #endif
let sysMalloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? = malloc @usableFromInline let sysMalloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? = malloc
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 /// 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 /// non-nullable. To that end, we define a thunk through to `free` that matches that constraint, as we
/// never pass a `nil` pointer to it. /// never pass a `nil` pointer to it.
let sysFree: @convention(c) (UnsafeMutableRawPointer) -> Void = { free($0) } @usableFromInline let sysFree: @convention(c) (UnsafeMutableRawPointer) -> Void = { free($0) }
extension _ByteBufferSlice: Equatable {} extension _ByteBufferSlice: Equatable {}
@ -45,17 +45,17 @@ struct _ByteBufferSlice {
// this cannot underflow. // this cannot underflow.
return Int(self.upperBound &- self.lowerBound) return Int(self.upperBound &- self.lowerBound)
} }
init() { @inlinable init() {
self._begin = .init(0) self._begin = .init(0)
self.upperBound = .init(0) self.upperBound = .init(0)
} }
static var maxSupportedLowerBound: ByteBuffer._Index { @inlinable static var maxSupportedLowerBound: ByteBuffer._Index {
return ByteBuffer._Index(_UInt24.max) return ByteBuffer._Index(_UInt24.max)
} }
} }
extension _ByteBufferSlice { extension _ByteBufferSlice {
init(_ range: Range<UInt32>) { @inlinable init(_ range: Range<UInt32>) {
self._begin = _UInt24(range.lowerBound) self._begin = _UInt24(range.lowerBound)
self.upperBound = range.upperBound self.upperBound = range.upperBound
} }
@ -77,13 +77,14 @@ public struct ByteBufferAllocator {
/// Create a fresh `ByteBufferAllocator`. In the future the allocator might use for example allocation pools and /// Create a fresh `ByteBufferAllocator`. In the future the allocator might use for example allocation pools and
/// therefore it's recommended to reuse `ByteBufferAllocators` where possible instead of creating fresh ones in /// therefore it's recommended to reuse `ByteBufferAllocators` where possible instead of creating fresh ones in
/// many places. /// many places.
public init() { @inlinable public init() {
self.init(hookedMalloc: { sysMalloc($0) }, self.init(hookedMalloc: { sysMalloc($0) },
hookedRealloc: { sysRealloc($0, $1) }, hookedRealloc: { sysRealloc($0, $1) },
hookedFree: { sysFree($0) }, hookedFree: { sysFree($0) },
hookedMemcpy: { $0.copyMemory(from: $1, byteCount: $2) }) hookedMemcpy: { $0.copyMemory(from: $1, byteCount: $2) })
} }
@inlinable
internal init(hookedMalloc: @escaping @convention(c) (size_t) -> UnsafeMutableRawPointer?, internal init(hookedMalloc: @escaping @convention(c) (size_t) -> UnsafeMutableRawPointer?,
hookedRealloc: @escaping @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?, hookedRealloc: @escaping @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?,
hookedFree: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void, hookedFree: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void,
@ -103,6 +104,7 @@ public struct ByteBufferAllocator {
/// ///
/// - parameters: /// - parameters:
/// - capacity: The initial capacity of the returned `ByteBuffer`. /// - capacity: The initial capacity of the returned `ByteBuffer`.
@inlinable
public func buffer(capacity: Int) -> ByteBuffer { public func buffer(capacity: Int) -> ByteBuffer {
precondition(capacity >= 0, "ByteBuffer capacity must be positive.") precondition(capacity >= 0, "ByteBuffer capacity must be positive.")
guard capacity > 0 else { guard capacity > 0 else {
@ -114,10 +116,10 @@ public struct ByteBufferAllocator {
@usableFromInline @usableFromInline
internal static let zeroCapacityWithDefaultAllocator = ByteBuffer(allocator: ByteBufferAllocator(), startingCapacity: 0) internal static let zeroCapacityWithDefaultAllocator = ByteBuffer(allocator: ByteBufferAllocator(), startingCapacity: 0)
internal let malloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? @usableFromInline internal let malloc: @convention(c) (size_t) -> UnsafeMutableRawPointer?
internal let realloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer? @usableFromInline internal let realloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?
internal let free: @convention(c) (UnsafeMutableRawPointer) -> Void @usableFromInline internal let free: @convention(c) (UnsafeMutableRawPointer) -> Void
internal let memcpy: @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void @usableFromInline internal let memcpy: @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void
} }
@inlinable func _toCapacity(_ value: Int) -> ByteBuffer._Capacity { @inlinable func _toCapacity(_ value: Int) -> ByteBuffer._Capacity {
@ -229,11 +231,12 @@ public struct ByteBuffer {
// MARK: Internal _Storage for CoW // MARK: Internal _Storage for CoW
@usableFromInline final class _Storage { @usableFromInline final class _Storage {
private(set) var capacity: _Capacity @usableFromInline private(set) var capacity: _Capacity
@usableFromInline private(set) var bytes: UnsafeMutableRawPointer @usableFromInline private(set) var bytes: UnsafeMutableRawPointer
private let allocator: ByteBufferAllocator @usableFromInline let allocator: ByteBufferAllocator
public init(bytesNoCopy: UnsafeMutableRawPointer, capacity: _Capacity, allocator: ByteBufferAllocator) { @inlinable
init(bytesNoCopy: UnsafeMutableRawPointer, capacity: _Capacity, allocator: ByteBufferAllocator) {
self.bytes = bytesNoCopy self.bytes = bytesNoCopy
self.capacity = capacity self.capacity = capacity
self.allocator = allocator self.allocator = allocator
@ -243,36 +246,42 @@ public struct ByteBuffer {
self.deallocate() self.deallocate()
} }
internal var fullSlice: _ByteBufferSlice { @inlinable
var fullSlice: _ByteBufferSlice {
return _ByteBufferSlice(0..<self.capacity) return _ByteBufferSlice(0..<self.capacity)
} }
private static func allocateAndPrepareRawMemory(bytes: _Capacity, allocator: Allocator) -> UnsafeMutableRawPointer { @inlinable
static func _allocateAndPrepareRawMemory(bytes: _Capacity, allocator: Allocator) -> UnsafeMutableRawPointer {
let ptr = allocator.malloc(size_t(bytes))! 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)) ptr.bindMemory(to: UInt8.self, capacity: Int(bytes))
return ptr return ptr
} }
public func allocateStorage() -> _Storage { @inlinable
func allocateStorage() -> _Storage {
return self.allocateStorage(capacity: self.capacity) return self.allocateStorage(capacity: self.capacity)
} }
fileprivate func allocateStorage(capacity: _Capacity) -> _Storage { @inlinable
func allocateStorage(capacity: _Capacity) -> _Storage {
let newCapacity = capacity == 0 ? 0 : capacity.nextPowerOf2ClampedToMax() let newCapacity = capacity == 0 ? 0 : capacity.nextPowerOf2ClampedToMax()
return _Storage(bytesNoCopy: _Storage.allocateAndPrepareRawMemory(bytes: newCapacity, allocator: self.allocator), return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: self.allocator),
capacity: newCapacity, capacity: newCapacity,
allocator: self.allocator) allocator: self.allocator)
} }
public func reallocSlice(_ slice: Range<ByteBuffer._Index>, capacity: _Capacity) -> _Storage { @inlinable
func reallocSlice(_ slice: Range<ByteBuffer._Index>, capacity: _Capacity) -> _Storage {
assert(slice.count <= capacity) assert(slice.count <= capacity)
let new = self.allocateStorage(capacity: capacity) let new = self.allocateStorage(capacity: capacity)
self.allocator.memcpy(new.bytes, self.bytes.advanced(by: Int(slice.lowerBound)), size_t(slice.count)) self.allocator.memcpy(new.bytes, self.bytes.advanced(by: Int(slice.lowerBound)), size_t(slice.count))
return new return new
} }
public func reallocStorage(capacity minimumNeededCapacity: _Capacity) { @inlinable
func reallocStorage(capacity minimumNeededCapacity: _Capacity) {
let newCapacity = minimumNeededCapacity.nextPowerOf2ClampedToMax() let newCapacity = minimumNeededCapacity.nextPowerOf2ClampedToMax()
let ptr = self.allocator.realloc(self.bytes, size_t(newCapacity))! 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 */
@ -285,15 +294,16 @@ public struct ByteBuffer {
self.allocator.free(self.bytes) self.allocator.free(self.bytes)
} }
public static func reallocated(minimumCapacity: _Capacity, allocator: Allocator) -> _Storage { @inlinable
static func reallocated(minimumCapacity: _Capacity, allocator: Allocator) -> _Storage {
let newCapacity = minimumCapacity == 0 ? 0 : minimumCapacity.nextPowerOf2ClampedToMax() let newCapacity = minimumCapacity == 0 ? 0 : minimumCapacity.nextPowerOf2ClampedToMax()
// TODO: Use realloc if possible // TODO: Use realloc if possible
return _Storage(bytesNoCopy: _Storage.allocateAndPrepareRawMemory(bytes: newCapacity, allocator: allocator), return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: allocator),
capacity: newCapacity, capacity: newCapacity,
allocator: allocator) allocator: allocator)
} }
public func dumpBytes(slice: Slice, offset: Int, length: Int) -> String { func dumpBytes(slice: Slice, offset: Int, length: Int) -> String {
var desc = "[" var desc = "["
let bytes = UnsafeRawBufferPointer(start: self.bytes, count: Int(self.capacity)) 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] {
@ -305,7 +315,9 @@ public struct ByteBuffer {
} }
} }
@usableFromInline mutating func _copyStorageAndRebase(capacity: _Capacity, resetIndices: Bool = false) { @inlinable
@inline(never)
mutating func _copyStorageAndRebase(capacity: _Capacity, resetIndices: Bool = false) {
let indexRebaseAmount = resetIndices ? self._readerIndex : 0 let indexRebaseAmount = resetIndices ? self._readerIndex : 0
let storageRebaseAmount = self._slice.lowerBound + indexRebaseAmount let storageRebaseAmount = self._slice.lowerBound + indexRebaseAmount
let newSlice = storageRebaseAmount ..< min(storageRebaseAmount + _toCapacity(self._slice.count), self._slice.upperBound, storageRebaseAmount + capacity) let newSlice = storageRebaseAmount ..< min(storageRebaseAmount + _toCapacity(self._slice.count), self._slice.upperBound, storageRebaseAmount + capacity)
@ -315,11 +327,15 @@ public struct ByteBuffer {
self._slice = self._storage.fullSlice self._slice = self._storage.fullSlice
} }
@usableFromInline mutating func _copyStorageAndRebase(extraCapacity: _Capacity = 0, resetIndices: Bool = false) { @inlinable
@inline(never)
mutating func _copyStorageAndRebase(extraCapacity: _Capacity = 0, resetIndices: Bool = false) {
self._copyStorageAndRebase(capacity: _toCapacity(self._slice.count) + extraCapacity, resetIndices: resetIndices) self._copyStorageAndRebase(capacity: _toCapacity(self._slice.count) + extraCapacity, resetIndices: resetIndices)
} }
@usableFromInline mutating func _ensureAvailableCapacity(_ capacity: _Capacity, at index: _Index) { @inlinable
@inline(never)
mutating func _ensureAvailableCapacity(_ capacity: _Capacity, at index: _Index) {
assert(isKnownUniquelyReferenced(&self._storage)) assert(isKnownUniquelyReferenced(&self._storage))
let totalNeededCapacityWhenKeepingSlice = self._slice.lowerBound + index + capacity let totalNeededCapacityWhenKeepingSlice = self._slice.lowerBound + index + capacity
@ -432,7 +448,7 @@ public struct ByteBuffer {
// MARK: Public Core API // MARK: Public Core API
fileprivate init(allocator: ByteBufferAllocator, startingCapacity: Int) { @inlinable init(allocator: ByteBufferAllocator, startingCapacity: Int) {
let startingCapacity = _toCapacity(startingCapacity) let startingCapacity = _toCapacity(startingCapacity)
self._readerIndex = 0 self._readerIndex = 0
self._writerIndex = 0 self._writerIndex = 0
@ -463,6 +479,7 @@ public struct ByteBuffer {
/// The current capacity of the underlying storage of this `ByteBuffer`. /// The current capacity of the underlying storage of this `ByteBuffer`.
/// A COW slice of the buffer (e.g. readSlice(length: x)) will posses the same storageCapacity as the original /// A COW slice of the buffer (e.g. readSlice(length: x)) will posses the same storageCapacity as the original
/// buffer until new data is written. /// buffer until new data is written.
@inlinable
public var storageCapacity: Int { public var storageCapacity: Int {
return self._storage.fullSlice.count return self._storage.fullSlice.count
} }
@ -476,6 +493,7 @@ public struct ByteBuffer {
/// ///
/// - parameters: /// - parameters:
/// - minimumCapacity: The minimum number of bytes this buffer must be able to store. /// - minimumCapacity: The minimum number of bytes this buffer must be able to store.
@inlinable
public mutating func reserveCapacity(_ minimumCapacity: Int) { public mutating func reserveCapacity(_ minimumCapacity: Int) {
guard minimumCapacity > self.capacity else { guard minimumCapacity > self.capacity else {
return return
@ -500,11 +518,13 @@ public struct ByteBuffer {
/// method will be a no-op. /// method will be a no-op.
/// ///
/// - Parameter minimumWritableBytes: The minimum number of writable bytes this buffer must have. /// - Parameter minimumWritableBytes: The minimum number of writable bytes this buffer must have.
@inlinable
public mutating func reserveCapacity(minimumWritableBytes: Int) { public mutating func reserveCapacity(minimumWritableBytes: Int) {
return self.reserveCapacity(self.writerIndex + minimumWritableBytes) return self.reserveCapacity(self.writerIndex + minimumWritableBytes)
} }
@usableFromInline @inlinable
@inline(never)
mutating func _copyStorageAndRebaseIfNeeded() { mutating func _copyStorageAndRebaseIfNeeded() {
if !isKnownUniquelyReferenced(&self._storage) { if !isKnownUniquelyReferenced(&self._storage) {
self._copyStorageAndRebase() self._copyStorageAndRebase()
@ -633,8 +653,9 @@ public struct ByteBuffer {
return try body(.init(self._slicedStorageBuffer), storageReference) return try body(.init(self._slicedStorageBuffer), storageReference)
} }
@inlinable
@inline(never) @inline(never)
private func copyIntoByteBufferWithSliceIndex0_slowPath(index: _Index, length: _Capacity) -> ByteBuffer { func _copyIntoByteBufferWithSliceIndex0_slowPath(index: _Index, length: _Capacity) -> ByteBuffer {
var new = self var new = self
new._moveWriterIndex(to: index + length) new._moveWriterIndex(to: index + length)
new._moveReaderIndex(to: index) new._moveReaderIndex(to: index)
@ -654,11 +675,13 @@ public struct ByteBuffer {
/// - length: The length of the requested slice. /// - length: The length of the requested slice.
/// - returns: A `ByteBuffer` containing the selected bytes as readable bytes or `nil` if the selected bytes were /// - returns: A `ByteBuffer` containing the selected bytes as readable bytes or `nil` if the selected bytes were
/// not readable in the initial `ByteBuffer`. /// not readable in the initial `ByteBuffer`.
@inlinable
public func getSlice(at index: Int, length: Int) -> ByteBuffer? { public func getSlice(at index: Int, length: Int) -> ByteBuffer? {
return self.getSlice_inlineAlways(at: index, length: length) return self.getSlice_inlineAlways(at: index, length: length)
} }
@inline(__always) @inline(__always)
@inlinable
internal func getSlice_inlineAlways(at index: Int, length: Int) -> ByteBuffer? { 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 return nil
@ -679,7 +702,7 @@ public struct ByteBuffer {
guard sliceStartIndex <= ByteBuffer.Slice.maxSupportedLowerBound else { guard sliceStartIndex <= ByteBuffer.Slice.maxSupportedLowerBound else {
// the slice's begin is past the maximum supported slice begin value (16 MiB) so the only option we have // the slice's begin is past the maximum supported slice begin value (16 MiB) so the only option we have
// is copy the slice into a fresh buffer. The slice begin will then be at index 0. // is copy the slice into a fresh buffer. The slice begin will then be at index 0.
return self.copyIntoByteBufferWithSliceIndex0_slowPath(index: index, length: length) return self._copyIntoByteBufferWithSliceIndex0_slowPath(index: index, length: length)
} }
var new = self var new = self
assert(sliceStartIndex == self._slice.lowerBound &+ index) assert(sliceStartIndex == self._slice.lowerBound &+ index)
@ -700,6 +723,7 @@ public struct ByteBuffer {
/// at index `0` after the call returns. /// at index `0` after the call returns.
/// ///
/// - returns: `true` if one or more bytes have been discarded, `false` if there are no bytes to discard. /// - returns: `true` if one or more bytes have been discarded, `false` if there are no bytes to discard.
@inlinable
@discardableResult public mutating func discardReadBytes() -> Bool { @discardableResult public mutating func discardReadBytes() -> Bool {
guard self._readerIndex > 0 else { guard self._readerIndex > 0 else {
return false return false
@ -745,6 +769,7 @@ public struct ByteBuffer {
/// ///
/// - note: This method will allocate if the underlying storage is referenced by another `ByteBuffer`. Even if an /// - note: This method will allocate if the underlying storage is referenced by another `ByteBuffer`. Even if an
/// allocation is necessary this will be cheaper as the copy of the storage is elided. /// allocation is necessary this will be cheaper as the copy of the storage is elided.
@inlinable
public mutating func clear() { public mutating func clear() {
if !isKnownUniquelyReferenced(&self._storage) { if !isKnownUniquelyReferenced(&self._storage) {
self._storage = self._storage.allocateStorage() self._storage = self._storage.allocateStorage()
@ -777,6 +802,7 @@ public struct ByteBuffer {
/// ///
/// - parameters: /// - parameters:
/// - minimumCapacity: The minimum capacity that will be (re)allocated for this buffer /// - minimumCapacity: The minimum capacity that will be (re)allocated for this buffer
@inlinable
public mutating func clear(minimumCapacity: Int) { public mutating func clear(minimumCapacity: Int) {
precondition(minimumCapacity >= 0, "Cannot have a minimum capacity < 0") precondition(minimumCapacity >= 0, "Cannot have a minimum capacity < 0")
precondition(minimumCapacity <= _Capacity.max, "Minimum capacity must be <= \(_Capacity.max)") precondition(minimumCapacity <= _Capacity.max, "Minimum capacity must be <= \(_Capacity.max)")
@ -852,6 +878,7 @@ extension ByteBuffer {
/// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour.
/// - parameters: /// - parameters:
/// - offset: The number of bytes to move the reader index forward by. /// - offset: The number of bytes to move the reader index forward by.
@inlinable
public mutating func moveReaderIndex(forwardBy offset: Int) { public mutating func moveReaderIndex(forwardBy offset: Int) {
let newIndex = self._readerIndex + _toIndex(offset) 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))")
@ -865,6 +892,7 @@ extension ByteBuffer {
/// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour.
/// - parameters: /// - parameters:
/// - offset: The offset in bytes to set the reader index to. /// - offset: The offset in bytes to set the reader index to.
@inlinable
public mutating func moveReaderIndex(to offset: Int) { public mutating func moveReaderIndex(to offset: Int) {
let newIndex = _toIndex(offset) 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))")
@ -878,6 +906,7 @@ extension ByteBuffer {
/// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour.
/// - parameters: /// - parameters:
/// - offset: The number of bytes to move the writer index forward by. /// - offset: The number of bytes to move the writer index forward by.
@inlinable
public mutating func moveWriterIndex(forwardBy offset: Int) { public mutating func moveWriterIndex(forwardBy offset: Int) {
let newIndex = self._writerIndex + _toIndex(offset) 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)))")
@ -891,6 +920,7 @@ extension ByteBuffer {
/// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour.
/// - parameters: /// - parameters:
/// - offset: The offset in bytes to set the reader index to. /// - offset: The offset in bytes to set the reader index to.
@inlinable
public mutating func moveWriterIndex(to offset: Int) { public mutating func moveWriterIndex(to offset: Int) {
let newIndex = _toIndex(offset) 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)))")
@ -965,6 +995,7 @@ extension ByteBuffer: Equatable {
// TODO: I don't think this makes sense. This should compare bytes 0..<writerIndex instead. // TODO: I don't think this makes sense. This should compare bytes 0..<writerIndex instead.
/// Compare two `ByteBuffer` values. Two `ByteBuffer` values are considered equal if the readable bytes are equal. /// 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 { guard lhs.readableBytes == rhs.readableBytes else {
return false return false
@ -986,6 +1017,7 @@ extension ByteBuffer: Equatable {
extension ByteBuffer: Hashable { extension ByteBuffer: Hashable {
/// The hash value for the readable bytes. /// The hash value for the readable bytes.
@inlinable
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
self.withUnsafeReadableBytes { ptr in self.withUnsafeReadableBytes { ptr in
hasher.combine(bytes: ptr) hasher.combine(bytes: ptr)

View File

@ -127,6 +127,7 @@ extension UInt32 {
/// Returns the next power of two unless that would overflow, in which case UInt32.max (on 64-bit systems) or /// Returns the next power of two unless that would overflow, in which case UInt32.max (on 64-bit systems) or
/// Int32.max (on 32-bit systems) is returned. The returned value is always safe to be cast to Int and passed /// Int32.max (on 32-bit systems) is returned. The returned value is always safe to be cast to Int and passed
/// to malloc on all platforms. /// to malloc on all platforms.
@inlinable
func nextPowerOf2ClampedToMax() -> UInt32 { func nextPowerOf2ClampedToMax() -> UInt32 {
guard self > 0 else { guard self > 0 else {
return 1 return 1

View File

@ -221,6 +221,7 @@ extension ByteBuffer {
extension ByteBufferView: Equatable { extension ByteBufferView: Equatable {
/// required by `Equatable` /// required by `Equatable`
@inlinable
public static func == (lhs: ByteBufferView, rhs: ByteBufferView) -> Bool { public static func == (lhs: ByteBufferView, rhs: ByteBufferView) -> Bool {
guard lhs._range.count == rhs._range.count else { guard lhs._range.count == rhs._range.count else {
@ -238,6 +239,7 @@ extension ByteBufferView: Equatable {
extension ByteBufferView: Hashable { extension ByteBufferView: Hashable {
/// required by `Hashable` /// required by `Hashable`
@inlinable
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
// A well-formed ByteBufferView can never have a range that is out-of-bounds of the backing ByteBuffer. // A well-formed ByteBufferView can never have a range that is out-of-bounds of the backing ByteBuffer.
// As a result, this getSlice call can never fail, and we'd like to know it if it does. // As a result, this getSlice call can never fail, and we'd like to know it if it does.
@ -247,6 +249,7 @@ extension ByteBufferView: Hashable {
extension ByteBufferView: ExpressibleByArrayLiteral { extension ByteBufferView: ExpressibleByArrayLiteral {
/// required by `ExpressibleByArrayLiteral` /// required by `ExpressibleByArrayLiteral`
@inlinable
public init(arrayLiteral elements: Element...) { public init(arrayLiteral elements: Element...) {
self.init(elements) self.init(elements)
} }