All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sanselan.common.mylzw.MyLZWCompressor Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sanselan.common.mylzw;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyLZWCompressor
{

    // private static final int MAX_TABLE_SIZE = 1 << 12;

    private int codeSize;
    private final int initialCodeSize;
    private int codes = -1;

    private final int byteOrder;
    private final boolean earlyLimit;
    private final int clearCode;
    private final int eoiCode;
    private final Listener listener;

    public MyLZWCompressor(int initialCodeSize, int byteOrder,
            boolean earlyLimit)
    {
        this(initialCodeSize, byteOrder, earlyLimit, null);
    }

    public MyLZWCompressor(int initialCodeSize, int byteOrder,
            boolean earlyLimit, Listener listener)
    {
        this.listener = listener;
        this.byteOrder = byteOrder;
        this.earlyLimit = earlyLimit;

        this.initialCodeSize = initialCodeSize;

        clearCode = 1 << initialCodeSize;
        eoiCode = clearCode + 1;

        if (null != listener)
            listener.init(clearCode, eoiCode);

        InitializeStringTable();
    }

    private final Map map = new HashMap();

    private final void InitializeStringTable()
    {
        codeSize = initialCodeSize;

        int intial_entries_count = (1 << codeSize) + 2;

        map.clear();
        for (codes = 0; codes < intial_entries_count; codes++)
        {
            if ((codes != clearCode) && (codes != eoiCode))
            {
                Object key = arrayToKey((byte) codes);

                map.put(key, new Integer(codes));
            }
        }
    }

    private final void clearTable()
    {
        InitializeStringTable();
        incrementCodeSize();
    }

    private final void incrementCodeSize()
    {
        if (codeSize != 12)
            codeSize++;
    }

    private final Object arrayToKey(byte b)
    {
        return arrayToKey(new byte[] { b, }, 0, 1);
    }

    private final static class ByteArray
    {
        private final byte bytes[];
        private final int start;
        private final int length;
        private final int hash;

        public ByteArray(byte bytes[])
        {
            this(bytes, 0, bytes.length);
        }

        public ByteArray(byte bytes[], int start, int length)
        {
            this.bytes = bytes;
            this.start = start;
            this.length = length;

            int tempHash = length;

            for (int i = 0; i < length; i++)
            {
                int b = 0xff & bytes[i + start];
                tempHash = tempHash + (tempHash << 8) ^ b ^ i;
            }

            hash = tempHash;
        }

        public final int hashCode()
        {
            return hash;
        }

        public final boolean equals(Object o)
        {
            ByteArray other = (ByteArray) o;
            if (other.hash != hash)
                return false;
            if (other.length != length)
                return false;

            for (int i = 0; i < length; i++)
            {
                if (other.bytes[i + other.start] != bytes[i + start])
                    return false;
            }

            return true;
        }
    }

    private final Object arrayToKey(byte bytes[], int start, int length)
    {
        return new ByteArray(bytes, start, length);
    }

    private final void writeDataCode(MyBitOutputStream bos, int code)
            throws IOException
    {
        if (null != listener)
            listener.dataCode(code);
        writeCode(bos, code);
    }


    private final void writeClearCode(MyBitOutputStream bos) throws IOException
    {
        if (null != listener)
            listener.dataCode(clearCode);
        writeCode(bos, clearCode);
    }

    private final void writeEoiCode(MyBitOutputStream bos) throws IOException
    {
        if (null != listener)
            listener.eoiCode(eoiCode);
        writeCode(bos, eoiCode);
    }

    private final void writeCode(MyBitOutputStream bos, int code)
            throws IOException
    {
        bos.writeBits(code, codeSize);
    }

    private final boolean isInTable(byte bytes[], int start, int length)
    {
        Object key = arrayToKey(bytes, start, length);

        return map.containsKey(key);
    }

    private final int codeFromString(byte bytes[], int start, int length)
            throws IOException
    {
        Object key = arrayToKey(bytes, start, length);
        Object o = map.get(key);
        if (o == null)
            throw new IOException("CodeFromString");
        return ((Integer) o).intValue();
    }

    private final boolean addTableEntry(MyBitOutputStream bos, byte bytes[],
            int start, int length) throws IOException
    {
        Object key = arrayToKey(bytes, start, length);
        return addTableEntry(bos, key);
    }

    private final boolean addTableEntry(MyBitOutputStream bos, Object key)
            throws IOException
    {
        boolean cleared = false;

        {
            int limit = (1 << codeSize);
            if (earlyLimit)
                limit--;

            if (codes == limit)
            {
                if (codeSize < 12)
                    incrementCodeSize();
                else
                {
                    writeClearCode(bos);
                    clearTable();
                    cleared = true;
                }
            }
        }

        if (!cleared)
        {
            map.put(key, new Integer(codes));
            codes++;
        }

        return cleared;
    }

    public static interface Listener
    {
        public void dataCode(int code);

        public void eoiCode(int code);

        public void clearCode(int code);

        public void init(int clearCode, int eoiCode);
    }

    public byte[] compress(byte bytes[]) throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length);
        MyBitOutputStream bos = new MyBitOutputStream(baos, byteOrder);

        InitializeStringTable();
        clearTable();
        writeClearCode(bos);
        boolean cleared = false;

        int w_start = 0;
        int w_length = 0;

        for (int i = 0; i < bytes.length; i++)
        {
            if (isInTable(bytes, w_start, w_length + 1))
            {
                w_length++;

                cleared = false;
            } else
            {
                int code = codeFromString(bytes, w_start, w_length);
                writeDataCode(bos, code);
                cleared = addTableEntry(bos, bytes, w_start, w_length + 1);

                w_start = i;
                w_length = 1;
            }
        } /* end of for loop */

        int code = codeFromString(bytes, w_start, w_length);
        writeDataCode(bos, code);

        writeEoiCode(bos);

        bos.flushCache();

        return baos.toByteArray();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy