1

How to convert swift's Int64 to UUID and back? Since UUID is 128 bit, I want to fill top 64 bits with zeroes.

I can construct UUID from uuid_t, which is a tuple of UInt8, by shifting and casting Int64 eight times.

Is there a better way?

user1256821
  • 1,158
  • 3
  • 15
  • 35
  • 1
    While you can create a 128-bit value from a 64-bit one will you have a [UUID](https://en.m.wikipedia.org/wiki/Universally_unique_identifier)? Of course it may not matter to you. – CRD Nov 09 '16 at 09:51
  • @CRD I am working with CXProvider which uses UUID as a call identifier, whereas we use Int64 in our framework. I need to map CXProvider's id to our id. – user1256821 Nov 09 '16 at 10:31

3 Answers3

3

UUID is the Swift overlay type for the Foundation type NSUUID, and the latter can be created from a byte buffer. With a little bit of pointer juggling this works with 64-bit integers as well:

let vals: [UInt64] = [0, 0x123456789abcdef]

let uuid = vals.withUnsafeBufferPointer {
    $0.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: 16) {
         NSUUID(uuidBytes: $0) as UUID
    }
}

print(uuid) // 00000000-0000-0000-EFCD-AB8967452301
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
2

Answering strictly your question

The "simplest" way to do it (without relying on the memory layout of CollectionType or Tuple, and doesn't need NSUUID) is:

extension UUID {
    init(number: Int64) {
        var number = number
        let numberData = Data(bytes: &number, count: MemoryLayout<Int64>.size)

        let bytes = [UInt8](numberData)

        let tuple: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0,
                             bytes[0], bytes[1], bytes[2], bytes[3],
                             bytes[4], bytes[5], bytes[6], bytes[7])

        self.init(uuid: tuple)
    }

    var intValue: Int64? {
        let tuple = self.uuid
        guard tuple.0 == 0 && tuple.1 == 0 && tuple.2 == 0 && tuple.3 == 0 &&
              tuple.4 == 0 && tuple.5 == 0 && tuple.6 == 0 && tuple.7 == 0 else {
                return nil
        }

        let bytes: [UInt8] = [tuple.8, tuple.9, tuple.10, tuple.11,
                              tuple.12, tuple.13, tuple.14, tuple.15]

        let numberData = Data(bytes: bytes)

        let number = numberData.withUnsafeBytes { $0.pointee } as Int64

        return number
    }
}

Also, you may want to throw/fatalError instead of returning nil.

Creating valid UUIDs

For completion sake (and to make it actually possible to create valid UUIDs) I added a way to create it from 2 Int64 and rewrote the answer to your question using that:

UUID creation from (Int64, Int64)

extension UUID {
    init(numbers: (Int64, Int64)) {
        var firstNumber = numbers.0
        var secondNumber = numbers.1
        let firstData = Data(bytes: &firstNumber, count: MemoryLayout<Int64>.size)
        let secondData = Data(bytes: &secondNumber, count: MemoryLayout<Int64>.size)

        let bytes = [UInt8](firstData) + [UInt8](secondData)

        let tuple: uuid_t = (bytes[0], bytes[1], bytes[2], bytes[3],
                             bytes[4], bytes[5], bytes[6], bytes[7],
                             bytes[8], bytes[9], bytes[10], bytes[11],
                             bytes[12], bytes[13], bytes[14], bytes[15])

        self.init(uuid: tuple)
    }

    var intTupleValue: (Int64, Int64) {
        let tuple = self.uuid

        let firstBytes: [UInt8] = [tuple.0, tuple.1, tuple.2, tuple.3,
                                   tuple.4, tuple.5, tuple.6, tuple.7]

        let secondBytes: [UInt8] = [tuple.8, tuple.9, tuple.10, tuple.11,
                                    tuple.12, tuple.13, tuple.14, tuple.15]

        let firstData = Data(bytes: firstBytes)
        let secondData = Data(bytes: secondBytes)

        let first = firstData.withUnsafeBytes { $0.pointee } as Int64
        let second = secondData.withUnsafeBytes { $0.pointee } as Int64

        return (first, second)
    }
}

UUID creation from Int64 (filling MSBs with 0s)

extension UUID {
    init(number: Int64) {
        self.init(numbers: (0, number))
    }

    var intValue: Int64? {
        let (first, second) = intTupleValue
        guard first == 0 else { return nil }

        return second
    }
}
fpg1503
  • 7,492
  • 6
  • 29
  • 49
  • That can still be simplified at bit, e.g. `let number = numberData.withUnsafeBytes { $0.pointee } as Int64` in the second method, compare [round trip Swift number types to/from Data](http://stackoverflow.com/questions/38023838/round-trip-swift-number-types-to-from-data). – Martin R Nov 09 '16 at 10:12
0

You can also use withUnsafeBytes and simply load it as UUID:

let vals: [UInt64] = [0, 0x123456789abcdef]
let uuid = vals.withUnsafeBytes { $0.load(as: UUID.self) }
print(uuid) //  00000000-0000-0000-EFCD-AB8967452301
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571