001/*
002 * $RCSfile: LZWStringTable.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.1 $
042 * $Date: 2005/02/11 05:01:23 $
043 * $State: Exp $
044 */
045
046package com.github.jaiimageio.impl.common;
047
048import java.io.PrintStream;
049
050/** 
051 * General purpose LZW String Table.
052 * Extracted from GIFEncoder by Adam Doppelt
053 * Comments added by Robin Luiten
054 * <code>expandCode</code> added by Robin Luiten
055 * The strLen_ table to give quick access to the lenght of an expanded
056 * code for use by the <code>expandCode</code> method added by Robin.
057 **/
058public class LZWStringTable
059{
060    /** codesize + Reserved Codes */
061    private     final static int RES_CODES = 2;
062
063    private     final static short HASH_FREE = (short)0xFFFF;
064    private     final static short NEXT_FIRST = (short)0xFFFF;
065
066    private     final static int MAXBITS = 12;
067    private     final static int MAXSTR = (1 << MAXBITS);
068
069    private     final static short HASHSIZE     = 9973;
070    private     final static short HASHSTEP     = 2039;
071
072    byte[]  strChr_;            // after predecessor character
073    short[] strNxt_;            // predecessor string 
074    short[] strHsh_;            // hash table to find  predecessor + char pairs
075    short numStrings_;          // next code if adding new prestring + char
076
077    /**
078         * each entry corresponds to a code and contains the length of data
079         * that the code expands to when decoded.
080         **/
081    int[] strLen_;
082
083    /**
084         * Constructor allocate memory for string store data
085         **/
086    public LZWStringTable()
087    {
088        strChr_ = new byte[MAXSTR];
089        strNxt_ = new short[MAXSTR];
090        strLen_ = new int[MAXSTR];
091        strHsh_ = new short[HASHSIZE];    
092    }
093
094    /**
095         * @param index value of -1 indicates no predecessor [used in initialisation]
096         * @param b the byte [character] to add to the string store which follows
097         * the predecessor string specified the index.
098         * @return 0xFFFF if no space in table left for addition of predecesor
099         * index and byte b. Else return the code allocated for combination index + b.
100         **/
101    public int AddCharString(short index, byte b)
102    {
103        int     hshidx;
104
105        if (numStrings_ >= MAXSTR)      // if used up all codes
106            {
107                return 0xFFFF;
108            }
109                
110        hshidx = Hash(index, b);
111        while (strHsh_[hshidx] != HASH_FREE)
112            hshidx = (hshidx + HASHSTEP) % HASHSIZE;
113                
114        strHsh_[hshidx] = numStrings_;
115        strChr_[numStrings_] = b;
116        if (index == HASH_FREE)
117            {
118                strNxt_[numStrings_] = NEXT_FIRST;
119                strLen_[numStrings_] = 1;
120            }
121        else
122            {
123                strNxt_[numStrings_] = index;
124                strLen_[numStrings_] = strLen_[index] + 1;
125            }
126
127        return numStrings_++;   // return the code and inc for next code
128    }
129
130    /**
131         * @param index index to prefix string
132         * @param b the character that follws the index prefix
133         * @return b if param index is HASH_FREE. Else return the code
134         * for this prefix and byte successor
135         **/    
136    public short FindCharString(short index, byte b)
137    {
138        int     hshidx, nxtidx;
139
140        if (index == HASH_FREE)
141            return (short)(b & 0xFF);    // Rob fixed used to sign extend
142
143        hshidx = Hash(index, b);
144        while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) // search
145            {
146                if (strNxt_[nxtidx]     == index &&     strChr_[nxtidx] == b)
147                    return (short)nxtidx;
148                hshidx = (hshidx + HASHSTEP) % HASHSIZE;
149            }
150
151        return (short)0xFFFF;
152    }
153
154    /**
155         * @param codesize the size of code to be preallocated for the
156         * string store.
157         **/
158    public void ClearTable(int codesize)
159    {
160        numStrings_     = 0;
161
162        for     (int q = 0;     q <     HASHSIZE; q++)
163            strHsh_[q] = HASH_FREE;
164
165        int     w =     (1 << codesize) + RES_CODES;
166        for     (int q = 0;     q <     w; q++)
167            AddCharString((short)0xFFFF, (byte)q);      // init with no prefix
168    }
169
170    static public int Hash(short index, byte lastbyte)
171    {
172        return ((int)((short)(lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
173    }
174
175    /**
176         * If expanded data doesnt fit into array only what will fit is written
177         * to buf and the return value indicates how much of the expanded code has
178         * been written to the buf. The next call to expandCode() should be with 
179         * the same code and have the skip parameter set the negated value of the 
180         * previous return. Succesive negative return values should be negated and
181         * added together for next skip parameter value with same code.
182         *
183         * @param buf buffer to place expanded data into
184         * @param offset offset to place expanded data
185         * @param code the code to expand to the byte array it represents.
186         * PRECONDITION This code must allready be in the LZSS
187         * @param skipHead is the number of bytes at the start of the expanded code to 
188         * be skipped before data is written to buf. It is possible that skipHead is
189         * equal to codeLen.
190         * @return the length of data expanded into buf. If the expanded code is longer
191         * than space left in buf then the value returned is a negative number which when
192         * negated is equal to the number of bytes that were used of the code being expanded.
193         * This negative value also indicates the buffer is full.
194         **/
195    public int expandCode(byte[] buf, int offset, short code, int skipHead)
196    {
197        if (offset == -2) 
198            {
199                if (skipHead == 1) skipHead = 0;
200            }
201        if (code == (short)0xFFFF ||                            // just in case
202            skipHead == strLen_[code])                          // DONE no more unpacked
203            return 0;
204
205        int expandLen;                                                  // how much data we are actually expanding
206        int codeLen = strLen_[code] - skipHead; // length of expanded code left
207        int bufSpace = buf.length - offset;             // how much space left
208        if (bufSpace > codeLen)
209            expandLen = codeLen;                                // only got this many to unpack
210        else
211            expandLen = bufSpace;
212
213        int skipTail = codeLen - expandLen;             // only > 0 if codeLen > bufSpace [left overs]
214
215        int idx = offset + expandLen;                   // initialise to exclusive end address of buffer area
216
217        // NOTE: data unpacks in reverse direction and we are placing the
218        // unpacked data directly into the array in the correct location.
219        while ((idx > offset) && (code != (short)0xFFFF))
220            {
221                if (--skipTail < 0)                                     // skip required of expanded data
222                    {
223                        buf[--idx] = strChr_[code];
224                    }
225                code = strNxt_[code];                           // to predecessor code
226            }
227
228        if (codeLen > expandLen)
229            return -expandLen;                                  // indicate what part of codeLen used
230        else
231            return expandLen;                                   // indicate length of dat unpacked
232    }
233
234    public void dump(PrintStream out)
235    {
236        int i;
237        for (i = 258; i < numStrings_; ++i)
238            out.println(  " strNxt_[" + i + "] = " + strNxt_[i]
239                          + " strChr_ " + Integer.toHexString(strChr_[i] & 0xFF)
240                          + " strLen_ " + Integer.toHexString(strLen_[i]));
241    }
242}