/* |
|
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code 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 |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package com.sun.imageio.plugins.common; |
|
import java.io.PrintStream; |
|
/** |
|
* General purpose LZW String Table. |
|
* Extracted from GIFEncoder by Adam Doppelt |
|
* Comments added by Robin Luiten |
|
* <code>expandCode</code> added by Robin Luiten |
|
* The strLen table to give quick access to the lenght of an expanded |
|
* code for use by the <code>expandCode</code> method added by Robin. |
|
**/ |
|
public class LZWStringTable { |
|
/** codesize + Reserved Codes */ |
|
private final static int RES_CODES = 2; |
|
private final static short HASH_FREE = (short)0xFFFF; |
|
private final static short NEXT_FIRST = (short)0xFFFF; |
|
private final static int MAXBITS = 12; |
|
private final static int MAXSTR = (1 << MAXBITS); |
|
private final static short HASHSIZE = 9973; |
|
private final static short HASHSTEP = 2039; |
|
byte[] strChr; // after predecessor character |
|
short[] strNxt; // predecessor string |
|
short[] strHsh; // hash table to find predecessor + char pairs |
|
short numStrings; // next code if adding new prestring + char |
|
/* |
|
* 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 initialisation] |
|
* @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 predecesor |
|
* index and byte b. Else return the code allocated for combination index + b. |
|
*/ |
|
public int addCharString(short index, byte b) { |
|
int hshidx; |
|
if (numStrings >= MAXSTR) { // if used up all codes |
|
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 numStrings++; // return the code and inc for next code |
|
} |
|
/* |
|
* @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) { |
|
return (short)(b & 0xFF); // Rob fixed used to sign extend |
|
} |
|
hshidx = hash(index, b); |
|
while ((nxtidx = strHsh[hshidx]) != HASH_FREE) { // search |
|
if (strNxt[nxtidx] == index && strChr[nxtidx] == b) { |
|
return (short)nxtidx; |
|
} |
|
hshidx = (hshidx + HASHSTEP) % HASHSIZE; |
|
} |
|
return (short)0xFFFF; |
|
} |
|
/* |
|
* @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++) { |
|
addCharString((short)0xFFFF, (byte)q); // init with no prefix |
|
} |
|
} |
|
static public int hash(short index, byte lastbyte) { |
|
return ((int)((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. Succesive 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; |
|
} |
|
} |
|
if (code == (short)0xFFFF || // just in case |
|
skipHead == strLen[code]) // DONE no more unpacked |
|
{ |
|
return 0; |
|
} |
|
int expandLen; // how much data we are actually expanding |
|
int codeLen = strLen[code] - skipHead; // length of expanded code left |
|
int bufSpace = buf.length - offset; // how much space left |
|
if (bufSpace > codeLen) { |
|
expandLen = codeLen; // only got this many to unpack |
|
} else { |
|
expandLen = bufSpace; |
|
} |
|
int skipTail = codeLen - expandLen; // only > 0 if codeLen > bufSpace [left overs] |
|
int idx = offset + expandLen; // initialise to exclusive end address of buffer area |
|
// 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 != (short)0xFFFF)) { |
|
if (--skipTail < 0) { // skip required of expanded data |
|
buf[--idx] = strChr[code]; |
|
} |
|
code = strNxt[code]; // to predecessor code |
|
} |
|
if (codeLen > expandLen) { |
|
return -expandLen; // indicate what part of codeLen used |
|
} else { |
|
return expandLen; // indicate length of dat unpacked |
|
} |
|
} |
|
public void dump(PrintStream out) { |
|
int i; |
|
for (i = 258; i < numStrings; ++i) { |
|
out.println(" strNxt[" + i + "] = " + strNxt[i] |
|
+ " strChr " + Integer.toHexString(strChr[i] & 0xFF) |
|
+ " strLen " + Integer.toHexString(strLen[i])); |
|
} |
|
} |
|
} |