Introduction
A pain to write, and my Google-fu was not strong enough to find someone who'd done it for me.
The class allows you to encode / decode 4, 9 or 17 bit (for example) integers to and from a byte[]
. I needed it to store some binary keys in a database that are made up of multiple keys, in my case the difference between a byte[]
storing 9 bit integers and one storing 16/32 bit integers would be very costly. If you are doing something similar in MSSQL, watch out for 0 byte right truncation (reserve the value of 0 perhaps).
This version of the code supports 1 though 31 bit unsigned integers, it could easily be extended beyond this if needed.
Using the Code
Use freely, keep attributions, no warranty as to usefulness or safety/reliability of the code of course.
using System;
namespace MhanoHarkness
{
public class BitIntConverter
{
public static int[] GetIntsFromBits(byte[] data, int bitsPerInt)
{
if (bitsPerInt < 1 || bitsPerInt > 31)
{
throw new ArgumentOutOfRangeException("bitsPerInt",
"1..31 bit unsigned integers supported only.");
}
var result = new int[(data.Length * 8) / bitsPerInt];
for (var i = 0; i < result.Length; i++)
{
var startBit = i * bitsPerInt;
var startByte = (startBit / 8);
var endBit = (startBit + bitsPerInt);
var endByte = (endBit-1) / 8;
var byteRange = endByte - startByte;
for (var b = 0; b <= byteRange; b++)
{
var bi = startByte + b;
Int64 byteAsI64 = data[bi];
var amountToShift = (b*8) - (startBit%8);
var shiftedForMerge = amountToShift < 0 ?
byteAsI64 >> (0 - amountToShift) :
byteAsI64 << amountToShift;
result[i] |= (int)shiftedForMerge;
}
result[i] &= (1 << bitsPerInt) - 1;
}
return result;
}
public static byte[] GetBytesFromInts(int[] ints, int bitsPerInt)
{
if (bitsPerInt < 1 || bitsPerInt > 31)
{
throw new ArgumentOutOfRangeException("bitsPerInt",
"1..31 bit unsigned integers supported only.");
}
foreach (var i in ints)
{
if (i < 0 || i > (Math.Pow(2, bitsPerInt) - 1))
{
throw new ArgumentOutOfRangeException("ints",
"Integer is <0 or >((2^bitsPerInt)-1)");
}
}
var totalBits = (bitsPerInt * ints.Length);
var result = new byte[(totalBits / 8) + (totalBits % 8 == 0 ? 0 : 1)];
for (var i = 0; i < ints.Length; i++)
{
var startBit = i * bitsPerInt;
var startByte = (startBit/8);
var endBit = (startBit + bitsPerInt);
var endByte = endBit / 8 + (endBit % 8 ==0 ? 0 : 1);
var byteRange = endByte - startByte;
for (var b = 0; b<byteRange; b++)
{
var bi = startByte + b;
Int64 asi64 = ints[i];
var shiftedForMerge = b == 0 ?
(asi64 << (startBit % 8)) :
(asi64 >> ((b * 8) - (startBit % 8)));
result[bi] |= (byte)shiftedForMerge;
}
}
return result;
}
}
}