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}