關於LZ77壓縮算法

感受這篇寫的還算完整,貼出來分享給你們。關於該算法的資料來源與網絡,版權歸原做者全部,若是侵權,請及時告知。之因此這樣說,是筆者據說在LZ系列算法中還有一部分壓縮算法有專利,另外一方面也是爲了尊總知識產權。c++

    如下內容來自互聯網:
============================================================================

全新的思路
算法

咱們在第三和第四章中討論的壓縮模型都是基於對信息中單個字符出現頻率的統計而設計的,直到 70 年代末期,這種思路在數據壓縮領域一直佔據着統治地位。在咱們今天看來,這種情形在某種程度上顯得有些好笑,但事情就是這樣,一旦某項技術在某一領域造成了慣例,人們就很難創造出在思路上與其截然不同的哪怕是更簡單更實用的技術來。windows

咱們敬佩那兩個在數據壓縮領域作出了傑出貢獻的以色列人,由於正是他們打破了 Huffman 編碼一統天下的格局,帶給了咱們既高效又簡便的「字典模型」。至今,幾乎咱們平常使用的全部通用壓縮工具,象 ARJ,PKZip,WinZip,LHArc,RAR,GZip,ACE,ZOO,TurboZip,Compress,JAR……甚至許多硬件如網絡設備中內置的壓縮算法,無一例外,均可以最終歸結爲這兩個以色列人的傑出貢獻。數組

提及來,字典模型的思路至關簡單,咱們平常生活中就常常在使用這種壓縮思想。咱們經常跟人說「奧運會」、「IBM」、「TCP」之類的詞彙,說者和聽者都明白它們指的是「奧林匹克運動會」、「國際商業機器公司」和「傳輸控制協議」,這實際就是信息的壓縮。咱們之因此能夠順利使用這種壓縮方式而不產生語義上的誤解,是由於在說者和聽者的心中都有一個事先定義好的縮略語字典,咱們在對信息進行壓縮(說)和解壓縮(聽)的過程當中都對字典進行了查詢操做。字典壓縮模型正是基於這一思路設計實現的。網絡

最簡單的狀況是,咱們擁有一本預先定義好的字典。例如,咱們要對一篇中文文章進行壓縮,咱們手中已經有一本《現代漢語詞典》。那麼,咱們掃描要壓縮的文章,並對其中的句子進行分詞操做,對每個獨立的詞語,咱們在《現代漢語詞典》查找它的出現位置,若是找到,咱們就輸出頁碼和該詞在該頁中的序號,若是沒有找到,咱們就輸出一個新詞。這就是靜態字典模型的基本算法了。數據結構

你必定能夠發現,靜態字典模型並非好的選擇。首先,靜態模型的適應性不強,咱們必須爲每類不一樣的信息創建不一樣的字典;其次,對靜態模型,咱們必須維護信息量並不算小的字典,這一額外的信息量影響了最終的壓縮效果。因此,幾乎全部通用的字典模型都使用了自適應的方式,也就是說,將已經編碼過的信息做爲字典,若是要編碼的字符串曾經出現過,就輸出該字符串的出現位置及長度,不然輸出新的字符串。根據這一思路,你能從下面這幅圖中讀出其中包含的原始信息嗎?
ide

啊,對了,是「吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮」。如今你該大體明白自適應字典模型的梗概了吧。好了,下面就讓咱們來深刻學習字典模型的第一類實現——LZ77 算法。函數

滑動的窗口工具

LZ77 算法在某種意義上又能夠稱爲「滑動窗口壓縮」,這是因爲該算法將一個虛擬的,能夠跟隨壓縮進程滑動的窗口做爲術語字典,要壓縮的字符串若是在該窗口中出現,則輸出其出現位置和長度。使用固定大小窗口進行術語匹配,而不是在全部已經編碼的信息中匹配,是由於匹配算法的時間消耗每每不少,必須限制字典的大小才能保證算法的效率;隨着壓縮的進程滑動字典窗口,使其中總包含最近編碼過的信息,是由於對大多數信息而言,要編碼的字符串每每在最近的上下文中更容易找到匹配串。性能

參照下圖,讓咱們熟悉一下 LZ77 算法的基本流程。

一、從當前壓縮位置開始,考察未編碼的數據,並試圖在滑動窗口中找出最長的匹配字符串,若是找到,則進行步驟 2,不然進行步驟 3。

二、輸出三元符號組 ( off, len, c )。其中 off 爲窗口中匹配字符串相對窗口邊界的偏移,len 爲可匹配的長度,c 爲下一個字符。而後將窗口向後滑動 len + 1 個字符,繼續步驟 1。

三、輸出三元符號組 ( 0, 0, c )。其中 c 爲下一個字符。而後將窗口向後滑動 len + 1 個字符,繼續步驟 1。

咱們結合實例來講明。假設窗口的大小爲 10 個字符,咱們剛編碼過的 10 個字符是:abcdbbccaa,即將編碼的字符爲:abaeaaabaee

咱們首先發現,能夠和要編碼字符匹配的最長串爲 ab ( off = 0, len = 2 ), ab 的下一個字符爲 a,咱們輸出三元組:( 0, 2, a )

如今窗口向後滑動 3 個字符,窗口中的內容爲:dbbccaaaba

下一個字符 e 在窗口中沒有匹配,咱們輸出三元組:( 0, 0, e )

窗口向後滑動 1 個字符,其中內容變爲:bbccaaabae

咱們立刻發現,要編碼的 aaabae 在窗口中存在( off = 4, len = 6 ),其後的字符爲 e,咱們能夠輸出:( 4, 6, e )

這樣,咱們將能夠匹配的字符串都變成了指向窗口內的指針,並由此完成了對上述數據的壓縮。

解壓縮的過程十分簡單,只要咱們向壓縮時那樣維護好滑動的窗口,隨着三元組的不斷輸入,咱們在窗口中找到相應的匹配串,綴上後繼字符 c 輸出(若是 off 和 len 都爲 0 則只輸出後繼字符 c )便可還原出原始數據。

固然,真正實現 LZ77 算法時還有許多複雜的問題須要解決,下面咱們就來對可能碰到的問題逐一加以探討。

編碼方法

咱們必須精心設計三元組中每一個份量的表示方法,才能達到較好的壓縮效果。通常來說,編碼的設計要根據待編碼的數值的分佈狀況而定。對於三元組的第一個份量——窗口內的偏移,一般的經驗是,偏移接近窗口尾部的狀況要多於接近窗口頭部的狀況,這是由於字符串在與其接近的位置較容易找到匹配串,但對於普通的窗口大小(例如 4096 字節)來講,偏移值基本仍是均勻分佈的,咱們徹底能夠用固定的位數來表示它。

編碼 off 須要的位數 bitnum = upper_bound( log2( MAX_WND_SIZE ))

由此,若是窗口大小爲 4096,用 12 位就能夠對偏移編碼。若是窗口大小爲 2048,用 11 位就能夠了。複雜一點的程序考慮到在壓縮開始時,窗口大小並無達到 MAX_WND_SIZE,而是隨着壓縮的進行增加,所以能夠根據窗口的當前大小動態計算所須要的位數,這樣能夠略微節省一點空間。

對於第二個份量——字符串長度,咱們必須考慮到,它在大多數時候不會太大,少數狀況下才會發生大字符串的匹配。顯然可使用一種變長的編碼方式來表示該長度值。在前面咱們已經知道,要輸出變長的編碼,該編碼必須知足前綴編碼的條件。其實 Huffman 編碼也能夠在此處使用,但卻不是最好的選擇。適用於此處的好的編碼方案不少,我在這裏介紹其中兩種應用很是普遍的編碼。

第一種叫 Golomb 編碼。假設對正整數 x 進行 Golomb 編碼,選擇參數 m,令

b = 2m

q = INT((x - 1)/b)

r = x - qb - 1

則 x 能夠被編碼爲兩部分,第一部分是由 q 個 1 加 1 個 0 組成,第二部分爲 m 位二進制數,其值爲 r。咱們將 m = 0, 1, 2, 3 時的 Golomb 編碼表列出:

值 x        m = 0       m = 1       m = 2       m = 3
-------------------------------------------------------------
    1             0         0 0        0 00        0 000
    2            10         0 1        0 01        0 001
    3           110        10 0        0 10        0 010
    4          1110        10 1        0 11        0 011
    5         11110       110 0       10 00        0 100
    6        111110       110 1       10 01        0 101
    7       1111110      1110 0       10 10        0 110
    8      11111110      1110 1       10 11        0 111
    9     111111110     11110 0      110 00       10 000

從表中咱們能夠看出,Golomb 編碼不但符合前綴編碼的規律,並且能夠用較少的位表示較小的 x 值,而用較長的位表示較大的 x 值。這樣,若是 x 的取值傾向於比較小的數值時,Golomb 編碼就能夠有效地節省空間。固然,根據 x 的分佈規律不一樣,咱們能夠選取不一樣的 m 值以達到最好的壓縮效果。

對咱們上面討論的三元組 len 值,咱們能夠採用 Golomb 方式編碼。上面的討論中 len 可能取 0,咱們只需用 len + 1 的 Golomb 編碼便可。至於參數 m 的選擇,通常經驗是取 3 或 4 便可。

能夠考慮的另外一種變長前綴編碼叫作 γ 編碼。它也分做先後兩個部分,假設對 x 編碼,令 q = int( log2x ),則編碼的前一部分是 q 個 1 加一個 0,後一部分是 q 位長的二進制數,其值等於 x - 2q 。γ編碼表以下:

值 x    γ編碼
---------------------
    1       0
    2      10 0
    3      10 1
    4     110 00
    5     110 01
    6     110 10
    7     110 11
    8    1110 000
    9    1110 001

其實,若是對 off 值考慮其傾向於窗口後部的規律,咱們也能夠採用變長的編碼方法。但這種方式對窗口較小的狀況改善並不明顯,有時壓縮效果還不如固定長編碼。

對三元組的最後一個份量——字符 c,由於其分佈並沒有規律可循,咱們只能老老實實地用 8 個二進制位對其編碼。

根據上面的敘述,相信你必定也能寫出高效的編碼和解碼程序了。

另外一種輸出方式

LZ77 的原始算法採用三元組輸出每個匹配串及其後續字符,即便沒有匹配,咱們仍然須要輸出一個 len = 0 的三元組來表示單個字符。試驗代表,這種方式對於某些特殊狀況(例如同一字符不斷重複的情形)有着較好的適應能力。但對於通常數據,咱們還能夠設計出另一種更爲有效的輸出方式:將匹配串和不能匹配的單個字符分別編碼、分別輸出,輸出匹配串時不一樣時輸出後續字符。

咱們將每個輸出分紅匹配串和單個字符兩種類型,並首先輸出一個二進制位對其加以區分。例如,輸出 0 表示下面是一個匹配串,輸出 1 表示下面是一個單個字符。

以後,若是要輸出的是單個字符,咱們直接輸出該字符的字節值,這要用 8 個二進制位。也就是說,咱們輸出一個單個的字符共須要 9 個二進制位。

若是要輸出的是匹配串,咱們按照前面的方法依次輸出 off 和 len。對 off,咱們能夠輸出定長編碼,也能夠輸出變長前綴碼,對 len 咱們輸出變長前綴碼。有時候咱們能夠對匹配長度加以限制,例如,咱們能夠限制最少匹配 3 個字符。由於,對於 2 個字符的匹配串,咱們使用匹配串的方式輸出並不必定比咱們直接輸出 2 個單個字符(須要 18 位)節省空間(是否節省取決於咱們採用何種編碼輸出 off 和 len)。

這種輸出方式的優勢是輸出單個字符的時候比較節省空間。另外,由於不強求每次都外帶一個後續字符,能夠適應一些較長匹配的狀況。

如何查找匹配串

在滑動窗口中查找最長的匹配串,大概是 LZ77 算法中的核心問題。容易知道,LZ77 算法中空間和時間的消耗集中於對匹配串的查找算法。每次滑動窗口以後,都要進行下一個匹配串的查找,若是查找算法的時間效率在 O(n2) 或者更高,總的算法時間效率就將達到 O(n3),這是咱們沒法容忍的。正常的順序匹配算法顯然沒法知足咱們的要求。事實上,咱們有如下幾種可選的方案。

一、限制可匹配字符串的最大長度(例如 20 個字節),將窗口中每個 20 字節長的串抽取出來,按照大小順序組織成二叉有序樹。在這樣的二叉有序樹中進行字符串的查找,其效率是很高的。樹中每個節點大小是 20(key) + 4(off) + 4(left child) + 4(right child) = 32。樹中共有 MAX_WND_SIZE - 19 個節點,假如窗口大小爲 4096 字節,樹的大小大約是 130k 字節。空間消耗也不算多。這種方法對匹配串長度的限制雖然影響了壓縮程序對一些特殊數據(又很長的匹配串)的壓縮效果,但就平均性能而言,壓縮效果仍是不錯的。

二、將窗口中每一個長度爲 3 (視狀況也可取 2 或 4)的字符串創建索引,先在此索引中匹配,以後對得出的每一個可匹配位置進行順序查找,直到找到最長匹配字符串。由於長度爲 3 的字符串能夠有 2563 種狀況,咱們不可能用靜態數組存儲該索引結構。使用 Hash 表是一個明智的選擇。咱們能夠僅用 MAX_WND_SIZE - 1 的數組存儲每一個索引點,Hash 函數的參數固然是字符串自己的 3 個字符值了,Hash 函數算法及 Hash 以後的散列函數很容易設計。每一個索引點以後是該字符串出現的全部位置,咱們可使用單鏈表來存儲每個位置。值得注意的是,對一些特殊狀況好比 aaaaaa...之類的連續字串,字符串 aaa 有不少連續出現位置,但咱們無需對其中的每個位置都進行匹配,只要對最左邊和最右邊的位置操做就能夠了。解決的辦法是在鏈表節點中紀錄相同字符連續出現的長度,對連續的出現位置再也不創建新的節點。這種方法能夠匹配任意長度的字符串,壓縮效果要好一些,但缺點是查找耗時多於第一種方法。

三、使用字符樹( trie )來對窗口內的字符串創建索引,由於字符的取值範圍是 0 - 255,字符樹自己的層次不可能太多,3 - 4 層之下就應該換用其餘的數據結構例如 Hash 表等。這種方法能夠做爲第二種方法的改進算法出現,能夠提升查找速度,但空間的消耗較多。

若是對窗口中的數據進行索引,就必然帶來一個索引位置表示的問題,即咱們在索引結構中該往偏移項中存儲什麼數據:首先,窗口是不斷向後滑動的,咱們每次將窗口向後滑動一個位置,索引結構就要做相應的更新,咱們必須刪除那些已經移動出窗口的數據,並增長新的索引信息。其次,窗口不斷向後滑動的事實使咱們沒法用相對窗口左邊界的偏移來表示索引位置,由於隨着窗口的滑動,每一個被索引的字符串相對窗口左邊界的位置都在改變,咱們沒法承擔更新全部索引位置的時間消耗。

解決這一問題的辦法是,使用一種能夠環形滾動的偏移系統來創建索引,而輸出匹配字符串時再將環形偏移還原爲相對窗口左邊界的真正偏移。讓咱們用圖形來講明,窗口剛剛達到最大時,環形偏移和原始偏移系統相同:

偏移:     0 1 2 3 4 ......                                              Max
          |--------------------------------------------------------------|
環形偏移: 0 1 2 3 4 ......                                              Max

窗口向後滑動一個字節後,滑出窗口左端的環形偏移 0 被補到了窗口右端:

偏移:     0 1 2 3 4 ......                                              Max
          |--------------------------------------------------------------|
環形偏移: 1 2 3 4 5 ......                                           Max 0

窗口再滑動 3 個子節後,偏移系統的狀況是:

偏移:     0 1 2 3 4 ......                                              Max
          |--------------------------------------------------------------|
環形偏移: 4 5 6 7 8......                                      Max 0 1 2 3

依此類推。

咱們在索引結構中保存環形偏移,但在查找到匹配字符串後,輸出的匹配位置 off 必須是原始偏移(相對窗口左邊),這樣才能夠保證解碼程序的順利執行。咱們用下面的代碼將環形偏移還原爲原始偏移:

// 由環形 off 獲得真正的off(相對於窗口左邊)
// 其中 nLeftOff 爲當前與窗口左邊對應的環形偏移值
int GetRealOff(int off)
{
    if (off >= nLeftOff)
        return off - nLeftOff;
    else
        return (_MAX_WINDOW_SIZE - (nLeftOff - off));
}

這樣,解碼程序無需考慮環形偏移系統就能夠順利高速解碼了。

資源

結合上面的討論,典型的 LZ77 算法應當不難實現,咱們本章給出的源碼是一個較爲特殊的實現。

示例程序 lz77.exe 使用對匹配串和單個字符分類輸出的模型,輸出匹配串時,off 採用定長編碼,len 採用γ編碼。索引結構採用 2 字節長字符串的索引,使用 256 * 256 大小的靜態數組存儲索引點,每一個索引點指向一個位置鏈表。鏈表節點考慮了對 aaaaa... 之類的重複串的優化。

示例程序的獨特之處在於使用了 64k 大小的固定長度窗口,窗口不作滑動(所以不須要環形偏移系統,也節省了刪除索引點的時間)。壓縮函數每次只對最多 64k 長的數據進行壓縮,主函數將原始文件分紅 64k 大小的塊逐個壓縮存儲。使用這種方法首先能夠增大匹配的機率,字符串能夠在 64k 空間內任意尋找最大匹配串,以此提升壓縮效率。其次,這種方法有利於實現解壓縮的同步。也就是說,利用這種方法分塊壓縮的數據,很容易從原始文件中間的任何一個位置開始解壓縮,這尤爲適用於全文檢索系統中全文信息的保存和隨機讀取。

結合上述示例程序,王笨笨開發了可壓縮多個文件並可同步(隨機)解壓縮的文件級接口,但此接口並不是自由代碼(free code)。若是須要能夠和王笨笨聯繫。

 

 

*********************************************************************** Project description:* Lz77 compression/decompression algorithm.**********************************************************************/#include <windows.h>#include <conio.h>#include <stdio.h>#include <assert.h>#define OFFSET_CODING_LENGTH (10)#define MAX_WND_SIZE 1024//#define MAX_WND_SIZE (1<<OFFSET_CODING_LENGTH)#define OFFSET_MASK_CODE (MAX_WND_SIZE-1)const ULONG m=3;UCHAR __buffer1__[0x200000];UCHAR __buffer2__[0x200000];////////////////////////////////////////////////////////////////////////////////voidWrite1ToBitStream(PUCHAR pBuffer,ULONG ulBitOffset){ULONG ulByteBoundary;ULONG ulOffsetInByte;ulByteBoundary = ulBitOffset>>3 ;ulOffsetInByte = ulBitOffset&7;*(pBuffer+ulByteBoundary) |= (1<<ulOffsetInByte);}voidWrite0ToBitStream(PUCHAR pBuffer,ULONG ulBitOffset){ULONG ulByteBoundary;ULONG ulOffsetInByte;ulByteBoundary = ulBitOffset>>3 ;ulOffsetInByte = ulBitOffset&7;*(pBuffer+ulByteBoundary) &= (~(1<<ulOffsetInByte));}ULONGReadBitFromBitStream(PUCHAR pBuffer,ULONG ulBitOffset){ULONG ulByteBoundary;ULONG ulOffsetInByte;ulByteBoundary = ulBitOffset>>3 ;ulOffsetInByte = ulBitOffset&7;return ((*(PULONG)(pBuffer+ulByteBoundary))>>ulOffsetInByte)&1 ;}ULONG WINAPIWriteGolombCode(ULONG x,PUCHAR pBuffer,ULONG ulBitOffset){ULONG q, r;int i;q = (x-1)>>m;r = x-(q<<m)-1;for(i=0; (ULONG)i<q; i++, ulBitOffset++){Write1ToBitStream(pBuffer, ulBitOffset);}Write0ToBitStream(pBuffer, ulBitOffset);ulBitOffset++;for(i=0; i<m; i++, ulBitOffset++){if( (r>>i)&1 ){Write1ToBitStream(pBuffer, ulBitOffset);}else{Write0ToBitStream(pBuffer, ulBitOffset);}}return m+q+1;}ULONGReadGolombCode(PULONG pulCodingLength,PUCHAR pBuffer,ULONG ulBitOffset){ULONG q, r;ULONG bit;int i;for(q=0; ;q++){bit = (ULONG)ReadBitFromBitStream(pBuffer, ulBitOffset);ulBitOffset++;if( !bit ){break;}}for(i=0, r=0; (ULONG)i<m; i++, ulBitOffset++){bit = (ULONG)ReadBitFromBitStream(pBuffer, ulBitOffset);bit <<= i;r |= bit;}*pulCodingLength = m + q + 1;return r+(q<<m)+1;}ULONGCompareStrings(PUCHAR string1,PUCHAR string2,ULONG length){ULONG i;PUCHAR p1, p2;p1 = string1;p2 = string2;for(i=0; i<length; i++){if( *p1==*p2 ){p1++;p2++;}else{break;}}return p1-string1;}void WINAPIFindLongestSubstring(PUCHAR pSourceString,PUCHAR pString,ULONG ulSourceStringLength,PULONG pulSubstringOffset,PULONG pulSubstringLength){PUCHAR pSrc;ULONG offset, length;ULONG ulMaxLength;*pulSubstringOffset = offset = 0;*pulSubstringLength = 0;if( NULL==pSourceString || NULL==pString ){return;}ulMaxLength = ulSourceStringLength;pSrc = pSourceString;while( ulMaxLength>0 ){length = CompareStrings(pSrc, pString, ulMaxLength);if( length>*pulSubstringLength ){*pulSubstringLength = length;*pulSubstringOffset = offset;}pSrc++;offset++;ulMaxLength--;}}/*voidFindLongestSubstring(PUCHAR pSourceString,PUCHAR pString,ULONG ulSourceStringLength,PULONG pulSubstringOffset,PULONG pulSubstringLength){PUCHAR pCurrentOffset;PUCHAR p1, p2;ULONG offset, length;pCurrentOffset = pSourceString;*pulSubstringOffset = offset = 0;*pulSubstringLength = length = 0;while( pCurrentOffset<pSourceString+ulSourceStringLength ){p1 = pCurrentOffset;p2 = pString;if( *p1==*p2 ){while( p1<pSourceString+ulSourceStringLength && *p1==*p2 ){p1++;p2++;}length = p1 - pCurrentOffset;}else{length = 0;}if( length>*pulSubstringLength ){*pulSubstringLength = length;*pulSubstringOffset = (ULONG)pCurrentOffset - (ULONG)pSourceString;}pCurrentOffset++;}}*/voidWriteBits(PUCHAR pDataBuffer,ULONG ulOffsetToWrite,ULONG ulBits,ULONG ulBitLength){ULONG ulDwordsOffset;ULONG ulBitsOffset, ulBitsRemained;ulDwordsOffset = ulOffsetToWrite>>5;ulBitsOffset = ulOffsetToWrite&31;ulBitsRemained = 32 - ulBitsOffset;if( 0==ulBitsOffset ){*((PULONG)pDataBuffer+ulDwordsOffset) = ulBits;}else if( ulBitsRemained>=ulBitLength ){*((PULONG)pDataBuffer+ulDwordsOffset) |= (ulBits<<ulBitsOffset);}else{*((PULONG)pDataBuffer+ulDwordsOffset) |= (ulBits<<ulBitsOffset);*((PULONG)pDataBuffer+ulDwordsOffset+1) = ulBits>>ulBitsRemained;}}voidReadBits(PUCHAR pDataBuffer,ULONG ulOffsetToRead,PULONG pulBits){ULONG ulDwordsOffset;ULONG ulBitsOffset, ulBitsLength;ulDwordsOffset = ulOffsetToRead>>5;ulBitsOffset = ulOffsetToRead&31;ulBitsLength = 32 - ulBitsOffset;*pulBits = *((PULONG)pDataBuffer+ulDwordsOffset);if( 0!=ulBitsOffset ){(*pulBits) >>= ulBitsOffset;(*pulBits) |= (*((PULONG)pDataBuffer+ulDwordsOffset+1))<<ulBitsLength;}}voidlz77compress(PUCHAR pDataBuffer,ULONG ulDataLength,PUCHAR pOutputBuffer,PULONG pulNumberOfBits){LONG iSlideWindowPtr;ULONG ulBytesCoded;ULONG ulMaxlength;PUCHAR pSlideWindowPtr;PUCHAR pUnprocessedDataPtr;ULONG offset;ULONG length;ULONG ulCodingLength;ULONG ulBitOffset;UCHAR cc;int i;iSlideWindowPtr = -MAX_WND_SIZE;pSlideWindowPtr = NULL;ulBitOffset = 0;ulBytesCoded = 0;while( ulBytesCoded<ulDataLength ){if( iSlideWindowPtr>=0 ){pSlideWindowPtr = pDataBuffer+iSlideWindowPtr;ulMaxlength = MAX_WND_SIZE;}else if( iSlideWindowPtr>=-MAX_WND_SIZE ){pSlideWindowPtr = pDataBuffer;ulMaxlength = MAX_WND_SIZE + iSlideWindowPtr;}else{pSlideWindowPtr = NULL;ulMaxlength = 0;}pUnprocessedDataPtr = pDataBuffer + ulBytesCoded;if( ulMaxlength>ulDataLength-ulBytesCoded ){ulMaxlength = ulDataLength-ulBytesCoded;}FindLongestSubstring(pSlideWindowPtr,pUnprocessedDataPtr,ulMaxlength,&offset,&length);assert( length<=MAX_WND_SIZE );assert( offset<MAX_WND_SIZE );if(length>1){Write1ToBitStream(pOutputBuffer, ulBitOffset);ulBitOffset++;for(i=0; i<OFFSET_CODING_LENGTH; i++, ulBitOffset++){if( (offset>>i)&1 ){Write1ToBitStream(pOutputBuffer, ulBitOffset);}else{Write0ToBitStream(pOutputBuffer, ulBitOffset);}}ulCodingLength = WriteGolombCode(length, pOutputBuffer, ulBitOffset);ulBitOffset += ulCodingLength;iSlideWindowPtr += length;ulBytesCoded += length;}else{Write0ToBitStream(pOutputBuffer, ulBitOffset);ulBitOffset++;cc = (*pUnprocessedDataPtr);for(i=0; i<8; i++, ulBitOffset++){if( (cc>>i)&1 ){Write1ToBitStream(pOutputBuffer, ulBitOffset);}else{Write0ToBitStream(pOutputBuffer, ulBitOffset);}}iSlideWindowPtr++;ulBytesCoded++;}}if( ulBytesCoded!=ulDataLength ){assert(ulBytesCoded==ulDataLength);}*pulNumberOfBits = ulBitOffset;}void lz77decompress(PUCHAR pDataBuffer,ULONG ulNumberOfBits,PUCHAR pOutputBuffer,PULONG pulNumberOfBytes){LONG iSlideWindowPtr;PUCHAR pSlideWindowPtr;ULONG length, offset;ULONG bit;UCHAR cc;int i;ULONG ulBytesDecoded;ULONG ulBitOffset;ULONG ulCodingLength;PUCHAR pWrite;iSlideWindowPtr = -MAX_WND_SIZE;pWrite = (PUCHAR)pOutputBuffer;ulBitOffset = 0;ulBytesDecoded = 0;while( ulBitOffset<ulNumberOfBits ){bit = ReadBitFromBitStream(pDataBuffer, ulBitOffset);ulBitOffset++;if( bit ){if( iSlideWindowPtr>=0 ){pSlideWindowPtr = pOutputBuffer + iSlideWindowPtr;}else if( iSlideWindowPtr>=-MAX_WND_SIZE ){pSlideWindowPtr = pOutputBuffer;}else{pSlideWindowPtr = NULL;}for(i=0, offset=0; i<OFFSET_CODING_LENGTH; i++, ulBitOffset++){bit = ReadBitFromBitStream(pDataBuffer, ulBitOffset);offset |= (bit<<i);}length= ReadGolombCode(&ulCodingLength, pDataBuffer, ulBitOffset);assert(offset<MAX_WND_SIZE);if( length>MAX_WND_SIZE ){assert(length<=MAX_WND_SIZE);}ulBitOffset += ulCodingLength;RtlMoveMemory(pWrite, pSlideWindowPtr+offset, length);pWrite+=length;iSlideWindowPtr+=length;ulBytesDecoded+=length;}else{for(i=0, cc=0; i<8 ; i++, ulBitOffset++){bit = ReadBitFromBitStream(pDataBuffer, ulBitOffset);cc |= ((UCHAR)bit<<i);}*pWrite++ = cc;iSlideWindowPtr++;ulBytesDecoded++;}}*pulNumberOfBytes = ulBytesDecoded;}extern "C"void WINAPILZ77Compress(PUCHAR __pDataBuffer,ULONG __ulDataLength,PUCHAR __pOutputBuffer,PULONG __pulNumberOfBits);extern "C"void WINAPILZ77Decompress(PUCHAR __pDataBuffer,ULONG __ulNumberOfBits,PUCHAR __pOutputBuffer,PULONG __pulNumberOfBytes);intmain(int argc,char *argv[]){FILE *fp=NULL;FILE *fp1;ULONG fsize;ULONG ulNumberOfBits;ULONG ulFileCompressedSize;ULONG ulFileDecompressedSize;SYSTEMTIME t1, t2;if( 3!=argc ){printf("Usage: lz77 [/c | /d] filename\n");return -1;}// char s1[]="abcdabcdefgabcdefaffasda";// ULONG a, b;// FindLongestSubstring((PUCHAR)s1, (PUCHAR)s1+11, 11,&a, &b );// return 0;fp = fopen(argv[2], "rb");if( !fp ){return -1;}fseek(fp, 0, SEEK_END);fsize = ftell(fp);fseek(fp, 0, SEEK_SET);fread(__buffer1__, 1, fsize, fp);GetSystemTime(&t1);lz77compress(__buffer1__, fsize, __buffer2__, &ulNumberOfBits);//LZ77Compress(__buffer1__, fsize, __buffer2__, &ulNumberOfBits);GetSystemTime(&t2);ulFileCompressedSize = ((ulNumberOfBits+7)>>3);fp1=fopen("peinfo.c_", "wb+");if( !fp1 ){goto l1;}fwrite(__buffer2__, 1, ulFileCompressedSize, fp1);fclose(fp1);RtlZeroMemory(__buffer1__, sizeof(__buffer1__));lz77decompress(__buffer2__, ulNumberOfBits, __buffer1__, &ulFileDecompressedSize);//LZ77Decompress(__buffer2__, ulNumberOfBits, __buffer1__, &ulFileDecompressedSize);fp1=fopen("peinfo.d_", "wb+");if( !fp1 ){goto l1;}fwrite(__buffer1__, 1, ulFileDecompressedSize, fp1);fclose(fp1);l1:if( fp ){fclose(fp);}ULONG milliS;milliS = ((t2.wHour - t1.wHour)*3600 + (t2.wMinute-t1.wMinute)*60 + (t2.wSecond-t1.wSecond)) * 1000 + (t2.wMilliseconds-t1.wMilliseconds);printf("Totally %ld milliseconds elapsed!\n\n", milliS);printf("Press any key to exit!\n");getch();return 0;}

相關文章
相關標籤/搜索