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

com.itextpdf.io.codec.LZWStringTable Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2023 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.io.codec;


import java.io.PrintStream;

/**
 * General purpose LZW String Table.
 * Extracted from GIFEncoder by Adam Doppelt
 * Comments added by Robin Luiten
 * expandCode added by Robin Luiten
 * The strLen_ table to give quick access to the lenght of an expanded
 * code for use by the expandCode method added by Robin.
 */
public class LZWStringTable {
    /**
     * codesize + Reserved Codes
     */
    private static final int RES_CODES = 2;

    //0xFFFF
    private static final short HASH_FREE = -1;
    //0xFFFF
    private static final short NEXT_FIRST = -1;

    private static final int MAXBITS = 12;
    private static final int MAXSTR = (1 << MAXBITS);

    private static final short HASHSIZE = 9973;
    private static final short HASHSTEP = 2039;

    // after predecessor character
    byte[] strChr_;

    // predecessor string
    short[] strNxt_;

    // hash table to find  predecessor + char pairs
    short[] strHsh_;

    // next code if adding new prestring + char
    short numStrings_;

    /**
     * each entry corresponds to a code and contains the length of data
     * that the code expands to when decoded.
     **/
    int[] strLen_;

    /**
     * Constructor allocate memory for string store data
     **/
    public LZWStringTable() {
        strChr_ = new byte[MAXSTR];
        strNxt_ = new short[MAXSTR];
        strLen_ = new int[MAXSTR];
        strHsh_ = new short[HASHSIZE];
    }

    /**
     * @param index value of -1 indicates no predecessor [used in initialization]
     * @param b     the byte [character] to add to the string store which follows
     *              the predecessor string specified the index.
     * @return 0xFFFF if no space in table left for addition of predecessor
     * index and byte b. Else return the code allocated for combination index + b.
     **/
    public int AddCharString(short index, byte b) {
        int hshidx;

        // if used up all codes
        if (numStrings_ >= MAXSTR)
        {
            return 0xFFFF;
        }

        hshidx = Hash(index, b);
        while (strHsh_[hshidx] != HASH_FREE)
            hshidx = (hshidx + HASHSTEP) % HASHSIZE;

        strHsh_[hshidx] = numStrings_;
        strChr_[numStrings_] = b;
        if (index == HASH_FREE) {
            strNxt_[numStrings_] = NEXT_FIRST;
            strLen_[numStrings_] = 1;
        } else {
            strNxt_[numStrings_] = index;
            strLen_[numStrings_] = strLen_[index] + 1;
        }

        // return the code and inc for next code
        return numStrings_++;
    }

    /**
     * @param index index to prefix string
     * @param b     the character that follws the index prefix
     * @return b if param index is HASH_FREE. Else return the code
     * for this prefix and byte successor
     **/
    public short FindCharString(short index, byte b) {
        int hshidx, nxtidx;

        if (index == HASH_FREE)

            // Rob fixed used to sign extend
            return (short) (b & 0xFF);

        hshidx = Hash(index, b);

        // search
        while ((nxtidx = strHsh_[hshidx]) != HASH_FREE)
        {
            if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
                return (short) nxtidx;
            hshidx = (hshidx + HASHSTEP) % HASHSIZE;
        }

        //return (short) 0xFFFF;
        return -1;
    }

    /**
     * @param codesize the size of code to be preallocated for the
     *                 string store.
     **/
    public void ClearTable(int codesize) {
        numStrings_ = 0;

        for (int q = 0; q < HASHSIZE; q++)
            strHsh_[q] = HASH_FREE;

        int w = (1 << codesize) + RES_CODES;
        for (int q = 0; q < w; q++) {

            // init with no prefix
            AddCharString((short)-1, (byte) q);
        }
    }

    public static int Hash(short index, byte lastbyte) {
        return (((short) (lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
    }

    /**
     * If expanded data doesn't fit into array only what will fit is written
     * to buf and the return value indicates how much of the expanded code has
     * been written to the buf. The next call to expandCode() should be with
     * the same code and have the skip parameter set the negated value of the
     * previous return. Successive negative return values should be negated and
     * added together for next skip parameter value with same code.
     *
     * @param buf      buffer to place expanded data into
     * @param offset   offset to place expanded data
     * @param code     the code to expand to the byte array it represents.
     *                 PRECONDITION This code must already be in the LZSS
     * @param skipHead is the number of bytes at the start of the expanded code to
     *                 be skipped before data is written to buf. It is possible that skipHead is
     *                 equal to codeLen.
     * @return the length of data expanded into buf. If the expanded code is longer
     * than space left in buf then the value returned is a negative number which when
     * negated is equal to the number of bytes that were used of the code being expanded.
     * This negative value also indicates the buffer is full.
     **/
    public int expandCode(byte[] buf, int offset, short code, int skipHead) {
        if (offset == -2) {
            if (skipHead == 1) skipHead = 0;
        }

        // code == -1 is checked just in case.
        //-1 ~ 0xFFFF
        if (code == -1 ||

                // DONE no more unpacked
                skipHead == strLen_[code])
            return 0;

        // how much data we are actually expanding
        int expandLen;

        // length of expanded code left
        int codeLen = strLen_[code] - skipHead;

        // how much space left
        int bufSpace = buf.length - offset;
        if (bufSpace > codeLen) {

            // only got this many to unpack
            expandLen = codeLen;
        } else {
            expandLen = bufSpace;
        }

        // only > 0 if codeLen > bufSpace [left overs]
        int skipTail = codeLen - expandLen;

        // initialise to exclusive end address of buffer area
        int idx = offset + expandLen;

        // NOTE: data unpacks in reverse direction and we are placing the
        // unpacked data directly into the array in the correct location.
        while ((idx > offset) && (code != -1)) {

            // skip required of expanded data
            if (--skipTail < 0)
            {
                buf[--idx] = strChr_[code];
            }

            // to predecessor code
            code = strNxt_[code];
        }

        if (codeLen > expandLen) {

            // indicate what part of codeLen used
            return -expandLen;
        } else {

            // indicate length of dat unpacked
            return expandLen;
        }
    }

    public void dump(PrintStream output) {
        int i;
        for (i = 258; i < numStrings_; ++i)
            output.println(" strNxt_[" + i + "] = " + strNxt_[i]
                    + " strChr_ " + Integer.toHexString(strChr_[i] & 0xFF)
                    + " strLen_ " + Integer.toHexString(strLen_[i]));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy