swift-nio/Sources/NIOCore/ByteBuffer-int.swift

179 lines
6.5 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
extension ByteBuffer {
@inlinable
func _toEndianness<T: FixedWidthInteger> (value: T, endianness: Endianness) -> T {
switch endianness {
case .little:
return value.littleEndian
case .big:
return value.bigEndian
}
}
/// Read an integer off this `ByteBuffer`, move the reader index forward by the integer's byte size and return the result.
///
/// - parameters:
/// - endianness: The endianness of the integer in this `ByteBuffer` (defaults to big endian).
/// - as: the desired `FixedWidthInteger` type (optional parameter)
/// - returns: An integer value deserialized from this `ByteBuffer` or `nil` if there aren't enough bytes readable.
@inlinable
public mutating func readInteger<T: FixedWidthInteger>(endianness: Endianness = .big, as: T.Type = T.self) -> T? {
guard let result = self.getInteger(at: self.readerIndex, endianness: endianness, as: T.self) else {
return nil
}
self._moveReaderIndex(forwardBy: MemoryLayout<T>.size)
return result
}
/// Get the integer at `index` from this `ByteBuffer`. Does not move the reader index.
/// The selected bytes must be readable or else `nil` will be returned.
///
/// - parameters:
/// - index: The starting index of the bytes for the integer into the `ByteBuffer`.
/// - endianness: The endianness of the integer in this `ByteBuffer` (defaults to big endian).
/// - as: the desired `FixedWidthInteger` type (optional parameter)
/// - returns: An integer value deserialized from this `ByteBuffer` or `nil` if the bytes of interest are not
/// readable.
@inlinable
public func getInteger<T: FixedWidthInteger>(at index: Int, endianness: Endianness = Endianness.big, as: T.Type = T.self) -> T? {
guard let range = self.rangeWithinReadableBytes(index: index, length: MemoryLayout<T>.size) else {
return nil
}
if T.self == UInt8.self {
assert(range.count == 1)
return self.withUnsafeReadableBytes { ptr in
ptr[range.startIndex] as! T
}
}
return self.withUnsafeReadableBytes { ptr in
var value: T = 0
withUnsafeMutableBytes(of: &value) { valuePtr in
valuePtr.copyMemory(from: UnsafeRawBufferPointer(fastRebase: ptr[range]))
}
return _toEndianness(value: value, endianness: endianness)
}
}
/// Write `integer` into this `ByteBuffer`, moving the writer index forward appropriately.
///
/// - parameters:
/// - integer: The integer to serialize.
/// - endianness: The endianness to use, defaults to big endian.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func writeInteger<T: FixedWidthInteger>(_ integer: T,
endianness: Endianness = .big,
as: T.Type = T.self) -> Int {
let bytesWritten = self.setInteger(integer, at: self.writerIndex, endianness: endianness)
self._moveWriterIndex(forwardBy: bytesWritten)
return Int(bytesWritten)
}
/// Write `integer` into this `ByteBuffer` starting at `index`. This does not alter the writer index.
///
/// - parameters:
/// - integer: The integer to serialize.
/// - index: The index of the first byte to write.
/// - endianness: The endianness to use, defaults to big endian.
/// - returns: The number of bytes written.
@discardableResult
@inlinable
public mutating func setInteger<T: FixedWidthInteger>(_ integer: T, at index: Int, endianness: Endianness = .big, as: T.Type = T.self) -> Int {
var value = _toEndianness(value: integer, endianness: endianness)
return Swift.withUnsafeBytes(of: &value) { ptr in
self.setBytes(ptr, at: index)
}
}
}
extension FixedWidthInteger {
/// Returns the next power of two.
@inlinable
func nextPowerOf2() -> Self {
guard self != 0 else {
return 1
}
return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount)
}
/// Returns the previous power of 2, or self if it already is.
@inlinable
func previousPowerOf2() -> Self {
guard self != 0 else {
return 0
}
return 1 << ((Self.bitWidth - 1) - self.leadingZeroBitCount)
}
}
extension UInt32 {
/// Returns the next power of two unless that would overflow, in which case UInt32.max (on 64-bit systems) or
/// Int32.max (on 32-bit systems) is returned. The returned value is always safe to be cast to Int and passed
/// to malloc on all platforms.
@inlinable
func nextPowerOf2ClampedToMax() -> UInt32 {
guard self > 0 else {
return 1
}
var n = self
#if arch(arm) || arch(i386)
// on 32-bit platforms we can't make use of a whole UInt32.max (as it doesn't fit in an Int)
let max = UInt32(Int.max)
#else
// on 64-bit platforms we're good
let max = UInt32.max
#endif
n -= 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n != max {
n += 1
}
return n
}
}
/// Endianness refers to the sequential order in which bytes are arranged into larger numerical values when stored in
/// memory or when transmitted over digital links.
public enum Endianness {
/// The endianness of the machine running this program.
public static let host: Endianness = hostEndianness0()
private static func hostEndianness0() -> Endianness {
let number: UInt32 = 0x12345678
return number == number.bigEndian ? .big : .little
}
/// big endian, the most significant byte (MSB) is at the lowest address
case big
/// little endian, the least significant byte (LSB) is at the lowest address
case little
}