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

I'm using Swift and trying to convert an Int (for example: -1333) to a byte array of 4 bytes. I was able to convert an Int to an array of 8 bytes (-1333 becomes [255, 255, 255, 255, 255, 255, 250, 203]), but I need it to be 4 bytes. I know that there are ways to do this in other languages like Java, but is there a way for Swift? Here's my code: (I used THIS answer)
func createByteArray(originalValue: Int)->[UInt8]{
var result:[UInt8]=Array()
var _number:Int = originalValue
let mask_8Bit=0xFF
var c=0
var size: Int = MemoryLayout.size(ofValue: originalValue)
for i in (0..<size).reversed(){
//at: 0 -> insert at the beginning of the array
result.insert(UInt8( _number&mask_8Bit),at:0)
_number >>= 8 //shift 8 times from left to right
}
return result
}

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]

However, not like Java using big endian, iOS platform uses 'little endian' instead.
So if it would be
let value: Int32 = -1333
let array2 = withUnsafeBytes(of: value.littleEndian, Array.init)// [203, 250, 255, 255]
You can verify it by wrapping the value into data with below extension and check the bytes
extension FixedWidthInteger {
var data: Data {
let data = withUnsafeBytes(of: self) { Data($0) }
return data
}
}
value.data
Just a reminder for those iOS developers who are digging into memory layout.

It's hard for beginners to understand what is going on in the answers above
public extension FixedWidthInteger {
var bytes: [UInt8] {
withUnsafeBytes(of: bigEndian, Array.init)
}
}
First of all lets rewrite computed property bytes more detailed
var bytes: [UInt8] {
var copyOfSelf = self.bigEndian // this defines the order of bytes in result array
// for int '1' it can be [0, 0, 0, 0, 0, 0, 0, 1]
// or [1, 0, 0, 0, 0, 0, 0, 0]
return withUnsafeBytes(of: copyOfSelf) { urbp: UnsafeRawBufferPointer in
// Array has a constructor
// init<S>(_ s: S) where Element == S.Element, S : Sequence
// so 'UnsafeRawBufferPointer' confirms 'Sequence' protocol
// and 'urbp' can be passed as a parameter to Array constructor
return Array(urbp)
}
}
And here some tests to explain the result
final class FixedWidthIntegerTests: XCTestCase {
func testBytes() {
XCTAssertEqual([0,0,0,0,0,0,0,1], Int(1).bytes)
XCTAssertEqual([255,255,255,255,255,255,255,255], UInt.max.bytes)
XCTAssertEqual([127,255,255,255,255,255,255,255], Int.max.bytes)
XCTAssertEqual([1], Int8(1).bytes)
XCTAssertEqual([0,1], Int16(1).bytes)
XCTAssertEqual([0,0,0,1], Int32(1).bytes)
}
}

Related

How to convert a pair of bytes into a Float using Swift

I am using this article to communicate with an IoT sensor via BLE. In the article, this quote is mentioned:
The first two bytes do not seem to belong to the data (probably a prefix to denote that it is a data packet), but the remaining ones are more interesting. For the accelerometer, we get three signed 16 bit integers (little endian), which can simply be scaled to the range we set up to get our setup sequence. So the +/-2^15 range of the signed 16bit integer corresponds to the +/-16g, resulting in a factor 1/2048. To get the acceleration in m/s², we apply a factor of 9.81/2048. So, the corresponding bluetooth part reads:
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="2" length="2">accXRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="4" length="2">accYRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="6" length="2">accZRaw</output>
To read this code, I am running this Swift code:
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = characteristic.value!
var values = [UInt8](repeating: 0, count: data.count)
data.copyBytes(to: &values, count: data.count)
print("values = \(values)")
}
The result once I do a print is:
values = [3, 4, 250, 255, 199, 249, 91, 191]
Alike the article mentions, I can confirm that the first two bytes do not belong to any data, and are consistently repeating. Bytes values[2-7] are constantly changing, which makes me more confident that the pairs represent accXRaw, accYRaw, and accZRaw. What I want to do now is convert the pairs to doubles.
For example:
values[2], values[3] = [250 255] (accXRaw)
values[4], values[5] = [199 249] (accYRaw)
values[6], values[7] = [91 191] (accZRaw)
In the article, the author does this via a int16 little endian. I want to do the same with swift 5, but not sure if I am doing it correctly. Here is my code:
let xAxis = Float(bitPattern: UInt32(littleEndian: [values[2], values[3], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let yAxis = Float(bitPattern: UInt32(littleEndian: [values[4], values[5], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let zAxis = Float(bitPattern: UInt32(littleEndian: [values[6], values[7], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
print("x=\(xAxis), y=\(yAxis), z=\(zAxis)");
The resulting printout is:
values = [3, 4, 250, 255, 199, 249, 91, 191]
x=9.1827e-41, y=8.9603e-41, z=6.8645e-41
These numbers just look weird, and I suspect I am doing something wrong. Am I reading the byte pairs correctly ( at least in line with the article ) ? If not, what mistakes did I make?
Your issue there is that you are not suppose to initialize your Float using the bitPattern initializer and/or use the UInt32(littleEndian:) initializer. What you need is to convert those 2 bytes to Int16, coerce it to Float and then multiply by the factor of 9.81/2048 to get its acceleration.
Expanding on that, you can create a Numeric initializer that takes an object that conforms to DataProtocol (Data or Bytes [UInt8]):
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let size = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(size == MemoryLayout.size(ofValue: value))
self = value
}
}
Then you can initialize your Int16 object with the subdata (two bytes).
let bytes: [UInt8] = [3, 4, 250, 255, 199, 249, 91, 191]
let xData = bytes[2..<4]
let yData = bytes[4..<6]
let zData = bytes[6..<8]
let factor: Float = 9.81/2048
let xAxis = Float(Int16(xData)) * factor
let yAxis = Float(Int16(yData)) * factor
let zAxis = Float(Int16(zData)) * factor
print("x:", xAxis, "y:", yAxis, "z:", zAxis) // x: -0.028740235 y: -7.6305327 z: -79.27036

Convert Int to Array of UInt8 in swift

I want to convert a standard integer in a list of UInt8 in swift.
var x:Int = 2019
2019 can be written (for example) in hexadecimal 7E3 so i want some kind of function that converts is to a list of UInt8s which looks like this.
var y:[Uint8] = [0x07, 0xE3]
I already found this: Convert integer to array of UInt8 units but he/she is convertign the ascii symbols of the number not the number itself. So his example 94887253 should give a list like [0x05, 0xA7, 0xDD, 0x55].
In the best case the function i'm looking for has some kind of usage so that i can also choose the minimum length of the resulting array so that for example
foo(42, length:2) -> [0x00, 0x2A]
or
foo(42, length:4) -> [0x00, 0x00, 0x00, 0x2A]
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 my bytes data to Hex String and then Signed integer (32-bit) Two's complement from that ..?

I have a data like a bellow:
let data = Data(bytes: [206, 66, 49, 62])
Then I used this extension (from How to convert Data to hex string in swift) to convert to a hex string:
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let hexDigits = Array((options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef").utf16)
var chars: [unichar] = []
chars.reserveCapacity(2 * count)
for byte in self {
chars.append(hexDigits[Int(byte / 16)])
chars.append(hexDigits[Int(byte % 16)])
}
return String(utf16CodeUnits: chars, count: chars.count)
}
}
And then it is giving "ce42313e" as hex string. Now I am trying to convert this to Signed integer (32-bit) Two's complement .. I tried a couple of ways but not find anything perfectly.
When I give "ce42313e" in this bellow link under hex decimal the value is -834522818
http://www.binaryconvert.com/convert_signed_int.html
bellow is one of those I tried to convert "ce42313e" to int and it's giving me 3460444478 ..instead of -834522818 .
let str = value
let number = Int(str, radix: 16)
Please help out to get that value.
Int(str, radix: 16) interprets the string as the hexadecimal
representation of an unsigned number. You could convert it to
Int32 with
let data = Data(bytes: [206, 66, 49, 62])
let str = data.hexEncodedString()
print(str) // ce42313e
let number = Int32(truncatingBitPattern: Int(str, radix: 16)!)
print(number) // -834522818
But actually you don't need the hex representation for that purpose.
Your data is the big-endian representation of a signed 32-bit integer,
and this is how you can get the number from the data directly:
let data = Data(bytes: [206, 66, 49, 62])
let number = Int32(bigEndian: data.withUnsafeBytes { $0.pointee })
print(number) // -834522818

Convert each Byte of NSData to equivalent Int Value

I have an NSData object that is being allocated with bytes that are representing unsigned integers from 0-255. What is the simplest way to convert these bytes to integers, or, if it is easier, to a string/NSString?
For instance, if I have bytes 0x00, 0x02, and 0x0A in my NSData object, then I would like to have either an integer array of {0,2,10} or the string/NSString "0210".
It seems that there are plenty of ways to turn NSData into ASCII NSStrings,UInt8 arrays, etc, but I cannot for the life of me figure out how to do this simple conversion in Swift.
Edit: Example Code of what I want:
var data:NSData
/*allocate NSData*/
var unsignedInts = [Int]()
/*Allocate each byte of data to an individual index of unsignedInts */
print(unsignedInts)
//should see {0, 1, 10} if bytes are 0x00, 0x01, and 0x0A
It is necessary to create a new Array of Int because Int require more bytes per entry.
var nsData: NSData = NSData(bytes: [0x00, 0x02, 0x0A] as [UInt8], length: 3)
let buffer = UnsafeBufferPointer<UInt8>(start:UnsafePointer<UInt8>(nsData.bytes), count:nsData.length)
print("nsData: \(nsData)")
var intData = [Int]()
intData.reserveCapacity(nsData.length)
var stringData = String()
for i in 0..<nsData.length {
intData.append(Int(buffer[i]))
stringData += String(buffer[i])
}
print("intData: \(intData)")
print("stringData: \(stringData)")
nsData: <00020a>
intData: [0, 2, 10]
stringData: 0210
I can recommend a method like this:
import Foundation
extension NSData {
var byteBuffer : UnsafeMutableBufferPointer<UInt8> {
return UnsafeMutableBufferPointer(start: UnsafeMutablePointer(bytes), count: length)
}
var array : [UInt8] {
return Array(byteBuffer)
}
var intArray : [Int] {
return byteBuffer.map(Int.init)
}
var byteString : String {
return byteBuffer.lazy.map(String.init).joinWithSeparator("")
}
}
Usage:
let data1 = NSData(bytes: [0x48, 0xa1, 0x95, 0xe, 0x1c, 0x38, 0x82] as [UInt8], length: 7) // <48a1950e 1c3882>
print(data1.array) // "[72, 161, 149, 14, 28, 56, 130]"
print(data1.intArray) // "[72, 161, 149, 14, 28, 56, 130]"
print(data1.byteString) // "72161149142856130"
let data2 = NSData(bytes: [0x00, 0x02, 0x0A] as [UInt8], length: 3)
print(data2.array) // "[0, 2, 10]"
print(data2.intArray) // "[0, 2, 10]"
print(data2.byteString) // "0210"
It's very easy, Swift-y and efficient like this.
Note that in some cases you don't actually need an array like this, because you can just access all the values of the data in the same way without allocating a new array (recommended):
data1.byteBuffer[4] // 28
data1.byteBuffer.count // 7
data1.byteBuffer(14) // 3
EDIT: I added a property that returns an Int array, although I can't see a reason you'd need it

How to byte reverse NSData output in Swift the littleEndian way?

I have this output from NSData: <00000100 84000c00 071490fe 4dfbd7e9>
So how could I byte reverse it in Swift and have this output: <00000001 0084000c 1407fe90 fb4de9d7>?
This should work to swap each pair of adjacent bytes in the data.
The idea is to interpret the bytes as an array of UInt16 integers
and use the built-in byteSwapped property.
func swapUInt16Data(data : NSData) -> NSData {
// Copy data into UInt16 array:
let count = data.length / sizeof(UInt16)
var array = [UInt16](count: count, repeatedValue: 0)
data.getBytes(&array, length: count * sizeof(UInt16))
// Swap each integer:
for i in 0 ..< count {
array[i] = array[i].byteSwapped // *** (see below)
}
// Create NSData from array:
return NSData(bytes: &array, length: count * sizeof(UInt16))
}
If your actual intention is to convert data from an (external)
big-endian representation to the host (native) byte order (which happens to be little-endian on all current iOS and OS X devices) then you should replace *** by
array[i] = UInt16(bigEndian: array[i])
Example:
var bytes : [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8]
let data = NSData(bytes: &bytes, length: bytes.count)
print(data)
// <01020304 05060708>
print(swapUInt16Data(data))
// <02010403 06050807>
Update for Swift 3: The generic withUnsafeMutableBytes()
methods allows to obtain a UnsafeMutablePointer<UInt16> to the bytes
and modify them directly:
func swapUInt16Data(data : Data) -> Data {
var mdata = data // make a mutable copy
let count = data.count / MemoryLayout<UInt16>.size
mdata.withUnsafeMutableBytes { (i16ptr: UnsafeMutablePointer<UInt16>) in
for i in 0..<count {
i16ptr[i] = i16ptr[i].byteSwapped
}
}
return mdata
}
Example:
let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8])
print(data as NSData) // <01020304 05060708>
let swapped = swapUInt16Data(data: data)
print(swapped as NSData) // <02010403 06050807>
CoreFoundation has CFSwapInt32BigToHost and CFSwapInt32LittleToHost and other swap functions.
In swift3 it looks like this
struct FileHeader {
var magicNumber: UInt32 = 0
var count: UInt32 = 0
var width: UInt32 = 0
var height: UInt32 = 0
static func create(data: Data) -> FileHeader {
let structSize = MemoryLayout<FileHeader>.size
assert(data.count >= structSize)
var result = FileHeader()
let nsdata = data as NSData
nsdata.getBytes(&result, range: NSRange(location: 0, length: structSize))
result.magicNumber = CFSwapInt32BigToHost(result.magicNumber)
result.count = CFSwapInt32BigToHost(result.count)
result.width = CFSwapInt32BigToHost(result.width)
result.height = CFSwapInt32BigToHost(result.height)
return result
}
}
For someone may want to restrict the byte pattern, it would be a solution:
func swap<U:IntegerType>(data:NSData,_ :U.Type) -> NSData{
var length = data.length / sizeof(U)
var bytes = [U](count: length, repeatedValue: 0)
data.getBytes(&bytes, length: data.length)
// since byteSwapped isn't declare in any protocol, so we have do it by ourselves manually.
var inverse = bytes.enumerate().reduce([U](count: length, repeatedValue: 0)) { (var pre, ele) -> [U] in
pre[length - 1 - ele.index] = ele.element
return pre
}
return NSData(bytes: inverse, length: data.length)
}
for example:
swap(data:data,UInt8.self)
//before <0c20207b 17>
//after <177b2020 0c>
swap(data:anotherData,UInt16.self)
//before <8e004c01 84008f05 0701>
//after <07018f05 84004c01 8e00>