/src/swift-nio/Sources/NIOCore/ByteBuffer-views.swift
Line | Count | Source |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors |
6 | | // Licensed under Apache License v2.0 |
7 | | // |
8 | | // See LICENSE.txt for license information |
9 | | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors |
10 | | // |
11 | | // SPDX-License-Identifier: Apache-2.0 |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | /// A view into a portion of a `ByteBuffer`. |
16 | | /// |
17 | | /// A `ByteBufferView` is useful whenever a `Collection where Element == UInt8` representing a portion of a |
18 | | /// `ByteBuffer` is needed. |
19 | | public struct ByteBufferView: RandomAccessCollection, Sendable { |
20 | | public typealias Element = UInt8 |
21 | | public typealias Index = Int |
22 | | public typealias SubSequence = ByteBufferView |
23 | | |
24 | | @usableFromInline var _buffer: ByteBuffer |
25 | | @usableFromInline var _range: Range<Index> |
26 | | |
27 | | @inlinable |
28 | 0 | internal init(buffer: ByteBuffer, range: Range<Index>) { |
29 | 0 | precondition(range.lowerBound >= 0 && range.upperBound <= buffer.capacity) |
30 | 0 | self._buffer = buffer |
31 | 0 | self._range = range |
32 | 0 | } |
33 | | |
34 | | /// Creates a `ByteBufferView` from the readable bytes of the given `buffer`. |
35 | | @inlinable |
36 | 0 | public init(_ buffer: ByteBuffer) { |
37 | 0 | self = ByteBufferView(buffer: buffer, range: buffer.readerIndex..<buffer.writerIndex) |
38 | 0 | } |
39 | | |
40 | | @inlinable |
41 | | public func withUnsafeBytes<R, ErrorType: Error>( |
42 | | _ body: (UnsafeRawBufferPointer) throws(ErrorType) -> R |
43 | 0 | ) throws(ErrorType) -> R { |
44 | 0 | try self._buffer.withVeryUnsafeBytes { (ptr: UnsafeRawBufferPointer) throws(ErrorType) -> R in |
45 | 0 | try body( |
46 | 0 | UnsafeRawBufferPointer( |
47 | 0 | start: ptr.baseAddress!.advanced(by: self._range.lowerBound), |
48 | 0 | count: self._range.count |
49 | 0 | ) |
50 | 0 | ) |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | @inlinable |
55 | 0 | public var startIndex: Index { |
56 | 0 | self._range.lowerBound |
57 | 0 | } |
58 | | |
59 | | @inlinable |
60 | 0 | public var endIndex: Index { |
61 | 0 | self._range.upperBound |
62 | 0 | } |
63 | | |
64 | | @inlinable |
65 | 0 | public func index(after i: Index) -> Index { |
66 | 0 | i + 1 |
67 | 0 | } |
68 | | |
69 | | @inlinable |
70 | 0 | public var count: Int { |
71 | 0 | // Unchecked is safe here: Range enforces that upperBound is strictly greater than |
72 | 0 | // lower bound, and we guarantee that _range.lowerBound >= 0. |
73 | 0 | self._range.upperBound &- self._range.lowerBound |
74 | 0 | } |
75 | | |
76 | | @inlinable |
77 | | public subscript(position: Index) -> UInt8 { |
78 | 0 | get { |
79 | 0 | guard position >= self._range.lowerBound && position < self._range.upperBound else { |
80 | 0 | preconditionFailure("index \(position) out of range") |
81 | 0 | } |
82 | 0 | return self._buffer.getInteger(at: position)! // range check above |
83 | 0 | } |
84 | 0 | set { |
85 | 0 | guard position >= self._range.lowerBound && position < self._range.upperBound else { |
86 | 0 | preconditionFailure("index \(position) out of range") |
87 | 0 | } |
88 | 0 | self._buffer.setInteger(newValue, at: position) |
89 | 0 | } |
90 | | } |
91 | | |
92 | | @inlinable |
93 | | public subscript(range: Range<Index>) -> ByteBufferView { |
94 | 0 | get { |
95 | 0 | ByteBufferView(buffer: self._buffer, range: range) |
96 | 0 | } |
97 | 0 | set { |
98 | 0 | self.replaceSubrange(range, with: newValue) |
99 | 0 | } |
100 | | } |
101 | | |
102 | | @inlinable |
103 | | public func withContiguousStorageIfAvailable<R, ErrorType: Error>( |
104 | | _ body: (UnsafeBufferPointer<UInt8>) throws(ErrorType) -> R |
105 | 0 | ) throws(ErrorType) -> R? { |
106 | 0 | try self.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) throws(ErrorType) -> R in |
107 | 0 | try body(bytes.bindMemory(to: UInt8.self)) |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | @inlinable |
112 | 0 | public func _customIndexOfEquatableElement(_ element: Element) -> Index?? { |
113 | 0 | .some( |
114 | 0 | self.withUnsafeBytes { ptr -> Index? in |
115 | 0 | ptr.firstIndex(of: element).map { $0 + self._range.lowerBound } |
116 | 0 | } |
117 | 0 | ) |
118 | 0 | } |
119 | | |
120 | | @inlinable |
121 | 0 | public func _customLastIndexOfEquatableElement(_ element: Element) -> Index?? { |
122 | 0 | .some( |
123 | 0 | self.withUnsafeBytes { ptr -> Index? in |
124 | 0 | ptr.lastIndex(of: element).map { $0 + self._range.lowerBound } |
125 | 0 | } |
126 | 0 | ) |
127 | 0 | } |
128 | | |
129 | | @inlinable |
130 | 0 | public func _customContainsEquatableElement(_ element: Element) -> Bool? { |
131 | 0 | .some( |
132 | 0 | self.withUnsafeBytes { ptr -> Bool in |
133 | 0 | ptr.contains(element) |
134 | 0 | } |
135 | 0 | ) |
136 | 0 | } |
137 | | |
138 | | @inlinable |
139 | | public func _copyContents( |
140 | | initializing ptr: UnsafeMutableBufferPointer<UInt8> |
141 | 0 | ) -> (Iterator, UnsafeMutableBufferPointer<UInt8>.Index) { |
142 | 0 | precondition(ptr.count >= self.count) |
143 | 0 |
|
144 | 0 | let bytesToWrite = self.count |
145 | 0 |
|
146 | 0 | let endIndex = self.withContiguousStorageIfAvailable { ourBytes in |
147 | 0 | ptr.initialize(from: ourBytes).1 |
148 | 0 | } |
149 | 0 | precondition(endIndex == bytesToWrite) |
150 | 0 |
|
151 | 0 | let iterator = self[self.endIndex..<self.endIndex].makeIterator() |
152 | 0 | return (iterator, bytesToWrite) |
153 | 0 | } |
154 | | |
155 | | // These are implemented as no-ops for performance reasons. |
156 | | @inlinable |
157 | 0 | public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {} |
158 | | |
159 | | @inlinable |
160 | 0 | public func _failEarlyRangeCheck(_ index: Index, bounds: ClosedRange<Index>) {} |
161 | | |
162 | | @inlinable |
163 | 0 | public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {} |
164 | | } |
165 | | |
166 | | extension ByteBufferView: MutableCollection {} |
167 | | |
168 | | extension ByteBufferView: RangeReplaceableCollection { |
169 | | // required by `RangeReplaceableCollection` |
170 | | @inlinable |
171 | 0 | public init() { |
172 | 0 | self = ByteBufferView(ByteBuffer()) |
173 | 0 | } |
174 | | |
175 | | /// Reserves enough space in the underlying `ByteBuffer` such that this view can |
176 | | /// store the specified number of bytes without reallocation. |
177 | | /// |
178 | | /// See the documentation for ``ByteBuffer/reserveCapacity(_:)`` for more details. |
179 | | @inlinable |
180 | 0 | public mutating func reserveCapacity(_ minimumCapacity: Int) { |
181 | 0 | let additionalCapacity = minimumCapacity - self.count |
182 | 0 | if additionalCapacity > 0 { |
183 | 0 | self._buffer.reserveCapacity(self._buffer.capacity + additionalCapacity) |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | /// Writes a single byte to the underlying `ByteBuffer`. |
188 | | @inlinable |
189 | 0 | public mutating func append(_ byte: UInt8) { |
190 | 0 | // ``CollectionOfOne`` has no witness for |
191 | 0 | // ``Sequence.withContiguousStorageIfAvailable(_:)``. so we do this instead: |
192 | 0 | self._buffer.setInteger(byte, at: self._range.upperBound) |
193 | 0 | self._range = self._range.lowerBound..<self._range.upperBound.advanced(by: 1) |
194 | 0 | self._buffer.moveWriterIndex(to: self._range.upperBound) |
195 | 0 | } |
196 | | |
197 | | /// Writes a sequence of bytes to the underlying `ByteBuffer`. |
198 | | @inlinable |
199 | 0 | public mutating func append<Bytes: Sequence>(contentsOf bytes: Bytes) where Bytes.Element == UInt8 { |
200 | 0 | let written = self._buffer.setBytes(bytes, at: self._range.upperBound) |
201 | 0 | self._range = self._range.lowerBound..<self._range.upperBound.advanced(by: written) |
202 | 0 | self._buffer.moveWriterIndex(to: self._range.upperBound) |
203 | 0 | } |
204 | | |
205 | | @inlinable |
206 | | public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) |
207 | 0 | where ByteBufferView.Element == C.Element { |
208 | 0 | precondition( |
209 | 0 | subrange.startIndex >= self.startIndex && subrange.endIndex <= self.endIndex, |
210 | 0 | "subrange out of bounds" |
211 | 0 | ) |
212 | 0 |
|
213 | 0 | if newElements.count == subrange.count { |
214 | 0 | self._buffer.setBytes(newElements, at: subrange.startIndex) |
215 | 0 | } else if newElements.count < subrange.count { |
216 | 0 | // Replace the subrange. |
217 | 0 | self._buffer.setBytes(newElements, at: subrange.startIndex) |
218 | 0 |
|
219 | 0 | // Remove the unwanted bytes between the newly copied bytes and the end of the subrange. |
220 | 0 | // try! is fine here: the copied range is within the view and the length can't be negative. |
221 | 0 | try! self._buffer.copyBytes( |
222 | 0 | at: subrange.endIndex, |
223 | 0 | to: subrange.startIndex.advanced(by: newElements.count), |
224 | 0 | length: subrange.endIndex.distance(to: self._buffer.writerIndex) |
225 | 0 | ) |
226 | 0 |
|
227 | 0 | // Shorten the range. |
228 | 0 | let removedBytes = subrange.count - newElements.count |
229 | 0 | self._buffer.moveWriterIndex(to: self._buffer.writerIndex - removedBytes) |
230 | 0 | self._range = self._range.dropLast(removedBytes) |
231 | 0 | } else { |
232 | 0 | // Make space for the new elements. |
233 | 0 | // try! is fine here: the copied range is within the view and the length can't be negative. |
234 | 0 | try! self._buffer.copyBytes( |
235 | 0 | at: subrange.endIndex, |
236 | 0 | to: subrange.startIndex.advanced(by: newElements.count), |
237 | 0 | length: subrange.endIndex.distance(to: self._buffer.writerIndex) |
238 | 0 | ) |
239 | 0 |
|
240 | 0 | // Replace the bytes. |
241 | 0 | self._buffer.setBytes(newElements, at: subrange.startIndex) |
242 | 0 |
|
243 | 0 | // Widen the range. |
244 | 0 | let additionalByteCount = newElements.count - subrange.count |
245 | 0 | self._buffer.moveWriterIndex(forwardBy: additionalByteCount) |
246 | 0 | self._range = self._range.startIndex..<self._range.endIndex.advanced(by: additionalByteCount) |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | /// Returns the first index in which a byte satisfies the given predicate. |
251 | | /// |
252 | | /// - Parameter predicate: A closure that takes a byte as its argument |
253 | | /// and returns a Boolean value that indicates whether the passed byte |
254 | | /// represents a match. |
255 | | /// - Returns: The index of the first byte for which `predicate` returns |
256 | | /// `true`. If no bytes in the collection satisfy the given predicate, |
257 | | /// returns `nil`. |
258 | | /// |
259 | | /// - Complexity: O(*n*), where *n* is the length of the collection. |
260 | | @inlinable |
261 | 0 | public func firstIndex(where predicate: (UInt8) throws -> Bool) rethrows -> Index? { |
262 | 0 | try self.withUnsafeBytes { ptr in |
263 | 0 | try ptr.firstIndex(where: predicate).map { $0 + self._range.lowerBound } |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | /// Returns the index of the last byte that matches the given predicate. |
268 | | /// |
269 | | /// - Parameter predicate: A closure that takes a byte as its argument |
270 | | /// and returns a Boolean value that indicates whether the passed byte |
271 | | /// represents a match. |
272 | | /// - Returns: The index of the last byte in the collection that matches |
273 | | /// `predicate`, or `nil` if no bytes match. |
274 | | /// |
275 | | /// - Complexity: O(*n*), where *n* is the length of the collection. |
276 | | @inlinable |
277 | 0 | public func lastIndex(where predicate: (UInt8) throws -> Bool) rethrows -> Index? { |
278 | 0 | try self.withUnsafeBytes { ptr in |
279 | 0 | try ptr.lastIndex(where: predicate).map { $0 + self._range.lowerBound } |
280 | 0 | } |
281 | 0 | } |
282 | | } |
283 | | |
284 | | extension ByteBuffer { |
285 | | /// A view into the readable bytes of the `ByteBuffer`. |
286 | | @inlinable |
287 | 0 | public var readableBytesView: ByteBufferView { |
288 | 0 | ByteBufferView(self) |
289 | 0 | } |
290 | | |
291 | | /// Returns a view into some portion of the readable bytes of a `ByteBuffer`. |
292 | | /// |
293 | | /// - Parameters: |
294 | | /// - index: The index the view should start at |
295 | | /// - length: The length of the view (in bytes) |
296 | | /// - Returns: A view into a portion of a `ByteBuffer` or `nil` if the requested bytes were not readable. |
297 | | @inlinable |
298 | 0 | public func viewBytes(at index: Int, length: Int) -> ByteBufferView? { |
299 | 0 | guard length >= 0 && index >= self.readerIndex && index <= self.writerIndex - length else { |
300 | 0 | return nil |
301 | 0 | } |
302 | 0 |
|
303 | 0 | return ByteBufferView(buffer: self, range: index..<(index + length)) |
304 | 0 | } |
305 | | |
306 | | /// Create a `ByteBuffer` from the given `ByteBufferView`s range. |
307 | | /// |
308 | | /// - Parameter view: The `ByteBufferView` which you want to get a `ByteBuffer` from. |
309 | | @inlinable |
310 | 0 | public init(_ view: ByteBufferView) { |
311 | 0 | self = view._buffer.getSlice(at: view.startIndex, length: view.count)! |
312 | 0 | } |
313 | | } |
314 | | |
315 | | extension ByteBufferView: Equatable { |
316 | | /// required by `Equatable` |
317 | | @inlinable |
318 | 0 | public static func == (lhs: ByteBufferView, rhs: ByteBufferView) -> Bool { |
319 | 0 |
|
320 | 0 | guard lhs._range.count == rhs._range.count else { |
321 | 0 | return false |
322 | 0 | } |
323 | 0 |
|
324 | 0 | // A well-formed ByteBufferView can never have a range that is out-of-bounds of the backing ByteBuffer. |
325 | 0 | // As a result, these getSlice calls can never fail, and we'd like to know it if they do. |
326 | 0 | let leftBufferSlice = lhs._buffer.getSlice(at: lhs._range.startIndex, length: lhs._range.count)! |
327 | 0 | let rightBufferSlice = rhs._buffer.getSlice(at: rhs._range.startIndex, length: rhs._range.count)! |
328 | 0 |
|
329 | 0 | return leftBufferSlice == rightBufferSlice |
330 | 0 | } |
331 | | } |
332 | | |
333 | | extension ByteBufferView: Hashable { |
334 | | /// required by `Hashable` |
335 | | @inlinable |
336 | 0 | public func hash(into hasher: inout Hasher) { |
337 | 0 | // A well-formed ByteBufferView can never have a range that is out-of-bounds of the backing ByteBuffer. |
338 | 0 | // As a result, this getSlice call can never fail, and we'd like to know it if it does. |
339 | 0 | hasher.combine(self._buffer.getSlice(at: self._range.startIndex, length: self._range.count)!) |
340 | 0 | } |
341 | | } |
342 | | |
343 | | extension ByteBufferView: ExpressibleByArrayLiteral { |
344 | | /// required by `ExpressibleByArrayLiteral` |
345 | | @inlinable |
346 | 0 | public init(arrayLiteral elements: Element...) { |
347 | 0 | self.init(elements) |
348 | 0 | } |
349 | | } |