Skip to content

Instantly share code, notes, and snippets.

@Gernot
Created August 3, 2016 09:00
Show Gist options
  • Save Gernot/604f295786100e7b7b629ea282771889 to your computer and use it in GitHub Desktop.
Save Gernot/604f295786100e7b7b629ea282771889 to your computer and use it in GitHub Desktop.
struct CharacteristicValue {
let format: Int8
let exponent: Int8 = -1
let unit: UInt16
let namespace: Int8
let descriptionInfo: Int16
}
var value = CharacteristicValue(format: 14, unit: 0x272F, namespace: 1, descriptionInfo: 0)
//This works
let oldData = NSData(bytes: &value, length: sizeof(CharacteristicValue.self))
//This gives me "Ambiguous Use of 'init'" on the `UnsafePointer` part.
let newData = Data(bytes: UnsafePointer<UInt8>(&value), count: sizeof(CharacteristicValue.self))
@ole
Copy link

ole commented Aug 3, 2016

This works but I'm not sure it is the most elegant way:

let data = withUnsafePointer(&value) { pointer -> Data in
    let int8Pointer = UnsafePointer<UInt8>(pointer)
    return Data(bytes: int8Pointer, count: sizeof(CharacteristicValue.self))
}

assert(newData == oldData) // passes

@Gernot
Copy link
Author

Gernot commented Aug 3, 2016

This works, thanks!

@ole
Copy link

ole commented Aug 3, 2016

Here’s an alternative: wrap the conversion in a function. Then you can call it with &value and the compiler will take care of guaranteeing the lifetime of the pointer. I think this reads really nicely:

func bytes<T>(_ value: UnsafePointer<T>) -> Data {
    return Data(bytes: UnsafePointer(value), count: sizeof(T.self))
}

let newData2 = bytes(&value)
assert(newData2 == oldData)

@Gernot
Copy link
Author

Gernot commented Aug 3, 2016

Nice, thanks! I put it into a Data initializer, and now I think I have something that I'm happy with:

extension Data {
    init<T>(unsafePointer: UnsafePointer<T>) {
        self.init(bytes: UnsafePointer(unsafePointer), count: sizeof(T.self))
    }
}

let newData3 = Data(unsafePointer: &value)

assert(newData3 == oldData)

@mzaks
Copy link

mzaks commented Aug 3, 2016

I would use strideof instead of sizeof
Let me explain why:
Lets say you have a struct:

struct S1{
    let a:Int
    let b:UInt8
}

sizeof(S1) == 9
strideof(S1)==16

so using sizeof will give you a smaller data which is good. However if you would like to concatenate structs. You have a memory alignment problem (https://en.wikipedia.org/wiki/Data_structure_alignment).

From my current experiences armv7 is not very good at resolving non aligned memory access (and it showed for me only in release mode -> optimisation level -O). arm64 seems much more stable.

So in your case if you only want to serialse single data structure / struct. It might work fine, but beware of potential memory alignment issues.

@ole
Copy link

ole commented Aug 4, 2016

@mzaks: Good point. I briefly considered strideof but for some reason assumed that it would be wrong to use it for a single struct (I guess because I implicitly assumed the padding bytes at the end could cause a problem). But that's not the case. /cc @Gernot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment