Split Uint32 into [Uint8] in Swift

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 a Collection (and in particular a
    Sequence) of UInt8, and
  • Array 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 == Element

    initializer.

Therefore you can create an [UInt8] array from a raw buffer pointer
ptr 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



Leave a reply



Submit