3

So, I have a stream of well-formed data coming from some hardware. The stream consists of a bunch of chunks of 8-bit data, some of which are meant to form into 32-bit integers. That's all good. The data moves along and now I want to parcel the sequence up.

The data is actually a block of contiguous bytes, with segments of it mapped to useful data. So, for example, the first byte is a confirmation code, the following four bytes represent a UInt32 of some application-specific meaning, followed by two bytes representing a UInt16, and so on for a couple dozen bytes.

I found two different ways to do that, both of which seem a bit..overwrought. It may just what happens when you get close to the metal.

But — are these two code idioms generally what one should expect to do? Or am I missing something more compact?

  // data : Data exists before this code, and has what we're transforming into UInt32

  // One Way to get 4  bytes from Data into a UInt32
  var y : [UInt8] = [UInt8](repeating:UInt8(0x0), count: 4)
  data.copyBytes(to: &y, from: Range(uncheckedBounds: (2,6)))
  let u32result = UnsafePointer(y).withMemoryRebound(to: UInt32.self, capacity: 1, {
       $0.pointee
  })
  // u32result contains the 4 bytes from data

  // Another Way to get 4 bytes from Data into a UInt32 via NSData
  var result : UInt32 = 0
  let resultAsNSData : NSData = data.subdata(in: Range(uncheckedBounds: (2,6))) as NSData
  resultAsNSData.getBytes(&result, range: NSRange(location: 0, length: 4))
  // result contains the 4 bytes from data
bleeckerj
  • 506
  • 7
  • 18
  • 1
    Have a look at [round trip Swift number types to/from Data](http://stackoverflow.com/questions/38023838/round-trip-swift-number-types-to-from-data). – Martin R Feb 06 '17 at 06:08
  • Thanks Martin. I've had this bookmarked and it was helpful. – bleeckerj Feb 06 '17 at 16:42

3 Answers3

2

Creating UInt32 array from well-formed data object.

Swift 3

// Create sample data
let data = "foo".data(using: .utf8)!

// Using pointers style constructor
let array = data.withUnsafeBytes {
    [UInt32](UnsafeBufferPointer(start: $0, count: data.count))
}
    

Swift 2

// Create sample data
let data = "foo".dataUsingEncoding(NSUTF8StringEncoding)!
        
// Using pointers style constructor
let array = Array(UnsafeBufferPointer(start: UnsafePointer<UInt32>(data.bytes), count: data.length))
Community
  • 1
  • 1
ERbittuu
  • 922
  • 8
  • 19
  • That's a helpful pointer to a part of the Swift 3 API I am not so familiar with. Thank you. More poking around, and revealing the case where my data is effectively an array of UInt8, I attempted this: `var bytes : [UInt8] = [0xFF, 0, 0xFF, 0] bytes.withUnsafeBytes {_ in // [UInt32](UnsafeBufferPointer.init(start: bytes, count: 4)) print(UnsafeBufferPointer.init(start: bytes, count: 4)) //print($0) }` The first commented line in the block fails. I can see that the second one (`UnsafeBufferPointer(start: 0x0000600000240320, count: 4) `)
    How can I get my UInt32?
    – bleeckerj Feb 06 '17 at 18:18
2

I found two other ways of doing this which is leading me to believe that there are plenty of ways to do it, which is good, I suppose. Two additional ways are described in some fashion over on Ray Wenderlich

This code dropped into your Xcode playground will reveal these two other idioms.

do {
let count = 1 // number of UInt32s
let stride = MemoryLayout<UInt32>.stride
let alignment = MemoryLayout<UInt32>.alignment
let byteCount = count * stride

var bytes : [UInt8] = [0x0D, 0x0C, 0x0B, 0x0A] // little-endian LSB -> MSB
var data : Data = Data.init(bytes: bytes) // In my situtation, I actually start with an instance of Data, so the [UInt8] above is a conceit.

print("---------------- 1 ------------------")

let placeholder = UnsafeMutableRawPointer.allocate(bytes: byteCount, alignedTo:alignment)

withUnsafeBytes(of: &data, { (bytes) in
    for (index, byte) in data.enumerated() {
        print("byte[\(index)]->\(String(format: "0x%02x",byte)) data[\(index)]->\(String(format: "0x%02x", data[index])) addr: \(bytes.baseAddress!+index)")

        placeholder.storeBytes(of: byte, toByteOffset: index, as: UInt8.self)

    }
})
let typedPointer1 = placeholder.bindMemory(to: UInt32.self, capacity: count)
print("u32: \(String(format: "0x%08x", typedPointer1.pointee))")



print("---------------- 2 ------------------")

for (index, byte) in bytes.enumerated() {
    placeholder.storeBytes(of: byte, toByteOffset: index, as: UInt8.self)
   // print("byte \(index): \(byte)")
    print("byte[\(index)]->\(String(format: "0x%02x",byte))")

}
let typedPointer = placeholder.bindMemory(to: UInt32.self, capacity: count)
print(typedPointer.pointee)
let result : UInt32 = typedPointer.pointee
print("u32: \(String(format: "0x%08x", typedPointer.pointee))")
}

With output:

---------------- 1 ------------------
byte[0]->0x0d data[0]->0x0d addr: 0x00007fff57243f68
byte[1]->0x0c data[1]->0x0c addr: 0x00007fff57243f69
byte[2]->0x0b data[2]->0x0b addr: 0x00007fff57243f6a
byte[3]->0x0a data[3]->0x0a addr: 0x00007fff57243f6b
u32: 0x0a0b0c0d
---------------- 2 ------------------
byte[0]->0x0d
byte[1]->0x0c
byte[2]->0x0b
byte[3]->0x0a
168496141
u32: 0x0a0b0c0d

Here's a Gist.

bleeckerj
  • 506
  • 7
  • 18
0
let a = [ 0x00, 0x00, 0x00, 0x0e ]
let b = a[0] << 24 + a[1] << 16 + a[2] << 8 + a[3]
print(b) // will print 14. 

Should I describe this operation ?

  • In your case `a` is an array of `Int`. If `a` were `[UInt8]`, you would have to add casts. This is my favourite solution for similar things though. – Sulthan Mar 10 '18 at 15:56