Reputation: 205
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
}
Upvotes: 12
Views: 14462
Reputation: 1570
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)
}
}
Upvotes: 1
Reputation: 1
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.
Upvotes: 0
Reputation: 539685
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]
Upvotes: 31