34

How to convert a 4-bytes array into the corresponding Int?

let array: [UInt8] ==> let value : Int

Example:

Input:

\0\0\0\x0e

Output:

14

Some code I found on the internet that doesn't work:

let data = NSData(bytes: array, length: 4)
data.getBytes(&size, length: 4)
// the output to size is 184549376
Community
  • 1
  • 1
Jerry
  • 1,614
  • 2
  • 19
  • 36

7 Answers7

51

There are two problems:

  • Int is a 64-bit integer on 64-bit platforms, your input data has only 32-bit.
  • Int uses a little-endian representation on all current Swift platforms, your input is big-endian.

That being said the following would work:

let array : [UInt8] = [0, 0, 0, 0x0E]
var value : UInt32 = 0
let data = NSData(bytes: array, length: 4)
data.getBytes(&value, length: 4)
value = UInt32(bigEndian: value)

print(value) // 14

Or using Data in Swift 3:

let array : [UInt8] = [0, 0, 0, 0x0E]
let data = Data(bytes: array)
let value = UInt32(bigEndian: data.withUnsafeBytes { $0.pointee })

With some buffer pointer magic you can avoid the intermediate copy to an NSData object (Swift 2):

let array : [UInt8] = [0, 0, 0, 0x0E]
var value = array.withUnsafeBufferPointer({ 
     UnsafePointer<UInt32>($0.baseAddress).memory
})
value = UInt32(bigEndian: value)

print(value) // 14

For a Swift 3 version of this approach, see ambientlight's answer.

Martin R
  • 510,973
  • 84
  • 1,183
  • 1,314
  • 1
    @HansBrende: `NSData` still has a `getBytes` method in Swift 3, only its overlay type `Data` hasn't. I have added a `Data` version, another solution for Swift 3 was already posted by ambientlight. – Martin R Nov 11 '16 at 18:26
  • 1
    @HansBrende: See also http://stackoverflow.com/questions/38023838/round-trip-swift-number-types-to-from-data for a more general treatment of conversion to Data and back. – Martin R Nov 11 '16 at 18:29
16

In Swift 3 it is now a bit more wordy:

let array : [UInt8] = [0, 0, 0, 0x0E]
let bigEndianValue = array.withUnsafeBufferPointer {
         ($0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 1) { $0 })
}.pointee
let value = UInt32(bigEndian: bigEndianValue)
ambientlight
  • 6,987
  • 3
  • 47
  • 59
  • How is this performance wise? Any overhead in terms of data-copying and allocations / retain cycles? I want to replace NSMutableData in my server-swift code with [UInt8] – Antwan van Houdt Sep 11 '17 at 12:44
12

I think Martin's answer is better than this, but I still want to post mine. Any suggestion would be really helpful.

let array : [UInt8] = [0, 0, 0, 0x0E]
var value : Int = 0
for byte in array {
    value = value << 8
    value = value | Int(byte)
}
print(value) // 14
Jerry
  • 1,614
  • 2
  • 19
  • 36
  • 3
    Than why not to accept Martin's answer? There is a tick button under the number of "upvotes". Its a tick, when you click on it, it becomes green. – smttsp Sep 24 '15 at 20:45
  • Actually, your answer was very helpful for me. I was getting wrong int values with the accepted method, but yours gave me the correct result. I was probably just implementing the accepted method wrong but still... – davis Aug 01 '17 at 10:25
  • This failed for signed number. How to modify this for Signed integer? Ex: [0xFF,0xFF,0xFF,0xAB] – krishnakumarcn Sep 17 '18 at 13:15
11

There's some good answers here, which is really nice to see ^^ However if you'd like to avoid interacting with the C-interopability API of Swift, then I recommend to take a look at my example. It's also just as generic for all the datatype sizes. Note that MemoryLayout is only being used a sanity check.

Code:

public extension UnsignedInteger {
    init(_ bytes: [UInt8]) {
        precondition(bytes.count <= MemoryLayout<Self>.size)

        var value: UInt64 = 0

        for byte in bytes {
            value <<= 8
            value |= UInt64(byte)
        }

        self.init(value)
    }
}

Example usage:

let someBytes = [UInt8](repeating: 0x42, count: 2)
let someValue = UInt16(someBytes)

For little endian support, you need for byte in bytes.reversed() instead.

Explanation:

<<= is the bitwise left shift assignment operator: It shifts the left hand operand (usually a numerical value type) by N bits defined by the right hand operand, for example:

0b00000001 << 7 == 0b10000000

|= is the bitwise or assignment operator: It applies a bitwise or on the left and right hand operands, for example:

0b00000001 | 0b10000000 == 0b10000001

So when you have an array of 2 unsinged bytes and want to convert it a unsinged short you can simply;

let bytes = [UInt8](repeating: UInt8(255), count: 2)
var short: UInt16 = 0

// "add" our first unsinged byte
short |= UInt16(bytes[0])
// our short now looks like this in memory: 0b0000000011111111

// make room for the unsinged byte ;)
short <<= 8 
// our short now looks like this in memory: 0b1111111100000000

// "add" our last unsinged byte
short |= UInt16(bytes[1])
// our short now looks like this in memory: 0b1111111111111111

print(short == UInt16.max)
Kim Visscher
  • 121
  • 1
  • 6
9

The problem with the accepted answer comes when you don't know the size of your bytes array (or your Data size)

It works well with let array : [UInt8] = [0, 0, 0x23, 0xFF]

But it won't work with let array : [UInt8] = [0x23, 0xFF]
(because it will be considered as [0x23, 0xFF, 0, 0])

That's why I like the @Jerry's one, with bitwise operation.

I've made a functional version of his code snippet.

let data = Data(bytes: [0x23, 0xFF])
let decimalValue = data.reduce(0) { v, byte in
    return v << 8 | Int(byte)
}
duan
  • 7,791
  • 3
  • 45
  • 65
Martin
  • 11,425
  • 6
  • 61
  • 103
7

Updated for Swift 5, two things to pay attention:

  • As [UInt8] is stored in a contiguous region of memory, there's no need to convert it to Data, pointer can access all bytes directly.

  • Int's byte order is little endian currently on all Apple platform, but this is not garanteed on other platforms.

say we want [0, 0, 0, 0x0e] to convert to 14. (big-endian byte order)

let source: [UInt8] = [0, 0, 0, 0x0e]
let bigEndianUInt32 = source.withUnsafeBytes { $0.load(as: UInt32.self) }
let value = CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderLittleEndian.rawValue)
    ? UInt32(bigEndian: bigEndianUInt32)
    : bigEndianUInt32
print(value) // 14
duan
  • 7,791
  • 3
  • 45
  • 65
1

For those who prefer to do it the old-fashioned way, here's a set of methods for getting int values from a byte array. This is intended for situations where a byte array containing various kinds of data is being processed sequentially.

/// Class which encapsulates a Swift byte array (an Array object with elements of type UInt8) and an
/// index into the array.
open class ByteArrayAndIndex {

   private var _byteArray : [UInt8]
   private var _arrayIndex = 0

   public init(_ byteArray : [UInt8]) {
      _byteArray = byteArray;
   }

   /// Property to provide read-only access to the current array index value.
   public var arrayIndex : Int {
      get { return _arrayIndex }
   }

   /// Property to calculate how many bytes are left in the byte array, i.e., from the index point
   /// to the end of the byte array.
   public var bytesLeft : Int {
      get { return _byteArray.count - _arrayIndex }
   }

   /// Method to get a single byte from the byte array.
   public func getUInt8() -> UInt8 {
      let returnValue = _byteArray[_arrayIndex]
      _arrayIndex += 1
      return returnValue
   }

   /// Method to get an Int16 from two bytes in the byte array (little-endian).
   public func getInt16() -> Int16 {
      return Int16(bitPattern: getUInt16())
   }

   /// Method to get a UInt16 from two bytes in the byte array (little-endian).
   public func getUInt16() -> UInt16 {
      let returnValue = UInt16(_byteArray[_arrayIndex]) |
                        UInt16(_byteArray[_arrayIndex + 1]) << 8
      _arrayIndex += 2
      return returnValue
   }

   /// Method to get a UInt from three bytes in the byte array (little-endian).
   public func getUInt24() -> UInt {
      let returnValue = UInt(_byteArray[_arrayIndex]) |
                        UInt(_byteArray[_arrayIndex + 1]) << 8 |
                        UInt(_byteArray[_arrayIndex + 2]) << 16
      _arrayIndex += 3
      return returnValue
   }

   /// Method to get an Int32 from four bytes in the byte array (little-endian).
   public func getInt32() -> Int32 {
      return Int32(bitPattern: getUInt32())
   }

   /// Method to get a UInt32 from four bytes in the byte array (little-endian).
   public func getUInt32() -> UInt32 {
      let returnValue = UInt32(_byteArray[_arrayIndex]) |
                        UInt32(_byteArray[_arrayIndex + 1]) << 8 |
                        UInt32(_byteArray[_arrayIndex + 2]) << 16 |
                        UInt32(_byteArray[_arrayIndex + 3]) << 24
      _arrayIndex += 4
      return returnValue
   }

   /// Method to get an Int64 from eight bytes in the byte array (little-endian).
   public func getInt64() -> Int64 {
      return Int64(bitPattern: getUInt64())
   }

   /// Method to get a UInt64 from eight bytes in the byte array (little-endian).
   public func getUInt64() -> UInt64 {
      let returnValue = UInt64(_byteArray[_arrayIndex]) |
                        UInt64(_byteArray[_arrayIndex + 1]) << 8 |
                        UInt64(_byteArray[_arrayIndex + 2]) << 16 |
                        UInt64(_byteArray[_arrayIndex + 3]) << 24 |
                        UInt64(_byteArray[_arrayIndex + 4]) << 32 |
                        UInt64(_byteArray[_arrayIndex + 5]) << 40 |
                        UInt64(_byteArray[_arrayIndex + 6]) << 48 |
                        UInt64(_byteArray[_arrayIndex + 7]) << 56
      _arrayIndex += 8
      return returnValue
   }
}

This is an extract from a larger class that includes methods for extracting strings and other kinds of data. See also here: https://stackoverflow.com/a/41592206/253938

Community
  • 1
  • 1
RenniePet
  • 10,920
  • 7
  • 73
  • 101
  • I did try to use this class but I am facing a swift compiler error while running on the Xcode 13 in the function `public func getUInt64() -> UInt64` `The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions` – Pradip Sutariya Apr 28 '22 at 08:11