C言語で1バイト単位のデータから1ビット単位で値を取得する

MPEG-2 TSファイル等だと上位ビットから、SWFファイルやzlib等だと下位ビットから何ビットずつ値を取得ってのが面倒なんで、C言語でそういった値の取得が出来る関数を組んでみた。
引数1に1バイト単位のデータが入った符号なし文字型配列を、引数2に取得ビット位置を、引数3に取得ビット数を指定して呼び出す。以後のロジックは以下の通り。

  1. 取得ビット位置を8ビットで割った値と余りからバイト位置とビット位置を求め、8からビット位置を引いた残りビット数を求める。
  2. ビット位置からマスク値を生成して、バイト位置の値をマスクして残りビット値を求める。
  3. 残りビット数が取得ビット数より小さければ、次のバイト位置の値を残りビット値と合成し、残りビット数に8を加える処理を繰り返す。
  4. 取得ビット数からマスク値を生成して、残りビット値をマスクして最終的な値を求める。

ついでに2進化10進数(BCD)みたいに見た目には2進数に見える8進数(仮に8進化2進数と呼ぶ)の相互変換を行う関数も符号なし64ビットに対応させたので32桁の2進数(32ビット)まで扱えるようになった。

#include <stdio.h>

/* unsigned long long to octal-coded binary */
unsigned long long ltob(unsigned long long a) {
	unsigned long long b,c;

	b=1;
	c=0;
	while(a>0) {
		c=(a%2)*b+c;
		b=b*8;
		a=a/2;
	}
	return c;
}
/* octal-coded binary to unsigned long long */
unsigned long long btol(unsigned long long a) {
	unsigned long long b,c;

	b=1;
	c=0;
	while(a>0) {
		c=(a%8)*b+c;
		b=b*2;
		a=a/8;
	}
	return c;
}
/* get binary from right */
unsigned long long getbr(unsigned char *a,int b,int c) {
	int i,j,k;
	unsigned long long m,n;

	i=b/8;
	j=b%8;
	k=8-j;
	m=((unsigned long long)0x100>>j)-1;
	n=a[i]&m;
	i++;
	while(k<c) {
		n=n<<8|a[i];
		i++;
		k=k+8;
	}
	m=(((unsigned long long)1<<c)-1)<<(k-c);
	n=(n&m)>>(k-c);
	return n;
}
/* get binary from left */
unsigned long long getbl(unsigned char *a,int b,int c) {
	int i,j,k;
	unsigned long long m,n;

	i=b/8;
	j=b%8;
	k=8-j;
	m=~(((unsigned long long)1<<j)-1);
	n=(a[i]&m)>>j;
	i++;
	while(k<c) {
		n=((unsigned long long)a[i]<<k)|n;
		i++;
		k=k+8;
	}
	m=((unsigned long long)1<<c)-1;
	n=n&m;
	return n;
}

int main(int argc,char *argv[]) {
	unsigned char buf[4];

	buf[0]=btol(001101110);
	buf[1]=btol(010011001);

	printf("byte 0 = %08o\n",ltob(buf[0]));
	printf("byte 1 = %08o\n",ltob(buf[1]));

	printf("bit 0 from right 3 bits = %03o\n",ltob(getbr(buf,0,3)));
	printf("bit 3 from right 3 bits = %03o\n",ltob(getbr(buf,3,3)));
	printf("bit 6 from right 3 bits = %03o\n",ltob(getbr(buf,6,3)));
	printf("bit 9 from right 3 bits = %03o\n",ltob(getbr(buf,9,3)));

	printf("bit 0 from left 3 bits = %03o\n",ltob(getbl(buf,0,3)));
	printf("bit 3 from left 3 bits = %03o\n",ltob(getbl(buf,3,3)));
	printf("bit 6 from left 3 bits = %03o\n",ltob(getbl(buf,6,3)));
	printf("bit 9 from left 3 bits = %03o\n",ltob(getbl(buf,9,3)));
}

上記を実行すると以下のように結果が表示される。

byte 0 = 01101110
byte 1 = 10011001
bit 0 from right 3 bits = 011
bit 3 from right 3 bits = 011
bit 6 from right 3 bits = 101
bit 9 from right 3 bits = 001
bit 0 from left 3 bits = 110
bit 3 from left 3 bits = 101
bit 6 from left 3 bits = 101
bit 9 from left 3 bits = 100