/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 | | } |