enLZ77 LZ77符号 deLZ77 LZ77符号からの復号
LZ77符号 long enLZ77(unsigned char *code, long clen, unsigned char *text, long tlen); LZ77符号からの復号 long deLZ77(unsigned char *text, long tlen, unsigned char *code, long clen);
LZ77符号関数において code (入出力)LZ77符号を格納する配列 clen (入力) 配列codeの長さ text (入力) テキストを格納する配列 tlen (入力) 配列textの長さ LZ77符号からの復号関数において text (入出力)テキストを格納する配列 tlen (入力) 配列textの長さ code (入力) LZ77符号を格納する配列 clen (入力) 配列codeの長さ
LZ77符号関数について LZ77符号の長さ。符号化に失敗したときは -1L。 LZ77符号からの復号関数について テキストの長さ。復号に失敗したときは -1L。
#define F 128 /* 最長一致文字列の長さの上限 */ #define LOG2F 7 /* 2^7 = 128 */ #define N0 4096 #define LOG2N 12 /* 2^12 = 4096 */ #define N (N0+F) /* スライド窓の大きさ */ long enLZ77(unsigned char *code, long clen, unsigned char *text, long tlen) { int i, j; int w, l, x; int ww, ll; long len; unsigned char *wintop, *winbrk; unsigned char *texttop, *textend; unsigned char prewin[N0]; static int bit0[12] = {~1,~2,~4,~8,~16,~32,~64,~128,~256,~512,~1024,~2048}; static int bit1[12] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}; for (i = 0; i < N0; i++) prewin[i] = 0; wintop = prewin; winbrk = wintop + N0; texttop = text; textend = text + tlen; for (i = 7, len = 0; ; ) { unsigned char *wp; if (texttop >= textend) break; /* 最長一致文字列を見つける */ w = l = 0; for (wp = wintop, ww = 0; ww < N0; ww++) { if (wp == winbrk) wp = text; if (*wp++ == *texttop) { unsigned char *p, *q; for (p = wp, q = texttop+1, ll = 1; ll < F; ll++) { if (p == winbrk) p = text; if (q == textend || *p++ != *q++) { if (ll >= l) { w = ww; l = ll; } break; } } } } x = *(texttop + l); /* 最長一致文字列の開始位置を出力 */ for (j = LOG2N-1; j >= 0; j--) { if (w & bit1[j]) *code |= bit1[i]; else *code &= bit0[i]; if (i-- == 0) { code++; if (++len > clen) return -1L; i = 7; } } /* 最長一致文字列の長さを出力 */ for (j = LOG2F-1; j >= 0; j--) { if (l & bit1[j]) *code |= bit1[i]; else *code &= bit0[i]; if (i-- == 0) { code++; if (++len > clen) return -1L; i = 7; } } /* 最長一致文字列のつぎの文字を出力 */ for (j = 7; j >= 0; j--) { if (x & bit1[j]) *code |= bit1[i]; else *code &= bit0[i]; if (i-- == 0) { code++; if (++len > clen) return -1L; i = 7; } } /* スライド窓を後ろにずらす */ texttop += ++l; if (wintop + l >= winbrk) wintop = text + (l - (winbrk-wintop)); else wintop += l; } if (i < 7) len++; return (len > clen) ? -1L : len; } long deLZ77(unsigned char *text, long tlen, unsigned char *code, long clen) { int i, j; int w, l, x; unsigned char *wintop, *winbrk, *texttop; unsigned char prewin[N0]; long c, len; static int bit1[12] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}; if (clen <= 0) return -1; for (i = 0; i < N0; i++) prewin[i] = 0; wintop = prewin; winbrk = wintop + N0; texttop = text; for (i = 7, len = 0, c = 1; ; ) { unsigned char *wp; w = l = x = 0; /* 最長一致文字列の開始位置 */ for (j = LOG2N-1; j >= 0; j--) { if (*code & bit1[i]) w |= bit1[j]; if (i-- == 0) { code++; if (++c > clen) return -1L; i = 7; } } /* 最長一致文字列の長さ */ for (j = LOG2F-1; j >= 0; j--) { if (*code & bit1[i]) l |= bit1[j]; if (i-- == 0) { code++; if (++c > clen) return -1L; i = 7; } } /* 最長一致文字列のつぎの文字 */ for (j = 7; j >= 0; j--) { if (*code & bit1[i]) x |= bit1[j]; if (i-- == 0) { code++; ++c; i = 7; } } /* 文字列を復号する */ if ((wp = wintop + w) >= winbrk) wp = texttop + (w - (winbrk-wintop)); for (j = l; j > 0; j--) { if (wp == winbrk) wp = texttop; *text++ = *wp++; } len += l++; if (c >= clen) break; /* スライド窓を後ろにずらす */ *text++ = x; if (wintop + l >= winbrk) wintop = texttop + (l - (winbrk-wintop)); else wintop += l; len++; } return len; }
LZ77符号では、現在符号を行っている文字を含めた文字列をスライド窓と呼び、 その中にある文字列が参照の対象になる。また、符号語は、m、l、x からなる 2進数で表す。
m: スライド窓にある最長一致文字列の位置
l: スライド窓にある最長一致文字列の長さ
x: 最長一致文字列のつぎの文字
文字列がスライド窓に存在しなかった場合は m = 0、l = 0 とする。また、ス ライド窓が符号語を出力するたびに、l+1文字分つぎの文字列にずらす。
このように、最長一致文字列を過去のテキストから見つけ(辞書の参照とも いう)、その開始位置と長さを符号語として使うのが、LZ77 符号の特徴である。 開始位置と長さに関する表現の仕方や最長一致文字列の探索の方法によって、 いくつかのバリエーションが存在している。