📄 gzipinputstream.java
字号:
//#condition MUJMAIL_COMPRESSED_CONNECTION/* * 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 mujmail.connections.gzip;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, simon.schmitt@enough.de */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. * Note: Alf inbuil have bud behavior for interactive protolocs ... try to fill output buffer as much as possible to decrease inflate call * Chnage needed to stop after e.g line end in do end of block */ 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; boolean refill = ( this.inStream.available() == 0); /// Flag whether refillSmallBuffer can call read (and so possibli block) if no data avilable boolean continueDecodingLoop = true; if (this.B0len==0){ if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer( false); refill = false; } } // 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){ // Process header only if datas are available if ( continueDecodingLoop == false ) break; 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){ continueDecodingLoop = refillSmallCodeBuffer( refill); refill = false; } } }// inflate else { if (this.smallCodeBuffer[1]<15){ refillSmallCodeBuffer( true); // Can block ... we need this data for decoding } 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( true); } // 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){ continueDecodingLoop = refillSmallCodeBuffer( refill); } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -