近日回想起j2mepolish上有个zip的源码,于是试验一番.
j2me的平台上还是比较少看到压缩与解压的,主要原因是效率不怎么高吧!但是,对于小文件还是有意义的,比如说用于保密.这个gzip有两种方式的压缩与解压,默认的那种特别适合用于加密,这种的RAR打不开,另外一种的则是zip类型的.不多说,上关键的:
GZipInputStream类:
/* * Created on Jun 25, 2007 at 11:12:23 AM. * * Copyright (c) 2007 Robert Virkus / Enough Software * * This file is part of J2ME Polish. * * J2ME Polish is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * J2ME Polish is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with J2ME Polish; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Commercial licenses are also available, please * refer to the accompanying LICENSE.txt or visit * http://www.j2mepolish.org for details. */ package zip; import java.io.IOException; import java.io.InputStream; /** * <p>Reads and uncompresses GZIP or DEFLATE encoded input streams.</p> * * <p>Copyright Enough Software 2007 - 2008</p> * <pre> * history * Jun 25, 2007 - Simon creation * </pre> * @author Simon Schmitt, [email protected] */ public class GZipInputStream extends InputStream { /** * This constant triggers the normal deflate compression as described in rfc 1951. */ public static final int TYPE_DEFLATE=0; /** * This constant triggers the gzip compression that is the same as deflate with * some extra header information (see rfc 1952). */ public static final int TYPE_GZIP=1; private InputStream inStream; private boolean inStreamEnded; private byte status; private static final byte EXPECTING_HEADER=0; private static final byte EXPECTING_DATA=1; private static final byte EXPECTING_CHECK=2; private static final byte FINISHED=3; private boolean hash; /** * The data seems to be decompressed sucessfull if vaildData * is true after processing the whole stream. This is determinded * via counting the processed bytes and depending on the * on the parameters given to the constructor also by using * a CRC32 checksum. */ private boolean vaildData; private int crc32; private int[] crc32Table=new int[256]; private int type; // Flags private boolean BFINAL;//indicates last block private int BTYPE;// type of compression // Buffer stuff: private byte[] window=new byte[32 * 1024]; // every decoder has to support windows up to 32k private int pProcessed=0; // data pointer = one after the last processed private long allPocessed=0; // amount of processed data mod 2^32 byte[] outBuff; // the plain data will be stored here before being requested private int buffsize; int outEnd=0; // the position AFTER the last byte of data int lastEnd=0; // " the point up to the crc32 was computed int outStart=0; // the position of the first bit of data private int B0len; // length of remaining plain bytes to process long[] smallCodeBuffer=new long[2];// (1) contains the merged bitcode and (2) contains the count of those bits static final byte BL=8; // Compression stuff short[] huffmanTree; short[] distHuffTree; /** * Creates an input stream capable of GZIP and Deflate with a buffer of 1024 bytes. * * @param inputStream the stream that contains the compressed data. * @param compressionType TYPE_GZIP or TYPE_DEFLATE * @param hash set true for data checking, set false for speed reading * @throws IOException when the header of a GZIP stream cannot be skipped * @see #TYPE_DEFLATE * @see #TYPE_GZIP */ public GZipInputStream(InputStream inputStream, int compressionType, boolean hash) throws IOException { this(inputStream, 1024, compressionType, hash); } /** * Creates an input stream capable of GZIP and Deflate. * * @param inputStream the stream that contains the compressed data. * @param size the size of the internally used buffer * @param compressionType TYPE_GZIP or TYPE_DEFLATE * @param hash set true for data checking, set false for speed reading * @throws IOException when the header of a GZIP stream cannot be skipped * @see #TYPE_DEFLATE * @see #TYPE_GZIP */ public GZipInputStream(InputStream inputStream, int size, int compressionType, boolean hash) throws IOException { this.inStream=inputStream; this.inStreamEnded=false; this.status=GZipInputStream.EXPECTING_HEADER; this.hash=hash; this.type=compressionType; this.smallCodeBuffer=new long[2]; this.huffmanTree=new short[288*4]; this.distHuffTree=new short[32*4]; this.buffsize=size; this.outBuff=new byte[size+300]; if (this.type==GZipInputStream.TYPE_GZIP){ ZipHelper.skipheader(inputStream); } this.crc32=0; } public void close() throws IOException{ this.inStream.close(); this.smallCodeBuffer=null; this.huffmanTree=null; this.distHuffTree=null; } /** * This function hides the fact that 'this.window' is a ring buffer * so just pass 'start' and 'len' of data in the window as well * as a destination and it will be copied there. * @param start * @param len * @param dest */ private void copyFromWindow(int start, int len, byte[] dest, int destoff){ if (start + len < this.window.length) { System.arraycopy(this.window, start, dest, 0+destoff, len); } else { System.arraycopy(this.window, start, dest, 0+destoff, this.window.length - start); System.arraycopy(this.window, 0, dest, this.window.length - start + destoff, len - (this.window.length - start) ); } } private void copyIntoWindow(int start, int len, byte[] src, int srcOff){ if(len + start < this.window.length) { System.arraycopy(src, srcOff, this.window, start, len); } else { System.arraycopy(src, srcOff, this.window, start, this.window.length-start); System.arraycopy(src, srcOff+(this.window.length-start), this.window, 0, len - (this.window.length-start)); } } /** * This function fills the internal outputbuffer reading data form the this.inputStream * and inflating it. */ private void inflate() throws IOException{ int val=0; int rep; int rem; int cLen; int cPos; int aPos; int copyBytes; byte[] myWindow=this.window; byte[] myOutBuff=this.outBuff; // shift outputbuffer to the beginning System.arraycopy(myOutBuff, this.outStart, myOutBuff, 0, this.outEnd-this.outStart); this.outEnd-=this.outStart; this.outStart=0; this.lastEnd = this.outEnd; if (this.B0len==0){ if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } } // and fill it by parsing the input-stream while ((myOutBuff.length-this.outEnd>300 && (this.smallCodeBuffer[1]>0 || this.B0len>0)) && this.status!=GZipInputStream.FINISHED){ // parse block header if (this.status == GZipInputStream.EXPECTING_HEADER){ processHeader(); } // deal with the data if (this.status==GZipInputStream.EXPECTING_DATA){ // just copy data if (this.BTYPE==0){ if (this.B0len>0){ // copy directly copyBytes=(myOutBuff.length-this.outEnd)>this.B0len ? this.B0len : myOutBuff.length-this.outEnd; //at most myOutBuff.length-this.outEnd copyBytes=this.inStream.read(myOutBuff, this.outEnd, copyBytes); copyIntoWindow(this.pProcessed, copyBytes, myOutBuff, this.outEnd); this.outEnd+=copyBytes; this.pProcessed=(this.pProcessed +copyBytes) & 32767;// % (1<<15); this.B0len-=copyBytes; }else{ if(this.BFINAL){ this.status=GZipInputStream.EXPECTING_CHECK; } else { this.status=GZipInputStream.EXPECTING_HEADER; } if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } } }// inflate else { if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } val=ZipHelper.deHuffNext(this.smallCodeBuffer,this.huffmanTree); // normal single byte if (val<256){ //this.window[this.pProcessed]=(byte)val; myWindow[this.pProcessed]=(byte)val; this.pProcessed=(this.pProcessed +1) & 32767;// % (1<<15); myOutBuff[this.outEnd]=(byte)val; this.outEnd++; }// copy: pointer + len else if (val!=256) { if (val>285){ //ERROR: data > 285 was decoded. This is invalid data.; throw new IOException("1"); } // parse the pointer // cLen // read some bits cLen=popSmallBuffer(ZipHelper.LENGTH_CODE[(val-257)<<1]); // add the offset cLen+=ZipHelper.LENGTH_CODE[((val-257)<<1)+1]; // cPos // resolve the index if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } // DISTANCE val=ZipHelper.deHuffNext(this.smallCodeBuffer, this.distHuffTree); // resolve the value cPos=popSmallBuffer(ZipHelper.DISTANCE_CODE[val<<1]); cPos+=ZipHelper.DISTANCE_CODE[(val<<1)+1]; // process the pointer (the data does always fit) // the absolute starting position for copying data aPos=this.pProcessed - cPos; aPos+=aPos<0 ? myWindow.length:0; // how often will the data be copied? rep=cLen/cPos; rem=cLen-cPos*rep; for (int j = 0; j < rep; j++) { // cPos < cLen copyFromWindow(aPos, cPos, myOutBuff,this.outEnd); copyIntoWindow(this.pProcessed, cPos, myOutBuff, this.outEnd); this.outEnd+=cPos; this.pProcessed=(this.pProcessed +cPos) & 32767;//% (1<<15); } // cPos > cLen OR remainder copyFromWindow(aPos, rem, myOutBuff,this.outEnd);// from window into buffer, and again into window copyIntoWindow(this.pProcessed, rem, myOutBuff, this.outEnd); this.outEnd+=rem; this.pProcessed=(this.pProcessed +rem) & 32767;// % (1<<15); }// val=256 else { //System.out.println("Block End code=" + huffmanCode[256] + " pP="+pProcessed + " popC: " + popcount[0]); if(this.BFINAL){ this.status=GZipInputStream.EXPECTING_CHECK; } else { this.status=GZipInputStream.EXPECTING_HEADER; } } if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } } } if (this.status == GZipInputStream.EXPECTING_CHECK){ //System.out.println(this.allPocessed + " data check"); this.status=GZipInputStream.FINISHED; this.allPocessed=(this.allPocessed+this.outEnd-this.lastEnd) & 4294967295L; if (this.hash){ // lastEnd -> End in CRC32 einbeziehen this.crc32=ZipHelper.crc32(this.crc32Table, this.crc32, myOutBuff, this.lastEnd, this.outEnd-this.lastEnd); } // skip till next byte boundary, read CRC , isize popSmallBuffer(this.smallCodeBuffer[1]&7); int cCrc=popSmallBuffer(8)|(popSmallBuffer(8)<<8)|(popSmallBuffer(8)<<16)|(popSmallBuffer(8)<<24); int iSize=popSmallBuffer(8)|(popSmallBuffer(8)<<8)|(popSmallBuffer(8)<<16)|(popSmallBuffer(8)<<24); this.vaildData=(iSize==this.allPocessed); if (this.hash){ this.vaildData &= (this.crc32==cCrc); } if (!this.vaildData){ //ERROR: the data check (size & hash) are wrong throw new IOException("2"); } } } // refresh the checksum at once if (this.status!=GZipInputStream.FINISHED){ this.allPocessed=(this.allPocessed+this.outEnd-this.lastEnd) & 4294967295L; if (this.hash){ // lastEnd -> End in CRC32 einbeziehen this.crc32 = ZipHelper.crc32(this.crc32Table, this.crc32, myOutBuff, this.lastEnd, this.outEnd-this.lastEnd); } } } private void processHeader () throws IOException{ int val; int HLIT; // number of miniHuff fragments int HDIST;// number of distance codes (should somehow lead to the same) int HCLEN;// number of length codes int[] distHuffCode=new int[30]; int[] distHuffData=new int[30]; byte[] distHuffCodeLength=new byte[30]; int[] huffmanCode=new int[286]; // this contains the codes according to the huffman tree/mapping int[] huffmanData=new int[286]; // this contains the data referring to the code. byte[] huffmanCodeLength=new byte[286]; this.BFINAL= (popSmallBuffer(1)==1); this.BTYPE=popSmallBuffer(2); if (this.BTYPE==3){ throw new IllegalArgumentException(); } else if (this.BTYPE==1){ //System.out.println(this.allPocessed + ": fixed tree"); ZipHelper.genFixedTree(huffmanCode, huffmanCodeLength, distHuffCode, distHuffCodeLength); for (int i = 0; i < 286; i++) { huffmanData[i]=i; } for (int i = 0; i < 30; i++) { distHuffData[i]=i; } // convert literal table to tree ZipHelper.convertTable2Tree(huffmanCode, huffmanCodeLength, huffmanData, this.huffmanTree); // convert distance table to tree ZipHelper.convertTable2Tree(distHuffCode, distHuffCodeLength, distHuffData, this.distHuffTree); } else if(this.BTYPE==2) { //System.out.println(this.allPocessed + ": dynamic tree"); // read/parse the length codes HLIT=popSmallBuffer(5); HDIST=popSmallBuffer(5); HCLEN=popSmallBuffer(4); // miniTree int[] miniHuffData= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; int[] seq={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}; byte[] miniHuffCodeLength=new byte[19]; int[] miniHuffCode=new int[19]; // read the miniHuffCodeLength for (int i = 0; i < HCLEN+4; i++) { miniHuffCodeLength[ miniHuffData[i] ]=(byte) popSmallBuffer(3); } ZipHelper.genHuffTree(miniHuffCode, miniHuffCodeLength); ZipHelper.revHuffTree(miniHuffCode, miniHuffCodeLength); short[] miniTree = new short[19*4]; ZipHelper.convertTable2Tree(miniHuffCode, miniHuffCodeLength, seq, miniTree); // parse the length code for the normal Tree and the distance Tree using the miniTree for (int i = 0; i < huffmanCodeLength.length; i++) { huffmanCodeLength[i]=0; } for (int i = 0; i < distHuffCodeLength.length; i++) { distHuffCodeLength[i]=0; } byte lastVal=0; for (int j = 0; j < HLIT + 257 + HDIST +1;) { if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } val=ZipHelper.deHuffNext(this.smallCodeBuffer, miniTree); // data if (val<16){ lastVal=(byte)val; val=1; } else{ // repeat code if (val==16){ val=popSmallBuffer(2)+3; } else if (val==17){ lastVal=0; val=popSmallBuffer(3)+3; } else if (val==18){ lastVal=0; val=popSmallBuffer(7)+11; } } // fill the value in for (int k = 0; k < val; k++,j++) { if (j<HLIT + 257){ huffmanCodeLength[j]=lastVal; } else { distHuffCodeLength[j-(HLIT + 257)]=lastVal; } } } // final tree: fill this.huffmanCode this.huffmanData ZipHelper.genHuffTree(huffmanCode, huffmanCodeLength); for (int i = 0; i < huffmanData.length; i++) { huffmanData[i]=i; } // converting literal table to tree ZipHelper.revHuffTree(huffmanCode, huffmanCodeLength); ZipHelper.convertTable2Tree(huffmanCode, huffmanCodeLength, huffmanData, this.huffmanTree); // Distance Tree // distHuffData for non fixed distance tree // this.distHuffCodeLength is read together with this.huffmanCodeLength for (int j = 0; j < distHuffCode.length; j++) { distHuffData[j]=j; } ZipHelper.genHuffTree(distHuffCode, distHuffCodeLength); ZipHelper.revHuffTree(distHuffCode, distHuffCodeLength); ZipHelper.convertTable2Tree(distHuffCode, distHuffCodeLength, distHuffData, this.distHuffTree); } else{ // just skip bits up to the next boundary popSmallBuffer(this.smallCodeBuffer[1]&7); //&7 == %8 // read and check the header this.B0len=popSmallBuffer(8)|popSmallBuffer(8)<<8; if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer(); } if (this.B0len + (popSmallBuffer(8)|popSmallBuffer(8)<<8) != 0xffff){ //Error: the header for the uncompressed is wrong; throw new IOException("3"); } // clear the buffer while(this.smallCodeBuffer[1]!=0 && this.B0len>0){ val = popSmallBuffer(8); this.window[this.pProcessed]=(byte)val; this.pProcessed=(this.pProcessed +1) & 32767; // == % (1<<15); this.outBuff[this.outEnd]=(byte)val; this.outEnd++; this.B0len--; } } this.status=GZipInputStream.EXPECTING_DATA; distHuffCode=null; distHuffData=null; distHuffCodeLength=null; huffmanCodeLength=null; huffmanCode=null; huffmanData=null; } /** * Checks if the current status is valid * * @return -1 if the stream is finished, 1 if the current data is valid, 0 if not * @throws IOException */ public int validData() throws IOException{ inflate(); if (this.status!=GZipInputStream.FINISHED){ return -1; } else { if (this.vaildData){ return 1; } else { return 0; } } } private int popSmallBuffer(long len) throws IOException{ if (len==0) return 0; if (this.smallCodeBuffer[1]<len){ refillSmallCodeBuffer(); } int ret= (int) (this.smallCodeBuffer[0] & ((1<<len)-1)); this.smallCodeBuffer[0]>>>=len; this.smallCodeBuffer[1]-=len; return ret; } /** * This function refills the smallCodeBuffer with data from the input * stream. * */ byte[] tmpRef = new byte[8]; // just one allocation private void refillSmallCodeBuffer() throws IOException{ // (re)fill this.smallBuffer reading this.inStream if (this.inStreamEnded==false){ int wanted=(int)(BL-this.smallCodeBuffer[1]/8-1); int count= this.inStream.read(this.tmpRef,0,wanted); if (count == -1){ this.inStreamEnded=true; } for (int i = 0; i < count; i++) { this.smallCodeBuffer[0]&= ~( (long)0xff << this.smallCodeBuffer[1]); if (this.tmpRef[i]<0){ this.smallCodeBuffer[0]|= (long)(this.tmpRef[i]+256) << this.smallCodeBuffer[1]; }else{ this.smallCodeBuffer[0]|= (long)this.tmpRef[i] << this.smallCodeBuffer[1]; } this.smallCodeBuffer[1]+=8; } } } /** * This function fills the buffer and returns the amount of * avialable data. Therefore it will always return the buffer * size that was given to the constructor. */ public int available() throws IOException { if ((this.outEnd-this.outStart)<this.outBuff.length-300){ inflate(); } return this.outEnd-this.outStart; } public long skip(long n) throws IOException{ long skipped=0; byte b[]=new byte[this.buffsize]; while(skipped<n && this.status != GZipInputStream.FINISHED){ skipped+=this.read(b); } return skipped; } public int read() throws IOException { if ((this.outEnd-this.outStart)==0){ inflate(); } if(this.outEnd-this.outStart==0 && this.inStreamEnded){ // the input stream ended return -1; } else { return (this.outBuff[this.outStart++] + 256) & 255; } } public int read(byte b[]) throws IOException { return read(b, 0, b.length); } /* (non-Javadoc) * @see java.io.InputStream#read(byte[], int, int) */ public int read(byte b[], int off, int len) throws IOException { // inflate as much as possible if ((this.outEnd-this.outStart)<this.outBuff.length-300){ inflate(); } // we can process just min(b.length, this.avialableBytes) Bytes int av=this.available(); int copyBytes= len > av ? av: len; // copy avialable data from the ouputBuffer to 'b' // here ocurred an error once System.arraycopy(this.outBuff, this.outStart, b, off, copyBytes); this.outStart+=copyBytes; // return the number of copied bytes if(copyBytes!=0){ return copyBytes; } else { return -1; } } }
GZipOutputStream类:
/* * Created on Jun 28, 2007 at 8:01:42 AM. * * Copyright (c) 2007 Robert Virkus / Enough Software * * This file is part of J2ME Polish. * * J2ME Polish is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * J2ME Polish is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with J2ME Polish; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Commercial licenses are also available, please * refer to the accompanying LICENSE.txt or visit * http://www.j2mepolish.org for details. */ package zip; import java.io.IOException; import java.io.OutputStream; /** * <p>Generates GZIP or DEFLATE encoded input streams from an InputStream.</p> * * <p>Copyright Enough Software 2007 - 2008</p> * <pre> * history * Jun 28, 2007 - Simon creation * </pre> * @author Simon Schmitt, [email protected] */ public class GZipOutputStream extends OutputStream { /** * This constant triggers the normal deflate compression as described in rfc 1951. */ public static final int TYPE_DEFLATE=0; /** * This constant triggers the gzip compression that is the same as deflate with * some extra header information (see rfc 1952). */ public static final int TYPE_GZIP=1; private OutputStream outStream; private byte[] outputWindow; private byte[] plainDataWindow; private int outProcessed; private int plainPointer; private final static int HASHMAP_COUNT=4; ZipIntMultShortHashMap[] HM = new ZipIntMultShortHashMap[HASHMAP_COUNT+1]; private byte[] inputBuffer; // the input data is read in chunks from the inputStream private int inEnd; private int inStart; private int[] smallCodeBuffer; // we have to buffer the output, because it comes in different bitlength int[] huffmanCode; // all necessary informations to store the trees byte[] huffmanCodeLength; int[] distHuffCode; byte[] distHuffCodeLength; private int[] litCount; // the frequency of all literal symbols private int[] distCount; // the frequency of all distance symbols private int isize; // # of all processed bytes from inputStream private int crc32; // crc32 checksum of " private int[] crc32Table=new int[256]; private int status; private final static int STREAM_INIT=0; private final static int STREAMING=4; private boolean lastBlock; // needed to set the lastBlock bit in the header private boolean lz77active; // activates the lz77 compression private int BTYPE; // =1 for fixed Tree this happens in case of huffmanwindowsize=0 // =2 in case of dynamix Trees /** * * @param outputStream stream to write the compressed data in * @param size prefered size of the internal buffer * @param compressionType TYPE_GZIP or TYPE_DEFLATE * @param plainWindowSize this size is important for the lz77 search. Larger values * will result in better compression. Maximum is 32768. * @param huffmanWindowSize this size is important for the huffmanencoding. A large * value will result to a better frequency statistic and therefore to a better compression. * @throws IOException might be thrown in case that the inputStream can not be read, * the outputStream can not be written into or in case of wrong arguments * * @see #TYPE_DEFLATE * @see #TYPE_GZIP */ public GZipOutputStream(OutputStream outputStream, int size, int compressionType, int plainWindowSize, int huffmanWindowSize) throws IOException { this.outStream = outputStream; this.inputBuffer=new byte[size+300]; this.litCount=new int[286]; this.distCount=new int[30]; this.smallCodeBuffer = new int[2]; // check plainWindowSize; this triggers the LZ77 compression if (plainWindowSize > 32768){ throw new IllegalArgumentException("plainWindowSize > 32768"); } if (plainWindowSize>=100){ this.plainDataWindow = new byte[(plainWindowSize/HASHMAP_COUNT)*HASHMAP_COUNT]; this.lz77active=true; } else { this.plainDataWindow=null; this.lz77active=false; } // check the huffmanWindowSize; this also triggers dynamic/fixed trees if (huffmanWindowSize > 32768){ throw new IllegalArgumentException("plainWindowSize > 32768"); } if (huffmanWindowSize<1024 && huffmanWindowSize>0){ huffmanWindowSize=1024; } this.outputWindow = new byte[huffmanWindowSize]; if(huffmanWindowSize==0){ this.lastBlock=true; // fixed tree: write header, generate huffman codes this.BTYPE=1; newBlock(); this.status=GZipOutputStream.STREAMING; } else { this.BTYPE=2; this.status=GZipOutputStream.STREAM_INIT; } for (int i = 0; i < HASHMAP_COUNT; i++) { this.HM[i] = new ZipIntMultShortHashMap(2*1024); } // write GZIP header, if wanted if (compressionType==GZipOutputStream.TYPE_GZIP){ /* +---+---+---+---+---+---+---+---+---+---+ |ID1|ID2|CM |FLG| MTIME |XFL|OS | +---+---+---+---+---+---+---+---+---+---+*/ this.outStream.write(31); this.outStream.write(139); this.outStream.write(8); this.outStream.write(new byte[6]); this.outStream.write(255); } } public void close() throws IOException{ this.flush(); // append the final tree, in case of dynamic trees if (this.BTYPE==2){ // empty the huffmanwindow and force a new block on purpose, if there might // occur a new block before the last one. This is to make sure that the // final flag is set only for the "very" last one if ((this.outProcessed+8 +(this.inEnd-this.inStart)*8/3 >this.outputWindow.length)){ compileOutput(); } // compile the few remaining bits into the final block LZ77(true); this.lastBlock=true; compileOutput(); } else { // no final tree, just flush since there is just one single // and therefore final block LZ77(true); } writeFooter(); this.outStream=null; this.outputWindow=null; this.inputBuffer=null; this.litCount=null; } /** * It is strongly recomended NOT to call flush before close() since * close() is able to handle the flushing better itself. */ public void flush() throws IOException{ // flush inputBuffer -> LZ77 LZ77(false); // do not set to true, since we still need sth. for the last block } /* (non-Javadoc) * @see java.io.OutputStream#write(int) */ public void write(int b) throws IOException { if(this.inputBuffer.length == this.inEnd ){ // process the inputBuffer if we need space LZ77(false); } // append the byte to the input buffer this.inputBuffer[this.inEnd++]=(byte) b; // ... and refresh the isize as well as crc32 this.isize++; byte[] bb = new byte[1]; bb[0]=(byte)b; this.crc32 = ZipHelper.crc32(this.crc32Table, this.crc32, bb, 0, 1); } public void write(byte b[]) throws IOException { write(b, 0, b.length); } public void write(byte b[], int off, int len) throws IOException { int processed=0; // refresh checksum this.crc32 = ZipHelper.crc32(this.crc32Table, this.crc32, b, off, len); this.isize+=len; while (processed!=len){ // fill data in if(this.inputBuffer.length - this.inEnd>=len-processed){ // if more than necessary fits in System.arraycopy(b, processed+off, this.inputBuffer, this.inEnd, len-processed); this.inEnd+=len-processed; processed=len; } else{ System.arraycopy(b, processed+off, this.inputBuffer, this.inEnd, this.inputBuffer.length-this.inEnd); processed+=this.inputBuffer.length-this.inEnd; this.inEnd=this.inputBuffer.length; } // LZ77 the inputBuffer LZ77(false); } } /** * This function searches through all hashmaps and returns the best pointer * that was found. * * @param bestPointer this will hold the resulting distance and lenth pair {distance, length} * @param position position to look at in the inputbuffer * @return if there was a pointer found */ private boolean search4LZ77(int[] bestPointer, int position){ ZipIntMultShortHashMap.Element found = null; int[] pointer = new int[2]; bestPointer[1]=0; for (int i = 0; i < HASHMAP_COUNT; i++) { // retrieve and compare the best pointers out of each hashmap found=null; found = this.HM[i].get( (128+this.inputBuffer[position])<<16 | (128+this.inputBuffer[position+1])<<8 | (128+this.inputBuffer[position+2]) ); if (found!=null && found.size!=0){ searchHM4LZ77(found, pointer, position); if (pointer[1]> bestPointer[1]){ bestPointer[0]=pointer[0]; bestPointer[1]=pointer[1]; } } } // was a pointer found? return bestPointer[1]!=0; } /** * This function takes the result element of a hashmap to look for previous reoccurrences of the data in the inputbuffer * starting at position. The best matching distance and length pair is return in * pointer. * * @param found the hashmap result of the thee chars * @param pointer distance and lenth pair {distance, length} * @param position position in the inputbuffer */ private void searchHM4LZ77(ZipIntMultShortHashMap.Element found, int[] pointer, int position) { int length; // check this.plainPointer-found0.values[k] from the end since shortest pointers are at the end int bestK=0, bestLength=0; for (int k = found.size-1; k >=0 ;k--) { length=3; int comparePointer=100000; // deal with index out of bounds in case of finish while(length<258 && position+length < this.inputBuffer.length){ if (found.values[k]<this.plainPointer){ comparePointer=(found.values[k]+ ( length % (this.plainPointer - found.values[k]) ) ) % this.plainDataWindow.length; } else { comparePointer=(found.values[k]+ ( length % (this.plainPointer + this.plainDataWindow.length - found.values[k]) ) ) % this.plainDataWindow.length; } // break, if the compared chars differ if (this.inputBuffer[position+length] == this.plainDataWindow[ comparePointer ] ){ length++; } else { break; } } // compare the recently found pointer pair with the currently best if (length>bestLength){ bestK=k; bestLength=length; if (length==258){ break; } } } // encode the pointer pointer[0]= /*distance=*/ (this.plainPointer-found.values[bestK] + this.plainDataWindow.length )% this.plainDataWindow.length; pointer[1]=/*length=*/bestLength; } /** * This function puts the pointer into the outputwindow where it will be taken off * when the huffman compression takes place. * * @param distance the distance information of the pointer * @param length the length information of the pointer * @throws IOException this might happen, when there occurr errors while writing into the outputStream */ private void encodePointer(int distance, int length) throws IOException{ int litlen; byte litextra; int di; int distExtra; //#debug info //# System.out.println(length + "-Tupel found at " + distance + "back"); // compute length information di = ZipHelper.encodeCode(ZipHelper.LENGTH_CODE, length); litlen=257+di; litextra=(byte) (length - ZipHelper.LENGTH_CODE[di*2+1]); //compute distance information di = ZipHelper.encodeCode(ZipHelper.DISTANCE_CODE, distance); distExtra = distance -ZipHelper.DISTANCE_CODE[di*2+1]; // write buffer information for compiler if(this.outputWindow.length!=0){ this.outputWindow[this.outProcessed]=(byte)255; // special distance stuff this.outputWindow[this.outProcessed+1]= (byte) (litlen -255); // 0 is reserved!! this.outputWindow[this.outProcessed+2]= litextra;// value of extra bits this.outputWindow[this.outProcessed+3]=(byte)(di);// id of Table == code this.outputWindow[this.outProcessed+4]=(byte)(distExtra&0xFF); this.outputWindow[this.outProcessed+5]=(byte)(distExtra>>8 &0xFF); this.outputWindow[this.outProcessed+6]=(byte)(distExtra>>16 &0xFF); this.outProcessed+=6; // 7-1 // increase distCount this.litCount[litlen]++; this.distCount[di]++; } else { // write direct when dynamic huffman is switched off // write litlen + extra bytes pushSmallBuffer(this.huffmanCode[litlen],this.huffmanCodeLength[litlen]); pushSmallBuffer(litextra, (byte) ZipHelper.LENGTH_CODE[2*(litlen-257)]); // write distance code + extra info pushSmallBuffer(this.distHuffCode[di],this.distHuffCodeLength[di]); pushSmallBuffer(distExtra, (byte) ZipHelper.DISTANCE_CODE[di*2]); } } /** * This method fills a single char into the huffmanwindow or in the outputStream * depending on the huffmanmode * * @param position take the char from inputbuffer at this postion * @throws IOException */ private void encodeChar(int position) throws IOException { int val = (this.inputBuffer[position] + 256)&255; if(this.outputWindow.length!=0){ // and increase the literal Count this.litCount[val]++; // fill the plain char in the output window ... encode 255 this.outputWindow[this.outProcessed] = (byte)val; if(val==255){ //special encoding for 255 this.outProcessed++; this.outputWindow[this.outProcessed]=(byte)0; } } else{ pushSmallBuffer(this.huffmanCode[val],this.huffmanCodeLength[val]); } } /** * This function applies the LZ77 algorithm on the inputBuffer. * The inputBuffer is cleared completely and filled into * outputWindow. If outputWindow is full, compileOutput is called. * @throws IOException */ //public int l=0; private void LZ77(boolean finish) throws IOException{ // prepate the inputbuffer if (this.inStart!=0){ System.arraycopy(this.inputBuffer, this.inStart, this.inputBuffer, 0, this.inEnd-this.inStart); this.inEnd-=this.inStart; this.inStart=0; } // do not leave sth. behind if finish is enabled int upTo; if(finish){ upTo=this.inEnd; } else { upTo=this.inEnd-300; } // process the inputbuffer int pointer[] = new int[2]; int lastpointer[] = new int[2]; int distance; int length; int i; for (i = 0; i < upTo; ) { length=1; distance=0; // TODO what about lazy matching?? // search for reoccurrence, if there is enough input data left if(this.lz77active && i < upTo-2 && search4LZ77(pointer, i)){ if (pointer[1]>lastpointer[1]){ // put single char of (i-1) and keep searching lastpointer[0]=pointer[0]; lastpointer[1]=pointer[1]; } else { distance=pointer[0]; length=pointer[1]; } } // the pointer is not allowed to exceed the inputBuffer!! if (finish && upTo-i<length){ length=upTo-i; } // encode pointer or char if( length>2 ){ encodePointer(distance, length); }else{ encodeChar(i); } // check if Huffman compression is requested if (this.outputWindow.length!=0){ // check, if we have to compile the output this.outProcessed++; // there has to be enough space for a length, distance pair if (this.outProcessed+8>this.outputWindow.length){ // if outputWindow full : call compileOutput compileOutput(); } } // refresh the plain window, if lz77 is active if (this.lz77active){ for (int k = 0; k < length; k++) { this.plainDataWindow[this.plainPointer] = this.inputBuffer[i+k]; // add the bytes to the hashmap this.HM[this.plainPointer/(this.plainDataWindow.length/HASHMAP_COUNT)].put( (128+this.inputBuffer[i+k])<<16 | (128+this.inputBuffer[i+k+1])<<8 | (128+this.inputBuffer[i+k+2]) , (short) this.plainPointer); // clear hashmap if (++this.plainPointer%(this.plainDataWindow.length/HASHMAP_COUNT)==0){ // wrap around if (this.plainPointer==this.plainDataWindow.length){ this.plainPointer=0; } this.HM[(this.plainPointer/(this.plainDataWindow.length/HASHMAP_COUNT)) % HASHMAP_COUNT].clear(); } } } // i is just increased here i+=length; } this.inStart=i; } /** * This function generates and writes the header for a new block. * Therefore it has to calculate and compress the huffman trees. * * @throws IOException */ private void newBlock() throws IOException{ if(this.status==GZipOutputStream.STREAM_INIT){ this.status=GZipOutputStream.STREAMING; } else { pushSmallBuffer(this.huffmanCode[256],this.huffmanCodeLength[256]); // write old 256, write blockheader } if(this.lastBlock){ pushSmallBuffer(1, (byte)1); //#debug System.out.println("final block"); } else{ pushSmallBuffer(0, (byte)1); } pushSmallBuffer(this.BTYPE, (byte)2); this.huffmanCode = new int[286]; this.huffmanCodeLength = new byte[286]; this.distHuffCode = new int[30]; this.distHuffCodeLength = new byte[30]; if (this.BTYPE==1){ ZipHelper.genFixedTree(this.huffmanCode, this.huffmanCodeLength, this.distHuffCode, this.distHuffCodeLength); } else if (this.BTYPE==2) { // fake two distance codes, if there are none for (int i = 0; i < 2; i++) { if (this.distCount[i]==0){ this.distCount[i]=1; } } // generate dynamic Huffmantree for Literals + Length this.litCount[256]=1; // theoretisch kann man es auch auf 2 setzen, was das Problem jedoch nicht loest ZipHelper.genTreeLength(this.litCount, this.huffmanCodeLength,15); ZipHelper.genHuffTree(this.huffmanCode, this.huffmanCodeLength); ZipHelper.revHuffTree(this.huffmanCode, this.huffmanCodeLength); // generate dynamic Huffmantree for Distances ZipHelper.genTreeLength(this.distCount, this.distHuffCodeLength,15); ZipHelper.genHuffTree(this.distHuffCode, this.distHuffCodeLength); ZipHelper.revHuffTree(this.distHuffCode, this.distHuffCodeLength); // save tree compressTree(this.huffmanCodeLength, this.distHuffCodeLength); // clear the counter for (int i = 0; i < 286; i++) { this.litCount[i]=0; } for (int i = 0; i < 30; i++) { this.distCount[i]=0; } } } /** * This function applies the huffmanencoding on the collected data in outputWindow. * The huffman encoded data is then written into the outputStream. * * @throws IOException */ private void compileOutput() throws IOException{ //#debug System.out.println(" compile Output; new Block"); // generate & store the tree newBlock(); int litlen, litextra,di,distExtra; // write the data int val=0; for (int i = 0; i < this.outProcessed; i++) { val=this.outputWindow[i]; if(val<0){ val+=256; } if(val!=255){ pushSmallBuffer(this.huffmanCode[val],this.huffmanCodeLength[val]); } else { if (val==255){ i++; if (this.outputWindow[i]==0){ // 255 char pushSmallBuffer(this.huffmanCode[255],this.huffmanCodeLength[255]); } else if(this.outputWindow[i]>0) { // compile encoded pointer // length information litlen=255+this.outputWindow[i]; i++; litextra=this.outputWindow[i]; i++; // distance information di=this.outputWindow[i]; i++; distExtra= ((this.outputWindow[i]+256) & 255) | ((this.outputWindow[i+1]+256) &255)<<8 | ((this.outputWindow[i+2]+256) &255) <<16; i+=3; //distance=distExtra + ZipUtil.DISTANCE_CODE[di*2+1]; // write litlen + extra bytes pushSmallBuffer(this.huffmanCode[litlen],this.huffmanCodeLength[litlen]); pushSmallBuffer(litextra, (byte) ZipHelper.LENGTH_CODE[2*(litlen-257)]); // write distance code + extra info pushSmallBuffer(this.distHuffCode[di],this.distHuffCodeLength[di]); pushSmallBuffer(distExtra, (byte) ZipHelper.DISTANCE_CODE[di*2]); i--; } else { throw new IOException("illegal code decoded"); } } } } this.outProcessed=0; } /** * This function finishes the last block and writes the checksums. * * @throws IOException */ private void writeFooter() throws IOException{ // write current 256 pushSmallBuffer(this.huffmanCode[256],this.huffmanCodeLength[256]); //#debug System.out.println(" wrote final 256;"); // flush the upto the byte boundrary if ((this.smallCodeBuffer[1]&7) != 0){ pushSmallBuffer(0, (byte) (8-(this.smallCodeBuffer[1]&7))); } //write CRC, count this.outStream.write(this.crc32 & 255); this.outStream.write((this.crc32>>>8) & 255); this.outStream.write((this.crc32>>>16) & 255); this.outStream.write((this.crc32>>>24) & 255); this.outStream.write(this.isize & 255); this.outStream.write((this.isize>>>8) & 255); this.outStream.write((this.isize>>>16) & 255); this.outStream.write((this.isize>>>24) & 255); // close the stream this.outStream.flush(); this.outStream.close(); //#debug System.out.println(" output finished"); } /** * This function compresses and writes the literal/length as well as the distance trees. * At first repeatcodes are determined, then the miniTree is generated and after all they * are written into the outputStream. * * @param huffmanCodeLength * @param distHuffCodeLength * @throws IOException */ private void compressTree(byte[] huffmanCodeLength, byte[] distHuffCodeLength) throws IOException{ int HLIT=285; int HDIST=29; while(huffmanCodeLength[HLIT]==0 && HLIT>29){HLIT--;} HLIT++; // dont worry: empty distance tree workaround is in newBlock(); while(distHuffCodeLength[HDIST]==0 && HDIST>0){HDIST--;} HDIST++; // # of all Distance Symbols // merge Hlit + Hdist byte[] len=new byte[HLIT+HDIST]; int j=0; for (int i = 0; i < HLIT; i++) { len[j]=huffmanCodeLength[i]; //System.out.println(" " + j + " " + huffmanCode[j] + ", " + huffmanCodeLength[j]); j++; } for (int i = 0; i < HDIST; i++) { len[j]=distHuffCodeLength[i]; //System.out.println(" " + j + " " + distHuffCode[i] + ", " + distHuffCodeLength[i]); j++; } int[] miniHuffData= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; // fill repeatcodes in and count them byte[] outLitLenDist=new byte[HLIT+HDIST]; int outCount=0; int[] miniCodeCount=new int[19]; // count of used miniCodes short k; for (int i = 0; i < len.length; i++) { if( i+3< len.length && len[i]==len[i+1] && len[i]==len[i+2] && len[i]==len[i+3]){ if(len[i]==0){ outLitLenDist[outCount]=0; // ausgliedern //System.out.println("wrinting outCount="+outCount + " = 0"); k=4; while(i+k<len.length && len[i]==len[i+k] && k<139){k++;} if (k<11+1) { //System.out.println("writing outCount="+outCount + " = 17"); outLitLenDist[outCount+1]=17; outLitLenDist[outCount+2]=(byte) (k-3-1); } else{ //System.out.println("writing outCount="+outCount + " = 18"); //System.out.println(" repeat count = " + (k-1)); outLitLenDist[outCount+1]=18; outLitLenDist[outCount+2]=(byte) (k-11-1); } i+=k-1; }else{ // encode repeat code ; char * n -> char, 16, n //System.out.println("writing outCount="+outCount + " = "+len[i]); outLitLenDist[outCount]=len[i]; outLitLenDist[outCount+1]=16; //System.out.println("writing outCount="+outCount + " = 16"); // repeat count k=4; while(i+k<len.length && len[i]==len[i+k] && k<7){k++;} outLitLenDist[outCount+2]= (byte) (k-4); i+=k-1; } // add value to statistic miniCodeCount[outLitLenDist[outCount]]++; // add repeat code to statistic miniCodeCount[outLitLenDist[outCount+1]]++; // +3 == 2 +1 outCount+=2; } else{ //System.out.println("wrinting outCount="+outCount + " = "+ len[i]); outLitLenDist[outCount]=len[i]; miniCodeCount[outLitLenDist[outCount]]++; } outCount++; } // generate mini Tree byte[] miniHuffCodeLength=new byte[19]; int[] miniHuffCode=new int[19]; int i=0; ZipHelper.genTreeLength(miniCodeCount, miniHuffCodeLength,7); ZipHelper.genHuffTree(miniHuffCode, miniHuffCodeLength); ZipHelper.revHuffTree(miniHuffCode, miniHuffCodeLength); // write Header-Header pushSmallBuffer(HLIT-257, (byte)5); pushSmallBuffer(HDIST-1, (byte)5); int HCLEN=19-1; while(miniHuffCodeLength[miniHuffData[HCLEN]]==0 && HCLEN>0){HCLEN--;} HCLEN++; pushSmallBuffer(HCLEN-4, (byte)4); // write Mini-Tree for (i = 0; i < HCLEN; i++) { //System.out.println("" + i + "=" +miniHuffData[i] + ":/t" + miniHuffCodeLength[miniHuffData[i]]); pushSmallBuffer(miniHuffCodeLength[miniHuffData[i]], (byte)3); } //#mdebug System.out.println(" HLIT: " + HLIT); System.out.println(" HDIST: " + HDIST); System.out.println(" HCLEN: " + HCLEN); //#enddebug // write Huffmanntree for (i = 0; i < outCount; i++) { // write code //System.out.println("wrinting i="+i + " = " + outLitLenDist[i]); pushSmallBuffer(miniHuffCode[outLitLenDist[i]], miniHuffCodeLength[outLitLenDist[i]]); // add special treatment if (outLitLenDist[i]>15){ switch (outLitLenDist[i]) { case 16: pushSmallBuffer(outLitLenDist[i+1], (byte)2); i++; break; case 17: pushSmallBuffer(outLitLenDist[i+1], (byte)3); i++; break; default: // 18 pushSmallBuffer(outLitLenDist[i+1], (byte)7); i++; break; } } //System.out.println(outLitLenDist[i] + " == code: " + miniHuffCode[outLitLenDist[i]]); } } /** * This function is able to write bits into the outStream. Please * mind that is uses a buffer. You might have to flush it, by giving zeros. * * @param val The bitsequence as an integer. * @param len The number of bits to process. * @throws IOException in case Errors within the outputStream ocurred */ private void pushSmallBuffer(int val, byte len) throws IOException{ int smallBuffer0 = this.smallCodeBuffer[0]; int smallBuffer1 = this.smallCodeBuffer[1]; // add the given data smallBuffer0 &= ~( ((1<<len)-1) << smallBuffer1); smallBuffer0|= (val<<smallBuffer1); smallBuffer1+=len; // clear the buffer except for a fraction of a reamining byte while(smallBuffer1>=8){ this.outStream.write(smallBuffer0&255); smallBuffer0>>>=8; smallBuffer1-=8; } this.smallCodeBuffer[0]=smallBuffer0; this.smallCodeBuffer[1]=smallBuffer1; } }
ZipHelper类:
/* * Created on Jun 25, 2007 at 11:12:23 AM. * * Copyright (c) 2007 Robert Virkus / Enough Software * * This file is part of J2ME Polish. * * J2ME Polish is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * J2ME Polish is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with J2ME Polish; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Commercial licenses are also available, please * refer to the accompanying LICENSE.txt or visit * http://www.j2mepolish.org for details. */ package zip; import java.io.IOException; import java.io.InputStream; /** * <p>Provides some helper methods for ZipInputStream and ZipOutputStream.</p> * * <p>Copyright Enough Software 2007 - 2008</p> * <pre> * history * Jun 25, 2007 - Simon creation * </pre> * @author Simon Schmitt, [email protected] */ public final class ZipHelper { // constants: public static final int[] LENGTH_CODE= { 0 , 3 , 0 , 4 , 0 , 5 , 0 , 6 , 0 , 7 , 0 , 8 , 0 , 9 , 0 , 10 , 1 , 11 , 1 , 13 , 1 , 15 , 1 , 17 , 2 , 19 , 2 , 23 , 2 , 27 , 2 , 31 , 3 , 35 , 3 , 43 , 3 , 51 , 3 , 59 , 4 , 67 , 4 , 83 , 4 , 99 , 4 , 115 , 5 , 131 , 5 , 163 , 5 , 195 , 5 , 227 , 0 , 258 }; public static final int[] DISTANCE_CODE= { 0 , 1 , 0 , 2 , 0 , 3 , 0 , 4 , 1 , 5 , 1 , 7 , 2 , 9 , 2 , 13 , 3 , 17 , 3 , 25 , 4 , 33 , 4 , 49 , 5 , 65 , 5 , 97 , 6 , 129 , 6 , 193 , 7 , 257 , 7 , 385 , 8 , 513 , 8 , 769 , 9 , 1025 , 9 , 1537 , 10 , 2049 , 10 , 3073 , 11 , 4097 , 11 , 6145 , 12 , 8193 , 12 , 12289 , 13 , 16385 , 13 , 24577 }; public static final int encodeCode(int[] Code , int distance){ int i=0; //while(i<Code.length/2 && distance>=Code[i*2+1]){i++;} while( i < (Code.length>>1) && distance >= Code[(i<<1)+1]){ i++; } // now: distance < Code[i*2+1] return i-1; } /** * This function generates the huffmann codes according to a given set of bitlegth. * * @param huffmanCode will be the resulting code pattern * @param huffmanCodeLength has to be the bitlegth for each code */ public static final void genHuffTree(int[] huffmanCode, byte[] huffmanCodeLength){ int maxbits=0; // generate bitlen_count for (int i = 0; i < huffmanCodeLength.length; i++) { int length = huffmanCodeLength[i]; maxbits= maxbits > length ? maxbits : length; } maxbits++; short[] bitlen_count = new short[maxbits]; for (int i = 0; i < huffmanCodeLength.length; i++) { bitlen_count[huffmanCodeLength[i]]++; } int code=0; int[] next_code=new int[maxbits]; bitlen_count[0]=0; // find the Codes for each smallest Code within all of the same bitlength for (int bits = 1; bits < maxbits; bits++) { //code=(code + bitlen_count[bits-1])*2; code=(code + bitlen_count[bits-1])<<1; next_code[bits]=code; } // generate all codes by adding 1 to the predecessor for (int i = 0; i < huffmanCode.length; i++) { byte length = huffmanCodeLength[i]; if (length!=0){ huffmanCode[i]=next_code[length]; next_code[length]++; } } } public final static void revHuffTree(int[] huffmanCode, byte[] huffmanCodeLength){ // reverse all: int tmp; int reversed; for (int i = 0; i < huffmanCode.length; i++) { tmp=huffmanCode[i]; reversed=0; for (int j = 0; j < huffmanCodeLength[i]; j++) { reversed|=((tmp>>>j)&1); reversed<<=1; } huffmanCode[i] = reversed>>>1; } } /** * Generate the fixed huffman trees as described in the rfc * * @param huffmanCode - pointer to where the code shall be inserted * @param huffmanCodeLength * @param distHuffCode * @param distHuffCodeLength */ public final static void genFixedTree(int[] huffmanCode, byte[] huffmanCodeLength, int[] distHuffCode, byte[] distHuffCodeLength){ int i; // huffmanCodes for (i= 0; i <= 143; i++) { huffmanCode[i]=48 + i; //48==00110000 huffmanCodeLength[i]=8; } for (i = 144; i <= 255; i++) { huffmanCode[i]=400 + i-144; //400==110010000 huffmanCodeLength[i]=9; } for (i = 256; i <= 279; i++) { huffmanCode[i]=i-256; //0==0 huffmanCodeLength[i]=7; } for (i = 280; i < 286; i++) { huffmanCode[i]=192+i-280; //192==11000000 huffmanCodeLength[i]=8; } // reverse all: ZipHelper.revHuffTree(huffmanCode, huffmanCodeLength); // distHuffCode for non fixed DISTANCE tree for (int j = 0; j < distHuffCode.length; j++) { distHuffCode[j]=j; distHuffCodeLength[j]=5; } // reverse all: ZipHelper.revHuffTree(distHuffCode, distHuffCodeLength); } /** * Compute the tree length according to the frequency of each code * * @param count * @param huffmanCodeLength * @param max_len */ public static final void genTreeLength(int[] count, byte[] huffmanCodeLength, int max_len){ int[] knotCount = new int[count.length]; int[] knotPointer = new int[count.length]; // initialise the knots for (short i = 0; i < count.length; i++) { if (count[i]!=0){ knotCount[i]=count[i]; }else{ knotCount[i]=Integer.MAX_VALUE; } knotPointer[i]= i; } // look for the smalest two knots int s1=0, s2=0; while(true){ // find Min1 and Min2 if(knotCount[0]<knotCount[1]){ s1=0; s2=1; } else { s1=1; s2=0; } for (int i = 2; i < count.length; i++) { if (knotCount[i]<knotCount[s1]){ s2=s1; s1=i; } else if (knotCount[i]<knotCount[s2]){ s2=i; } } // if we got all Minima -> exit if(knotCount[s2]== Integer.MAX_VALUE){ break; } // merge the knots knotCount[s1]+=knotCount[s2]; int tmp=knotPointer[s2]; knotCount[s2]=Integer.MAX_VALUE; // set all knot pointer of Min2 -> Min1, add one to CodeLength, and remove old knots from search for (int i = 0; i <count.length; i++) { if(knotPointer[i]==tmp){ knotPointer[i]=knotPointer[s1]; huffmanCodeLength[i]++; } else if (knotPointer[i]==knotPointer[s1]){ huffmanCodeLength[i]++; } } } // check for overflow int overflowCount=0; for (int i = 0; i < huffmanCodeLength.length; i++) { if (huffmanCodeLength[i]>max_len) { overflowCount++; } } // fix the overflow if (overflowCount!=0){ //#debug System.out.println(" fixing " + overflowCount + " overflows because of max=" + max_len); // list the overflows short overflows[]= new short[overflowCount]; overflowCount=0; for (short i = 0; i < huffmanCodeLength.length; i++) { if (huffmanCodeLength[i]>max_len) { overflows[overflowCount++]=i; } } // find the index of the smalest node int minNode=0; for (int i = 0; i < huffmanCodeLength.length; i++) { if (huffmanCodeLength[i]!=0 && huffmanCodeLength[minNode]>huffmanCodeLength[i]){ minNode=i; } } // ok lets go and fix it.... int exendableNode; int overflow1, overflow2; while(overflowCount!=0){ exendableNode=minNode; // find the largest expandable length for (int i = 0; i < huffmanCodeLength.length; i++) { if(huffmanCodeLength[i]<max_len && huffmanCodeLength[exendableNode]<huffmanCodeLength[i]){ exendableNode=i; } } // find the deepest two overflows overflow1=0; overflow2=0; for (int i = 0; i < overflows.length; i++) { if(huffmanCodeLength[overflows[i]]> huffmanCodeLength[overflow1]){ overflow1=overflows[i]; } else if(huffmanCodeLength[overflows[i]]== huffmanCodeLength[overflow1]) { overflow2=overflows[i]; } } // insert one of them at the best exendableNode huffmanCodeLength[exendableNode]++; huffmanCodeLength[overflow1]=huffmanCodeLength[exendableNode]; // lift the other one huffmanCodeLength[overflow2]--; // refresh the overflow count overflowCount--; if (huffmanCodeLength[overflow2]==max_len){ overflowCount--; } } } } /** * This function converts the representation of a huffman tree from * table to tree structure. * * @param huffmanCode input * @param huffmanCodeLength input * @param huffmanData input * @param huffmanTree resulting tree */ public static final void convertTable2Tree(int[] huffmanCode, byte[] huffmanCodeLength, int[] huffmanData, short[] huffmanTree){ for (int i = 0; i < huffmanTree.length; i++) { huffmanTree[i]=0; } short pointer; short nextNode=1; // fill each code in short j; for (short i = 0; i < huffmanCode.length; i++) { if (huffmanCodeLength[i]!=0){ pointer=0; for (j = 0; j < huffmanCodeLength[i]; j++) { if(huffmanTree[pointer*2]==0){ // undefined internal NODE huffmanTree[pointer*2]=nextNode++; huffmanTree[pointer*2+1]=nextNode++; } // go to known internal NODE pointer=huffmanTree[pointer*2+ ((huffmanCode[i]>>>j)&1)]; } // set empty LEAF to data if (pointer<0){ //#debug error System.out.println("error pointer=-1"); } huffmanTree[pointer*2]=-1; huffmanTree[pointer*2+1]=(short)huffmanData[i]; } } } /** * This function parses and pops one huffmancode out of the given buffer * and returs the associated value. * * @param smallCodeBuffer the buffer that is parsed * @param huffmanTree is the representation of the tree * @return the data represented by the parsed huffman code * @throws IOException */ public static final int deHuffNext(long[] smallCodeBuffer, short[] huffmanTree) throws IOException{ if (smallCodeBuffer[1]<15){ //#debug error System.out.println("smallCodebuffer is too small"); } short pointer=0; while(huffmanTree[pointer*2]!=-1){ // go to next internal NODE pointer=huffmanTree[pointer*2+ (int)(smallCodeBuffer[0]&1)]; smallCodeBuffer[0]>>>=1; smallCodeBuffer[1]--; if (pointer==0){ //System.out.println("small code buffer DAta "+smallCodeBuffer[0] + "/n"); // error: in walking tree no leaf found throw new IOException("5"); } } // return the data return huffmanTree[pointer*2+1]; } /** * This function skips the Gzip Header of a given stream. * @param in the stream * @throws IOException ocurrs if the stream is not readable */ public static final void skipheader(InputStream in) throws IOException{ // check magic number and compression algorithm if (in.read()!=31 || in.read()!=139 || in.read()!=8){ // wrong gzip header throw new IOException("0"); } int flag = in.read(); in.skip(6); // skip FEXTRA if((flag & 4)==4){ in.skip( in.read() | in.read()<<8); } // skip FNAME if((flag&8)==8){ while(in.read()!=0){ // } } // skip FCOMMENT if((flag&16)==16){ while(in.read()!=0){ // } } // skip FHCRC if((flag & 2)==2){ in.skip(2); } } /** * In order to compute the CRC checksum fast it is crucial to * have a precomputed table. * * @param table store the table here */ private static final void initCrc32Table(int[] table){ int c; int n,k; for(n=0; n<256; n++){ c=n; for(k=0; k<8;k++){ if((c&1)==1){ c=0xedb88320 ^ (c >>> 1); } else{ c>>>=1; } } table[n]=c; } } /** * This function computes / refreshes a crc32 checksum. * * @param table this is the precomputed crc32 table * @param crc set this value to zero if starting the computation * or to the last retrieved value when processing * further data. * @param buffer the buffer to be walked * @param off the offset to start * @param len the amount of bytes to process * @return the new/refreshed crc checksum */ public static final int crc32(int[] table, int crc, byte[] buffer, int off, int len){ if (table[2]==0){ initCrc32Table(table); } crc = crc ^ 0xffffffff; for(int n=0; n<len;n++){ crc=table[(crc^buffer[n+off]) & 0xff]^(crc>>>8); } return 0xffffffff ^ crc ; } }
ZipIntMultShortHashMap类:
/* * Created on 30-Nov-2005 at 23:12:37. * * Copyright (c) 2005 Robert Virkus / Enough Software * * This file is part of J2ME Polish. * * J2ME Polish is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * J2ME Polish is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with J2ME Polish; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Commercial licenses are also available, please * refer to the accompanying LICENSE.txt or visit * http://www.j2mepolish.org for details. */ package zip; /** * <p>Provides the functionality of the J2SE java.util.HashMap for J2ME applications and uses reference checks for comparing keys.</p> * <p>WARNING: Only use this implementation when you can ensure that you always * use the original keys! (see below) * </p> * <p>In contrast to the java.util.Hashtable (which is available on J2ME platforms), * this implementation is not synchronized and faster. This implementation * also uses only reference checks (==) for testing keys and values on * equality instead of calling equals() for comparing them. This is considerably * faster, but you need to ensure that you only use references (=the original) keys when * storing or retrieving values. * </p> * <p>This implementation uses chains for resolving collisions, that means * when a key-value pair has the same hash code as a previous inserted item, * the new item is linked to the previous item. Depending on your situation * The OpenAddressingHashMap implementation might be better, especially when you * do not have many collisions (items with the same hash code). * </p> * * <p>Copyright (c) Enough Software 2005 - 2008</p> * <pre> * history * 30-Nov-2005 - rob creation * 28-Jun-2007 - Simon boiled down to short arrays * </pre> * @author Robert Virkus, [email protected] */ public class ZipIntMultShortHashMap { // /** The default capacity is 16 */ // public static final int DEFAULT_INITIAL_CAPACITY = 16; /** The default load factor is 75 (=75%), so the HashMap is increased when 75% of it's capacity is reached */ public static final int DEFAULT_LOAD_FACTOR = 75; public static final int SUB_ELEMENT_SIZE =3; private final int loadFactor; private Element[] buckets; private final boolean isPowerOfTwo; private int size; /*/** * Creates a new HashMap with the default initial capacity 16 and a load factor of 75%. */ /*public ZipIntMultShortHashMap() { this( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR ); }*/ /** * Creates a new HashMap with the specified initial capacity. * * @param initialCapacity the initial number of elements that this map can hold without needing to * increase it's internal size. */ public ZipIntMultShortHashMap(int initialCapacity ) { this( initialCapacity, DEFAULT_LOAD_FACTOR ); } /** * Creates a new HashMap with the specified initial capacity and the specified load factor. * * @param initialCapacity the initial number of elements that this map can hold without needing to * increase it's internal size. * @param loadFactor the loadfactor in percent, a number between 0 and 100. When the loadfactor is 100, * the size of this map is only increased after all slots have been filled. */ public ZipIntMultShortHashMap(int initialCapacity, int loadFactor) { initialCapacity = (initialCapacity * 100) / loadFactor; // check if initial capacity is a power of 2: int capacity = 1; while (initialCapacity > capacity) { capacity <<= 1; } this.isPowerOfTwo = (capacity == initialCapacity); //System.out.println("isPowerOfTwo: " + this.isPowerOfTwo ); this.buckets = new Element[ initialCapacity ]; this.loadFactor = loadFactor; } /* (non-Javadoc) * @see de.enough.polish.util.Map#put(java.lang.Object, java.lang.Object) */ public boolean put( int key, short value ) { if ( (this.size * 100) / this.buckets.length > this.loadFactor ) { increaseSize(); } int index; if (this.isPowerOfTwo) { index = (key & 0x7FFFFFFF) & (this.buckets.length - 1); } else { index = (key & 0x7FFFFFFF) % this.buckets.length; } Element element = this.buckets[ index ]; if (element == null) { // new List // new element element = new Element( key, new short[SUB_ELEMENT_SIZE] ); element.values[0]=value; element.size++; //==1 this.buckets[index] = element; this.size++; return true; } else { // add a value to the array Element lastElement = element; do { if (element.key == key ) { // element found! element.size++; // add data if(element.size==element.values.length){ // increase the value array short[] newValues = new short[element.values.length*2];//TODO constant System.arraycopy(element.values, 0, newValues, 0, element.values.length); element.values=newValues; } element.values[element.size-1]=value; return true; } lastElement = element; element = element.next; } while ( element != null ); // insert new element at the end since no other was found // new element element = new Element( key, new short[SUB_ELEMENT_SIZE] ); element.values[0]=value; element.size++; //==1 this.buckets[index] = element; this.size++; lastElement.next = element; return true; } } /* (non-Javadoc) * @see de.enough.polish.util.Map#get(java.lang.Object) */ public Element get( int key ) { int index; if (this.isPowerOfTwo) { index = (key & 0x7FFFFFFF) & (this.buckets.length - 1); } else { index = (key & 0x7FFFFFFF) % this.buckets.length; } Element element = this.buckets[ index ]; if (element == null) { return null; } do { if (element.key == key ) { return element; } element = element.next; } while (element != null); return null; } /* (non-Javadoc) * @see de.enough.polish.util.Map#remove(java.lang.Object) */ public short remove( int key ) { int index; if (this.isPowerOfTwo) { index = (key & 0x7FFFFFFF) & (this.buckets.length - 1); } else { index = (key & 0x7FFFFFFF) % this.buckets.length; } Element element = this.buckets[ index ]; if (element == null) { //System.out.println("remove: No bucket found for key " + key + ", containsKey()=" + containsKey(key)); return -1; } Element lastElement = null; do { if (element.key == key ) { if (lastElement == null) { this.buckets[ index ] = element.next; } else { lastElement.next = element.next; } this.size--; return 1;//element.values; } lastElement = element; element = element.next; } while (element != null); //System.out.println("No element found for key " + key + ", containsKey()=" + containsKey(key)); return -1; } /* (non-Javadoc) * @see de.enough.polish.util.Map#isEmpty() */ public boolean isEmpty() { return (this.size == 0); } /* (non-Javadoc) * @see de.enough.polish.util.Map#size() */ public int size() { return this.size; } /* (non-Javadoc) * @see de.enough.polish.util.Map#containsKey(java.lang.Object) */ public boolean containsKey( int key ) { return get( key ) != null; } /* (non-Javadoc) * @see de.enough.polish.util.Map#containsValue(java.lang.Object) */ /*public boolean containsValue( short value ) { for (int i = 0; i < this.buckets.length; i++) { Element element = this.buckets[i]; while (element != null) { if (element.values == value ) { return true; } element = element.next; } } return false; }*/ /* (non-Javadoc) * @see de.enough.polish.util.Map#clear() */ public void clear() { for (int i = 0; i < this.buckets.length; i++) { this.buckets[i] = null; } this.size = 0; } /* (non-Javadoc) * @see de.enough.polish.util.Map#values() */ /*public short[] values() { return values( new short[ this.size ] ); }*/ /* (non-Javadoc) * @see de.enough.polish.util.Map#values(java.lang.Object[]) */ /*public short[] values(short[] objects) { int index = 0; for (int i = 0; i < this.buckets.length; i++) { Element element = this.buckets[i]; while (element != null) { objects[index] = element.values; index++; element = element.next; } } return objects; } */ /* (non-Javadoc) * @see de.enough.polish.util.Map#keys() */ public int[] keys() { int[] keys = new int[ this.size ]; int index = 0; for (int i = 0; i < this.buckets.length; i++) { Element element = this.buckets[i]; while (element != null) { keys[index] = element.key; index++; element = element.next; } } return keys; } /** * Returns String containing the String representations of all objects of this map. * * @return the stored elements in a String representation. */ /*public String toString() { StringBuffer buffer = new StringBuffer( this.size * 23 ); buffer.append( super.toString() ).append( "{/n" ); short[] values = values(); for (int i = 0; i < values.length; i++) { buffer.append( values[i] ); buffer.append('/n'); } buffer.append('}'); return buffer.toString(); }*/ /** * Increaases the internal capacity of this map. */ private void increaseSize() { int newCapacity; if (this.isPowerOfTwo) { newCapacity = this.buckets.length << 1; // * 2 } else { newCapacity = (this.buckets.length << 1) - 1; // * 2 - 1 } Element[] newBuckets = new Element[ newCapacity ]; for (int i = 0; i < this.buckets.length; i++) { Element element = this.buckets[i]; while (element != null) { int index; if (this.isPowerOfTwo) { index = (element.key & 0x7FFFFFFF) & (newCapacity - 1); } else { index = (element.key & 0x7FFFFFFF) % newCapacity; } Element newElement = newBuckets[ index ]; if (newElement == null ) { newBuckets[ index ] = element; } else { // add element at the end of the bucket: while (newElement.next != null) { newElement = newElement.next; } newElement.next = element; } Element lastElement = element; element = element.next; lastElement.next = null; } } this.buckets = newBuckets; } public static final class Element { public final int key; public short[] values; public short size; // TODO undefined == value<0 public Element next; public Element ( int key, short[] value ) { this.key = key; this.values = value; } } }
ZipUtil调用类:
package zip; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import javax.microedition.io.Connector; import javax.microedition.io.file.FileConnection; /** * 压缩:byte[] zipbyte=new ZipUtil().Filetobyte("/config.ini",true,GZipOutputStream.TYPE_DEFLATE); * ZipUtil.CreateFile(zipbyte,"file:///E:/config.g"); * * 解压:byte[] zipbyte=new ZipUtil().Filetobyte("/config.g",false,GZipInputStream.TYPE_DEFLATE); * String content=ZipUtil.Bytetostring(zipbyte); * ZipUtil.CreateFile(zipbyte,"file://localhost/root1/config.ini"); * * @author new soul * */ public final class ZipUtil { /** * 解压 * * @param data the input data to be decompressed * @return the resulting uncompressed byte array * @throws IOException */ public static byte[] decompress( byte[] data ,int type) throws IOException{ byte[] tmp=new byte[1024]; int read; GZipInputStream zipInputStream = new GZipInputStream(new ByteArrayInputStream( data ) ,1024 ,type,true); ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); // take from ZipInputStream and fill into ByteArrayOutputStream while ( (read=zipInputStream.read(tmp, 0, 1024))>0 ){ bout.write(tmp,0,read); } return bout.toByteArray(); } /** * 压缩 * * @param data the input data to be compressed * @return the resulting compressed byte array * @throws IOException */ public static byte[] compress( byte[] data ,int type) throws IOException{ if (data.length>1<<15){ return compress(data,type,1<<15,1<<15); } else{ return compress(data,type,data.length,data.length); } } /** * 压缩 * * @param data the input data to be compressed * @param plainWindowSize this size is important for the lz77 search. Larger values * will result in better compression. Maximum is 32768. * @param huffmanWindowSize this size is important for the huffmanencoding. A large * value will result to a better frequency statistic and therefore to a better compression. * @return the resulting compressed byte array * @throws IOException */ private static byte[] compress( byte[] data,int type,int plainWindowSize, int huffmanWindowSize) throws IOException{ ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); GZipOutputStream zipOutputStream = new GZipOutputStream(bout ,1024 ,type,plainWindowSize,huffmanWindowSize); zipOutputStream.write(data); zipOutputStream.close(); return bout.toByteArray(); } /** * 将本地文件转换成byte[] * @param res 文件名 * @param iszip zip OR unzip * @return byte[] */ public byte[] Filetobyte(String res,boolean iszip,int gtype){ InputStream zipis=null; int ic; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); byte[] myData,result=null; byte[] buffer = new byte[1024]; try { zipis = getClass().getResourceAsStream(res); if (zipis != null) { while ((ic = zipis.read(buffer)) > 0) { dos.write(buffer, 0, ic); } myData = baos.toByteArray(); zipis.close(); if(iszip) result=compress(myData,gtype); else result=decompress(myData,gtype); } dos.close(); baos.close(); } catch (Exception e) { e.printStackTrace(); } finally { zipis = null; dos = null; baos = null; } return result; } /** * 创建文件 * @param b 待写入的byte[] * @param path 文件的路径 */ public static void CreateFile(byte[] b,String path){ // path="file://localhost/root1/config.ini"; FileConnection fc=null; DataOutputStream os=null; try { fc = (FileConnection) Connector.open(path); if(!fc.exists()) fc.create(); os = fc.openDataOutputStream(); os.write(b); os.close(); fc.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 将byte[]转成utf-8字符 * @param data * @return */ public static String Bytetostring(byte[] data){ String content=""; try { content= new String(data, "UTF-8").substring(1);//因为是特殊的字符所以截取 } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return content; } }
另外,由于可能会在PC端产生文件,特弄了个粗糙的工具,只有一个调用类不同.
package zip; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; /** * 压缩:byte[] zipbyte=new ZipUtil().Filetobyte("/config.ini",true); * ZipUtil.CreateFile(zipbyte,"file:///E:/config.g"); * * 解压:byte[] zipbyte=new ZipUtil().Filetobyte("/config.g",false); * String content=ZipUtil.Bytetostring(zipbyte); * ZipUtil.CreateFile(zipbyte,"file://localhost/root1/config.ini"); * * @author new soul * */ public final class ZipUtil extends JFrame implements ActionListener{ JPanel jpanel=new JPanel(); JButton jbutton1=new JButton(); JButton jbutton2=new JButton(); public ZipUtil(){ this.setTitle("窗体"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jbutton1.setText("解压"); jbutton2.setText("压缩"); jpanel.add(jbutton1); jpanel.add(jbutton2); this.add(jpanel); jbutton1.addActionListener(this); jbutton2.addActionListener(this); this.setBounds(500, 100, 200, 300); this.setVisible(true); } public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if(e.getActionCommand()=="解压"){ byte[] zipbyte=new ZipUtil().Filetobyte("./org/config.g",false,GZipInputStream.TYPE_DEFLATE); String content=ZipUtil.Bytetostring(zipbyte); System.out.println("解压长度:"+zipbyte.length+'/r'+content); ZipUtil.CreateFile(zipbyte,"./config.ini"); } else if(e.getActionCommand()=="压缩"){ byte[] zipbyte=new ZipUtil().Filetobyte("./org/config.ini",true,GZipOutputStream.TYPE_DEFLATE); System.out.println("压缩长度:"+zipbyte.length); ZipUtil.CreateFile(zipbyte,"./config.g"); } } public static void main(String args[]) { new ZipUtil(); } /** * 解压 * * @param data the input data to be decompressed * @return the resulting uncompressed byte array * @throws IOException */ public static byte[] decompress( byte[] data ,int inputtype) throws IOException{ byte[] tmp=new byte[1024]; int read; GZipInputStream zipInputStream = new GZipInputStream(new ByteArrayInputStream( data ) ,1024 ,inputtype,true); ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); // take from ZipInputStream and fill into ByteArrayOutputStream while ( (read=zipInputStream.read(tmp, 0, 1024))>0 ){ bout.write(tmp,0,read); } return bout.toByteArray(); } /** * 压缩 * * @param data the input data to be compressed * @return the resulting compressed byte array * @throws IOException */ public static byte[] compress( byte[] data ,int outputstreamtype) throws IOException{ if (data.length>1<<15){ return compress(data,outputstreamtype,1<<15,1<<15); } else{ return compress(data,outputstreamtype,data.length,data.length); } } /** * 压缩 * * @param data the input data to be compressed * @param plainWindowSize this size is important for the lz77 search. Larger values * will result in better compression. Maximum is 32768. * @param huffmanWindowSize this size is important for the huffmanencoding. A large * value will result to a better frequency statistic and therefore to a better compression. * @return the resulting compressed byte array * @throws IOException */ private static byte[] compress( byte[] data,int ouputtype,int plainWindowSize, int huffmanWindowSize) throws IOException{ ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); GZipOutputStream zipOutputStream = new GZipOutputStream(bout ,1024 ,ouputtype,plainWindowSize,huffmanWindowSize); zipOutputStream.write(data); zipOutputStream.close(); return bout.toByteArray(); } /** * 将本地文件转换成byte[] * @param res 文件名 * @param iszip zip OR unzip * @return byte[] */ public byte[] Filetobyte(String res,boolean iszip,int type){ // InputStream zipis=null; // int ic; // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // DataOutputStream dos = new DataOutputStream(baos); // byte[] myData,result=null; // byte[] buffer = new byte[1024]; // try { // zipis = getClass().getResourceAsStream(res); // // if (zipis != null) { // while ((ic = zipis.read(buffer)) > 0) { // dos.write(buffer, 0, ic); // } // myData = baos.toByteArray(); // zipis.close(); // if(iszip) // result=compress(myData,type); // else // result=decompress(myData,type); // } // dos.close(); // baos.close(); // } catch (Exception e) { // e.printStackTrace(); // } finally { // zipis = null; // dos = null; // baos = null; // } // return result; byte[] result=null; try{ FileInputStream stream = new FileInputStream(new File(res)); ByteArrayOutputStream out = new ByteArrayOutputStream(1000); byte[] b = new byte[1000]; int n; while ((n = stream.read(b)) != -1) out.write(b, 0, n); stream.close(); out.close(); if(iszip) result=compress(out.toByteArray(),type); else result=decompress(out.toByteArray(),type); }catch(Exception e){ e.printStackTrace(); } return result; } /** * 创建文件 * @param b 待写入的byte[] * @param path 文件的路径 */ public static void CreateFile(byte[] b,String path){ // path="file://localhost/root1/config.ini"; FileOutputStream fos=null; try{ fos=new FileOutputStream(path,true); fos.write(b); fos.flush(); } catch(Exception e){ e.printStackTrace(); } finally{ try{ fos.close(); } catch(IOException iex){} } } /** * 将byte[]转成utf-8字符 * @param data * @return */ public static String Bytetostring(byte[] data){ String content=""; try { content= new String(data, "UTF-8").substring(1);//因为是特殊的字符所以截取 } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return content; } }