www.pudn.com > cezip.rar > compress.cpp, change:2005-03-19,size:15819b


#include "stdafx.h" 
#include "compress.h" 
 
///////////////////////////////////////////////////////////////////// 
//hashfun:计算hash值 
//此前已经保证至少有三个元素 
word hashfun(char *windows) 
{        
	word a,b,c; 
	a=windows[0]; 
	a*=256;            //左移八位 
	a+=windows[1]; 
	b=windows[1]; 
	b*=256;            //左移八位 
	b+=windows[2]; 
	c=a^b; 
	return c; 
} 
///////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//scrollwindows :滚动窗口 
//i 开始位址   windows 窗口   content 缓存数组 
//最多读入17个 
//返回窗口读入的个数 
int scrollwindows(word i,char *windows, char *content,word last) 
{       
	word k,c; 
	c=i; 
	for( k = 0;k < 17 ; k++) 
	{ 
		if(c+k == last) return k; 
		windows[k]=content[k+c]; 
	} 
	return 17; 
} 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//seek :真正寻找匹配串 
//返回匹配个数,并通过address传地址 
char seek(char *windows,word *address,char *content,word i,word *hash,word *hashtable,word flag,word c_flag) 
{ 
	word hashnum,k,index,j; 
	char tem_match,match=0; 
	j=(word) flag; 
	hashnum=hashfun(windows);   
	if(hash[hashnum] == 65535) 
	{ 
		hash[hashnum]=i; 
		hashtable[i]=65535;            //防止死循环 
		*address=65535; 
		return 1; 
	} 
	else 
	{       
		k=hash[hashnum]; 
		if(k == i) k=hashtable[i];   //前次已在hashtable中了,再找是自己和自己比,现在应跳过 
 
		while(k != 65535) 
		{    
			tem_match=0; 
			index=0; 
			while(windows[index] == content[k+index]) 
			{ 
				tem_match++; 
				index++; 
				if(index >= j)  break;    //最大匹配长度为窗口读入的个数 
				if(k+index >= 65534)  break;   //防止超出content的范围 
			} 
			if(tem_match>match)             //k表示当前正在比较的字符串地址 
			{ 
				*address=k; 
				match=tem_match; 
			} 
			if(match == j) break;//已到最大匹配 
			k=hashtable[k]; 
		} 
		if(c_flag == 0) 
		{ 
			hashtable[i]=hash[hashnum]; 
			hash[hashnum]=i; 
		} 
		return match; 
	} 
} 
 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//seek_phrase:寻找匹配的字符串 
//两次调用seek比较最佳的压缩方式 
//返回匹配个数,并通过address传地址 
char seek_phrase(char *windows,word flag ,word *c_flag,word *address,word i,char *content,word *hash,word *hashtable,word last,int type) 
{ 
	char match1,match2,match; 
	word address1,address2; 
	int lev;                                     //随type 的不同而不同 
	if(type == 1)  lev =2; 
	else lev=3; 
	match1=seek(windows,&address1,content,i,hash,hashtable,flag,*c_flag); 
	if( flag <= 3)                                                                          //跳过贪婪匹配 
	{match2=0;address2=address1;} 
	else 
		match2=seek(windows+1,&address2,content,i+1,hash,hashtable,flag-1,*c_flag);         //注意windows+1和i+1 
	if(match1 < match2 || match1 <= lev)       
	{ 
		match=1; 
		*c_flag=1;                                           //匹配一个,下个的hashtable已写入,否则会死循环 
		*address=address2; 
	} 
	else 
	{ 
		match=match1; 
		*c_flag=0;                                          //下个的hashtable已写入,但可以跳过它 
		*address=address1; 
	} 
	return match; 
} 
 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//out_code2:将压缩结果写入特定的缓存 
void out_code2(char match,word *af_length,word *cp_length,word address,char *windows,char *neirong,char *len_date) 
{                 
	word ah,al; 
	int c; 
	char len; 
	if(match == 0|| match== 2 || match == 3)   
	{ 
		MessageBox(NULL,_T("Compress Fatal Error!"),_T("Error!"),MB_OK); 
		exit(0); 
	}    //检验 
	if(match == 1) 
	{ 
		neirong[*af_length-1]=windows[0]; 
		if(windows[0] == -1)  
		{ 
			(*cp_length)++;                       //++  比  * 优先!!! 
			c=(*cp_length-1)/2; 
			len_date[c]=(len_date[c]*16+0);     //写入len_date  左移4位 
		} 
	} 
	else 
	{      
		len=match-2;      
		c=(*cp_length-1)/2; 
		len_date[c]=(len_date[c]*16+len);     //写入len_date  左移4位 
		neirong[*af_length-1]=-1; 
		if(address<0)  
		{ 
			MessageBox(NULL,_T("Compress Fatal Error!"),_T("Error!"),MB_OK); 
			exit(0); 
		}    //检验 
		ah=address/256; 
		al=address%256; 
		neirong[*af_length]=(char)ah;    //写入outdate 
		neirong[*af_length+1]=(char)al;    
		(*af_length)++; 
		(*af_length)++; 
	} 
} 
 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//file_out_code2:写入文件 
// 
void file_out_code2(FILE *out,word af_length,word cp_length,char *neirong,word last,char *len_date) 
{ 
	int i,c=0; 
	word len=0; 
	fwrite(&last,2,1,out); 
	fwrite(&af_length,2,1,out); 
	fwrite(&cp_length,2,1,out); 
	i=cp_length/2; 
	if(cp_length%2 != 0)  
	{ 
		len_date[i]=len_date[i]*16; 
		i++; 
	} 
	fwrite(len_date,i,1,out); 
	fwrite(neirong,af_length,1,out); 
} 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//compress: 压缩数据的函数 
//in 源数据区;out 目标数据区; 
//一次最大处理65536个字节; 
//type : 1 为文本型,2为其他型。 
int compress(FILE *in,FILE *out,int type) 
{ 
	word af_length;                      //type1时 :计算处理的次数,为对照表(table)提供长度。//type2时 :为neirong提供长度 
	word cp_length;                      //type1时 :计算作缩略的次数,为len_date提供长度。 //type2时 :计算-1和作缩略的次数,为len_date提供长度。 
	char* content;                         //将待压数据读入缓存 
	word* hash;                            //hash值的索引 
	char windows[17];                      //预读区,限制了最大匹配长度为17 
	word* hashtable;                       //hash 表 
	char* len_date;                        //记录每次缩略的长度 
	char *neirong;                      //经处理过的字符或地址。255  -1  代表缩略 ,后跟两字节地址, 其余皆为字符。     
	word i;                                 //计数,指明当前处理的位置 
	word address;                          //能匹配的地址 
	char match;                           //能匹配的长度 
	int k=1;                                 //辅助计数 
	fseek(in,0,SEEK_END); 
	long length=ftell(in);                //计算文件长度 
	fseek(in,0,SEEK_SET); 
	a_file+=length; 
	len_fi=length; 
	fwrite(&length,4,1,out); 
	word last;                             //本次处理的个数 
	word flag;                             //窗口中读入的个数 
	word c_flag;                           //是否加入hashtable过。 0 为否;1 为加入过 
	word ii=0;                               //遇到文件的末尾使用,加0 
	word a;                              //初始化使用	 
	last=(word)((65535>length)?length:65535); 
	al_se=no_se=0; 
	len_se=0; 
	len_db=0; 
	pe_num=0; 
	al_se=length/16384+1; 
 
	content = (char*)malloc(65536*sizeof(char));           //各数据缓存的初始化 
	memset(content,0,sizeof(content)); 
	hash = (word*)malloc(65536*sizeof(word)); 
	memset(hash,0,sizeof(hash)); 
	hashtable = (word*)malloc(65536*sizeof(word)); 
	memset(hashtable,0,sizeof(hashtable)); 
	len_date = (char*)malloc(65536*sizeof(char)); 
	memset(len_date,0,sizeof(len_date)); 
 
	while(k>0) 
	{  
		len_se=16387; 
		c_flag=0; 
		af_length=0; 
		cp_length=0; 
		length -= 65535; 
		for(a=0;a<65535;a++)                   //初始化为0 
			len_date[a]=0; 
		len_date[a]=0; 
		neirong=(char *)malloc(last); 
		fread(content,1,last,in); 
		for(a=0;a<65535;a++)                   //初始化为65535 
		{ 
			hash[a]=65535; 
			hashtable[a]=65535; 
		} 
		hash[65535]=65536; 
		hashtable[65535]=65535;    //单独初始化,防止上个for死循环 
		flag=scrollwindows(0,windows,content,last);        
		for(i=0;i 17) 
			{  
				MessageBox(NULL,_T("Compress Error!"),_T("Error"),MB_OK);  
				return 1; 
			}            //做一些检测 
			if(match > 2)  cp_length++;   
			out_code2(match,&af_length,&cp_length,address,windows,neirong,len_date);            
			i+=match; 
			if(i == 0) break;//防止i重回0,死循环 
			flag=scrollwindows(i,windows,content,last);       //滚动窗口 
		} 
		len_cp+=cp_length;len_cp+=2;  
		len_af+=af_length;len_af+=2; 
		file_out_code2(out,af_length,cp_length,neirong,last,len_date); 
		free(neirong); 
		k = (65535>length)?length:65535; 
		if(k < 0) break; 
		last=(word) k; 
	}  
	fwrite(&ii,2,1,out); 
	result=2.5*len_cp+len_af; 
	af_file+=result; 
	result=result/len_fi; 
	len_af=len_cp=0; 
	return 0; 
} 
//////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////// 
//decompress : 解压缩 
// 
int decompress(FILE *in,int type) 
{ 
	char* content;                    //解压完的缓存 
	unsigned char* len_date;                        //缩略的长度对照表           用af_index寻址。 
	content = (char*)malloc(65536*sizeof(char)); 
	memset(content,0,sizeof(content)); 
	len_date = (unsigned char*)malloc(65536*sizeof(unsigned char)); 
	memset(len_date,0,sizeof(len_date)); 
	unsigned short last,af_length,cp_length; 
	unsigned char tem,ah,al; 
	unsigned int a; 
	unsigned int b;                         //负责len_date中的寻址 
	unsigned int c;                         //缩略长度 
	unsigned int d;                         // 
	unsigned int address;                   //负责content中寻址。 
	FILE *out; 
	char  filena[20];                       //放文件名 
	unsigned char co_len=0;                   //密码长度 
	unsigned char fi_num;                   //文件个数 
	long fi_len=0;                            //文件长度 
	unsigned char fn_len=0;                   //文件名长度 
	int flag=1; 
	fread(&co_len,1,1,in); 
	fi_num=co_len; 
	for(;;) 
	{ 
		fi_len=0;                   //文件长度 
		fn_len=0; 
		fread(&fn_len,1,1,in); 
		if(feof(in)) break; 
		fread(filena,fn_len,1,in); 
		if((out=fopen(filena,"wb")) == NULL) 
		{ 
			MessageBox(NULL,_T("Can't open file!"),_T("Error"),MB_OK); 
			return 1; 
		} 
		fread(&fi_len,4,1,in); 
		al_se=no_se=0; 
		al_se=fi_len/16384+1; 
		while(1) 
		{ 
			last=0; 
			af_length=0; 
			cp_length=0; 
			fread(&last,2,1,in); 
			if(last == 0) break; 
			fread(&af_length,2,1,in); 
			fread(&cp_length,2,1,in); 
			if(al_se-no_se >= 4) 
				len_se=last/4+3; 
			else  
				len_se=last/(al_se-no_se)+2; 
			for(a=0;;) 
			{ 
				if(cp_length == 0) break; 
				fread(&tem,1,1,in); 
				len_date[a]=tem/16; 
				a++; 
				if(a == cp_length) break; 
				len_date[a]=tem%16; 
				a++; 
				if(a == cp_length) break; 
			}  
			for(a=b=0;a=-1;i--) 
	{ 
		if(savePath[i] == '\\') break; 
	} 
	if(i<0) i=0; 
	strcpy(file1,savePath+i+1); 
	fn_len = strlen(file1); 
	fn_len++; 
	fseek(out,8260,0); 
	fwrite(file1,fn_len,1,out); 
	fclose(out); 
	if((out=fopen(savePath,"ab")) == NULL) 
	{ 
		MessageBox(NULL,_T("Can't create exe file!"),_T("Error"),MB_OK); 
		return 1; 
	} 
	fwrite(&itemCount,1,1,out); 
	for(i=0;i