Coverage Report

Created: 2026-03-12 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOCore/ByteBuffer-int.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
extension ByteBuffer {
16
    @inlinable
17
0
    func _toEndianness<T: FixedWidthInteger>(value: T, endianness: Endianness) -> T {
18
0
        switch endianness {
19
0
        case .little:
20
0
            return value.littleEndian
21
0
        case .big:
22
0
            return value.bigEndian
23
0
        }
24
0
    }
25
26
    /// Read an integer off this `ByteBuffer`, move the reader index forward by the integer's byte size and return the result.
27
    ///
28
    /// - Parameters:
29
    ///   - endianness: The endianness of the integer in this `ByteBuffer` (defaults to big endian).
30
    ///   - as: the desired `FixedWidthInteger` type (optional parameter)
31
    /// - Returns: An integer value deserialized from this `ByteBuffer` or `nil` if there aren't enough bytes readable.
32
    @inlinable
33
0
    public mutating func readInteger<T: FixedWidthInteger>(endianness: Endianness = .big, as: T.Type = T.self) -> T? {
34
0
        guard let result = self.getInteger(at: self.readerIndex, endianness: endianness, as: T.self) else {
35
0
            return nil
36
0
        }
37
0
        self._moveReaderIndex(forwardBy: MemoryLayout<T>.size)
38
0
        return result
39
0
    }
40
41
    /// Get the integer at `index` from this `ByteBuffer`. Does not move the reader index.
42
    /// The selected bytes must be readable or else `nil` will be returned.
43
    ///
44
    /// - Parameters:
45
    ///   - index: The starting index of the bytes for the integer into the `ByteBuffer`.
46
    ///   - endianness: The endianness of the integer in this `ByteBuffer` (defaults to big endian).
47
    ///   - as: the desired `FixedWidthInteger` type (optional parameter)
48
    /// - Returns: An integer value deserialized from this `ByteBuffer` or `nil` if the bytes of interest are not
49
    ///            readable.
50
    @inlinable
51
    public func getInteger<T: FixedWidthInteger>(
52
        at index: Int,
53
        endianness: Endianness = Endianness.big,
54
        as: T.Type = T.self
55
0
    ) -> T? {
56
0
        guard let range = self.rangeWithinReadableBytes(index: index, length: MemoryLayout<T>.size) else {
57
0
            return nil
58
0
        }
59
0
60
0
        if T.self == UInt8.self {
61
0
            assert(range.count == 1)
62
0
            return self.withUnsafeReadableBytes { ptr in
63
0
                ptr[range.startIndex] as! T
64
0
            }
65
0
        }
66
0
67
0
        return self.withUnsafeReadableBytes { ptr in
68
0
            var value: T = 0
69
0
            withUnsafeMutableBytes(of: &value) { valuePtr in
70
0
                valuePtr.copyMemory(from: UnsafeRawBufferPointer(rebasing: ptr[range]))
71
0
            }
72
0
            return _toEndianness(value: value, endianness: endianness)
73
0
        }
74
0
    }
75
76
    /// Write `integer` into this `ByteBuffer`, moving the writer index forward appropriately.
77
    ///
78
    /// - Parameters:
79
    ///   - integer: The integer to serialize.
80
    ///   - endianness: The endianness to use, defaults to big endian.
81
    ///   - as: the desired `FixedWidthInteger` type (optional parameter)
82
    /// - Returns: The number of bytes written.
83
    @discardableResult
84
    @inlinable
85
    public mutating func writeInteger<T: FixedWidthInteger>(
86
        _ integer: T,
87
        endianness: Endianness = .big,
88
        as: T.Type = T.self
89
0
    ) -> Int {
90
0
        let bytesWritten = self.setInteger(integer, at: self.writerIndex, endianness: endianness)
91
0
        self._moveWriterIndex(forwardBy: bytesWritten)
92
0
        return Int(bytesWritten)
93
0
    }
94
95
    /// Write `integer` into this `ByteBuffer` starting at `index`. This does not alter the writer index.
96
    ///
97
    /// - Parameters:
98
    ///   - integer: The integer to serialize.
99
    ///   - index: The index of the first byte to write.
100
    ///   - endianness: The endianness to use, defaults to big endian.
101
    ///   - as: the desired `FixedWidthInteger` type (optional parameter)
102
    /// - Returns: The number of bytes written.
103
    @discardableResult
104
    @inlinable
105
    public mutating func setInteger<T: FixedWidthInteger>(
106
        _ integer: T,
107
        at index: Int,
108
        endianness: Endianness = .big,
109
        as: T.Type = T.self
110
0
    ) -> Int {
111
0
        var value = _toEndianness(value: integer, endianness: endianness)
112
0
        return Swift.withUnsafeBytes(of: &value) { ptr in
113
0
            self.setBytes(ptr, at: index)
114
0
        }
115
0
    }
116
117
    /// Returns the integer at the current reader index without advancing it.
118
    ///
119
    /// This method is equivalent to calling `getInteger(at: readerIndex, ...)`
120
    ///
121
    /// - Parameters:
122
    ///   - endianness: The endianness of the integer (defaults to big endian).
123
    ///   - as: The desired `FixedWidthInteger` type (optional parameter).
124
    /// - Returns: An integer value deserialized from this `ByteBuffer` or `nil` if the bytes are not readable.
125
    @inlinable
126
    public func peekInteger<T: FixedWidthInteger>(
127
        endianness: Endianness = .big,
128
        as: T.Type = T.self
129
0
    ) -> T? {
130
0
        self.getInteger(at: self.readerIndex, endianness: endianness, as: `as`)
131
0
    }
132
}
133
134
extension FixedWidthInteger {
135
136
    /// Returns the next power of two.
137
    @inlinable
138
5.05M
    func nextPowerOf2() -> Self {
139
5.05M
        guard self != 0 else {
140
0
            return 1
141
5.05M
        }
142
5.05M
        return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount)
143
5.05M
    }
144
145
    /// Returns the previous power of 2, or self if it already is.
146
    @inlinable
147
0
    func previousPowerOf2() -> Self {
148
0
        guard self != 0 else {
149
0
            return 0
150
0
        }
151
0
152
0
        return 1 << ((Self.bitWidth - 1) - self.leadingZeroBitCount)
153
0
    }
154
155
    /// Initialize an integer from a byte buffer of exactly the right size.
156
    /// The bytes will be read using the host system's endianness.
157
    ///
158
    /// - Parameters:
159
    ///   - buffer: The ByteBuffer to read from
160
    ///   - endianness: The endianness to use when reading the integer, defaults to the host system's endianness.
161
    @inlinable
162
0
    public init?(buffer: ByteBuffer, endianness: Endianness = .host) {
163
0
        var buffer = buffer
164
0
        guard let value = buffer.readInteger(endianness: endianness, as: Self.self), buffer.readableBytes == 0 else {
165
0
            return nil
166
0
        }
167
0
        self = value
168
0
    }
169
}
170
171
extension UInt32 {
172
    /// Returns the next power of two unless that would overflow, in which case UInt32.max (on 64-bit systems) or
173
    /// Int32.max (on 32-bit systems) is returned. The returned value is always safe to be cast to Int and passed
174
    /// to malloc on all platforms.
175
    @inlinable
176
186k
    func nextPowerOf2ClampedToMax() -> UInt32 {
177
186k
        guard self > 0 else {
178
0
            return 1
179
186k
        }
180
186k
181
186k
        var n = self
182
186k
183
        #if arch(arm) || arch(i386) || arch(arm64_32) || arch(wasm32)
184
        // on 32-bit platforms we can't make use of a whole UInt32.max (as it doesn't fit in an Int)
185
        let max = UInt32(Int.max)
186
        #else
187
186k
        // on 64-bit platforms we're good
188
186k
        let max = UInt32.max
189
        #endif
190
186k
191
186k
        n -= 1
192
186k
        n |= n >> 1
193
186k
        n |= n >> 2
194
186k
        n |= n >> 4
195
186k
        n |= n >> 8
196
186k
        n |= n >> 16
197
186k
        if n != max {
198
186k
            n += 1
199
186k
        }
200
186k
201
186k
        return n
202
186k
    }
203
}
204
205
/// Endianness refers to the sequential order in which bytes are arranged into larger numerical values when stored in
206
/// memory or when transmitted over digital links.
207
public enum Endianness: Sendable {
208
    /// The endianness of the machine running this program.
209
    public static let host: Endianness = hostEndianness0()
210
211
0
    private static func hostEndianness0() -> Endianness {
212
0
        let number: UInt32 = 0x1234_5678
213
0
        return number == number.bigEndian ? .big : .little
214
0
    }
215
216
    /// big endian, the most significant byte (MSB) is at the lowest address
217
    case big
218
219
    /// little endian, the least significant byte (LSB) is at the lowest address
220
    case little
221
}