001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.compressors.z._internal_;
020
021import java.io.IOException;
022import java.io.InputStream;
023
024import org.apache.commons.compress.compressors.CompressorInputStream;
025
026/**
027 * <strong>This class is only public for technical reasons and is not
028 * part of Commons Compress' published API - it may change or
029 * disappear without warning.</strong>
030 *
031 * <p>Base-class for traditional Unix ".Z" compression and the
032 * Unshrinking method of ZIP archive.</p>
033 *
034 * @NotThreadSafe
035 * @since 1.7
036 */
037public abstract class InternalLZWInputStream extends CompressorInputStream {
038    private final byte[] oneByte = new byte[1];
039
040    protected final InputStream in;
041    protected int clearCode = -1;
042    protected int codeSize = 9;
043    protected int bitsCached = 0;
044    protected int bitsCachedSize = 0;
045    protected int previousCode = -1;
046    protected int tableSize = 0;
047    protected int[] prefixes;
048    protected byte[] characters;
049    private byte[] outputStack;
050    private int outputStackLocation;
051
052    protected InternalLZWInputStream(InputStream inputStream) {
053        this.in = inputStream;
054    }
055
056    @Override
057    public void close() throws IOException {
058        in.close();
059    }
060    
061    @Override
062    public int read() throws IOException {
063        int ret = read(oneByte);
064        if (ret < 0) {
065            return ret;
066        }
067        return 0xff & oneByte[0];
068    }
069    
070    @Override
071    public int read(byte[] b, int off, int len) throws IOException {
072        int bytesRead = readFromStack(b, off, len);
073        while (len - bytesRead > 0) {
074            int result = decompressNextSymbol();
075            if (result < 0) {
076                if (bytesRead > 0) {
077                    count(bytesRead);
078                    return bytesRead;
079                }
080                return result;
081            }
082            bytesRead += readFromStack(b, off + bytesRead, len - bytesRead);
083        }
084        count(bytesRead);
085        return bytesRead;
086    }
087
088    /**
089     * Read the next code and expand it.
090     */
091    protected abstract int decompressNextSymbol() throws IOException;
092
093    /**
094     * Add a new entry to the dictionary.
095     */
096    protected abstract int addEntry(int previousCode, byte character)
097        throws IOException;
098
099    /**
100     * Sets the clear code based on the code size.
101     */
102    protected void setClearCode(int codeSize) {
103        clearCode = (1 << (codeSize - 1));
104    }
105
106    /**
107     * Initializes the arrays based on the maximum code size.
108     */
109    protected void initializeTables(int maxCodeSize) {
110        final int maxTableSize = 1 << maxCodeSize;
111        prefixes = new int[maxTableSize];
112        characters = new byte[maxTableSize];
113        outputStack = new byte[maxTableSize];
114        outputStackLocation = maxTableSize;
115        final int max = 1 << 8;
116        for (int i = 0; i < max; i++) {
117            prefixes[i] = -1;
118            characters[i] = (byte) i;
119        }
120    }
121
122    /**
123     * Reads the next code from the stream.
124     */
125    protected int readNextCode() throws IOException {
126        while (bitsCachedSize < codeSize) {
127            final int nextByte = in.read();
128            if (nextByte < 0) {
129                return nextByte;
130            }
131            bitsCached |= (nextByte << bitsCachedSize);
132            bitsCachedSize += 8;
133        }
134        final int mask = (1 << codeSize) - 1;
135        final int code = (bitsCached & mask);
136        bitsCached >>>= codeSize;
137        bitsCachedSize -= codeSize;
138        return code;
139    }
140    
141    /**
142     * Adds a new entry if the maximum table size hasn't been exceeded
143     * and returns the new index.
144     */
145    protected int addEntry(int previousCode, byte character, int maxTableSize) {
146        if (tableSize < maxTableSize) {
147            final int index = tableSize;
148            prefixes[tableSize] = previousCode;
149            characters[tableSize] = character;
150            tableSize++;
151            return index;
152        }
153        return -1;
154    }
155
156    /**
157     * Add entry for repeat of previousCode we haven't added, yet.
158     */
159    protected int addRepeatOfPreviousCode() throws IOException {
160        if (previousCode == -1) {
161            // can't have a repeat for the very first code
162            throw new IOException("The first code can't be a reference to its preceding code");
163        }
164        byte firstCharacter = 0;
165        for (int last = previousCode; last >= 0; last = prefixes[last]) {
166            firstCharacter = characters[last];
167        }
168        return addEntry(previousCode, firstCharacter);
169    }
170
171    /**
172     * Expands the entry with index code to the output stack and may
173     * create a new entry
174     */
175    protected int expandCodeToOutputStack(int code, boolean addedUnfinishedEntry)
176        throws IOException {
177        for (int entry = code; entry >= 0; entry = prefixes[entry]) {
178            outputStack[--outputStackLocation] = characters[entry];
179        }
180        if (previousCode != -1 && !addedUnfinishedEntry) {
181            addEntry(previousCode, outputStack[outputStackLocation]);
182        }
183        previousCode = code;
184        return outputStackLocation;
185    }
186
187    private int readFromStack(byte[] b, int off, int len) {
188        int remainingInStack = outputStack.length - outputStackLocation;
189        if (remainingInStack > 0) {
190            int maxLength = Math.min(remainingInStack, len);
191            System.arraycopy(outputStack, outputStackLocation, b, off, maxLength);
192            outputStackLocation += maxLength;
193            return maxLength;
194        }
195        return 0;
196    }
197}