22

I know how to do it in java (see here), but I couldn't find a swift equivalent for java's ByteBuffer, and consequently its .putDouble(double value) method.

Basically, I'm looking for a function like this:

func doubleToByteArray(value: Double) -> [UInt8]? {
    . . .
}
doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]

7 Answers 7

57
typealias Byte = UInt8

func toByteArray<T>(var value: T) -> [Byte] {
    return withUnsafePointer(&value) {
        Array(UnsafeBufferPointer(start: UnsafePointer<Byte>($0), count: sizeof(T)))
    }
}

toByteArray(1729.1729)
toByteArray(1729.1729 as Float)
toByteArray(1729)
toByteArray(-1729)

But the results are reversed from your expectations (because of endianness):

[234, 149, 178, 12, 177, 4, 155, 64]
[136, 37, 216, 68]
[193, 6, 0, 0, 0, 0, 0, 0]
[63, 249, 255, 255, 255, 255, 255, 255]

Added:

func fromByteArray<T>(value: [Byte], _: T.Type) -> T {
    return value.withUnsafeBufferPointer {
        return UnsafePointer<T>($0.baseAddress).memory
    }
}

let a: Double = 1729.1729
let b = toByteArray(a) // -> [234, 149, 178, 12, 177, 4, 155, 64]
let c = fromByteArray(b, Double.self) // -> 1729.1729

For Xcode8/Swift3.0:

func toByteArray<T>(_ value: T) -> [UInt8] {
    var value = value
    return withUnsafePointer(to: &value) {
        $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<T>.size) {
            Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<T>.size))
        }
    }
}

func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
    return value.withUnsafeBufferPointer {
        $0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) {
            $0.pointee
        }
    }
}

For Xcode8.1/Swift3.0.1

func toByteArray<T>(_ value: T) -> [UInt8] {
    var value = value
    return withUnsafeBytes(of: &value) { Array($0) }
}

func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
    return value.withUnsafeBytes {
        $0.baseAddress!.load(as: T.self)
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Just use toByteArray(1729.1729).reverse() to get the order you are looking for.
I recommend to use functions defined in CFByteOrder.h, for example toByteArray(CFConvertDoubleHostToSwapped(1729.1729).v).
I have added typealias Byte = UInt8, because Byte isn`t a native data type. :-)
A word of caution if you're using the first function to serialize data going to some non-Swift platform. If you pass it an "optional" Swift value type then it adds an extra byte to the output, presumably to indicate if the value is nil or not. So giving it a UInt64? value produces 9 bytes instead of 8. One way to avoid this would be to change the parameter type of "value" from T to T?, and then replace the first statement with "var value = value!".
|
7

Well, it wasn't easy, but here it is:

func doubleToByteArray(value: Double) -> [UInt8] {
    let count = sizeof(Double)
    var doubles: [Double] = [value]
    let data = NSData(bytes: doubles, length: count)
    var result = [UInt8](count: count, repeatedValue: 0)
    data.getBytes(&result, length: count)
    return result
}

Use with caution.

Comments

5

Here's my updated version to the original solution.

/// input: array of bytes 
/// -> get pointer to byte array (UnsafeBufferPointer<[Byte]>)
/// -> access its base address
/// -> rebind memory to target type T (UnsafeMutablePointer<T>)
/// -> extract and return the value of target type
func binarytotype <T> (_ value: [Byte], _: T.Type) -> T
{
    return value.withUnsafeBufferPointer {
        $0.baseAddress!
          .withMemoryRebound(to: T.self, capacity: 1) {
            $0.pointee
        }
    }
}

/// input type: value of type T
/// -> get pointer to value of T
/// -> rebind memory to the target type, which is a byte array
/// -> create array with a buffer pointer initialized with the     source pointer
/// -> return the resulted array
func typetobinary <T> (_ value: T) -> [Byte]
{
    var mv : T = value
    let s : Int = MemoryLayout<T>.size
    return withUnsafePointer(to: &mv) {
        $0.withMemoryRebound(to: Byte.self, capacity: s) {
            Array(UnsafeBufferPointer(start: $0, count: s))
        }
    }
}

PS: Don't forget to replace Byte with UInt8.

Comments

4

Solution in swift 3:

public func toByteArray<T>(_ value: T) -> [Byte] {
  let totalBytes = MemoryLayout<T>.size
  var value = value
  return withUnsafePointer(to: &value) { valuePtr in
    return valuePtr.withMemoryRebound(to: Byte.self, capacity: totalBytes) { reboundPtr in
      return Array(UnsafeBufferPointer(start: reboundPtr, count: totalBytes))
    }
  }
}

Comments

4

The accepted answers are dangerous because of the fact that the MemoryLayout provides you the size of the static type T!

To workaround the problem you should create a custom protocol and ask for Self in it:

protocol ByteConvertible {}

extension ByteConvertible {

    func toBytes() -> [UInt8] {

        let capacity = MemoryLayout<Self>.size
        var mutableValue = self
        return withUnsafePointer(to: &mutableValue) {

            return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {

                return Array(UnsafeBufferPointer(start: $0, count: capacity))
            }
        }
    }
}

I mentioned before that the accepted answers are dangerous and here is an example why:

let num = UInt8(42)
MemoryLayout.size(ofValue: num) //=> 1 byte as expected
let any: Any = num
MemoryLayout.size(ofValue: any) //=> 32 bytes which is what will happen in the generic functions from the all the answers 

Swift 3.0

Comments

1

The method above works, using Swift 2 but, I discovered a much more simpler and faster method to do this conversion and vice versa:

func binarytotype <T> (value: [UInt8], _: T.Type) -> T
{
    return value.withUnsafeBufferPointer
    {
        return UnsafePointer<T>($0.baseAddress).memory
    }
}

func typetobinary <T> (var value: T) -> [UInt8]
{
    return withUnsafePointer(&value)
    {
        Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
    }
}

let a: Double = 0.25
let b: [UInt8] = typetobinary(a) // -> [0, 0, 0, 0, 0, 0, 208, 63]
let c = binarytotype(b, Double.self) // -> 0.25

I have tested it with Xcode 7.2 in the playground.

1 Comment

I would like to use this in Swift 3 beta 6 and the new withMemoryRebound command. Does anybody know how to convert it to swift 3 beta 6?
1
func byteArray<T>(_ value: T) -> [UInt8] {
    var value = value
    var initialArray = withUnsafeBytes(of: &value) { Array($0) }

    initialArray.reverse()
    var count = initialArray.count
    while initialArray.first == 0 && count > 1 {
        initialArray[0...count - 2] = initialArray[1...count - 1]
        count -= 1
    }
    if initialArray[0] >= 128 {
        var newArray = [UInt8](repeating: 0, count: count + 1)
        newArray[0] = UInt8(0)
        newArray[1...count] = initialArray[0...count - 1]
        return newArray
    } else {
        return Array(initialArray[0...count - 1])
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.