Convert a two byte UInt8 array to a UInt16 in Swift

前端 未结 7 829
醉话见心
醉话见心 2020-11-30 03:00

With Swift I want to convert bytes from a uint8_t array to an integer.

\"C\" Example:

char bytes[2] = {0x01, 0x02};
NSData *data = [NSData dataWithBy         


        
相关标签:
7条回答
  • 2020-11-30 03:43

    Assuming little endian encoding.

    To convert to UInt16 from [UInt8], you can do something like

    var x: [UInt8] = [0x01, 0x02]
    var y: UInt16 = 0
    y += UInt16(x[1]) << 0o10
    y += UInt16(x[0]) << 0o00
    

    For conversion to UInt32, this pattern extends to

    var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
    var y: UInt32 = 0
    y += UInt32(x[3]) << 0o30
    y += UInt32(x[2]) << 0o20
    y += UInt32(x[1]) << 0o10
    y += UInt32(x[0]) << 0o00
    

    Octal representation of the shift amount gives a nice indication on how many full bytes are shifted (8 becomes 0o10, 16 becomes 0o20 etc).

    This can be reduced to the following for UInt16:

    var x: [UInt8] = [0x01, 0x02]
    let y: UInt16 = reverse(x).reduce(UInt16(0)) {
        $0 << 0o10 + UInt16($1)
    }
    

    and to the following for UInt32:

    var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
    let y: UInt32 = reverse(x).reduce(UInt32(0)) {
        $0 << 0o10 + UInt32($1)
    }
    

    The reduced version also works for UInt64, and also handles values where the byte encoding does not use all bytes, like [0x01, 0x02, 0x03]

    0 讨论(0)
  • 2020-11-30 03:44

    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
    
    0 讨论(0)
  • 2020-11-30 03:45

    I don't know the syntax for swift, but what about something like:

    let a:UInt16 = UInt16(bytes[0]) * 256 + UInt16(bytes[1])
    
    0 讨论(0)
  • 2020-11-30 03:47

    How about

    let bytes:[UInt8] = [0x01, 0x02]
    let result = (UInt16(bytes[1]) << 8) + UInt16(bytes[0])
    

    With a loop, this easily generalizes to larger byte arrays, and it can be wrapped in a function for readability:

    let bytes:[UInt8] = [0x01, 0x02, 0x03, 0x04]
    
    func bytesToUInt(byteArray: [UInt8]) -> UInt {
      assert(byteArray.count <= 4)
      var result: UInt = 0
      for idx in 0..<(byteArray.count) {
        let shiftAmount = UInt((byteArray.count) - idx - 1) * 8
        result += UInt(byteArray[idx]) << shiftAmount
      }
      return result
    }
    
    println(bytesToUInt(bytes))    // result is 16909060
    
    0 讨论(0)
  • 2020-11-30 03:47

    In Swift 5 or later you can convert the bytes [UInt8] to UInt16 value using withUnsafeBytes { $0.load(as: UInt16.self) }

    let bytes: [UInt8] = [1, 2]
    

    loading as UInt16

    let uint16 = bytes.withUnsafeBytes { $0.load(as: UInt16.self) } 
    

    To get rid of the verbosity we can create a generic method extending ContiguousBytes:

    extension ContiguousBytes {
        func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
    }
    

    Usage:

    let bytes: [UInt8] = [1, 2]
    let uint16: UInt16 = bytes.object()  // 513
    
    0 讨论(0)
  • 2020-11-30 03:51

    Martin R's answer is great and nicely updated for beta 6. However, if you need to get at bytes that are not at the start of your buffer the proposed withMemoryRebound method does not offer a range to rebind from. My solution to this, eg. pick out the second UInt16 from an array was:

    var val: UInt16 = 0
    let buf = UnsafeMutableBufferPointer(start: &val, count: 1)
    _ = dat.copyBytes(to: buf, from: Range(2...3))
    
    0 讨论(0)
提交回复
热议问题