1 /* 2 * LZDecoder 3 * 4 * Authors: Lasse Collin <lasse.collin@tukaani.org> 5 * Igor Pavlov <http://7-zip.org/> 6 * 7 * This file has been put into the public domain. 8 * You can do whatever you want with this file. 9 */ 10 11 package org.tukaani.xz.lz; 12 13 import java.io.DataInputStream; 14 import java.io.IOException; 15 import org.tukaani.xz.ArrayCache; 16 import org.tukaani.xz.CorruptedInputException; 17 18 public final class LZDecoder { 19 private final byte[] buf; 20 private final int bufSize; // To avoid buf.length with an array-cached buf. 21 private int start = 0; 22 private int pos = 0; 23 private int full = 0; 24 private int limit = 0; 25 private int pendingLen = 0; 26 private int pendingDist = 0; 27 LZDecoder(int dictSize, byte[] presetDict, ArrayCache arrayCache)28 public LZDecoder(int dictSize, byte[] presetDict, ArrayCache arrayCache) { 29 bufSize = dictSize; 30 buf = arrayCache.getByteArray(bufSize, false); 31 32 if (presetDict != null) { 33 pos = Math.min(presetDict.length, dictSize); 34 full = pos; 35 start = pos; 36 System.arraycopy(presetDict, presetDict.length - pos, buf, 0, pos); 37 } 38 } 39 putArraysToCache(ArrayCache arrayCache)40 public void putArraysToCache(ArrayCache arrayCache) { 41 arrayCache.putArray(buf); 42 } 43 reset()44 public void reset() { 45 start = 0; 46 pos = 0; 47 full = 0; 48 limit = 0; 49 buf[bufSize - 1] = 0x00; 50 } 51 setLimit(int outMax)52 public void setLimit(int outMax) { 53 if (bufSize - pos <= outMax) 54 limit = bufSize; 55 else 56 limit = pos + outMax; 57 } 58 hasSpace()59 public boolean hasSpace() { 60 return pos < limit; 61 } 62 hasPending()63 public boolean hasPending() { 64 return pendingLen > 0; 65 } 66 getPos()67 public int getPos() { 68 return pos; 69 } 70 getByte(int dist)71 public int getByte(int dist) { 72 int offset = pos - dist - 1; 73 if (dist >= pos) 74 offset += bufSize; 75 76 return buf[offset] & 0xFF; 77 } 78 putByte(byte b)79 public void putByte(byte b) { 80 buf[pos++] = b; 81 82 if (full < pos) 83 full = pos; 84 } 85 repeat(int dist, int len)86 public void repeat(int dist, int len) throws IOException { 87 if (dist < 0 || dist >= full) 88 throw new CorruptedInputException(); 89 90 int left = Math.min(limit - pos, len); 91 pendingLen = len - left; 92 pendingDist = dist; 93 94 int back = pos - dist - 1; 95 if (back < 0) { 96 // The distance wraps around to the end of the cyclic dictionary 97 // buffer. We cannot get here if the dictionary isn't full. 98 assert full == bufSize; 99 back += bufSize; 100 101 // Here we will never copy more than dist + 1 bytes and 102 // so the copying won't repeat from its own output. 103 // Thus, we can always use arraycopy safely. 104 int copySize = Math.min(bufSize - back, left); 105 assert copySize <= dist + 1; 106 107 System.arraycopy(buf, back, buf, pos, copySize); 108 pos += copySize; 109 back = 0; 110 left -= copySize; 111 112 if (left == 0) 113 return; 114 } 115 116 assert back < pos; 117 assert left > 0; 118 119 do { 120 // Determine the number of bytes to copy on this loop iteration: 121 // copySize is set so that the source and destination ranges 122 // don't overlap. If "left" is large enough, the destination 123 // range will start right after the last byte of the source 124 // range. This way we don't need to advance "back" which 125 // allows the next iteration of this loop to copy (up to) 126 // twice the number of bytes. 127 int copySize = Math.min(left, pos - back); 128 System.arraycopy(buf, back, buf, pos, copySize); 129 pos += copySize; 130 left -= copySize; 131 } while (left > 0); 132 133 if (full < pos) 134 full = pos; 135 } 136 repeatPending()137 public void repeatPending() throws IOException { 138 if (pendingLen > 0) 139 repeat(pendingDist, pendingLen); 140 } 141 copyUncompressed(DataInputStream inData, int len)142 public void copyUncompressed(DataInputStream inData, int len) 143 throws IOException { 144 int copySize = Math.min(bufSize - pos, len); 145 inData.readFully(buf, pos, copySize); 146 pos += copySize; 147 148 if (full < pos) 149 full = pos; 150 } 151 flush(byte[] out, int outOff)152 public int flush(byte[] out, int outOff) { 153 int copySize = pos - start; 154 if (pos == bufSize) 155 pos = 0; 156 157 System.arraycopy(buf, start, out, outOff, copySize); 158 start = pos; 159 160 return copySize; 161 } 162 } 163