How to Convert Int to Byte Array of 4 Bytes in Swift

How to convert Int to byte array of 4 bytes in Swift?

In Java an integer is always 32-bit, but in Swift it can be 32-bit or 64-bit, depending on the platform. Your code creates a byte array with the same size as that of the Int type, on a 64-bit platform that are 8 bytes.

If you want to restrict the conversion to 32-bit integers then use Int32 instead of Int, the result will then be an array of 4 bytes, independent of the platform.

An alternative conversion method is

let value: Int32 = -1333
let array = withUnsafeBytes(of: value.bigEndian, Array.init)
print(array) // [255, 255, 250, 203]

Or as a generic function for integer type of all sizes:

func byteArray<T>(from value: T) -> [UInt8] where T: FixedWidthInteger {
withUnsafeBytes(of: value.bigEndian, Array.init)
}

Example:

print(byteArray(from: -1333))        // [255, 255, 255, 255, 255, 255, 250, 203]
print(byteArray(from: Int32(-1333))) // [255, 255, 250, 203]
print(byteArray(from: Int16(-1333))) // [250, 203]

Convert Int to Array of UInt8 in swift

You could do it this way:

let x: Int = 2019
let length: Int = 2 * MemoryLayout<UInt8>.size //You could specify the desired length

let a = withUnsafeBytes(of: x) { bytes in
Array(bytes.prefix(length))
}

let result = Array(a.reversed()) //[7, 227]

Or more generally, we could use a modified version of this snippet:

func bytes<U: FixedWidthInteger,V: FixedWidthInteger>(
of value : U,
to type : V.Type,
droppingZeros: Bool
) -> [V]{

let sizeInput = MemoryLayout<U>.size
let sizeOutput = MemoryLayout<V>.size

precondition(sizeInput >= sizeOutput, "The input memory size should be greater than the output memory size")

var value = value
let a = withUnsafePointer(to: &value, {
$0.withMemoryRebound(
to: V.self,
capacity: sizeInput,
{
Array(UnsafeBufferPointer(start: $0, count: sizeInput/sizeOutput))
})
})

let lastNonZeroIndex =
(droppingZeros ? a.lastIndex { $0 != 0 } : a.indices.last) ?? a.startIndex

return Array(a[...lastNonZeroIndex].reversed())
}

let x: Int = 2019
bytes(of: x, to: UInt8.self, droppingZeros: true) // [7, 227]
bytes(of: x, to: UInt8.self, droppingZeros: false) // [0, 0, 0, 0, 0, 0, 7, 227]

How to convert a double into a byte array in swift?


typealias Byte = UInt8

func toByteArray<T>(var value: T) -> [Byte] {
return withUnsafePointer(&value) {
Array(UnsafeBufferPointer(start: UnsafePointer<Byte>($0), count: sizeof(T)))
}
}

toByteArray(1729.1729)
toByteArray(1729.1729 as Float)
toByteArray(1729)
toByteArray(-1729)

But the results are reversed from your expectations (because of endianness):

[234, 149, 178, 12, 177, 4, 155, 64]
[136, 37, 216, 68]
[193, 6, 0, 0, 0, 0, 0, 0]
[63, 249, 255, 255, 255, 255, 255, 255]

Added:

func fromByteArray<T>(value: [Byte], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
return UnsafePointer<T>($0.baseAddress).memory
}
}

let a: Double = 1729.1729
let b = toByteArray(a) // -> [234, 149, 178, 12, 177, 4, 155, 64]
let c = fromByteArray(b, Double.self) // -> 1729.1729

For Xcode8/Swift3.0:

func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<T>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<T>.size))
}
}
}

func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee
}
}
}

For Xcode8.1/Swift3.0.1

func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafeBytes(of: &value) { Array($0) }
}

func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBytes {
$0.baseAddress!.load(as: T.self)
}
}

Swift 4 convert bytes to Int and ntohl

The C int type is a 32-bit integer (on all current Apple platforms),
whereas the Swift Int is 64-bit integer on 64-bit platforms.

Therefore in Swift you'll have to use UInt32 for 32-bit integers:

var msgLength = (inputBuffer.bytes).load(as: UInt32.self)
msgLength = UInt32(bigEndian: msgLength)

Or, if you switch from NSData to Data:

let msgLength = UInt32(bigEndian:inputBuffer.withUnsafeBytes { $0.pointee })

(Even in the C code uint32_t would be better suited than int to emphasize that 4 bytes are read.)

How to convert NSDecimalNumber to byte array if NSDecimalNumber is bigger than Uint64?

The problem is obviously the use of uint64Value, which obviously cannot represent any value greater than UInt64.max, and your example, 59,785,897,542,892,656,787,456, is larger than that.

If you want to grab the byte representations of the 128 bit mantissa, you can use _mantissa tuple of UInt16 words of Decimal, and convert them to bytes if you want. E.g.

extension Decimal {
var byteArray: [UInt8] {
return [_mantissa.0,
_mantissa.1,
_mantissa.2,
_mantissa.3,
_mantissa.4,
_mantissa.5,
_mantissa.6,
_mantissa.7]
.flatMap { [UInt8($0 & 0xff), UInt8($0 >> 8)] }
}
}

And

if let foo = Decimal(string: "59785897542892656787456") {
print(foo.byteArray)
}

Returning:

[0, 0, 0, 0, 0, 0, 0, 0, 169, 12, 0, 0, 0, 0, 0, 0]

This, admittedly, only defers the problem, breaking the 64-bit limit of your uint64Value approach, but is still constrained to the inherent 128-bit limit of NSDecimalNumber/Decimal. To capture numbers greater than 128 bits, you'd need a completely different representation.


NB: This also assumes that the exponent is 0. If, however, you had some large number, e.g. 4.2e101 (4.2 * 10101), the exponent will be 100 and the mantissa will simply be 42, which I bet is probably not what you want in your byte array. Then again, this is an example of a number that is too large to represent as a single 128 bit integer, anyway:

if let foo = Decimal(string: "4.2e101") {
print(foo.byteArray)
print(foo.exponent)
}

Yielding:

[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

100

How to convert an ContiguousArray of Floats into a byte array in Swift?

You can use the withUnsafeBytes() method to get a buffer pointer to the underlying bytes of the array's contiguous storage, and directly initialize an [UInt8] array from that buffer pointer. Example:

let floatArray: [Float] = [1.0, 2.0]
// Works also with a ContiguousArray:
// let floatArray: ContiguousArray<Float> = [1.0, 2.0]

let byteArray = floatArray.withUnsafeBytes { Array($0) }
print(byteArray) // [0, 0, 128, 63, 0, 0, 0, 64]

Equivalently (based on Leo's suggestion):

let byteArray = floatArray.withUnsafeBytes(Array.init)

The byte array contains the binary representation of the floating point numbers in host byte order (which is little-endian on all current Apple platforms). A conversion to big-endian is possible, but not without an intermediate copy to an integer array:

let floatArray: ContiguousArray<Float> = [1.0, 2.0]
let intArray = floatArray.map { $0.bitPattern.bigEndian }
let byteArray = intArray.withUnsafeBytes(Array.init)
print(byteArray) // 63, 128, 0, 0, 64, 0, 0, 0]

Reverse conversion: A simple method would be

let floatArray2 = byteArray.withUnsafeBytes { Array($0.bindMemory(to: Float.self)) }
print(floatArray2) // [1.0, 2.0]

However, that requires that the element storage of the byte array is properly aligned for floating point numbers. If that is not guaranteed then you can do

var floatArray2 = [Float](repeating: 0.0, count: byteArray.count / MemoryLayout<Float>.stride)
_ = floatArray2.withUnsafeMutableBytes { byteArray.copyBytes(to: $0) }
print(floatArray2) // [1.0, 2.0]

How to change Java's byte to long in Swift

Assuming that Byte is a type alias for UInt8: You have to convert the bytes to Int before shifting the values to the left, otherwise the result can overflow. This also helps the compiler to compile the expression successfully:

func setData(raw : [Byte]) {
mode_time = Int(raw[3] << 24) | Int(raw[2] << 16) | Int(raw[1] << 8) | Int(raw[0] << 0)
}

Note also that the Swift Int type is platform dependent, it can be a 32-bit or a 64-bit integer, and on a 32-bit platform the above code an overflow if the result exceeds the maximal value of a signed integer. You might want to use UInt, UInt32, or Int64 instead. The latter is a 64-bit integer on all platforms, like Java's long.

An alternative implementation is

func setData(raw : [Byte]) {
mode_time = raw.reversed().reduce(Int(0)) { ($0 << 8) + Int($1) }
}

if you don't feed more bytes than what fits into an Int.

How to convert Swift Date into Byte Array?

You can create a method to convert FixedWidthInteger to bytes (big or little endian) for the year component, convert the other date componentes to bytes and append them to the result:

enum Endianness {
case big, little
}

extension FixedWidthInteger {
func data<D: DataProtocol & RangeReplaceableCollection>(
using endianness: Endianness = .big
) -> D {
withUnsafeBytes(of: endianness == .big ? bigEndian : littleEndian, D.init)
}
}


extension Calendar {
static let iso8601 = Self(identifier: .iso8601)
}


extension Date {
func component(_ component: Calendar.Component, using calendar: Calendar = .iso8601) -> Int {
calendar.component(component, from: self)
}
func data<D: DataProtocol & RangeReplaceableCollection>() -> D {
var dataProtocol: D = .init()
dataProtocol += UInt16(component(.year)).data() as D
dataProtocol.append(UInt8(component(.month)))
dataProtocol.append(UInt8(component(.day)))
dataProtocol.append(UInt8(component(.hour)))
dataProtocol.append(UInt8(component(.minute)))
dataProtocol.append(UInt8(component(.second)))
return dataProtocol
}
}

Usage:

let data: Data = Date().data()
print(Array(data))

This will print

[7, 230, 6, 17, 2, 10, 46]



Related Topics



Leave a reply



Submit