Elegant transform low int values to short bit representation

Hello,
what is an elegant way to transform low int values into a simple bit representation to store on disc and load it back. At best without external libraries.

I have exact 8 times pos integer values with max value of 31. This 8 times are so 2^5 different values. I looking for an elegant way to store it in 6 bytes (aka UInt8) and take the other 4 bits to store/load similar data with max 15.

For nibbles (right and left as UInt8) I use simple functions like
let storage : UInt8 = (left << 4) + right
let right : UInt8 = (storage << 4) >> 4
let left : UInt8 = (container >> 4)

Is it elegant to take an [UInt8] to store my 8 values or is it better to use UInt64 and what is a very fast way to store and read this values?

Thanks
Sebastian

UInt64 + the ops similar to what you have above would be the fastest. Nowhere near as elegant as C-s bit fields (which Swift can handle), but those were never truly portable nor appropriate for serialising to / from disk / network.

2 Likes

You can do something along the lines of:

struct BitField<T: FixedWidthInteger> {
    var value: T
    subscript<Other: FixedWidthInteger>(range: Range<Int>) -> Other {
        get {
            let width = range.upperBound - range.lowerBound
            let mask: T = 1 << width &- 1
            return Other(truncatingIfNeeded: value >> range.lowerBound & mask)
        }
        set {
            let width = range.upperBound - range.lowerBound
            let mask: T = 1 << width &- 1
            value &= ~(mask << range.lowerBound)
            value |= (T(truncatingIfNeeded: newValue) & mask) << range.lowerBound
        }
    }
}

extension FixedWidthInteger {
    var bitField: BitField<Self> {
        get { BitField(value: self) }
        set { self = newValue.value }
    }
}


func pack(fiveBits: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8),
          nibbles: (UInt8, UInt8, UInt8, UInt8)) -> UInt64 {
    var packed: UInt64 = 0
    packed.bitField[ 0 ..<  5] = fiveBits.0
    packed.bitField[ 5 ..< 10] = fiveBits.1
    packed.bitField[10 ..< 15] = fiveBits.2
    packed.bitField[15 ..< 20] = fiveBits.3
    packed.bitField[20 ..< 25] = fiveBits.4
    packed.bitField[25 ..< 30] = fiveBits.5
    packed.bitField[30 ..< 35] = fiveBits.6
    packed.bitField[35 ..< 40] = fiveBits.7
    packed.bitField[48 ..< 52] = nibbles.0
    packed.bitField[52 ..< 56] = nibbles.1
    packed.bitField[56 ..< 60] = nibbles.2
    packed.bitField[60 ..< 64] = nibbles.3
    return packed
}

(I'm sketching this in a browser window, so apologies for any errors. I also didn't include any checking, which you would probably want to add.)

3 Likes