Coverage Report

Created: 2026-09-14 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}