Reputation: 173
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.
Upvotes: 9
Views: 11625
Reputation: 3
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
}
Upvotes: 0
Reputation: 881
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)
Upvotes: 0
Reputation: 161
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
}
}
Upvotes: 3
Reputation: 12053
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)
Upvotes: 0
Reputation: 623
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]
Upvotes: 19