LZ77符号


関数名
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。
注意事項

用例(lz77-test.c

プログラム(lz77.c
#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符号は J. Ziv と A. Lempel が1977年考案された、新しい発想に 基づく符号である。符号しようとする文字列が前のテキストに存在していた ならば、前にある同じ文字列の位置と長さを符号語として使う。同じ文字列 がテキストに繰り返し現れた場合に、大きな圧縮効果が得られる。

LZ77符号では、現在符号を行っている文字を含めた文字列をスライド窓と呼び、 その中にある文字列が参照の対象になる。また、符号語は、m、l、x からなる 2進数で表す。

m: スライド窓にある最長一致文字列の位置
l: スライド窓にある最長一致文字列の長さ
x: 最長一致文字列のつぎの文字

文字列がスライド窓に存在しなかった場合は m = 0、l = 0 とする。また、ス ライド窓が符号語を出力するたびに、l+1文字分つぎの文字列にずらす。

このように、最長一致文字列を過去のテキストから見つけ(辞書の参照とも いう)、その開始位置と長さを符号語として使うのが、LZ77 符号の特徴である。 開始位置と長さに関する表現の仕方や最長一致文字列の探索の方法によって、 いくつかのバリエーションが存在している。

関連関数
シャノン・ファノ符号ハフマン符号算術符号FGK符号LZW符号