Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work with bit operations in Swift?

I need to form a 20 byte data packet and send the payload to a hardware peripheral over bluetooth.

This 20 byte data packet is divided into 14 data sets internally, each of 11 bits, the last 6 bits should be null character.

Hence, Total: 160 bits(20 bytes) = 14 (sets) * 11 (bits) + 6 (null characters)

The 11 bit are again divided into 3 sets of 2 bit, 3 bit and 6 bit each. However it's not important to the main question, I am currently able to form the 11 bits, by taking a 'Int16'. I will do the shift operation for filtering the 11 bits, I know that.

If I have only one data set then I should fill all the 20 bytes except first 11 bits with null character, if two data set then all except the 22 bits should be null character and respectively.

The issue I am facing is to form this sequential 160 bits because of the odd bits which is 11. I thought of taking an 'Int' and do shift(<<) operation and then do bitwise OR(|) but an Int is 64 bits.

Currently I think of taking a fixed size character array of 20 would fit this situation. Though conceptually I think it's the best possible way, programmatically I'm not able to form the logic with all the conditions to achieve this. I think I need to put all the logic in a loop.

Can anyone tell is it the right way to achieve this, and guide me on solving it, if this is the best way. Or point out any other way if available.

like image 517
Rameswar Prasad Avatar asked Oct 18 '22 14:10

Rameswar Prasad


2 Answers

You don't need to pack the all the datasets into the 20-byte array until the very end so keep them in a an Int array of length 14. Easier to work with that way. When you need to send it over to the hardware, convert it to a UInt8 array of length 20:

struct DataPacket {
    var dataSets = [Int](count: 14, repeatedValue: 0)

    func toCArray() -> [UInt8] {
        var result = [UInt8](count: 20, repeatedValue: 0)
        var index = 0
        var bitsRemaining = 8
        var offset = 0

        for value in self.dataSets {
            offset = 10

            while offset >= 0 {
                let mask = 1 << offset
                let bit = ((value & mask) >> offset) << (bitsRemaining - 1)
                result[index] |= UInt8(bit)

                offset -= 1
                bitsRemaining -= 1
                if bitsRemaining == 0 {
                    index += 1
                    bitsRemaining = 8
                }
            }
        }

        return result
    }
}

// Usage:
var packet = DataPacket()
packet.dataSets[0] = 0b11111111111
packet.dataSets[1] = 0b00000000011
// etc...

let arr = packet.toCArray()

There is a lot of shifting operations going on so I can't explain them all. The general ideal is allocate each of those 11-bit dataset into bytes, spilling over to the next byte as necessary.

like image 179
Code Different Avatar answered Oct 21 '22 05:10

Code Different


A variant of the solution proposed by Code Different:

struct DataPacket {
    var dataSets = [Int16](count: 14, repeatedValue: 0)

    func getPacket() -> [UInt8] {
        var packet = [UInt8](count: 20, repeatedValue: 0)
        var idxPacket = 0

        for dataSet in dataSets {
            for idxBit in 1...11 {
                if dataSet & 1 << (11 - idxBit) != 0 {
                    packet[idxPacket / 8] |= UInt8(0b1000_0000 >> (idxPacket % 8))
                }
                idxPacket += 1
            }
        }

        return packet
    }
}
like image 45
Kid Limonade Avatar answered Oct 21 '22 06:10

Kid Limonade