enHuffman ハフマン符号 deHuffman ハフマン符号からの復号
ハフマン符号
long enHuffman(unsigned char *code, long clen,
unsigned char *text, long tlen);
ハフマン符号からの復号
long deHuffman(unsigned char *text, long tlen,
unsigned char *code, long clen);
ハフマン符号関数において
code (入出力)ハフマン符号を格納する配列
clen (入力) 配列codeの長さ
text (入力) テキストを格納する配列
tlen (入力) 配列textの長さ
ハフマン符号からの復号関数において
text (入出力)テキストを格納する配列
tlen (入力) 配列textの長さ
code (入力) ハフマン符号を格納する配列
clen (入力) 配列codeの長さ
ハフマン符号関数について
ハフマン符号の長さ。符号化に失敗したときは -1L。
ハフマン符号からの復号関数について
テキストの長さ。復号に失敗したときは -1L。
typedef struct {
long count; /* 子ノードの出現頻度の和 */
int parent; /* 親ノードへ */
int left; /* 左側の子ノードへ */
int right; /* 右側の子ノードへ */
} NODE;
#define CHAR_SIZE 256
#define NODE_SIZE (2*CHAR_SIZE+2)
long enHuffman(unsigned char *code, long clen,
unsigned char *text, long tlen)
{
int i, k;
long c, len;
int min1, min2;
long max;
int root;
NODE node[NODE_SIZE]; int freeNode;
char bit[NODE_SIZE]; char *bitp;
static unsigned char bit0[8] = {~128,~64,~32,~16,~8,~4,~2,~1};
static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1};
if (tlen <= 0 || clen <= 2*CHAR_SIZE) return -1L;
/* 各文字の出現頻度を計算 */
for (i = 0; i < NODE_SIZE; i++) node[i].count = 0;
for (c = 0; c < tlen; c++) node[text[c]].count++;
/* 最大出現頻度の値が2バイトを越えないように調整する */
for (i = 0, max = 0; i < CHAR_SIZE; i++)
if (node[i].count > max) max = node[i].count;
if ((k = max/0x10000L) > 0)
for (i = 0, k++; i < CHAR_SIZE; i++)
if (node[i].count > 0) {
node[i].count /= k;
if (node[i].count == 0) node[i].count = 1;
}
/* 各文字の出現頻度を出力 */
for (i = 0; i < CHAR_SIZE; i++) {
*code++ = (node[i].count >> 8) & 0xff;
*code++ = node[i].count & 0xff;
}
/* EOF */
node[CHAR_SIZE].count = 1;
/* ハフマン木をつくる */
node[NODE_SIZE - 1].count = 0x10000L; /* 番兵 */
for (freeNode = CHAR_SIZE+1; ; freeNode++) {
min1 = min2 = NODE_SIZE - 1;
for (i = NODE_SIZE - 2; i >= 0; i--)
if (node[i].count > 0) {
if (node[i].count < node[min1].count) {
min2 = min1;
min1 = i;
} else if (node[i].count < node[min2].count)
min2 = i;
}
if (min2 == NODE_SIZE - 1) break;
node[freeNode].left = min1;
node[freeNode].right = min2;
node[freeNode].count = node[min1].count + node[min2].count;
node[min1].parent = node[min2].parent = freeNode;
node[min1].count = node[min2].count = 0;
}
root = min1;
/* 各文字を符号化 */
for (c = 0, i = 0, len = 2*CHAR_SIZE; ; c++) {
k = (c == tlen) ? CHAR_SIZE : text[c];
/* 符号語を得る */
bitp = bit;
do {
int parent = node[k].parent;
*bitp++ = (node[parent].left == k) ? 0 : 1;
k = parent;
} while (k != root);
/* 逆順に出力 */
do {
if (*--bitp) *code |= bit1[i];
else *code &= bit0[i];
if (++i == 8) {
code++;
if (++len > clen) return -1L;
i = 0;
}
} while (bitp != bit);
if (c == tlen) break;
}
if (i > 0) len++;
return (len > clen) ? -1L : len;
}
long deHuffman(unsigned char *text, long tlen,
unsigned char *code, long clen)
{
int i, k;
long c, len;
int min1, min2;
int root;
NODE node[NODE_SIZE]; int freeNode;
static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1};
if (clen <= 2*CHAR_SIZE) return -1L;
/* 各文字の出現頻度をセット */
for (i = 0; i < NODE_SIZE; i++) node[i].count = 0;
for (i = 0; i < CHAR_SIZE; i++) {
node[i].count = *code++;
node[i].count = (node[i].count << 8) | *code++;
}
/* EOF */
node[CHAR_SIZE].count = 1;
/* ハフマン木をつくる */
node[NODE_SIZE - 1].count = 0x10000L; /* 番兵 */
for (freeNode = CHAR_SIZE+1; ; freeNode++) {
min1 = min2 = NODE_SIZE - 1;
for (i = NODE_SIZE - 2; i >= 0; i--)
if (node[i].count > 0)
if (node[i].count < node[min1].count) {
min2 = min1;
min1 = i;
} else if (node[i].count < node[min2].count)
min2 = i;
if (min2 == NODE_SIZE - 1) break;
node[freeNode].left = min1;
node[freeNode].right = min2;
node[freeNode].count = node[min1].count + node[min2].count;
node[min1].parent = node[min2].parent = freeNode;
node[min1].count = node[min2].count = 0;
}
root = min1;
/* 復号化 */
for (i = 0, len = 0, c = 2*CHAR_SIZE; ; ) {
/* 符号語に対応する文字ノードを得る */
k = root;
do {
int parent = k;
k = (*code & bit1[i]) ? node[k].right : node[k].left;
if (k >= parent) return -1L; /* あり得ない */
if (++i == 8) {
code++;
if (++c > clen) return -1L;
i = 0;
}
} while (k > CHAR_SIZE);
if (k == CHAR_SIZE) break;
if (++len > tlen) return -1L;
*text++ = k;
}
return len;
}
ハフマン符号を以下のように構成できる。
実際の符号化プログラムでは、テキストを2回走査する。1回目では各 記号(文字)の出現頻度を調べた上で、ハフマン木をつくる。また復号 できるように、各記号の出現頻度を符号の一部として出力する。2回目の 走査では、ハフマン木をはどりながら各文字を符号化する。
出力符号のフォーマットは、先頭に1文字2バイトずつの出現頻度、計 512バイトのヘッダ部と、その後に続く符号語列からなる。
なお、符号語列の終了を表すために、出現頻度が1のEOF記号(コード256) に対応する符号語を書き込む。