Sunday, May 10, 2009

Bit packing preprocessor

When working at lower-levels, sometimes it pays to conserve bits. Binary formats sometimes use all of the bits they are given - when I was working with MIDI, I came across this a lot. (If you have a full int, you might as well use all 32 of those guys.)

On a more practical note, 16bit color is often 5bits red, 6bits green, and 5bits blue. A color is stored as a 16 bit integer in the format rrrrrggggggbbbbb, where each letter represents a bit. It isn't difficult to write code to extract these bit fields, but to make it faster and more readable, I wrote some Python to write the C for me. The Python script takes a string like "00rrr000" and outputs C code with the appropriate shifts and masks.

Bit tools. Example usage:
> necessarybits(640)
10 bits are required to store values up to 640
2 ** 10 = 1024

> print tobinary(46)
00101110

> print frombinary('1100_1100') 
204

> pattern('00rrr000', True)
  r is a 3bit number
  Packing:
  assert(r<8);
  unsigned char packed |= r<<3;
  Unpacking:
  unsigned char r = (packed & 0x3f)>>3; //packed & 0b00111111

> pattern('00000bbb')
  b is a 3bit number
  Packing:
  unsigned char packed |= b;
  Unpacking:
  unsigned char b = packed & 0x7; //packed & 0b00000111


I find "00rrr000" to be a lot clearer than '(packed & 0x3f)>>3' .

Python source
(34 lines of quick/unpretty code)

1 comment:

ben.fisher said...

In Python 2.7 and 3.1, numbers have
a .bit_length() method.

>>> n = 37
>>> bin(n)
'0b100101'
>>> n.bit_length()
6