Split UInt32 into [UInt8] in swift
Your loop can more compactly be written as
let byteArray = 24.stride(through: 0, by: -8).map {
UInt8(truncatingBitPattern: example >> UInt32($0))
}
Alternatively, create an UnsafeBufferPointer
and convert that
to an array:
let example: UInt32 = 72 << 24 | 66 << 16 | 1 << 8 | 15
var bigEndian = example.bigEndian
let bytePtr = withUnsafePointer(&bigEndian) {
UnsafeBufferPointer<UInt8>(start: UnsafePointer($0), count: sizeofValue(bigEndian))
}
let byteArray = Array(bytePtr)
print(byteArray) // [72, 66, 1, 15]
Update for Swift 3 (Xcode 8 beta 6):
var bigEndian = example.bigEndian
let count = MemoryLayout<UInt32>.size
let bytePtr = withUnsafePointer(to: &bigEndian) {
$0.withMemoryRebound(to: UInt8.self, capacity: count) {
UnsafeBufferPointer(start: $0, count: count)
}
}
let byteArray = Array(bytePtr)
split uint32 value to uint8 values in swift
It is quite similar in Swift:
let value : UInt32 = 39434
let firstByte = UInt8(truncatingBitPattern: value) // 10
let secondByte = UInt8(truncatingBitPattern: value >> 8) // 154
The special initializer init(truncatingBitPattern:)
is required here because Swift (in contrast to C), does not implicitly
truncate integers:
let firstByte = UInt8(value)
would result in a runtime exception if value
does not fit into
the range of UInt8
.
See also Split UInt32 into [UInt8] in swift
for possible solutions which give you an array with the
four bytes of the input value.
Convert [UInt32] - [UInt8] - [[UInt8]] in Swift
If you profile (Cmd + I
) your code, you will see that most of the time is on various "copy to buffer" functions. This happens when you append a new element to the array but it has run out of its initial allocated space so it must be moved to a location on the heap with more memory. Morals of the lesson: heap allocation is slow but unavoidable with arrays. Do it as few times as possible.
Try this:
func convertWordToBytes2(fullW: [UInt32]) -> [[UInt8]] {
let subSize = 6
// We allocate the array only once per run since allocation is so slow
// There will only be assignment to it after
var combined48 = [UInt8](count: fullW.count * 4, repeatedValue: 0).splitBy(subSize)
var row = 0
var col = 0
for i in 0...16 {
for j in 24.stride(through: 0, by: -8) {
let value = UInt8(truncatingBitPattern: fullW[i] >> UInt32(j))
combined48[row][col] = value
col += 1
if col >= subSize {
row += 1
col = 0
}
}
}
return combined48
}
Benchmark code:
let testCases = (0..<1_000_000).map { _ in
(0..<17).map { _ in arc4random() }
}
testCases.forEach {
convertWordToBytes($0)
convertWordToBytes2($0)
}
Result (on my 2012 iMac)
Weight Self Weight Symbol Name
9.35 s 53.2% 412.00 ms specialized convertWordToBytes([UInt32]) -> [[UInt8]]
3.28 s 18.6% 344.00 ms specialized convertWordToBytes2([UInt32]) -> [[UInt8]]
By eliminating multiple allocations, we already reduced the run time by 60%. But each test case is independent, which lends itself perfectly to parallel processing with today's multi-core CPU. A modified loop...:
dispatch_apply(testCases.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { i in
convertWordToBytes2(testCases[i])
}
... will shave about 1 second off the wall time when executed on my quad-core i7 with 8 threads:
Weight Self Weight Symbol Name
2.28 s 6.4% 0 s _dispatch_worker_thread3 0x58467
2.24 s 6.3% 0 s _dispatch_worker_thread3 0x58463
2.22 s 6.2% 0 s _dispatch_worker_thread3 0x58464
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58466
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58465
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58461
2.18 s 6.1% 0 s _dispatch_worker_thread3 0x58462
The time saving is not as much as I hoped for. Apparently there's some contention when accessing the heap memory. For anything even faster, you should explore a C-based solution.
Swift 4 Int32 to [UInt8]
You can create a Data
value from the integer with
let encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
(In Swift versions before 4.2 you'll have to write
var encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: &encodedMessageSize) { Data($0) }
instead.)
The data can then be appended to the array with
buffer.append(contentsOf: data)
Alternatively you can use a data
buffer instead of an array:
func send(message: String) {
let messageSize = message.utf8.count
let encodedMessageSize = Int32(messageSize).bigEndian
var data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
data.append(Data(message.utf8))
let amountWritten = data.withUnsafeBytes { [count = data.count] in
outputStream.write($0, maxLength: count)
}
}
Finally note that that the write()
method might write less bytes than
provided (e.g. on network connections), so you should always check
the return value.
A proper way to marshall an array of UInt32 to UInt8
As Jeremy already said, you have to call the withUnsafeBytes
method on the array to get an UnsafeRawBufferPointer
to the element storage.
Now
UnsafeRawBufferPointer
is aCollection
(and in particular aSequence
) ofUInt8
, andArray
has a/// Creates an array containing the elements of a sequence.
///
/// - Parameter s: The sequence of elements to turn into an array.
public init<S>(_ s: S) where S : Sequence, S.Iterator.Element == Elementinitializer.
Therefore you can create an [UInt8]
array from a raw buffer pointerptr
with Array(ptr)
:
let eByteArr = entropySliceHashes32.withUnsafeBytes {
ptr in return Array(ptr)
}
which can be shortened to
let eByteArr = entropySliceHashes32.withUnsafeBytes { Array($0) }
Split uint32 into two uint16
Use the following code to assign the 16 most significant bits in number
to a
and the 16 least significant bits to b
:
a, b := uint16(number>>16), uint16(number)
Run it on the playground.
Convert UInt32 to 4 bytes Swift
let value: UInt32 = 1
var u32LE = value.littleEndian // or simply value
let dataLE = Data(bytes: &u32LE, count: 4)
let bytesLE = Array(dataLE) // [1, 0, 0, 0]
var u32BE = value.bigEndian
let dataBE = Data(bytes: &u32BE, count: 4)
let bytesBE = Array(dataBE) // [0, 0, 0, 1]
Convert a two byte UInt8 array to a UInt16 in Swift
If you want to go via NSData
then it would work like this:
let bytes:[UInt8] = [0x01, 0x02]
println("bytes: \(bytes)") // bytes: [1, 2]
let data = NSData(bytes: bytes, length: 2)
print("data: \(data)") // data: <0102>
var u16 : UInt16 = 0 ; data.getBytes(&u16)
// Or:
let u16 = UnsafePointer<UInt16>(data.bytes).memory
println("u16: \(u16)") // u16: 513
Alternatively:
let bytes:[UInt8] = [0x01, 0x02]
let u16 = UnsafePointer<UInt16>(bytes).memory
print("u16: \(u16)") // u16: 513
Both variants assume that the bytes are in the host byte order.
Update for Swift 3 (Xcode 8):
let bytes: [UInt8] = [0x01, 0x02]
let u16 = UnsafePointer(bytes).withMemoryRebound(to: UInt16.self, capacity: 1) {
$0.pointee
}
print("u16: \(u16)") // u16: 513
UInt32 array to String Byte Array in Swift
You can copy the [UInt32]
array values to the allocated memory without creating an intermediate [Int8]
array, and use the bigEndian
property instead of bit shifting and masking:
func writeCArrayValue(from pointer:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?,
withUInt32Values array: [UInt32]){
pointer?.pointee = UnsafeMutablePointer<Int8>.allocate(capacity: MemoryLayout<UInt32>.size * array.count)
pointer?.pointee?.withMemoryRebound(to: UInt32.self, capacity: array.count) {
for i in 0..<array.count {
$0[i] = array[i].bigEndian
}
}
}
In the same way you can do the decoding:
func decodeArrayID(aArray:UnsafeMutablePointer<CChar>, aTokenLen:UInt32)->[UInt32] {
let arrayCount = Int(aTokenLen / 4)
var idArrayTemp = [UInt32]()
aArray.withMemoryRebound(to: UInt32.self, capacity: arrayCount) {
for i in 0..<arrayCount {
idArrayTemp.append(UInt32(bigEndian: $0[i]))
}
}
return idArrayTemp
}
Related Topics
What Is the Use of the Validate() Method in Alamofire.Request
Nsimage Getting Resized When I Draw Text on It
Swift Maximum Consecutive Positive Numbers
Swift UI MACos Background Transparent Textfield
Enumerate Is Unavailable Call the Enumerate Method on the Sequence
Cannot Convert Value of Type '()' to Specified Type Bool
Wkwebview: How to Handle Blob Url
Type of Expression Is Ambiguous Without More Context Swift
Continuously Train Coreml Model After Shipping
Using Compiler Variables in Swift
Difference Between Nsrange and Nsmakerange
How to Check If a Property Has Been Set Using Swift Reflection
Can You Override Nsdateformatter 12 VS 24 Hour Time Format Without Using a Custom Dateformat