9

I'm trying to decode a protobuff encoded message, so I need to convert the first byte (the key) in the protobuff message into bits, so I can find the field number. How do I convert a UInt8 (the byte) into an array of bits?

Pseudo Code

private func findFieldNum(from byte: UInt8) -> Int {
    //Byte is 0001 1010
    var fieldNumBits = byte[1] ++ byte[2] ++ byte[3] ++ byte[4] //concatentates bits to get 0011
    getFieldNum(from: fieldNumBits) //Converts 0011 to field number, 2^1 + 2^0 = 3
}

I saw this question, which converts an array of bits into array of bytes.

5 Answers 5

19

Here's a basic function to get a Bit array from a byte:

func bits(fromByte byte: UInt8) -> [Bit] {
    var byte = byte
    var bits = [Bit](repeating: .zero, count: 8)
    for i in 0..<8 {
        let currentBit = byte & 0x01
        if currentBit != 0 {
            bits[i] = .one
        }

        byte >>= 1
    }

    return bits
}

Here, Bit is a custom enum type that I have defined as follows:

enum Bit: UInt8, CustomStringConvertible {
    case zero, one

    var description: String {
        switch self {
        case .one:
            return "1"
        case .zero:
            return "0"
        }
    }
}

With this setup, the output of the following code:

let byte: UInt8 = 0x1f

print(bits(fromByte: byte))

would be:

[1, 1, 1, 1, 1, 0, 0, 0]
Sign up to request clarification or add additional context in comments.

5 Comments

Do you have to write a byte as a hexadecimal or is it possible to write it as a binary like 00011010?
You can write in binary with the 0b suffix. e.g. 0b10001111
This works but returns the reverse of the original bit ordering (as expressed by a binary literal such as 0b11111000).
@NikolaySuvandzhiev The array returned by bits() has the 0th bit on the 0 index, 1st bit on the 1 index. This makes semantic sense, right? The issue is that the print function treats the bit array like a normal list and prints its elements starting with the 0 index whereas the literal is usually written with 7th bit (MSb) first.
Reverse order: Array(bits(fromByte: byte).reversed())
3

Improving on mohak's answer. With a generic function or an extension to cater for more than just UInt8.

enum Bit: UInt8, CustomStringConvertible {
    case zero, one

    var description: String {
        switch self {
        case .one:
            return "1"
        case .zero:
            return "0"
        }
    }
}

func bits<T: FixedWidthInteger>(fromBytes bytes: T) -> [Bit] {
    // Make variable
    var bytes = bytes
    // Fill an array of bits with zeros to the fixed width integer length
    var bits = [Bit](repeating: .zero, count: T.bitWidth)
    // Run through each bit (LSB first)
    for i in 0..<T.bitWidth {
        let currentBit = bytes & 0x01
        if currentBit != 0 {
            bits[i] = .one
        }

        bytes >>= 1
    }

    return bits
}

extension FixedWidthInteger {
    var bits: [Bit] {
        // Make variable
        var bytes = self
        // Fill an array of bits with zeros to the fixed width integer length
        var bits = [Bit](repeating: .zero, count: self.bitWidth)
        // Run through each bit (LSB first)
        for i in 0..<self.bitWidth {
            let currentBit = bytes & 0x01
            if currentBit != 0 {
                bits[i] = .one
            }

            bytes >>= 1
        }

        return bits
    }
}

Comments

0

You can do some bitwise operations to get the value of the bits at indices 1-4.

// An example byte
let a: UInt8 = 0b00011010

// Extract the bits (using 0b01111000 as a bitmask) 
// in indices 1 to 4, then shift right by 3 places
// to remove indices 5 to 7
var b = (a & 0b01111000) >> 3 

// In this case, b is now 3 (or 11 in binary)
print(b)

Comments

0

A shorter one:

enum Bit { case zero, one }

func bit(_ i: Int, of uint8: UInt8) -> Bit {
  let first8PowersOf2 = (0...7).map { return UInt8(1) << $0 }
  return (uint8 & first8PowersOf2[i] != 0) ? Bit.one : Bit.zero
}

func bitsFrom(_ uint8: UInt8) -> [Bit] {
  return Array((0...7)).reversed().map { bit($0, of: uint8) }
}

We can use it as:

let fromUInt8  = bitsFrom(42)
let fromBinary = bitsFrom(0b01011010)
let fromHexa   = bitsFrom(0xFF)

Comments

0

Swift 4,5 works fine for me :

func bits(fromByte byte: UInt8) -> [Bool] {
  var byte = byte
  var bits = [Bool](repeating: false, count: 8)
  for i in 0..<8 {
      let currentBit = byte & 0x01
      if currentBit != 0 {
          bits[i] = true
      }
      byte >>= 1
  }
  return bits
}

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.