Thursday, June 9, 2011

Compressor Decompressor Class


import java.io.IOException;
import java.io.ByteArrayOutputStream;

public class Compressor {
   
    /* Tailored to int as 32bit signed */
    private final static int FIRST_QUARTER = 0x200000;
    private final static int THIRD_QUARTER = 0x600000;
    private final static int HALF          = 0x400000;
    private final static int HIGH          = 0x7fffff;
    private final static int INITIAL_READ  = 23;
   
    public static byte[] compress(byte[] in) {
       
        class BitOutputBuffer {
            ByteArrayOutputStream buf;
            byte[] currentByte;
            byte currentBit;
            BitOutputBuffer() {
                buf = new ByteArrayOutputStream();
                currentByte = new byte[1];
                currentByte[0] = 0;
                currentBit = 0;
            }
            void writeBit(byte bit) throws IOException {
                currentByte[0] = (byte) ((currentByte[0]) << 1);
                currentByte[0] += bit;
                currentBit+=1;
                if (currentBit==8) {
                    buf.write(currentByte);
                    currentByte[0] = 0;
                    currentBit = 0;
                }
            }
            void flush() throws IOException {
                /* Pad the buffer with zeros */
                while (currentBit!=0) {
                    writeBit((byte) 0);
                }
                buf.flush();
            }
           
            byte[] toByteArray() {
                try {
                    buf.flush();
                    return buf.toByteArray();
                }
                catch (IOException e) {
                    return null;
                }
            }
           
        }
       
        BitOutputBuffer bitBuf = new BitOutputBuffer();
       
        int low = 0, high = HIGH, total;
        int mLow = low, mHigh = high, mStep = 0;
        int mScale = 0;
        int current = 0;
       
        /* Initialize frequency table */
        int[] freq = new int[257];
        for (int i=0; i<257; i++) freq[i] = 1;
        total = 257;
       
        try {
           
            for (int i=0; i<in.length + 1; i++) {
               
                if (i == in.length) {
                    /* Encode terminator if necessary */
                    low = total - 1;
                    high = total;
                }
                else {
                    /* Otherwise retrieve cumulative freq */
                    current = in[i] & 0xff; // Get unsigned value
                    low = 0;
                    for (int j=0; j<current; j++) {
                        low += freq[j];
                    }
                    high = low + freq[current];
                }
               
                /* 2. Update the coder */
                mStep = ( mHigh - mLow + 1 ) / total;
                mHigh = (mLow + mStep * high) - 1;
                mLow = mLow + mStep * low;
               
                /* Renormalize if possible */
                while( (mHigh < HALF) || (mLow >= HALF) )
                {
                    if( mHigh < HALF )
                    {
                        bitBuf.writeBit((byte) 0);
                        mLow = mLow * 2;
                        mHigh = mHigh * 2 + 1;
                       
                        /* Perform e3 mappings */
                        for(; mScale > 0; mScale-- )
                            bitBuf.writeBit((byte) 1);
                    }
                    else if( mLow >= HALF )
                    {
                        bitBuf.writeBit((byte) 1);
                        mLow = ( mLow - HALF ) * 2;
                        mHigh = ( mHigh - HALF ) * 2 + 1;
                       
                        /* Perform e3 mappings */
                        for(; mScale > 0; mScale-- )
                            bitBuf.writeBit((byte) 0);
                    }
                }
               
                while((FIRST_QUARTER <= mLow) && (mHigh < THIRD_QUARTER)) {
                    mScale++;
                    mLow = ( mLow - FIRST_QUARTER ) * 2;
                    mHigh = ( mHigh - FIRST_QUARTER ) * 2 + 1;
                }       
               
                /* 3. Update model */
                freq[current]+=1;
                total+=1;
               
            }
            /* Finish encoding */
            if( mLow < FIRST_QUARTER ) {
                /* Case: mLow < FirstQuarter < Half <= mHigh */
                bitBuf.writeBit((byte) 0);
                /* Perform e3-scaling */
                for( int i=0; i<mScale+1; i++ )
                    bitBuf.writeBit((byte) 1);
            } else {
                /* Case: mLow < Half < ThirdQuarter <= mHigh */
                bitBuf.writeBit((byte) 1 );
            }
            bitBuf.flush();
        }
        catch (IOException e) {
            return null;
        }
        return bitBuf.toByteArray();
    }
   
    public static byte[] decompress(byte[] in) {
       
        class BitInputBuffer {
            byte[] source;
            int bytep = 0, bitp = 0;
            byte currentByte = 0;
            BitInputBuffer(byte [] source) {
                this.source = source;
                currentByte = source[0];// & 0xff;
            }
            int readBit() {
                int result = (currentByte >> 7) & 1;
                currentByte = (byte) (currentByte << 1);
                if (bitp++==7) {
                    bytep++;
                    if (bytep > source.length - 1) {
                        currentByte = 0;
                    }
                    else {
                        currentByte = source[bytep];
                        bitp = 0;
                    }
                }
                return result;
            }
        }
       
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        /* Initialise frequency table */
       
        int[] freq = new int[257];
        for (int i=0; i<257; i++) freq[i] = 1;
        int total = 257;
        int current = 0;
        int value;
        int low = 0, high = HIGH;
        int mLow = low, mHigh = high, mStep = 0, mScale = 0, mBuffer = 0;
        BitInputBuffer inbuf = new BitInputBuffer(in);
        /*    Fill buffer with bits from the input stream */

        for( int i=0; i<INITIAL_READ; i++ ) {
            mBuffer = 2 * mBuffer;
            mBuffer += inbuf.readBit();
        }
           
        while(true) {
            /* 1. Retrieve current byte */
            mStep = ( mHigh - mLow + 1 ) / total;
            value = ( mBuffer - mLow ) / mStep;
            low = 0;
            for (current=0; current<256 && low + freq[current] <= value; current++)
                low += freq[current];
           
            if (current==256) break;
           
            buf.write(current);
            high = low + freq[current];
           
            /* 2. Update the decoder */
            mHigh = mLow + mStep * high - 1; // interval open at the top => -1
           
            /* Update lower bound */
            mLow = mLow + mStep * low;
           
            /* e1/e2 mapping */
            while( ( mHigh < HALF ) || ( mLow >= HALF ) )
            {
                if( mHigh < HALF )
                {
                    mLow = mLow * 2;
                    mHigh = ((mHigh * 2) + 1);
                    mBuffer = (2 * mBuffer);
                }
                else if( mLow >= HALF )
                {
                    mLow = 2 * ( mLow - HALF );
                    mHigh = 2 * ( mHigh - HALF ) + 1;
                    mBuffer = 2 * ( mBuffer - HALF );
                }
   
                mBuffer += inbuf.readBit();
                mScale = 0;
            }
           
            /* e3 mapping */
            while( ( FIRST_QUARTER <= mLow ) && ( mHigh < THIRD_QUARTER ) )
            {
                mScale++;
                mLow = 2 * ( mLow - FIRST_QUARTER );
                mHigh = 2 * ( mHigh - FIRST_QUARTER ) + 1;
                mBuffer = 2 * ( mBuffer - FIRST_QUARTER ) ;
                mBuffer += inbuf.readBit();
            }
           
            /* 3. Update frequency table */
            freq[current]+=1;
            total+=1;
        }
       
        return buf.toByteArray();
    }
}

No comments:

Post a Comment