Skip to content

Instantly share code, notes, and snippets.

@Alan-FGR
Last active July 8, 2023 04:16
Show Gist options
  • Save Alan-FGR/04938e93e2bffdf5802ceb218a37c195 to your computer and use it in GitHub Desktop.
Save Alan-FGR/04938e93e2bffdf5802ceb218a37c195 to your computer and use it in GitHub Desktop.
Packing Bits in C#
using System;
class PackingBits
{
static void Main(string[] args)
{
// say you have this data:
byte byteA = 7;
byte byteB = 5;
ushort someNumber = 1500;
// you pack that into an uint like this:
byte[] packingFormat = {8, 8, 16};
ushort[] dataToPack = { byteA, byteB, someNumber };
uint packedData = PackBitFields(dataToPack, packingFormat);
// print the value
Console.Write("packed: ");
foreach (ushort v in dataToPack) { Console.Write(v + ", "); }
Console.WriteLine("into: "+packedData);
//now you unpack the data back
ushort[] unpackedData = GetBitFields(packedData, packingFormat);
// print for confirmation (you may want to cast back)
Console.Write("unpacked: ");
foreach (ushort v in unpackedData) {Console.Write(v + ", ");}
Console.WriteLine();
//#####################################
//another example
byte[] format2 = new byte[] {4,4,4,4,4,4,4,4};
uint packedData2 = PackBitFields(new ushort[]{1,2,3,4,11,12,13,14}, format2);
ushort[] unpackedData2 = GetBitFields(packedData2, format2);
// print for confirmation - WHOAH! 8 numbers in a single UINT! (32-bit) :)
Console.Write("unpacked: ");
foreach (ushort v in unpackedData2) { Console.Write(v + ", "); }
Console.WriteLine();
//#####################################
// here's a more useful example:
uint packedColor = PackBitFields(new ushort[] { 31, 0, 0, 0, 63, 0 }, RGBRGB565565);
var unpackedColor = GetBitFields(packedColor, RGBRGB565565);
// RED AND GREEN in a uint
Console.Write("unpacked: ");
foreach (ushort v in unpackedColor) { Console.Write(v + ", "); }
Console.WriteLine();
}
const int MaxBits = 32; // you may want to pass this and use generics to allow more or less bits
static readonly byte[] RGBRGB565565 = { 5,6,5,5,6,5 }; // make some common formats like this
public static uint PackBitFields(ushort[] values, byte[] bitFields)
{
uint retVal = values[0]; //we set the first value right away
for (int f = 1; f < values.Length; f++)
{
retVal <<= bitFields[f]; //we shift the previous value
retVal += values[f]; //and add our current value //on some processors | (pipe) will be faster here
}
return retVal;
}
public static ushort[] GetBitFields(uint packedBits, byte[] bitFields)
{
int fields = bitFields.Length-1; // number of fields to unpack
ushort[] retArr = new ushort[fields+1]; // init return array
int curPos = 0; // current field bit position (start)
int lastEnd; // position where last field ended
for (int f = fields; f >= 0; f--) // loop from last
{
lastEnd = curPos; // we store where the last value ended
curPos += bitFields[f]; // we get where the current value starts
int leftShift = MaxBits - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
retArr[f] = (ushort)((packedBits << leftShift) >> leftShift + lastEnd); // we do magic
}
return retArr;
}
}
@Alan-FGR
Copy link
Author

Read usage and proper applications here: http://alangamedev.com/mind-your-bits/
If you came here through Google, this is most likely premature optimization, i.e.: the root of all evil.

@Alan-FGR
Copy link
Author

For some reason, on line 61 addition is faster on a Haswell Pentium G, while 'logic OR' (pipe) is faster on a Haswell i7... the mysteries of modern computing... in any case the difference between + and | is below 1ns per million operations, relative to the total cost of a million of PackBitFields which is about 10ms that's negligible.

Compared to the BitConverter though, a throughput of 100 million ops/second isn't very impressive, although we are using the bits in a much more efficient way and retrieving more variables. I'm going to do a fair comparison someday...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment