//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #if os(Windows) import ucrt #elseif os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin #else import Glibc #endif @usableFromInline let sysMalloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? = malloc @usableFromInline let sysRealloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer? = realloc /// Xcode 13 GM shipped with a bug in the SDK that caused `free`'s first argument to be annotated as /// non-nullable. To that end, we define a thunk through to `free` that matches that constraint, as we /// never pass a `nil` pointer to it. @usableFromInline let sysFree: @convention(c) (UnsafeMutableRawPointer) -> Void = { free($0) } extension _ByteBufferSlice: Equatable {} /// The slice of a `ByteBuffer`, it's different from `Range` because the lower bound is actually only /// 24 bits (the upper bound is still 32). Before constructing, you need to make sure the lower bound actually /// fits within 24 bits, otherwise the behaviour is undefined. @usableFromInline struct _ByteBufferSlice: Sendable { @usableFromInline private(set) var upperBound: ByteBuffer._Index @usableFromInline private(set) var _begin: _UInt24 @inlinable var lowerBound: ByteBuffer._Index { return UInt32(self._begin) } @inlinable var count: Int { // Safe: the only constructors that set this enforce that upperBound > lowerBound, so // this cannot underflow. return Int(self.upperBound &- self.lowerBound) } @inlinable init() { self._begin = .init(0) self.upperBound = .init(0) } @inlinable static var maxSupportedLowerBound: ByteBuffer._Index { return ByteBuffer._Index(_UInt24.max) } } extension _ByteBufferSlice { @inlinable init(_ range: Range) { self._begin = _UInt24(range.lowerBound) self.upperBound = range.upperBound } } extension _ByteBufferSlice: CustomStringConvertible { @usableFromInline var description: String { return "_ByteBufferSlice { \(self.lowerBound)..<\(self.upperBound) }" } } /// The preferred allocator for `ByteBuffer` values. The allocation strategy is opaque but is currently libc's /// `malloc`, `realloc` and `free`. /// /// - note: `ByteBufferAllocator` is thread-safe. public struct ByteBufferAllocator: Sendable { /// 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 /// many places. @inlinable public init() { self.init(hookedMalloc: { sysMalloc($0) }, hookedRealloc: { sysRealloc($0, $1) }, hookedFree: { sysFree($0) }, hookedMemcpy: { $0.copyMemory(from: $1, byteCount: $2) }) } @inlinable internal init(hookedMalloc: @escaping @convention(c) (size_t) -> UnsafeMutableRawPointer?, hookedRealloc: @escaping @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer?, hookedFree: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void, hookedMemcpy: @escaping @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void) { self.malloc = hookedMalloc self.realloc = hookedRealloc self.free = hookedFree self.memcpy = hookedMemcpy } /// Request a freshly allocated `ByteBuffer` of size `capacity` or larger. /// /// - note: The passed `capacity` is the `ByteBuffer`'s initial capacity, it will grow automatically if necessary. /// /// - note: If `capacity` is `0`, this function will not allocate. If you want to trigger an allocation immediately, /// also call `.clear()`. /// /// - parameters: /// - capacity: The initial capacity of the returned `ByteBuffer`. @inlinable public func buffer(capacity: Int) -> ByteBuffer { precondition(capacity >= 0, "ByteBuffer capacity must be positive.") guard capacity > 0 else { return ByteBufferAllocator.zeroCapacityWithDefaultAllocator } return ByteBuffer(allocator: self, startingCapacity: capacity) } @usableFromInline internal static let zeroCapacityWithDefaultAllocator = ByteBuffer(allocator: ByteBufferAllocator(), startingCapacity: 0) @usableFromInline internal let malloc: @convention(c) (size_t) -> UnsafeMutableRawPointer? @usableFromInline internal let realloc: @convention(c) (UnsafeMutableRawPointer?, size_t) -> UnsafeMutableRawPointer? @usableFromInline internal let free: @convention(c) (UnsafeMutableRawPointer) -> Void @usableFromInline internal let memcpy: @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer, size_t) -> Void } @inlinable func _toCapacity(_ value: Int) -> ByteBuffer._Capacity { return ByteBuffer._Capacity(truncatingIfNeeded: value) } @inlinable func _toIndex(_ value: Int) -> ByteBuffer._Index { return ByteBuffer._Index(truncatingIfNeeded: value) } /// `ByteBuffer` stores contiguously allocated raw bytes. It is a random and sequential accessible sequence of zero or /// more bytes (octets). /// /// ### Allocation /// Use `allocator.buffer(capacity: desiredCapacity)` to allocate a new `ByteBuffer`. /// /// ### Supported types /// A variety of types can be read/written from/to a `ByteBuffer`. Using Swift's `extension` mechanism you can easily /// create `ByteBuffer` support for your own data types. Out of the box, `ByteBuffer` supports for example the following /// types (non-exhaustive list): /// /// - `String`/`StaticString` /// - Swift's various (unsigned) integer types /// - `Foundation`'s `Data` /// - `[UInt8]` and generally any `Collection` of `UInt8` /// /// ### Random Access /// For every supported type `ByteBuffer` usually contains two methods for random access: /// /// 1. `get(at: Int, length: Int)` where `` is for example `String`, `Data`, `Bytes` (for `[UInt8]`) /// 2. `set(at: Int)` /// /// Example: /// /// var buf = ... /// buf.setString("Hello World", at: 0) /// buf.moveWriterIndex(to: 11) /// let helloWorld = buf.getString(at: 0, length: 11) /// /// let written = buf.setInteger(17 as Int, at: 11) /// buf.moveWriterIndex(forwardBy: written) /// let seventeen: Int? = buf.getInteger(at: 11) /// /// If needed, `ByteBuffer` will automatically resize its storage to accommodate your `set` request. /// /// ### Sequential Access /// `ByteBuffer` provides two properties which are indices into the `ByteBuffer` to support sequential access: /// - `readerIndex`, the index of the next readable byte /// - `writerIndex`, the index of the next byte to write /// /// For every supported type `ByteBuffer` usually contains two methods for sequential access: /// /// 1. `read(length: Int)` to read `length` bytes from the current `readerIndex` (and then advance the reader /// index by `length` bytes) /// 2. `write(Type)` to write, advancing the `writerIndex` by the appropriate amount /// /// Example: /// /// var buf = ... /// buf.writeString("Hello World") /// buf.writeInteger(17 as Int) /// let helloWorld = buf.readString(length: 11) /// let seventeen: Int = buf.readInteger() /// /// ### Layout /// +-------------------+------------------+------------------+ /// | discardable bytes | readable bytes | writable bytes | /// | | (CONTENT) | | /// +-------------------+------------------+------------------+ /// | | | | /// 0 <= readerIndex <= writerIndex <= capacity /// /// The 'discardable bytes' are usually bytes that have already been read, they can however still be accessed using /// the random access methods. 'Readable bytes' are the bytes currently available to be read using the sequential /// access interface (`read`/`write`). Getting `writableBytes` (bytes beyond the writer index) is undefined /// behaviour and might yield arbitrary bytes (_not_ `0` initialised). /// /// ### Slicing /// `ByteBuffer` supports slicing a `ByteBuffer` without copying the underlying storage. /// /// Example: /// /// var buf = ... /// let dataBytes: [UInt8] = [0xca, 0xfe, 0xba, 0xbe] /// let dataBytesLength = UInt32(dataBytes.count) /// buf.writeInteger(dataBytesLength) /* the header */ /// buf.writeBytes(dataBytes) /* the data */ /// let bufDataBytesOnly = buf.getSlice(at: 4, length: dataBytes.count) /// /* `bufDataByteOnly` and `buf` will share their storage */ /// /// ### Notes /// All `ByteBuffer` methods that don't contain the word 'unsafe' will only allow you to access the 'readable bytes'. /// public struct ByteBuffer { @usableFromInline typealias Slice = _ByteBufferSlice @usableFromInline typealias Allocator = ByteBufferAllocator // these two type aliases should be made `@usableFromInline internal` for // the 2.0 release when we can drop Swift 4.0 & 4.1 support. The reason they // must be public is because Swift 4.0 and 4.1 don't support attributes for // typealiases and Swift 4.2 warns if those attributes aren't present and // the type is internal. public typealias _Index = UInt32 public typealias _Capacity = UInt32 @usableFromInline var _storage: _Storage @usableFromInline var _readerIndex: _Index @usableFromInline var _writerIndex: _Index @usableFromInline var _slice: Slice // MARK: Internal _Storage for CoW /// Note: This class is **not** thread-safe @usableFromInline final class _Storage { @usableFromInline private(set) var capacity: _Capacity @usableFromInline private(set) var bytes: UnsafeMutableRawPointer @usableFromInline let allocator: ByteBufferAllocator @inlinable init(bytesNoCopy: UnsafeMutableRawPointer, capacity: _Capacity, allocator: ByteBufferAllocator) { self.bytes = bytesNoCopy self.capacity = capacity self.allocator = allocator } deinit { self.deallocate() } @inlinable var fullSlice: _ByteBufferSlice { return _ByteBufferSlice(0.. UnsafeMutableRawPointer { let ptr = allocator.malloc(size_t(bytes))! /* bind the memory so we can assume it elsewhere to be bound to UInt8 */ ptr.bindMemory(to: UInt8.self, capacity: Int(bytes)) return ptr } @inlinable func allocateStorage() -> _Storage { return self.allocateStorage(capacity: self.capacity) } @inlinable func allocateStorage(capacity: _Capacity) -> _Storage { let newCapacity = capacity == 0 ? 0 : capacity.nextPowerOf2ClampedToMax() return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: self.allocator), capacity: newCapacity, allocator: self.allocator) } @inlinable func reallocSlice(_ slice: Range, capacity: _Capacity) -> _Storage { assert(slice.count <= capacity) let new = self.allocateStorage(capacity: capacity) self.allocator.memcpy(new.bytes, self.bytes.advanced(by: Int(slice.lowerBound)), size_t(slice.count)) return new } @inlinable func reallocStorage(capacity minimumNeededCapacity: _Capacity) { let newCapacity = minimumNeededCapacity.nextPowerOf2ClampedToMax() let ptr = self.allocator.realloc(self.bytes, size_t(newCapacity))! /* bind the memory so we can assume it elsewhere to be bound to UInt8 */ ptr.bindMemory(to: UInt8.self, capacity: Int(newCapacity)) self.bytes = ptr self.capacity = newCapacity } private func deallocate() { self.allocator.free(self.bytes) } @inlinable static func reallocated(minimumCapacity: _Capacity, allocator: Allocator) -> _Storage { let newCapacity = minimumCapacity == 0 ? 0 : minimumCapacity.nextPowerOf2ClampedToMax() // TODO: Use realloc if possible return _Storage(bytesNoCopy: _Storage._allocateAndPrepareRawMemory(bytes: newCapacity, allocator: allocator), capacity: newCapacity, allocator: allocator) } func dumpBytes(slice: Slice, offset: Int, length: Int) -> String { var desc = "[" let bytes = UnsafeRawBufferPointer(start: self.bytes, count: Int(self.capacity)) for byte in bytes[Int(slice.lowerBound) + offset ..< Int(slice.lowerBound) + offset + length] { let hexByte = String(byte, radix: 16) desc += " \(hexByte.count == 1 ? "0" : "")\(hexByte)" } desc += " ]" return desc } } @inlinable @inline(never) mutating func _copyStorageAndRebase(capacity: _Capacity, resetIndices: Bool = false) { // This math has to be very careful, because we already know that in some call paths _readerIndex exceeds 1 << 24, and lots of this math // is in UInt32 space. It's not hard for us to trip some of these conditions. As a result, I've heavily commented this // fairly heavily to explain the math. // Step 1: If we are resetting the indices, we need to slide the allocation by at least the current value of _readerIndex, so the new // value of _readerIndex will be 0. Otherwise we can leave them as they are. let indexRebaseAmount = resetIndices ? self._readerIndex : 0 // Step 2: We also want to only copy the bytes within the slice, and move them to index 0. As a result, we have this // state space after the copy-and-rebase: // // +--------------+------------------------+-------------------+ // | resetIndices | self._slice.lowerBound | self._readerIndex | // +--------------+------------------------+-------------------+ // | true | 0 | 0 | // | false | 0 | self._readerIndex | // +--------------+------------------------+-------------------+ // // The maximum value of _readerIndex (and so indexRebaseAmount) is UInt32.max, but that can only happen if the lower bound // of the slice is 0, when this addition would be safe. Recall that _readerIndex is an index _into_ the slice, and the upper bound of // the slice must be stored into a UInt32, so the capacity can never be more than UInt32.max. As _slice.lowerBound advances, _readerIndex // must shrink. In the worst case, _readerIndex == _slice.count == (_slice.upperBound - _slice.lowerBound), so it is always safe to add // _slice.lowerBound to _readerIndex. This cannot overflow. // // This value ends up storing the lower bound of the bytes we want to copy. let storageRebaseAmount = self._slice.lowerBound + indexRebaseAmount // Step 3: Here we need to find out the range within the slice that defines the upper bound of the range of bytes we want to copy. // This will be the smallest of: // // 1. The target requested capacity, in bytes, as a UInt32 (the argument `capacity`), added to the lower bound. The resulting size of // the slice is equal to the argument `capacity`. // 2. The upper bound of the current slice. This will be smaller than (1) if there are fewer bytes in the current buffer than the size of // the requested capacity. // // This math is checked on purpose: we should not pass a value of capacity that is larger than the size of the buffer, and if resetIndices is // true then we shouldn't pass a value that is larger than readable bytes. If we do, that's an error. let storageUpperBound = min(self._slice.upperBound, storageRebaseAmount + capacity) // Step 4: Allocate the new buffer and copy the slice of bytes we want to keep. let newSlice = storageRebaseAmount ..< storageUpperBound self._storage = self._storage.reallocSlice(newSlice, capacity: capacity) // Step 5: Fixup the indices. These should never trap, but we're going to leave them checked because this method is fiddly. self._moveReaderIndex(to: self._readerIndex - indexRebaseAmount) self._moveWriterIndex(to: self._writerIndex - indexRebaseAmount) // Step 6: As we've reallocated the buffer, we can now use the entire new buffer as our slice. self._slice = self._storage.fullSlice } @inlinable @inline(never) mutating func _copyStorageAndRebase(extraCapacity: _Capacity = 0, resetIndices: Bool = false) { self._copyStorageAndRebase(capacity: _toCapacity(self._slice.count) + extraCapacity, resetIndices: resetIndices) } @inlinable @inline(never) mutating func _ensureAvailableCapacity(_ capacity: _Capacity, at index: _Index) { assert(isKnownUniquelyReferenced(&self._storage)) let totalNeededCapacityWhenKeepingSlice = self._slice.lowerBound + index + capacity if totalNeededCapacityWhenKeepingSlice > self._slice.upperBound { // we need to at least adjust the slice's upper bound which we can do as we're the unique owner of the storage, // let's see if adjusting the slice's upper bound buys us enough storage if totalNeededCapacityWhenKeepingSlice > self._storage.capacity { let newStorageMinCapacity = index + capacity // nope, we need to actually re-allocate again. If our slice does not start at 0, let's also rebase if self._slice.lowerBound == 0 { self._storage.reallocStorage(capacity: newStorageMinCapacity) } else { self._storage = self._storage.reallocSlice(self._slice.lowerBound ..< self._slice.upperBound, capacity: newStorageMinCapacity) } self._slice = self._storage.fullSlice } else { // yes, let's just extend the slice until the end of the buffer self._slice = _ByteBufferSlice(_slice.lowerBound ..< self._storage.capacity) } } assert(self._slice.lowerBound + index + capacity <= self._slice.upperBound) assert(self._slice.lowerBound >= 0, "illegal slice: negative lower bound: \(self._slice.lowerBound)") assert(self._slice.upperBound <= self._storage.capacity, "illegal slice: upper bound (\(self._slice.upperBound)) exceeds capacity: \(self._storage.capacity)") } // MARK: Internal API @inlinable mutating func _moveReaderIndex(to newIndex: _Index) { assert(newIndex >= 0 && newIndex <= writerIndex) self._readerIndex = newIndex } @inlinable mutating func _moveReaderIndex(forwardBy offset: Int) { let newIndex = self._readerIndex + _toIndex(offset) self._moveReaderIndex(to: newIndex) } @inlinable mutating func _moveWriterIndex(to newIndex: _Index) { assert(newIndex >= 0 && newIndex <= _toCapacity(self._slice.count)) self._writerIndex = newIndex } @inlinable mutating func _moveWriterIndex(forwardBy offset: Int) { let newIndex = self._writerIndex + _toIndex(offset) self._moveWriterIndex(to: newIndex) } @inlinable mutating func _setBytes(_ bytes: UnsafeRawBufferPointer, at index: _Index) -> _Capacity { let bytesCount = bytes.count let newEndIndex: _Index = index + _toIndex(bytesCount) if !isKnownUniquelyReferenced(&self._storage) { let extraCapacity = newEndIndex > self._slice.upperBound ? newEndIndex - self._slice.upperBound : 0 self._copyStorageAndRebase(extraCapacity: extraCapacity) } self._ensureAvailableCapacity(_Capacity(bytesCount), at: index) self._setBytesAssumingUniqueBufferAccess(bytes, at: index) return _toCapacity(bytesCount) } @inlinable mutating func _setBytesAssumingUniqueBufferAccess(_ bytes: UnsafeRawBufferPointer, at index: _Index) { let targetPtr = UnsafeMutableRawBufferPointer(fastRebase: self._slicedStorageBuffer.dropFirst(Int(index))) targetPtr.copyMemory(from: bytes) } @inline(never) @inlinable @_specialize(where Bytes == CircularBuffer) mutating func _setSlowPath(bytes: Bytes, at index: _Index) -> _Capacity where Bytes.Element == UInt8 { func ensureCapacityAndReturnStorageBase(capacity: Int) -> UnsafeMutablePointer { self._ensureAvailableCapacity(_Capacity(capacity), at: index) let newBytesPtr = UnsafeMutableRawBufferPointer(fastRebase: self._slicedStorageBuffer[Int(index) ..< Int(index) + Int(capacity)]) return newBytesPtr.bindMemory(to: UInt8.self).baseAddress! } let underestimatedByteCount = bytes.underestimatedCount let newPastEndIndex: _Index = index + _toIndex(underestimatedByteCount) if !isKnownUniquelyReferenced(&self._storage) { let extraCapacity = newPastEndIndex > self._slice.upperBound ? newPastEndIndex - self._slice.upperBound : 0 self._copyStorageAndRebase(extraCapacity: extraCapacity) } var base = ensureCapacityAndReturnStorageBase(capacity: underestimatedByteCount) var (iterator, idx) = UnsafeMutableBufferPointer(start: base, count: underestimatedByteCount).initialize(from: bytes) assert(idx == underestimatedByteCount) while let b = iterator.next() { base = ensureCapacityAndReturnStorageBase(capacity: idx + 1) base[idx] = b idx += 1 } return _toCapacity(idx) } @inlinable mutating func _setBytes(_ bytes: Bytes, at index: _Index) -> _Capacity where Bytes.Element == UInt8 { if let written = bytes.withContiguousStorageIfAvailable({ bytes in self._setBytes(UnsafeRawBufferPointer(bytes), at: index) }) { // fast path, we've got access to the contiguous bytes return written } else { return self._setSlowPath(bytes: bytes, at: index) } } // MARK: Public Core API @inlinable init(allocator: ByteBufferAllocator, startingCapacity: Int) { let startingCapacity = _toCapacity(startingCapacity) self._readerIndex = 0 self._writerIndex = 0 self._storage = _Storage.reallocated(minimumCapacity: startingCapacity, allocator: allocator) self._slice = self._storage.fullSlice } /// The number of bytes writable until `ByteBuffer` will need to grow its underlying storage which will likely /// trigger a copy of the bytes. @inlinable public var writableBytes: Int { // this cannot over/overflow because both values are positive and writerIndex<=slice.count, checked on ingestion return Int(_toCapacity(self._slice.count) &- self._writerIndex) } /// The number of bytes readable (`readableBytes` = `writerIndex` - `readerIndex`). @inlinable public var readableBytes: Int { // this cannot under/overflow because both are positive and writer >= reader (checked on ingestion of bytes). return Int(self._writerIndex &- self._readerIndex) } /// The current capacity of the storage of this `ByteBuffer`, this is not constant and does _not_ signify the number /// of bytes that have been written to this `ByteBuffer`. @inlinable public var capacity: Int { return self._slice.count } /// 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 /// buffer until new data is written. @inlinable public var storageCapacity: Int { return self._storage.fullSlice.count } /// Reserves enough space to store the specified number of bytes. /// /// This method will ensure that the buffer has space for at least as many bytes as requested. /// This includes any bytes already stored, and completely disregards the reader/writer indices. /// If the buffer already has space to store the requested number of bytes, this method will be /// a no-op. /// /// - parameters: /// - minimumCapacity: The minimum number of bytes this buffer must be able to store. @inlinable public mutating func reserveCapacity(_ minimumCapacity: Int) { guard minimumCapacity > self.capacity else { return } let targetCapacity = _toCapacity(minimumCapacity) if isKnownUniquelyReferenced(&self._storage) { // We have the unique reference. If we have the full slice, we can realloc. Otherwise // we have to copy memory anyway. self._ensureAvailableCapacity(targetCapacity, at: 0) } else { // We don't have a unique reference here, so we need to allocate and copy, no // optimisations available. self._copyStorageAndRebase(capacity: targetCapacity) } } /// Reserves enough space to write at least the specified number of bytes. /// /// This method will ensure that the buffer has enough writable space for at least as many bytes /// as requested. If the buffer already has space to write the requested number of bytes, this /// method will be a no-op. /// /// - Parameter minimumWritableBytes: The minimum number of writable bytes this buffer must have. @inlinable public mutating func reserveCapacity(minimumWritableBytes: Int) { return self.reserveCapacity(self.writerIndex + minimumWritableBytes) } @inlinable @inline(never) mutating func _copyStorageAndRebaseIfNeeded() { if !isKnownUniquelyReferenced(&self._storage) { self._copyStorageAndRebase() } } @inlinable var _slicedStorageBuffer: UnsafeMutableRawBufferPointer { return UnsafeMutableRawBufferPointer(start: self._storage.bytes.advanced(by: Int(self._slice.lowerBound)), count: self._slice.count) } /// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify those bytes. /// /// - warning: Do not escape the pointer from the closure for later use. /// /// - parameters: /// - body: The closure that will accept the yielded bytes. /// - returns: The value returned by `body`. @inlinable public mutating func withUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T { self._copyStorageAndRebaseIfNeeded() // this is safe because we always know that readerIndex >= writerIndex let range = Range(uncheckedBounds: (lower: self.readerIndex, upper: self.writerIndex)) return try body(.init(fastRebase: self._slicedStorageBuffer[range])) } /// Yields the bytes currently writable (`bytesWritable` = `capacity` - `writerIndex`). Before reading those bytes you must first /// write to them otherwise you will trigger undefined behaviour. The writer index will remain unchanged. /// /// - note: In almost all cases you should use `writeWithUnsafeMutableBytes` which will move the write pointer instead of this method /// /// - warning: Do not escape the pointer from the closure for later use. /// /// - parameters: /// - body: The closure that will accept the yielded bytes and return the number of bytes written. /// - returns: The number of bytes written. @inlinable public mutating func withUnsafeMutableWritableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T { self._copyStorageAndRebaseIfNeeded() return try body(.init(fastRebase: self._slicedStorageBuffer.dropFirst(self.writerIndex))) } /// This vends a pointer of the `ByteBuffer` at the `writerIndex` after ensuring that the buffer has at least `minimumWritableBytes` of writable bytes available. /// /// - warning: Do not escape the pointer from the closure for later use. /// /// - parameters: /// - minimumWritableBytes: The number of writable bytes to reserve capacity for before vending the `ByteBuffer` pointer to `body`. /// - body: The closure that will accept the yielded bytes and return the number of bytes written. /// - returns: The number of bytes written. @discardableResult @inlinable public mutating func writeWithUnsafeMutableBytes(minimumWritableBytes: Int, _ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int { if minimumWritableBytes > 0 { self.reserveCapacity(minimumWritableBytes: minimumWritableBytes) } let bytesWritten = try self.withUnsafeMutableWritableBytes({ try body($0) }) self._moveWriterIndex(to: self._writerIndex + _toIndex(bytesWritten)) return bytesWritten } @available(*, deprecated, message: "please use writeWithUnsafeMutableBytes(minimumWritableBytes:_:) instead to ensure sufficient write capacity.") @discardableResult @inlinable public mutating func writeWithUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int { return try self.writeWithUnsafeMutableBytes(minimumWritableBytes: 0, { try body($0) }) } /// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain /// uninitialised memory and it's undefined behaviour to read it. In most cases you should use `withUnsafeReadableBytes`. /// /// - warning: Do not escape the pointer from the closure for later use. @inlinable public func withVeryUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T { return try body(.init(self._slicedStorageBuffer)) } /// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain /// uninitialised memory and it's undefined behaviour to read it. In most cases you should use `withUnsafeMutableWritableBytes`. /// /// - warning: Do not escape the pointer from the closure for later use. @inlinable public mutating func withVeryUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T { self._copyStorageAndRebaseIfNeeded() // this will trigger a CoW if necessary return try body(.init(self._slicedStorageBuffer)) } /// Yields a buffer pointer containing this `ByteBuffer`'s readable bytes. /// /// - warning: Do not escape the pointer from the closure for later use. /// /// - parameters: /// - body: The closure that will accept the yielded bytes. /// - returns: The value returned by `body`. @inlinable public func withUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T { // This is safe, writerIndex >= readerIndex let range = Range(uncheckedBounds: (lower: self.readerIndex, upper: self.writerIndex)) return try body(.init(fastRebase: self._slicedStorageBuffer[range])) } /// Yields a buffer pointer containing this `ByteBuffer`'s readable bytes. You may hold a pointer to those bytes /// even after the closure returned iff you model the lifetime of those bytes correctly using the `Unmanaged` /// instance. If you don't require the pointer after the closure returns, use `withUnsafeReadableBytes`. /// /// If you escape the pointer from the closure, you _must_ call `storageManagement.retain()` to get ownership to /// the bytes and you also must call `storageManagement.release()` if you no longer require those bytes. Calls to /// `retain` and `release` must be balanced. /// /// - parameters: /// - body: The closure that will accept the yielded bytes and the `storageManagement`. /// - returns: The value returned by `body`. @inlinable public func withUnsafeReadableBytesWithStorageManagement(_ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T) rethrows -> T { let storageReference: Unmanaged = Unmanaged.passUnretained(self._storage) // This is safe, writerIndex >= readerIndex let range = Range(uncheckedBounds: (lower: self.readerIndex, upper: self.writerIndex)) return try body(.init(fastRebase: self._slicedStorageBuffer[range]), storageReference) } /// See `withUnsafeReadableBytesWithStorageManagement` and `withVeryUnsafeBytes`. @inlinable public func withVeryUnsafeBytesWithStorageManagement(_ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T) rethrows -> T { let storageReference: Unmanaged = Unmanaged.passUnretained(self._storage) return try body(.init(self._slicedStorageBuffer), storageReference) } @inlinable @inline(never) func _copyIntoByteBufferWithSliceIndex0_slowPath(index: _Index, length: _Capacity) -> ByteBuffer { var new = self new._moveWriterIndex(to: index + length) new._moveReaderIndex(to: index) new._copyStorageAndRebase(capacity: length, resetIndices: true) return new } /// Returns a slice of size `length` bytes, starting at `index`. The `ByteBuffer` this is invoked on and the /// `ByteBuffer` returned will share the same underlying storage. However, the byte at `index` in this `ByteBuffer` /// will correspond to index `0` in the returned `ByteBuffer`. /// The `readerIndex` of the returned `ByteBuffer` will be `0`, the `writerIndex` will be `length`. /// /// The selected bytes must be readable or else `nil` will be returned. /// /// - parameters: /// - index: The index the requested slice starts at. /// - length: The length of the requested slice. /// - returns: A `ByteBuffer` containing the selected bytes as readable bytes or `nil` if the selected bytes were /// not readable in the initial `ByteBuffer`. @inlinable public func getSlice(at index: Int, length: Int) -> ByteBuffer? { return self.getSlice_inlineAlways(at: index, length: length) } @inline(__always) @inlinable internal func getSlice_inlineAlways(at index: Int, length: Int) -> ByteBuffer? { guard index >= 0 && length >= 0 && index >= self.readerIndex && length <= self.writerIndex && index <= self.writerIndex &- length else { return nil } let index = _toIndex(index) let length = _toCapacity(length) // The arithmetic below is safe because: // 1. maximum `writerIndex` <= self._slice.count (see `_moveWriterIndex`) // 2. `self._slice.lowerBound + self._slice.count` is always safe (because it's `self._slice.upperBound`) // 3. `index` is inside the range `self.readerIndex ... self.writerIndex` (the `guard` above) // // This means that the largest number that `index` could have is equal to // `self._slice_.upperBound = self._slice.lowerBound + self._slice.count` and that // is guaranteed to be expressible as a `UInt32` (because it's actually stored as such). let sliceStartIndex: UInt32 = self._slice.lowerBound + index 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 // 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) } var new = self assert(sliceStartIndex == self._slice.lowerBound &+ index) // - The arithmetic below is safe because // 1. `writerIndex` <= `self._slice.count` (see `_moveWriterIndex`) // 2. `length` <= `self.writerIndex` (see `guard`s) // 3. `sliceStartIndex` + `self._slice.count` is always safe (because that's `self._slice.upperBound`. // - The range construction is safe because `length` >= 0 (see `guard` at the beginning of the function). new._slice = _ByteBufferSlice(Range(uncheckedBounds: (lower: sliceStartIndex, upper: sliceStartIndex &+ length))) new._moveReaderIndex(to: 0) new._moveWriterIndex(to: length) return new } /// Discard the bytes before the reader index. The byte at index `readerIndex` before calling this method will be /// 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. @inlinable @discardableResult public mutating func discardReadBytes() -> Bool { guard self._readerIndex > 0 else { return false } if self._readerIndex == self._writerIndex { // If the whole buffer was consumed we can just reset the readerIndex and writerIndex to 0 and move on. self._moveWriterIndex(to: 0) self._moveReaderIndex(to: 0) return true } if isKnownUniquelyReferenced(&self._storage) { self._storage.bytes.advanced(by: Int(self._slice.lowerBound)) .copyMemory(from: self._storage.bytes.advanced(by: Int(self._slice.lowerBound + self._readerIndex)), byteCount: self.readableBytes) let indexShift = self._readerIndex self._moveReaderIndex(to: 0) self._moveWriterIndex(to: self._writerIndex - indexShift) } else { self._copyStorageAndRebase(extraCapacity: 0, resetIndices: true) } return true } /// The reader index or the number of bytes previously read from this `ByteBuffer`. `readerIndex` is `0` for a /// newly allocated `ByteBuffer`. @inlinable public var readerIndex: Int { return Int(self._readerIndex) } /// The write index or the number of bytes previously written to this `ByteBuffer`. `writerIndex` is `0` for a /// newly allocated `ByteBuffer`. @inlinable public var writerIndex: Int { return Int(self._writerIndex) } /// Set both reader index and writer index to `0`. This will reset the state of this `ByteBuffer` to the state /// of a freshly allocated one, if possible without allocations. This is the cheapest way to recycle a `ByteBuffer` /// for a new use-case. /// /// - 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. @inlinable public mutating func clear() { if !isKnownUniquelyReferenced(&self._storage) { self._storage = self._storage.allocateStorage() } self._slice = self._storage.fullSlice self._moveWriterIndex(to: 0) self._moveReaderIndex(to: 0) } /// Set both reader index and writer index to `0`. This will reset the state of this `ByteBuffer` to the state /// of a freshly allocated one, if possible without allocations. This is the cheapest way to recycle a `ByteBuffer` /// for a new use-case. /// /// - 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. /// /// - parameters: /// - minimumCapacity: The minimum capacity that will be (re)allocated for this buffer @available(*, deprecated, message: "Use an `Int` as the argument") public mutating func clear(minimumCapacity: UInt32) { self.clear(minimumCapacity: Int(minimumCapacity)) } /// Set both reader index and writer index to `0`. This will reset the state of this `ByteBuffer` to the state /// of a freshly allocated one, if possible without allocations. This is the cheapest way to recycle a `ByteBuffer` /// for a new use-case. /// /// - 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. /// /// - parameters: /// - minimumCapacity: The minimum capacity that will be (re)allocated for this buffer @inlinable public mutating func clear(minimumCapacity: Int) { precondition(minimumCapacity >= 0, "Cannot have a minimum capacity < 0") precondition(minimumCapacity <= _Capacity.max, "Minimum capacity must be <= \(_Capacity.max)") let minimumCapacity = _Capacity(minimumCapacity) if !isKnownUniquelyReferenced(&self._storage) { self._storage = self._storage.allocateStorage(capacity: minimumCapacity) } else if minimumCapacity > self._storage.capacity { self._storage.reallocStorage(capacity: minimumCapacity) } self._slice = self._storage.fullSlice self._moveWriterIndex(to: 0) self._moveReaderIndex(to: 0) } } extension ByteBuffer: CustomStringConvertible, CustomDebugStringConvertible { /// A `String` describing this `ByteBuffer`. Example: /// /// ByteBuffer { readerIndex: 0, writerIndex: 4, readableBytes: 4, capacity: 512, storageCapacity: 1024, slice: 256..<768, storage: 0x0000000103001000 (1024 bytes)} /// /// The format of the description is not API. /// /// - returns: A description of this `ByteBuffer`. public var description: String { return """ ByteBuffer { \ readerIndex: \(self.readerIndex), \ writerIndex: \(self.writerIndex), \ readableBytes: \(self.readableBytes), \ capacity: \(self.capacity), \ storageCapacity: \(self.storageCapacity), \ slice: \(self._slice), \ storage: \(self._storage.bytes) (\(self._storage.capacity) bytes) \ } """ } /// A `String` describing this `ByteBuffer` with some portion of the readable bytes dumped too. Example: /// /// ByteBuffer { readerIndex: 0, writerIndex: 4, readableBytes: 4, capacity: 512, slice: 256..<768, storage: 0x0000000103001000 (1024 bytes)} /// readable bytes (max 1k): [ 00 01 02 03 ] /// /// The format of the description is not API. /// /// - returns: A description of this `ByteBuffer` useful for debugging. public var debugDescription: String { return "\(self.description)\nreadable bytes (max 1k): \(self._storage.dumpBytes(slice: self._slice, offset: self.readerIndex, length: min(1024, self.readableBytes)))" } } /* change types to the user visible `Int` */ extension ByteBuffer { /// Copy the collection of `bytes` into the `ByteBuffer` at `index`. Does not move the writer index. @discardableResult @inlinable public mutating func setBytes(_ bytes: Bytes, at index: Int) -> Int where Bytes.Element == UInt8 { return Int(self._setBytes(bytes, at: _toIndex(index))) } /// Copy `bytes` into the `ByteBuffer` at `index`. Does not move the writer index. @discardableResult @inlinable public mutating func setBytes(_ bytes: UnsafeRawBufferPointer, at index: Int) -> Int { return Int(self._setBytes(bytes, at: _toIndex(index))) } /// Move the reader index forward by `offset` bytes. /// /// - warning: By contract the bytes between (including) `readerIndex` and (excluding) `writerIndex` must be /// initialised, ie. have been written before. Also the `readerIndex` must always be less than or equal /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// - parameters: /// - offset: The number of bytes to move the reader index forward by. @inlinable public mutating func moveReaderIndex(forwardBy offset: Int) { let newIndex = self._readerIndex + _toIndex(offset) precondition(newIndex >= 0 && newIndex <= writerIndex, "new readerIndex: \(newIndex), expected: range(0, \(writerIndex))") self._moveReaderIndex(to: newIndex) } /// Set the reader index to `offset`. /// /// - warning: By contract the bytes between (including) `readerIndex` and (excluding) `writerIndex` must be /// initialised, ie. have been written before. Also the `readerIndex` must always be less than or equal /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// - parameters: /// - offset: The offset in bytes to set the reader index to. @inlinable public mutating func moveReaderIndex(to offset: Int) { let newIndex = _toIndex(offset) precondition(newIndex >= 0 && newIndex <= writerIndex, "new readerIndex: \(newIndex), expected: range(0, \(writerIndex))") self._moveReaderIndex(to: newIndex) } /// Move the writer index forward by `offset` bytes. /// /// - warning: By contract the bytes between (including) `readerIndex` and (excluding) `writerIndex` must be /// initialised, ie. have been written before. Also the `readerIndex` must always be less than or equal /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// - parameters: /// - offset: The number of bytes to move the writer index forward by. @inlinable public mutating func moveWriterIndex(forwardBy offset: Int) { let newIndex = self._writerIndex + _toIndex(offset) precondition(newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))") self._moveWriterIndex(to: newIndex) } /// Set the writer index to `offset`. /// /// - warning: By contract the bytes between (including) `readerIndex` and (excluding) `writerIndex` must be /// initialised, ie. have been written before. Also the `readerIndex` must always be less than or equal /// to the `writerIndex`. Failing to meet either of these requirements leads to undefined behaviour. /// - parameters: /// - offset: The offset in bytes to set the reader index to. @inlinable public mutating func moveWriterIndex(to offset: Int) { let newIndex = _toIndex(offset) precondition(newIndex >= 0 && newIndex <= _toCapacity(self._slice.count),"new writerIndex: \(newIndex), expected: range(0, \(_toCapacity(self._slice.count)))") self._moveWriterIndex(to: newIndex) } } extension ByteBuffer { /// Copies `length` `bytes` starting at the `fromIndex` to `toIndex`. Does not move the writer index. /// /// - Note: Overlapping ranges, for example `copyBytes(at: 1, to: 2, length: 5)` are allowed. /// - Precondition: The range represented by `fromIndex` and `length` must be readable bytes, /// that is: `fromIndex >= readerIndex` and `fromIndex + length <= writerIndex`. /// - Parameter fromIndex: The index of the first byte to copy. /// - Parameter toIndex: The index into to which the first byte will be copied. /// - Parameter length: The number of bytes which should be copied. @discardableResult @inlinable public mutating func copyBytes(at fromIndex: Int, to toIndex: Int, length: Int) throws -> Int { switch length { case ..<0: throw CopyBytesError.negativeLength case 0: return 0 default: () } guard self.readerIndex <= fromIndex && fromIndex + length <= self.writerIndex else { throw CopyBytesError.unreadableSourceBytes } if !isKnownUniquelyReferenced(&self._storage) { let newEndIndex = max(self._writerIndex, _toIndex(toIndex + length)) self._copyStorageAndRebase(capacity: newEndIndex) } self._ensureAvailableCapacity(_Capacity(length), at: _toIndex(toIndex)) self.withVeryUnsafeMutableBytes { ptr in let srcPtr = UnsafeRawBufferPointer(start: ptr.baseAddress!.advanced(by: fromIndex), count: length) let targetPtr = UnsafeMutableRawBufferPointer(fastRebase: ptr.dropFirst(toIndex)) targetPtr.copyMemory(from: srcPtr) } return length } /// Errors thrown when calling `copyBytes`. public struct CopyBytesError: Error { private enum BaseError: Hashable { case negativeLength case unreadableSourceBytes } private var baseError: BaseError /// The length of the bytes to copy was negative. public static let negativeLength: CopyBytesError = .init(baseError: .negativeLength) /// The bytes to copy are not readable. public static let unreadableSourceBytes: CopyBytesError = .init(baseError: .unreadableSourceBytes) } } extension ByteBuffer.CopyBytesError: Hashable { } extension ByteBuffer.CopyBytesError: CustomDebugStringConvertible { public var debugDescription: String { return String(describing: self.baseError) } } extension ByteBuffer: Equatable { // TODO: I don't think this makes sense. This should compare bytes 0.. Bool { guard lhs.readableBytes == rhs.readableBytes else { return false } if lhs._slice == rhs._slice && lhs._storage === rhs._storage { return true } return lhs.withUnsafeReadableBytes { lPtr in rhs.withUnsafeReadableBytes { rPtr in // Shouldn't get here otherwise because of readableBytes check assert(lPtr.count == rPtr.count) return memcmp(lPtr.baseAddress!, rPtr.baseAddress!, lPtr.count) == 0 } } } } extension ByteBuffer: Hashable { /// The hash value for the readable bytes. @inlinable public func hash(into hasher: inout Hasher) { self.withUnsafeReadableBytes { ptr in hasher.combine(bytes: ptr) } } } extension ByteBuffer: @unchecked Sendable {} extension ByteBuffer { /// Modify this `ByteBuffer` if this `ByteBuffer` is known to uniquely own its storage. /// /// In some cases it is possible that code is holding a `ByteBuffer` that has been shared with other /// parts of the code, and may want to mutate that `ByteBuffer`. In some cases it may be worth modifying /// a `ByteBuffer` only if that `ByteBuffer` is guaranteed to not perform a copy-on-write operation to do /// so, for example when a different buffer could be used or more cheaply allocated instead. /// /// This function will execute the provided block only if it is guaranteed to be able to avoid a copy-on-write /// operation. If it cannot execute the block the returned value will be `nil`. /// /// - parameters: /// - body: The modification operation to execute, with this `ByteBuffer` passed `inout` as an argument. /// - returns: The return value of `body`. @inlinable public mutating func modifyIfUniquelyOwned(_ body: (inout ByteBuffer) throws -> T) rethrows -> T? { if isKnownUniquelyReferenced(&self._storage) { return try body(&self) } else { return nil } } } extension ByteBuffer { @inlinable func rangeWithinReadableBytes(index: Int, length: Int) -> Range? { guard index >= self.readerIndex && length >= 0 else { return nil } // both these &-s are safe, they can't underflow because both left & right side are >= 0 (and index >= readerIndex) let indexFromReaderIndex = index &- self.readerIndex assert(indexFromReaderIndex >= 0) guard indexFromReaderIndex <= self.readableBytes &- length else { return nil } let upperBound = indexFromReaderIndex &+ length // safe, can't overflow, we checked it above. // uncheckedBounds is safe because `length` is >= 0, so the lower bound will always be lower/equal to upper return Range(uncheckedBounds: (lower: indexFromReaderIndex, upper: upperBound)) } }