/src/swift-nio/Sources/NIOCore/ByteBuffer-aux.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 | | import _NIOBase64 |
16 | | |
17 | | #if canImport(Dispatch) |
18 | | import Dispatch |
19 | | #endif |
20 | | |
21 | | extension ByteBuffer { |
22 | | |
23 | | // MARK: Bytes ([UInt8]) APIs |
24 | | |
25 | | /// Get `length` bytes starting at `index` and return the result as `[UInt8]`. This will not change the reader index. |
26 | | /// The selected bytes must be readable or else `nil` will be returned. |
27 | | /// |
28 | | /// - Parameters: |
29 | | /// - index: The starting index of the bytes of interest into the `ByteBuffer`. |
30 | | /// - length: The number of bytes of interest. |
31 | | /// - Returns: A `[UInt8]` value containing the bytes of interest or `nil` if the bytes `ByteBuffer` are not readable. |
32 | | @inlinable |
33 | 0 | public func getBytes(at index: Int, length: Int) -> [UInt8]? { |
34 | 0 | guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { |
35 | 0 | return nil |
36 | 0 | } |
37 | 0 |
|
38 | 0 | return self.withUnsafeReadableBytes { ptr in |
39 | 0 | // this is not technically correct because we shouldn't just bind |
40 | 0 | // the memory to `UInt8` but it's not a real issue either and we |
41 | 0 | // need to work around https://bugs.swift.org/browse/SR-9604 |
42 | 0 | [UInt8](UnsafeRawBufferPointer(rebasing: ptr[range]).bindMemory(to: UInt8.self)) |
43 | 0 | } |
44 | 0 | } |
45 | | |
46 | | /// Read `length` bytes off this `ByteBuffer`, move the reader index forward by `length` bytes and return the result |
47 | | /// as `[UInt8]`. |
48 | | /// |
49 | | /// - Parameters: |
50 | | /// - length: The number of bytes to be read from this `ByteBuffer`. |
51 | | /// - Returns: A `[UInt8]` value containing `length` bytes or `nil` if there aren't at least `length` bytes readable. |
52 | | @inlinable |
53 | 0 | public mutating func readBytes(length: Int) -> [UInt8]? { |
54 | 0 | guard let result = self.getBytes(at: self.readerIndex, length: length) else { |
55 | 0 | return nil |
56 | 0 | } |
57 | 0 | self._moveReaderIndex(forwardBy: length) |
58 | 0 | return result |
59 | 0 | } |
60 | | |
61 | | #if compiler(>=6.2) |
62 | | @inlinable |
63 | | @available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *) |
64 | | public mutating func readInlineArray< |
65 | | let count: Int, |
66 | | IntegerType: FixedWidthInteger |
67 | | >( |
68 | | endianness: Endianness = .big, |
69 | | as: InlineArray<count, IntegerType>.Type = InlineArray<count, IntegerType>.self |
70 | 0 | ) -> InlineArray<count, IntegerType>? { |
71 | 0 | // use stride to account for padding bytes |
72 | 0 | let stride = MemoryLayout<IntegerType>.stride |
73 | 0 | let bytesRequired = stride * count |
74 | 0 |
|
75 | 0 | guard self.readableBytes >= bytesRequired else { |
76 | 0 | return nil |
77 | 0 | } |
78 | 0 |
|
79 | 0 | let readerIndex = self.readerIndex |
80 | 0 | let inlineArray = InlineArray<count, IntegerType> { (outputSpan: inout OutputSpan<IntegerType>) in |
81 | 0 | for index in 0..<count { |
82 | 0 | // 'getInteger(at:)' takes an absolute index into the buffer, so we offset from |
83 | 0 | // the reader index. We already made sure of 'self.readableBytes >= bytesRequired' |
84 | 0 | // above, so this is safe to force-unwrap as it's guaranteed to exist. |
85 | 0 | let integer = self.getInteger( |
86 | 0 | // 'stride &* index' is less than 'bytesRequired' so is safe to multiply, and |
87 | 0 | // adding the reader index can't overflow because it stays within the writer index. |
88 | 0 | at: readerIndex &+ (stride &* index), |
89 | 0 | endianness: endianness, |
90 | 0 | as: IntegerType.self |
91 | 0 | )! |
92 | 0 | outputSpan.append(integer) |
93 | 0 | } |
94 | 0 | } |
95 | 0 | // already made sure of 'self.readableBytes >= bytesRequired' above |
96 | 0 | self._moveReaderIndex(forwardBy: bytesRequired) |
97 | 0 | return inlineArray |
98 | 0 | } |
99 | | #endif |
100 | | |
101 | | /// Returns the Bytes at the current reader index without advancing it. |
102 | | /// |
103 | | /// This method is equivalent to calling `getBytes(at: readerIndex, ...)` |
104 | | /// |
105 | | /// - Parameters: |
106 | | /// - length: The number of bytes of interest. |
107 | | /// - Returns: A `[UInt8]` value containing the bytes of interest or `nil` if the bytes `ByteBuffer` are not readable. |
108 | | @inlinable |
109 | 0 | public func peekBytes(length: Int) -> [UInt8]? { |
110 | 0 | self.getBytes(at: self.readerIndex, length: length) |
111 | 0 | } |
112 | | |
113 | | // MARK: StaticString APIs |
114 | | |
115 | | /// Write the static `string` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately. |
116 | | /// |
117 | | /// - Parameters: |
118 | | /// - string: The string to write. |
119 | | /// - Returns: The number of bytes written. |
120 | | @discardableResult |
121 | | @inlinable |
122 | 0 | public mutating func writeStaticString(_ string: StaticString) -> Int { |
123 | 0 | let written = self.setStaticString(string, at: self.writerIndex) |
124 | 0 | self._moveWriterIndex(forwardBy: written) |
125 | 0 | return written |
126 | 0 | } |
127 | | |
128 | | /// Write the static `string` into this `ByteBuffer` at `index` using UTF-8 encoding, moving the writer index forward appropriately. |
129 | | /// |
130 | | /// - Parameters: |
131 | | /// - string: The string to write. |
132 | | /// - index: The index for the first serialized byte. |
133 | | /// - Returns: The number of bytes written. |
134 | | @inlinable |
135 | 0 | public mutating func setStaticString(_ string: StaticString, at index: Int) -> Int { |
136 | 0 | // please do not replace the code below with code that uses `string.withUTF8Buffer { ... }` (see SR-7541) |
137 | 0 | self.setBytes( |
138 | 0 | UnsafeRawBufferPointer( |
139 | 0 | start: string.utf8Start, |
140 | 0 | count: string.utf8CodeUnitCount |
141 | 0 | ), |
142 | 0 | at: index |
143 | 0 | ) |
144 | 0 | } |
145 | | |
146 | | // MARK: Hex encoded string APIs |
147 | | /// Write ASCII hexadecimal `string` into this `ByteBuffer` as raw bytes, decoding the hexadecimal & moving the writer index forward appropriately. |
148 | | /// This method will throw if the string input is not of the "plain" hex encoded format. |
149 | | /// - Parameters: |
150 | | /// - plainHexEncodedBytes: The hex encoded string to write. Plain hex dump format is hex bytes optionally separated by spaces, i.e. `48 65 6c 6c 6f` or `48656c6c6f` for `Hello`. |
151 | | /// This format is compatible with `xxd` CLI utility. |
152 | | /// - Returns: The number of bytes written. |
153 | | @discardableResult |
154 | | @inlinable |
155 | 0 | public mutating func writePlainHexEncodedBytes(_ plainHexEncodedBytes: String) throws -> Int { |
156 | 0 | var slice = plainHexEncodedBytes.utf8[...] |
157 | 0 | let initialWriterIndex = self.writerIndex |
158 | 0 |
|
159 | 0 | do { |
160 | 0 | while let nextByte = try slice.popNextHexByte() { |
161 | 0 | self.writeInteger(nextByte) |
162 | 0 | } |
163 | 0 | return self.writerIndex - initialWriterIndex |
164 | 0 | } catch { |
165 | 0 | self.moveWriterIndex(to: initialWriterIndex) |
166 | 0 | throw error |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | // MARK: String APIs |
171 | | /// Write `string` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately. |
172 | | /// |
173 | | /// - Parameters: |
174 | | /// - string: The string to write. |
175 | | /// - Returns: The number of bytes written. |
176 | | @discardableResult |
177 | | @inlinable |
178 | 0 | public mutating func writeString(_ string: String) -> Int { |
179 | 0 | let written = self.setString(string, at: self.writerIndex) |
180 | 0 | self._moveWriterIndex(forwardBy: written) |
181 | 0 | return written |
182 | 0 | } |
183 | | |
184 | | /// Write `string` into this `ByteBuffer` null terminated using UTF-8 encoding, moving the writer index forward appropriately. |
185 | | /// |
186 | | /// - Parameters: |
187 | | /// - string: The string to write. |
188 | | /// - Returns: The number of bytes written. |
189 | | @discardableResult |
190 | | @inlinable |
191 | 0 | public mutating func writeNullTerminatedString(_ string: String) -> Int { |
192 | 0 | let written = self.setNullTerminatedString(string, at: self.writerIndex) |
193 | 0 | self._moveWriterIndex(forwardBy: written) |
194 | 0 | return written |
195 | 0 | } |
196 | | |
197 | | @inline(never) |
198 | | @inlinable |
199 | 0 | mutating func _setStringSlowpath(_ string: String, at index: Int) -> Int { |
200 | 0 | // slow path, let's try to force the string to be native |
201 | 0 | if let written = (string + "").utf8.withContiguousStorageIfAvailable({ utf8Bytes in |
202 | 0 | self.setBytes(utf8Bytes, at: index) |
203 | 0 | }) { |
204 | 0 | return written |
205 | 0 | } else { |
206 | 0 | return self.setBytes(string.utf8, at: index) |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | | /// Write `string` into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index. |
211 | | /// |
212 | | /// - Parameters: |
213 | | /// - string: The string to write. |
214 | | /// - index: The index for the first serialized byte. |
215 | | /// - Returns: The number of bytes written. |
216 | | @discardableResult |
217 | | @inlinable |
218 | 0 | public mutating func setString(_ string: String, at index: Int) -> Int { |
219 | 0 | // Do not implement setString via setSubstring. Before Swift version 5.3, |
220 | 0 | // Substring.UTF8View did not implement withContiguousStorageIfAvailable |
221 | 0 | // and therefore had no fast access to the backing storage. |
222 | 0 | if let written = string.utf8.withContiguousStorageIfAvailable({ utf8Bytes in |
223 | 0 | self.setBytes(utf8Bytes, at: index) |
224 | 0 | }) { |
225 | 0 | // fast path, directly available |
226 | 0 | return written |
227 | 0 | } else { |
228 | 0 | return self._setStringSlowpath(string, at: index) |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | /// Write `string` null terminated into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index. |
233 | | /// |
234 | | /// - Parameters: |
235 | | /// - string: The string to write. |
236 | | /// - index: The index for the first serialized byte. |
237 | | /// - Returns: The number of bytes written. |
238 | | @inlinable |
239 | 0 | public mutating func setNullTerminatedString(_ string: String, at index: Int) -> Int { |
240 | 0 | let length = self.setString(string, at: index) |
241 | 0 | self.setInteger(UInt8(0), at: index &+ length) |
242 | 0 | return length &+ 1 |
243 | 0 | } |
244 | | |
245 | | /// Get the string at `index` from this `ByteBuffer` decoding using the UTF-8 encoding. Does not move the reader index. |
246 | | /// The selected bytes must be readable or else `nil` will be returned. |
247 | | /// |
248 | | /// - Parameters: |
249 | | /// - index: The starting index into `ByteBuffer` containing the string of interest. |
250 | | /// - length: The number of bytes making up the string. |
251 | | /// - Returns: A `String` value containing the UTF-8 decoded selected bytes from this `ByteBuffer` or `nil` if |
252 | | /// the requested bytes are not readable. |
253 | | @inlinable |
254 | 0 | public func getString(at index: Int, length: Int) -> String? { |
255 | 0 | guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { |
256 | 0 | return nil |
257 | 0 | } |
258 | 0 | return self.withUnsafeReadableBytes { pointer in |
259 | 0 | assert(range.lowerBound >= 0 && (range.upperBound - range.lowerBound) <= pointer.count) |
260 | 0 | return String( |
261 | 0 | decoding: UnsafeRawBufferPointer(rebasing: pointer[range]), |
262 | 0 | as: Unicode.UTF8.self |
263 | 0 | ) |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | /// Get the string at `index` from this `ByteBuffer` decoding using the UTF-8 encoding. Does not move the reader index. |
268 | | /// The selected bytes must be readable or else `nil` will be returned. |
269 | | /// |
270 | | /// - Parameters: |
271 | | /// - index: The starting index into `ByteBuffer` containing the null terminated string of interest. |
272 | | /// - Returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there isn't a complete null-terminated string, |
273 | | /// including null-terminator, in the readable bytes after `index` in the buffer |
274 | | @inlinable |
275 | 0 | public func getNullTerminatedString(at index: Int) -> String? { |
276 | 0 | guard let stringLength = self._getNullTerminatedStringLength(at: index) else { |
277 | 0 | return nil |
278 | 0 | } |
279 | 0 | return self.getString(at: index, length: stringLength) |
280 | 0 | } |
281 | | |
282 | | @inlinable |
283 | 0 | func _getNullTerminatedStringLength(at index: Int) -> Int? { |
284 | 0 | guard self.readerIndex <= index && index < self.writerIndex else { |
285 | 0 | return nil |
286 | 0 | } |
287 | 0 | guard let endIndex = self.readableBytesView[index...].firstIndex(of: 0) else { |
288 | 0 | return nil |
289 | 0 | } |
290 | 0 | return endIndex &- index |
291 | 0 | } |
292 | | |
293 | | /// Read `length` bytes off this `ByteBuffer`, decoding it as `String` using the UTF-8 encoding. Move the reader index forward by `length`. |
294 | | /// |
295 | | /// - Parameters: |
296 | | /// - length: The number of bytes making up the string. |
297 | | /// - Returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there aren't at least `length` bytes readable. |
298 | | @inlinable |
299 | 0 | public mutating func readString(length: Int) -> String? { |
300 | 0 | guard let result = self.getString(at: self.readerIndex, length: length) else { |
301 | 0 | return nil |
302 | 0 | } |
303 | 0 | self._moveReaderIndex(forwardBy: length) |
304 | 0 | return result |
305 | 0 | } |
306 | | |
307 | | /// Read a null terminated string off this `ByteBuffer`, decoding it as `String` using the UTF-8 encoding. Move the reader index |
308 | | /// forward by the string's length and its null terminator. |
309 | | /// |
310 | | /// - Returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there isn't a complete null-terminated string, |
311 | | /// including null-terminator, in the readable bytes of the buffer |
312 | | @inlinable |
313 | 0 | public mutating func readNullTerminatedString() -> String? { |
314 | 0 | guard let stringLength = self._getNullTerminatedStringLength(at: self.readerIndex) else { |
315 | 0 | return nil |
316 | 0 | } |
317 | 0 | let result = self.readString(length: stringLength) |
318 | 0 | self.moveReaderIndex(forwardBy: 1) // move forward by null terminator |
319 | 0 | return result |
320 | 0 | } |
321 | | |
322 | | // MARK: Substring APIs |
323 | | /// Write `substring` into this `ByteBuffer` using UTF-8 encoding, moving the writer index forward appropriately. |
324 | | /// |
325 | | /// - Parameters: |
326 | | /// - substring: The substring to write. |
327 | | /// - Returns: The number of bytes written. |
328 | | @discardableResult |
329 | | @inlinable |
330 | 0 | public mutating func writeSubstring(_ substring: Substring) -> Int { |
331 | 0 | let written = self.setSubstring(substring, at: self.writerIndex) |
332 | 0 | self._moveWriterIndex(forwardBy: written) |
333 | 0 | return written |
334 | 0 | } |
335 | | |
336 | | /// Write `substring` into this `ByteBuffer` at `index` using UTF-8 encoding. Does not move the writer index. |
337 | | /// |
338 | | /// - Parameters: |
339 | | /// - substring: The substring to write. |
340 | | /// - index: The index for the first serialized byte. |
341 | | /// - Returns: The number of bytes written |
342 | | @discardableResult |
343 | | @inlinable |
344 | 0 | public mutating func setSubstring(_ substring: Substring, at index: Int) -> Int { |
345 | 0 | // Substring.UTF8View implements withContiguousStorageIfAvailable starting with |
346 | 0 | // Swift version 5.3. |
347 | 0 | if let written = substring.utf8.withContiguousStorageIfAvailable({ utf8Bytes in |
348 | 0 | self.setBytes(utf8Bytes, at: index) |
349 | 0 | }) { |
350 | 0 | // fast path, directly available |
351 | 0 | return written |
352 | 0 | } else { |
353 | 0 | // slow path, convert to string |
354 | 0 | return self.setString(String(substring), at: index) |
355 | 0 | } |
356 | 0 | } |
357 | | |
358 | | /// Return a String decoded from the bytes at the current reader index using UTF-8 encoding. |
359 | | /// |
360 | | /// This is equivalent to calling `getString(at: readerIndex, length: ...)` and does not advance the reader index. |
361 | | /// |
362 | | /// - Parameter length: The number of bytes making up the string. |
363 | | /// - Returns: A String containing the decoded bytes, or `nil` if the requested bytes are not readable. |
364 | | @inlinable |
365 | 0 | public func peekString(length: Int) -> String? { |
366 | 0 | self.getString(at: self.readerIndex, length: length) |
367 | 0 | } |
368 | | |
369 | | /// Return a null-terminated String starting at the current reader index. |
370 | | /// |
371 | | /// This is equivalent to calling `getNullTerminatedString(at: readerIndex)` and does not advance the reader index. |
372 | | /// |
373 | | /// - Returns: A String decoded from the null-terminated bytes, or `nil` if a complete null-terminated string is not available. |
374 | | @inlinable |
375 | 0 | public func peekNullTerminatedString() -> String? { |
376 | 0 | self.getNullTerminatedString(at: self.readerIndex) |
377 | 0 | } |
378 | | |
379 | | #if canImport(Dispatch) |
380 | | // MARK: DispatchData APIs |
381 | | /// Write `dispatchData` into this `ByteBuffer`, moving the writer index forward appropriately. |
382 | | /// |
383 | | /// - Parameters: |
384 | | /// - dispatchData: The `DispatchData` instance to write to the `ByteBuffer`. |
385 | | /// - Returns: The number of bytes written. |
386 | | @discardableResult |
387 | | @inlinable |
388 | 0 | public mutating func writeDispatchData(_ dispatchData: DispatchData) -> Int { |
389 | 0 | let written = self.setDispatchData(dispatchData, at: self.writerIndex) |
390 | 0 | self._moveWriterIndex(forwardBy: written) |
391 | 0 | return written |
392 | 0 | } |
393 | | |
394 | | /// Write `dispatchData` into this `ByteBuffer` at `index`. Does not move the writer index. |
395 | | /// |
396 | | /// - Parameters: |
397 | | /// - dispatchData: The `DispatchData` to write. |
398 | | /// - index: The index for the first serialized byte. |
399 | | /// - Returns: The number of bytes written. |
400 | | @discardableResult |
401 | | @inlinable |
402 | 0 | public mutating func setDispatchData(_ dispatchData: DispatchData, at index: Int) -> Int { |
403 | 0 | let allBytesCount = dispatchData.count |
404 | 0 | self.reserveCapacity(index + allBytesCount) |
405 | 0 | self.withVeryUnsafeMutableBytes { destCompleteStorage in |
406 | 0 | assert(destCompleteStorage.count >= index + allBytesCount) |
407 | 0 | let dest = destCompleteStorage[index..<index + allBytesCount] |
408 | 0 | dispatchData.copyBytes(to: .init(rebasing: dest), count: dest.count) |
409 | 0 | } |
410 | 0 | return allBytesCount |
411 | 0 | } |
412 | | |
413 | | /// Get the bytes at `index` from this `ByteBuffer` as a `DispatchData`. Does not move the reader index. |
414 | | /// The selected bytes must be readable or else `nil` will be returned. |
415 | | /// |
416 | | /// - Parameters: |
417 | | /// - index: The starting index into `ByteBuffer` containing the string of interest. |
418 | | /// - length: The number of bytes. |
419 | | /// - Returns: A `DispatchData` value deserialized from this `ByteBuffer` or `nil` if the requested bytes |
420 | | /// are not readable. |
421 | | @inlinable |
422 | 0 | public func getDispatchData(at index: Int, length: Int) -> DispatchData? { |
423 | 0 | guard let range = self.rangeWithinReadableBytes(index: index, length: length) else { |
424 | 0 | return nil |
425 | 0 | } |
426 | 0 | return self.withUnsafeReadableBytes { pointer in |
427 | 0 | DispatchData(bytes: UnsafeRawBufferPointer(rebasing: pointer[range])) |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | /// Read `length` bytes off this `ByteBuffer` and return them as a `DispatchData`. Move the reader index forward by `length`. |
432 | | /// |
433 | | /// - Parameters: |
434 | | /// - length: The number of bytes. |
435 | | /// - Returns: A `DispatchData` value containing the bytes from this `ByteBuffer` or `nil` if there aren't at least `length` bytes readable. |
436 | | @inlinable |
437 | 0 | public mutating func readDispatchData(length: Int) -> DispatchData? { |
438 | 0 | guard let result = self.getDispatchData(at: self.readerIndex, length: length) else { |
439 | 0 | return nil |
440 | 0 | } |
441 | 0 | self._moveReaderIndex(forwardBy: length) |
442 | 0 | return result |
443 | 0 | } |
444 | | |
445 | | /// Return a DispatchData object containing the bytes at the current reader index. |
446 | | /// |
447 | | /// This is equivalent to calling `getDispatchData(at: readerIndex, length: ...)` and does not advance the reader index. |
448 | | /// |
449 | | /// - Parameter length: The number of bytes to be retrieved. |
450 | | /// - Returns: A DispatchData object, or `nil` if the requested bytes are not readable. |
451 | | @inlinable |
452 | 0 | public func peekDispatchData(length: Int) -> DispatchData? { |
453 | 0 | self.getDispatchData(at: self.readerIndex, length: length) |
454 | 0 | } |
455 | | #endif |
456 | | |
457 | | // MARK: Other APIs |
458 | | |
459 | | /// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index |
460 | | /// by the number of bytes returned by `body`. |
461 | | /// |
462 | | /// - warning: Do not escape the pointer from the closure for later use. |
463 | | /// |
464 | | /// - Parameters: |
465 | | /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed. |
466 | | /// - Returns: The number of bytes read. |
467 | | @discardableResult |
468 | | @inlinable |
469 | | public mutating func readWithUnsafeReadableBytes<ErrorType: Error>( |
470 | | _ body: (UnsafeRawBufferPointer) throws(ErrorType) -> Int |
471 | 0 | ) throws(ErrorType) -> Int { |
472 | 0 | let bytesRead = try self.withUnsafeReadableBytes({ (ptr: UnsafeRawBufferPointer) throws(ErrorType) -> Int in |
473 | 0 | try body(ptr) |
474 | 0 | }) |
475 | 0 | self._moveReaderIndex(forwardBy: bytesRead) |
476 | 0 | return bytesRead |
477 | 0 | } |
478 | | |
479 | | /// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes. |
480 | | /// Will move the reader index by the number of bytes returned by `body` but leave writer index as it was. |
481 | | /// |
482 | | /// - warning: Do not escape the pointer from the closure for later use. |
483 | | /// |
484 | | /// - Parameters: |
485 | | /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed. |
486 | | /// - Returns: The number of bytes read. |
487 | | @discardableResult |
488 | | @inlinable |
489 | | public mutating func readWithUnsafeMutableReadableBytes<ErrorType: Error>( |
490 | | _ body: (UnsafeMutableRawBufferPointer) throws(ErrorType) -> Int |
491 | 0 | ) throws(ErrorType) -> Int { |
492 | 0 | let bytesRead = try self.withUnsafeMutableReadableBytes({ |
493 | 0 | (ptr: UnsafeMutableRawBufferPointer) throws(ErrorType) -> Int in try body(ptr) |
494 | 0 | }) |
495 | 0 | self._moveReaderIndex(forwardBy: bytesRead) |
496 | 0 | return bytesRead |
497 | 0 | } |
498 | | |
499 | | /// Copy `buffer`'s readable bytes into this `ByteBuffer` starting at `index`. Does not move any of the reader or writer indices. |
500 | | /// |
501 | | /// - Parameters: |
502 | | /// - buffer: The `ByteBuffer` to copy. |
503 | | /// - index: The index for the first byte. |
504 | | /// - Returns: The number of bytes written. |
505 | | @discardableResult |
506 | | @available(*, deprecated, renamed: "setBuffer(_:at:)") |
507 | 0 | public mutating func set(buffer: ByteBuffer, at index: Int) -> Int { |
508 | 0 | self.setBuffer(buffer, at: index) |
509 | 0 | } |
510 | | |
511 | | /// Copy `buffer`'s readable bytes into this `ByteBuffer` starting at `index`. Does not move any of the reader or writer indices. |
512 | | /// |
513 | | /// - Parameters: |
514 | | /// - buffer: The `ByteBuffer` to copy. |
515 | | /// - index: The index for the first byte. |
516 | | /// - Returns: The number of bytes written. |
517 | | @discardableResult |
518 | | @inlinable |
519 | 0 | public mutating func setBuffer(_ buffer: ByteBuffer, at index: Int) -> Int { |
520 | 0 | buffer.withUnsafeReadableBytes { p in |
521 | 0 | self.setBytes(p, at: index) |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | /// Write `buffer`'s readable bytes into this `ByteBuffer` starting at `writerIndex`. This will move both this |
526 | | /// `ByteBuffer`'s writer index as well as `buffer`'s reader index by the number of bytes readable in `buffer`. |
527 | | /// |
528 | | /// - Parameters: |
529 | | /// - buffer: The `ByteBuffer` to write. |
530 | | /// - Returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`. |
531 | | @discardableResult |
532 | | @inlinable |
533 | 0 | public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int { |
534 | 0 | let written = self.setBuffer(buffer, at: writerIndex) |
535 | 0 | self._moveWriterIndex(forwardBy: written) |
536 | 0 | buffer._moveReaderIndex(forwardBy: written) |
537 | 0 | return written |
538 | 0 | } |
539 | | |
540 | | /// Write `bytes`, a `Sequence` of `UInt8` into this `ByteBuffer`. Moves the writer index forward by the number of bytes written. |
541 | | /// |
542 | | /// - Parameters: |
543 | | /// - bytes: A `Collection` of `UInt8` to be written. |
544 | | /// - Returns: The number of bytes written or `bytes.count`. |
545 | | @discardableResult |
546 | | @inlinable |
547 | 0 | public mutating func writeBytes<Bytes: Sequence>(_ bytes: Bytes) -> Int where Bytes.Element == UInt8 { |
548 | 0 | let written = self.setBytes(bytes, at: self.writerIndex) |
549 | 0 | self._moveWriterIndex(forwardBy: written) |
550 | 0 | return written |
551 | 0 | } |
552 | | |
553 | | /// Write `bytes` into this `ByteBuffer`. Moves the writer index forward by the number of bytes written. |
554 | | /// |
555 | | /// - Parameters: |
556 | | /// - bytes: An `UnsafeRawBufferPointer` |
557 | | /// - Returns: The number of bytes written or `bytes.count`. |
558 | | @discardableResult |
559 | | @inlinable |
560 | 34.5k | public mutating func writeBytes(_ bytes: UnsafeRawBufferPointer) -> Int { |
561 | 34.5k | let written = self.setBytes(bytes, at: self.writerIndex) |
562 | 34.5k | self._moveWriterIndex(forwardBy: written) |
563 | 34.5k | return written |
564 | 34.5k | } |
565 | | |
566 | | #if compiler(>=6.2) |
567 | | /// Write `bytes` into this `ByteBuffer`. Moves the writer index forward by the number of bytes written. |
568 | | /// |
569 | | /// - Parameters: |
570 | | /// - bytes: A `RawSpan` |
571 | | /// - Returns: The number of bytes written or `bytes.byteCount`. |
572 | | @discardableResult |
573 | | @inlinable |
574 | | @available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.0, *) |
575 | 0 | public mutating func writeBytes(_ bytes: RawSpan) -> Int { |
576 | 0 | let written = self.setBytes(bytes, at: self.writerIndex) |
577 | 0 | self._moveWriterIndex(forwardBy: written) |
578 | 0 | return written |
579 | 0 | } |
580 | | #endif |
581 | | |
582 | | /// Writes `byte` `count` times. Moves the writer index forward by the number of bytes written. |
583 | | /// |
584 | | /// - Parameters: |
585 | | /// - byte: The `UInt8` byte to repeat. |
586 | | /// - count: How many times to repeat the given `byte` |
587 | | /// - Returns: How many bytes were written. |
588 | | @discardableResult |
589 | | @inlinable |
590 | 0 | public mutating func writeRepeatingByte(_ byte: UInt8, count: Int) -> Int { |
591 | 0 | let written = self.setRepeatingByte(byte, count: count, at: self.writerIndex) |
592 | 0 | self._moveWriterIndex(forwardBy: written) |
593 | 0 | return written |
594 | 0 | } |
595 | | |
596 | | /// Sets the given `byte` `count` times at the given `index`. Will reserve more memory if necessary. Does not move the writer index. |
597 | | /// |
598 | | /// - Parameters: |
599 | | /// - byte: The `UInt8` byte to repeat. |
600 | | /// - count: How many times to repeat the given `byte` |
601 | | /// - index: The starting index of the bytes into the `ByteBuffer`. |
602 | | /// - Returns: How many bytes were written. |
603 | | @discardableResult |
604 | | @inlinable |
605 | 0 | public mutating func setRepeatingByte(_ byte: UInt8, count: Int, at index: Int) -> Int { |
606 | 0 | precondition(count >= 0, "Can't write fewer than 0 bytes") |
607 | 0 | self.reserveCapacity(index + count) |
608 | 0 | self.withVeryUnsafeMutableBytes { pointer in |
609 | 0 | let dest = UnsafeMutableRawBufferPointer(rebasing: pointer[index..<index + count]) |
610 | 0 | _ = dest.initializeMemory(as: UInt8.self, repeating: byte) |
611 | 0 | } |
612 | 0 | return count |
613 | 0 | } |
614 | | |
615 | | /// Slice the readable bytes off this `ByteBuffer` without modifying the reader index. This method will return a |
616 | | /// `ByteBuffer` sharing the underlying storage with the `ByteBuffer` the method was invoked on. The returned |
617 | | /// `ByteBuffer` will contain the bytes in the range `readerIndex..<writerIndex` of the original `ByteBuffer`. |
618 | | /// |
619 | | /// - 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. |
620 | | /// |
621 | | /// - Returns: A `ByteBuffer` sharing storage containing the readable bytes only. |
622 | | @inlinable |
623 | 0 | public func slice() -> ByteBuffer { |
624 | 0 | // must work, bytes definitely in the buffer// must work, bytes definitely in the buffer |
625 | 0 | self.getSlice(at: self.readerIndex, length: self.readableBytes)! |
626 | 0 | } |
627 | | |
628 | | /// Slice `length` bytes off this `ByteBuffer` and move the reader index forward by `length`. |
629 | | /// If enough bytes are readable the `ByteBuffer` returned by this method will share the underlying storage with |
630 | | /// the `ByteBuffer` the method was invoked on. |
631 | | /// The returned `ByteBuffer` will contain the bytes in the range `readerIndex..<(readerIndex + length)` of the |
632 | | /// original `ByteBuffer`. |
633 | | /// The `readerIndex` of the returned `ByteBuffer` will be `0`, the `writerIndex` will be `length`. |
634 | | /// |
635 | | /// - 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. |
636 | | /// |
637 | | /// - Parameters: |
638 | | /// - length: The number of bytes to slice off. |
639 | | /// - Returns: A `ByteBuffer` sharing storage containing `length` bytes or `nil` if the not enough bytes were readable. |
640 | | @inlinable |
641 | 2.08M | public mutating func readSlice(length: Int) -> ByteBuffer? { |
642 | 2.08M | guard let result = self.getSlice_inlineAlways(at: self.readerIndex, length: length) else { |
643 | 0 | return nil |
644 | 2.08M | } |
645 | 2.08M | self._moveReaderIndex(forwardBy: length) |
646 | 2.08M | return result |
647 | 2.08M | } |
648 | | |
649 | | @discardableResult |
650 | | @inlinable |
651 | 0 | public mutating func writeImmutableBuffer(_ buffer: ByteBuffer) -> Int { |
652 | 0 | var mutable = buffer |
653 | 0 | return self.writeBuffer(&mutable) |
654 | 0 | } |
655 | | } |
656 | | |
657 | | // swift-format-ignore: AmbiguousTrailingClosureOverload |
658 | | extension ByteBuffer { |
659 | | /// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes. |
660 | | /// Will move the reader index by the number of bytes `body` returns in the first tuple component but leave writer index as it was. |
661 | | /// |
662 | | /// - warning: Do not escape the pointer from the closure for later use. |
663 | | /// |
664 | | /// - Parameters: |
665 | | /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value. |
666 | | /// - Returns: The value `body` returned in the second tuple component. |
667 | | @inlinable |
668 | | public mutating func readWithUnsafeMutableReadableBytes<T, ErrorType: Error>( |
669 | | _ body: (UnsafeMutableRawBufferPointer) throws(ErrorType) -> (Int, T) |
670 | 0 | ) throws(ErrorType) -> T { |
671 | 0 | let (bytesRead, ret) = try self.withUnsafeMutableReadableBytes({ |
672 | 0 | (ptr: UnsafeMutableRawBufferPointer) throws(ErrorType) -> (Int, T) in try body(ptr) |
673 | 0 | }) |
674 | 0 | self._moveReaderIndex(forwardBy: bytesRead) |
675 | 0 | return ret |
676 | 0 | } |
677 | | |
678 | | /// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index |
679 | | /// by the number of bytes `body` returns in the first tuple component. |
680 | | /// |
681 | | /// - warning: Do not escape the pointer from the closure for later use. |
682 | | /// |
683 | | /// - Parameters: |
684 | | /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value. |
685 | | /// - Returns: The value `body` returned in the second tuple component. |
686 | | @inlinable |
687 | | public mutating func readWithUnsafeReadableBytes<T, ErrorType: Error>( |
688 | | _ body: (UnsafeRawBufferPointer) throws(ErrorType) -> (Int, T) |
689 | 0 | ) throws(ErrorType) -> T { |
690 | 0 | let (bytesRead, ret) = try self.withUnsafeReadableBytes({ |
691 | 0 | (ptr: UnsafeRawBufferPointer) throws(ErrorType) -> (Int, T) in try body(ptr) |
692 | 0 | }) |
693 | 0 | self._moveReaderIndex(forwardBy: bytesRead) |
694 | 0 | return ret |
695 | 0 | } |
696 | | } |
697 | | |
698 | | extension ByteBuffer { |
699 | | /// Return an empty `ByteBuffer` allocated with `ByteBufferAllocator()`. |
700 | | /// |
701 | | /// Calling this constructor will not allocate because it will return a `ByteBuffer` that wraps a shared storage |
702 | | /// object. As soon as you write to the constructed buffer however, you will incur an allocation because a |
703 | | /// copy-on-write will happen. |
704 | | /// |
705 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` it is |
706 | | /// recommended using `channel.allocator.buffer(capacity: 0)`. This allows SwiftNIO to do |
707 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
708 | | @inlinable |
709 | 0 | public init() { |
710 | 0 | self = ByteBufferAllocator.zeroCapacityWithDefaultAllocator |
711 | 0 | } |
712 | | |
713 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
714 | | /// |
715 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using |
716 | | /// the default allocator. |
717 | | /// |
718 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
719 | | /// recommend using `channel.allocator.buffer(string:)`. Or if you want to write multiple items into the |
720 | | /// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
721 | | /// size followed by a `writeString` instead of using this method. This allows SwiftNIO to do |
722 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
723 | | @inlinable |
724 | 0 | public init(string: String) { |
725 | 0 | self = ByteBufferAllocator().buffer(string: string) |
726 | 0 | } |
727 | | |
728 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
729 | | /// |
730 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using |
731 | | /// the default allocator. |
732 | | /// |
733 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
734 | | /// recommend using `channel.allocator.buffer(substring:)`. Or if you want to write multiple items into |
735 | | /// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
736 | | /// size followed by a `writeSubstring` instead of using this method. This allows SwiftNIO to do |
737 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
738 | | @inlinable |
739 | 0 | public init(substring string: Substring) { |
740 | 0 | self = ByteBufferAllocator().buffer(substring: string) |
741 | 0 | } |
742 | | |
743 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
744 | | /// |
745 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space using |
746 | | /// the default allocator. |
747 | | /// |
748 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
749 | | /// recommend using `channel.allocator.buffer(staticString:)`. Or if you want to write multiple items into |
750 | | /// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
751 | | /// size followed by a `writeStaticString` instead of using this method. This allows SwiftNIO to do |
752 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
753 | | @inlinable |
754 | 0 | public init(staticString string: StaticString) { |
755 | 0 | self = ByteBufferAllocator().buffer(staticString: string) |
756 | 0 | } |
757 | | |
758 | | /// Create a fresh `ByteBuffer` containing the `bytes`. |
759 | | /// |
760 | | /// This will allocate a new `ByteBuffer` with enough space to fit `bytes` and potentially some extra space using |
761 | | /// the default allocator. |
762 | | /// |
763 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
764 | | /// recommend using `channel.allocator.buffer(bytes:)`. Or if you want to write multiple items into the |
765 | | /// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
766 | | /// size followed by a `writeBytes` instead of using this method. This allows SwiftNIO to do |
767 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
768 | | @inlinable |
769 | 0 | public init<Bytes: Sequence>(bytes: Bytes) where Bytes.Element == UInt8 { |
770 | 0 | self = ByteBufferAllocator().buffer(bytes: bytes) |
771 | 0 | } |
772 | | |
773 | | /// Create a fresh `ByteBuffer` containing the bytes of the byte representation in the given `endianness` of |
774 | | /// `integer`. |
775 | | /// |
776 | | /// This will allocate a new `ByteBuffer` with enough space to fit `integer` and potentially some extra space using |
777 | | /// the default allocator. |
778 | | /// |
779 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
780 | | /// recommend using `channel.allocator.buffer(integer:)`. Or if you want to write multiple items into the |
781 | | /// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
782 | | /// size followed by a `writeInteger` instead of using this method. This allows SwiftNIO to do |
783 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
784 | | @inlinable |
785 | 0 | public init<I: FixedWidthInteger>(integer: I, endianness: Endianness = .big, as: I.Type = I.self) { |
786 | 0 | self = ByteBufferAllocator().buffer(integer: integer, endianness: endianness, as: `as`) |
787 | 0 | } |
788 | | |
789 | | /// Create a fresh `ByteBuffer` containing `count` repetitions of `byte`. |
790 | | /// |
791 | | /// This will allocate a new `ByteBuffer` with at least `count` bytes. |
792 | | /// |
793 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
794 | | /// recommend using `channel.allocator.buffer(repeating:count:)`. Or if you want to write multiple items |
795 | | /// into the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
796 | | /// size followed by a `writeRepeatingByte` instead of using this method. This allows SwiftNIO to do |
797 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
798 | | @inlinable |
799 | 0 | public init(repeating byte: UInt8, count: Int) { |
800 | 0 | self = ByteBufferAllocator().buffer(repeating: byte, count: count) |
801 | 0 | } |
802 | | |
803 | | /// Create a fresh `ByteBuffer` containing the readable bytes of `buffer`. |
804 | | /// |
805 | | /// This may allocate a new `ByteBuffer` with enough space to fit `buffer` and potentially some extra space using |
806 | | /// the default allocator. |
807 | | /// |
808 | | /// - Note: Use this method only if you deliberately want to reallocate a potentially smaller `ByteBuffer` than the |
809 | | /// one you already have. Given that `ByteBuffer` is a value type, defensive copies are not necessary. If |
810 | | /// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead. |
811 | | /// |
812 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
813 | | /// recommend using `channel.allocator.buffer(buffer:)`. Or if you want to write multiple items into the |
814 | | /// buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
815 | | /// size followed by a `writeImmutableBuffer` instead of using this method. This allows SwiftNIO to do |
816 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
817 | | @inlinable |
818 | 0 | public init(buffer: ByteBuffer) { |
819 | 0 | self = ByteBufferAllocator().buffer(buffer: buffer) |
820 | 0 | } |
821 | | |
822 | | #if canImport(Dispatch) |
823 | | /// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`. |
824 | | /// |
825 | | /// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially |
826 | | /// some extra space using the default allocator. |
827 | | /// |
828 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
829 | | /// recommend using `channel.allocator.buffer(dispatchData:)`. Or if you want to write multiple items into |
830 | | /// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
831 | | /// size followed by a `writeDispatchData` instead of using this method. This allows SwiftNIO to do |
832 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
833 | | @inlinable |
834 | 0 | public init(dispatchData: DispatchData) { |
835 | 0 | self = ByteBufferAllocator().buffer(dispatchData: dispatchData) |
836 | 0 | } |
837 | | #endif |
838 | | |
839 | | #if compiler(>=6.2) |
840 | | /// Create a fresh ``ByteBuffer`` with a minimum size, and initializing it safely via an `OutputSpan`. |
841 | | /// |
842 | | /// This will allocate a new ``ByteBuffer`` with at least `capacity` bytes of storage, and then calls |
843 | | /// `initializer` with an `OutputSpan` over the entire allocated storage. This is a convenient method |
844 | | /// to initialize a buffer directly and safely in a single allocation, including from C code. |
845 | | /// |
846 | | /// Once this call returns, the buffer will have its ``ByteBuffer/writerIndex`` appropriately advanced to encompass |
847 | | /// any memory initialized by the `initializer`. Uninitialized memory will be after the ``ByteBuffer/writerIndex``, |
848 | | /// available for subsequent use. |
849 | | /// |
850 | | /// - info: If you have access to a `Channel`, `ChannelHandlerContext`, or `ByteBufferAllocator` we |
851 | | /// recommend using `channel.allocator.buffer(capacity:initializingWith:)`. Or if you want to write multiple items into |
852 | | /// the buffer use `channel.allocator.buffer(capacity: ...)` to allocate a `ByteBuffer` of the right |
853 | | /// size followed by a `write(minimumWritableBytes:initializingWith:)` instead of using this method. This allows SwiftNIO to do |
854 | | /// accounting and optimisations of resources acquired for operations on a given `Channel` in the future. |
855 | | /// |
856 | | /// - parameters: |
857 | | /// - capacity: The minimum initial space to allocate for the buffer. |
858 | | /// - initializer: The initializer that will be invoked to initialize the allocated memory. |
859 | | @inlinable |
860 | | @available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.0, *) |
861 | | public init<ErrorType: Error>( |
862 | | initialCapacity capacity: Int, |
863 | | initializingWith initializer: (_ span: inout OutputRawSpan) throws(ErrorType) -> Void |
864 | 0 | ) throws(ErrorType) { |
865 | 0 | self = try ByteBufferAllocator().buffer(capacity: capacity, initializingWith: initializer) |
866 | 0 | } |
867 | | #endif |
868 | | } |
869 | | |
870 | | extension ByteBuffer: Codable { |
871 | | |
872 | | /// Creates a ByteByffer by decoding from a Base64 encoded single value container. |
873 | 0 | public init(from decoder: Decoder) throws { |
874 | 0 | let container = try decoder.singleValueContainer() |
875 | 0 | let base64String = try container.decode(String.self) |
876 | 0 | self = try ByteBuffer(bytes: base64String._base64Decoded()) |
877 | 0 | } |
878 | | |
879 | | /// Encodes this buffer as a base64 string in a single value container. |
880 | 0 | public func encode(to encoder: Encoder) throws { |
881 | 0 | var container = encoder.singleValueContainer() |
882 | 0 | let base64String = String(_base64Encoding: self.readableBytesView) |
883 | 0 | try container.encode(base64String) |
884 | 0 | } |
885 | | } |
886 | | |
887 | | extension ByteBufferAllocator { |
888 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
889 | | /// |
890 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. |
891 | | /// |
892 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
893 | | @inlinable |
894 | 0 | public func buffer(string: String) -> ByteBuffer { |
895 | 0 | var buffer = self.buffer(capacity: string.utf8.count) |
896 | 0 | buffer.writeString(string) |
897 | 0 | return buffer |
898 | 0 | } |
899 | | |
900 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
901 | | /// |
902 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. |
903 | | /// |
904 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
905 | | @inlinable |
906 | 0 | public func buffer(substring string: Substring) -> ByteBuffer { |
907 | 0 | var buffer = self.buffer(capacity: string.utf8.count) |
908 | 0 | buffer.writeSubstring(string) |
909 | 0 | return buffer |
910 | 0 | } |
911 | | |
912 | | /// Create a fresh `ByteBuffer` containing the bytes of the `string` encoded as UTF-8. |
913 | | /// |
914 | | /// This will allocate a new `ByteBuffer` with enough space to fit `string` and potentially some extra space. |
915 | | /// |
916 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
917 | | @inlinable |
918 | 0 | public func buffer(staticString string: StaticString) -> ByteBuffer { |
919 | 0 | var buffer = self.buffer(capacity: string.utf8CodeUnitCount) |
920 | 0 | buffer.writeStaticString(string) |
921 | 0 | return buffer |
922 | 0 | } |
923 | | |
924 | | /// Create a fresh `ByteBuffer` containing the `bytes`. |
925 | | /// |
926 | | /// This will allocate a new `ByteBuffer` with enough space to fit `bytes` and potentially some extra space. |
927 | | /// |
928 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
929 | | @inlinable |
930 | 0 | public func buffer<Bytes: Sequence>(bytes: Bytes) -> ByteBuffer where Bytes.Element == UInt8 { |
931 | 0 | var buffer = self.buffer(capacity: bytes.underestimatedCount) |
932 | 0 | buffer.writeBytes(bytes) |
933 | 0 | return buffer |
934 | 0 | } |
935 | | |
936 | | /// Create a fresh `ByteBuffer` containing the `bytes` decoded from the ASCII `plainHexEncodedBytes` string . |
937 | | /// |
938 | | /// This will allocate a new `ByteBuffer` with enough space to fit `bytes` and potentially some extra space. |
939 | | /// |
940 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
941 | | @inlinable |
942 | 0 | public func buffer(plainHexEncodedBytes string: String) throws -> ByteBuffer { |
943 | 0 | var buffer = self.buffer(capacity: string.utf8.count / 2) |
944 | 0 | try buffer.writePlainHexEncodedBytes(string) |
945 | 0 | return buffer |
946 | 0 | } |
947 | | |
948 | | /// Create a fresh `ByteBuffer` containing the bytes of the byte representation in the given `endianness` of |
949 | | /// `integer`. |
950 | | /// |
951 | | /// This will allocate a new `ByteBuffer` with enough space to fit `integer` and potentially some extra space. |
952 | | /// |
953 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
954 | | @inlinable |
955 | | public func buffer<I: FixedWidthInteger>( |
956 | | integer: I, |
957 | | endianness: Endianness = .big, |
958 | | as: I.Type = I.self |
959 | 0 | ) -> ByteBuffer { |
960 | 0 | var buffer = self.buffer(capacity: MemoryLayout<I>.size) |
961 | 0 | buffer.writeInteger(integer, endianness: endianness, as: `as`) |
962 | 0 | return buffer |
963 | 0 | } |
964 | | |
965 | | /// Create a fresh `ByteBuffer` containing `count` repetitions of `byte`. |
966 | | /// |
967 | | /// This will allocate a new `ByteBuffer` with at least `count` bytes. |
968 | | /// |
969 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
970 | | @inlinable |
971 | 0 | public func buffer(repeating byte: UInt8, count: Int) -> ByteBuffer { |
972 | 0 | var buffer = self.buffer(capacity: count) |
973 | 0 | buffer.writeRepeatingByte(byte, count: count) |
974 | 0 | return buffer |
975 | 0 | } |
976 | | |
977 | | /// Create a fresh `ByteBuffer` containing the readable bytes of `buffer`. |
978 | | /// |
979 | | /// This may allocate a new `ByteBuffer` with enough space to fit `buffer` and potentially some extra space. |
980 | | /// |
981 | | /// - Note: Use this method only if you deliberately want to reallocate a potentially smaller `ByteBuffer` than the |
982 | | /// one you already have. Given that `ByteBuffer` is a value type, defensive copies are not necessary. If |
983 | | /// you have a `ByteBuffer` but would like the `readerIndex` to start at `0`, consider `readSlice` instead. |
984 | | /// |
985 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
986 | | @inlinable |
987 | 0 | public func buffer(buffer: ByteBuffer) -> ByteBuffer { |
988 | 0 | var newBuffer = self.buffer(capacity: buffer.readableBytes) |
989 | 0 | newBuffer.writeImmutableBuffer(buffer) |
990 | 0 | return newBuffer |
991 | 0 | } |
992 | | |
993 | | #if canImport(Dispatch) |
994 | | /// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`. |
995 | | /// |
996 | | /// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially |
997 | | /// some extra space. |
998 | | /// |
999 | | /// - Returns: The `ByteBuffer` containing the written bytes. |
1000 | | @inlinable |
1001 | 0 | public func buffer(dispatchData: DispatchData) -> ByteBuffer { |
1002 | 0 | var buffer = self.buffer(capacity: dispatchData.count) |
1003 | 0 | buffer.writeDispatchData(dispatchData) |
1004 | 0 | return buffer |
1005 | 0 | } |
1006 | | #endif |
1007 | | |
1008 | | #if compiler(>=6.2) |
1009 | | /// Create a fresh ``ByteBuffer`` with a minimum size, and initializing it safely via an `OutputSpan`. |
1010 | | /// |
1011 | | /// This will allocate a new ``ByteBuffer`` with at least `capacity` bytes of storage, and then calls |
1012 | | /// `initializer` with an `OutputSpan` over the entire allocated storage. This is a convenient method |
1013 | | /// to initialize a buffer directly and safely in a single allocation, including from C code. |
1014 | | /// |
1015 | | /// Once this call returns, the buffer will have its ``ByteBuffer/writerIndex`` appropriately advanced to encompass |
1016 | | /// any memory initialized by the `initializer`. Uninitialized memory will be after the ``ByteBuffer/writerIndex``, |
1017 | | /// available for subsequent use. |
1018 | | /// |
1019 | | /// - parameters: |
1020 | | /// - capacity: The minimum initial space to allocate for the buffer. |
1021 | | /// - initializer: The initializer that will be invoked to initialize the allocated memory. |
1022 | | @inlinable |
1023 | | @available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.0, *) |
1024 | | public func buffer<ErrorType: Error>( |
1025 | | capacity: Int, |
1026 | | initializingWith initializer: (_ span: inout OutputRawSpan) throws(ErrorType) -> Void |
1027 | 0 | ) throws(ErrorType) -> ByteBuffer { |
1028 | 0 | var buffer = self.buffer(capacity: capacity) |
1029 | 0 | try buffer.writeWithOutputRawSpan(minimumWritableBytes: capacity, initializingWith: initializer) |
1030 | 0 | return buffer |
1031 | 0 | } |
1032 | | #endif |
1033 | | } |
1034 | | |
1035 | | extension Optional where Wrapped == ByteBuffer { |
1036 | | /// If `nil`, replace `self` with `.some(buffer)`. If non-`nil`, write `buffer`'s readable bytes into the |
1037 | | /// `ByteBuffer` starting at `writerIndex`. |
1038 | | /// |
1039 | | /// This method will not modify `buffer`, meaning its `readerIndex` and `writerIndex` stays intact. |
1040 | | /// |
1041 | | /// - Parameters: |
1042 | | /// - buffer: The `ByteBuffer` to write. |
1043 | | /// - Returns: The number of bytes written to this `ByteBuffer` which is equal to the number of `readableBytes` in |
1044 | | /// `buffer`. |
1045 | | @discardableResult |
1046 | | @inlinable |
1047 | 0 | public mutating func setOrWriteImmutableBuffer(_ buffer: ByteBuffer) -> Int { |
1048 | 0 | var mutable = buffer |
1049 | 0 | return self.setOrWriteBuffer(&mutable) |
1050 | 0 | } |
1051 | | |
1052 | | /// If `nil`, replace `self` with `.some(buffer)`. If non-`nil`, write `buffer`'s readable bytes into the |
1053 | | /// `ByteBuffer` starting at `writerIndex`. |
1054 | | /// |
1055 | | /// This will move both this `ByteBuffer`'s writer index as well as `buffer`'s reader index by the number of bytes |
1056 | | /// readable in `buffer`. |
1057 | | /// |
1058 | | /// - Parameters: |
1059 | | /// - buffer: The `ByteBuffer` to write. |
1060 | | /// - Returns: The number of bytes written to this `ByteBuffer` which is equal to the number of bytes read from `buffer`. |
1061 | | @discardableResult |
1062 | | @inlinable |
1063 | 0 | public mutating func setOrWriteBuffer(_ buffer: inout ByteBuffer) -> Int { |
1064 | 0 | if self == nil { |
1065 | 0 | let readableBytes = buffer.readableBytes |
1066 | 0 | self = buffer |
1067 | 0 | buffer.moveReaderIndex(to: buffer.writerIndex) |
1068 | 0 | return readableBytes |
1069 | 0 | } else { |
1070 | 0 | return self!.writeBuffer(&buffer) |
1071 | 0 | } |
1072 | 0 | } |
1073 | | } |
1074 | | |
1075 | | extension ByteBuffer { |
1076 | | /// Get the string at `index` from this `ByteBuffer` decoding using the UTF-8 encoding. Does not move the reader index. |
1077 | | /// The selected bytes must be readable or else `nil` will be returned. |
1078 | | /// |
1079 | | /// This is an alternative to `ByteBuffer.getString(at:length:)` which ensures the returned string is valid UTF8. If the |
1080 | | /// string is not valid UTF8 then a `ReadUTF8ValidationError` error is thrown. |
1081 | | /// |
1082 | | /// - Parameters: |
1083 | | /// - index: The starting index into `ByteBuffer` containing the string of interest. |
1084 | | /// - length: The number of bytes making up the string. |
1085 | | /// - Returns: A `String` value containing the UTF-8 decoded selected bytes from this `ByteBuffer` or `nil` if |
1086 | | /// the requested bytes are not readable. |
1087 | | @inlinable |
1088 | | @available(macOS 15, iOS 18, tvOS 18, watchOS 11, visionOS 2, *) |
1089 | 0 | public func getUTF8ValidatedString(at index: Int, length: Int) throws -> String? { |
1090 | 0 | guard let slice = self.getSlice(at: index, length: length) else { |
1091 | 0 | return nil |
1092 | 0 | } |
1093 | 0 | guard |
1094 | 0 | let string = String( |
1095 | 0 | validating: slice.readableBytesView, |
1096 | 0 | as: Unicode.UTF8.self |
1097 | 0 | ) |
1098 | 0 | else { |
1099 | 0 | throw ReadUTF8ValidationError.invalidUTF8 |
1100 | 0 | } |
1101 | 0 | return string |
1102 | 0 | } |
1103 | | |
1104 | | /// Read `length` bytes off this `ByteBuffer`, decoding it as `String` using the UTF-8 encoding. Move the reader index |
1105 | | /// forward by `length`. |
1106 | | /// |
1107 | | /// This is an alternative to `ByteBuffer.readString(length:)` which ensures the returned string is valid UTF8. If the |
1108 | | /// string is not valid UTF8 then a `ReadUTF8ValidationError` error is thrown and the reader index is not advanced. |
1109 | | /// |
1110 | | /// - Parameters: |
1111 | | /// - length: The number of bytes making up the string. |
1112 | | /// - Returns: A `String` value deserialized from this `ByteBuffer` or `nil` if there aren't at least `length` bytes readable. |
1113 | | @inlinable |
1114 | | @available(macOS 15, iOS 18, tvOS 18, watchOS 11, visionOS 2, *) |
1115 | 0 | public mutating func readUTF8ValidatedString(length: Int) throws -> String? { |
1116 | 0 | guard let result = try self.getUTF8ValidatedString(at: self.readerIndex, length: length) else { |
1117 | 0 | return nil |
1118 | 0 | } |
1119 | 0 | self.moveReaderIndex(forwardBy: length) |
1120 | 0 | return result |
1121 | 0 | } |
1122 | | |
1123 | | /// Errors thrown when calling `readUTF8ValidatedString` or `getUTF8ValidatedString`. |
1124 | | public struct ReadUTF8ValidationError: Error, Equatable { |
1125 | | private enum BaseError: Hashable { |
1126 | | case invalidUTF8 |
1127 | | } |
1128 | | |
1129 | | private var baseError: BaseError |
1130 | | |
1131 | | /// The length of the bytes to copy was negative. |
1132 | | public static let invalidUTF8: ReadUTF8ValidationError = .init(baseError: .invalidUTF8) |
1133 | | } |
1134 | | |
1135 | | /// Return a UTF-8 validated String decoded from the bytes at the current reader index. |
1136 | | /// |
1137 | | /// This is equivalent to calling `getUTF8ValidatedString(at: readerIndex, length: ...)` and does not advance the reader index. |
1138 | | /// |
1139 | | /// - Parameter length: The number of bytes making up the string. |
1140 | | /// - Returns: A validated String, or `nil` if the requested bytes are not readable. |
1141 | | /// - Throws: `ReadUTF8ValidationError.invalidUTF8` if the bytes are not valid UTF8. |
1142 | | @inlinable |
1143 | | @available(macOS 15, iOS 18, tvOS 18, watchOS 11, visionOS 2, *) |
1144 | 0 | public func peekUTF8ValidatedString(length: Int) throws -> String? { |
1145 | 0 | try self.getUTF8ValidatedString(at: self.readerIndex, length: length) |
1146 | 0 | } |
1147 | | } |