1 /* 2 * DeltaInputStream 3 * 4 * Author: Lasse Collin <lasse.collin (at) tukaani.org> 5 * 6 * This file has been put into the public domain. 7 * You can do whatever you want with this file. 8 */ 9 10 package org.tukaani.xz; 11 12 import java.io.InputStream; 13 import java.io.IOException; 14 import org.tukaani.xz.delta.DeltaDecoder; 15 16 /** 17 * Decodes raw Delta-filtered data (no XZ headers). 18 * <p> 19 * The delta filter doesn't change the size of the data and thus it 20 * cannot have an end-of-payload marker. It will simply decode until 21 * its input stream indicates end of input. 22 */ 23 public class DeltaInputStream extends InputStream { 24 /** 25 * Smallest supported delta calculation distance. 26 */ 27 public static final int DISTANCE_MIN = 1; 28 29 /** 30 * Largest supported delta calculation distance. 31 */ 32 public static final int DISTANCE_MAX = 256; 33 34 private InputStream in; 35 private final DeltaDecoder delta; 36 37 private IOException exception = null; 38 39 private final byte[] tempBuf = new byte[1]; 40 41 /** 42 * Creates a new Delta decoder with the given delta calculation distance. 43 * 44 * @param in input stream from which Delta filtered data 45 * is read 46 * 47 * @param distance delta calculation distance, must be in the 48 * range [<code>DISTANCE_MIN</code>, 49 * <code>DISTANCE_MAX</code>] 50 */ 51 public DeltaInputStream(InputStream in, int distance) { 52 // Check for null because otherwise null isn't detect 53 // in this constructor. 54 if (in == null) 55 throw new NullPointerException(); 56 57 this.in = in; 58 this.delta = new DeltaDecoder(distance); 59 } 60 61 /** 62 * Decode the next byte from this input stream. 63 * 64 * @return the next decoded byte, or <code>-1</code> to indicate 65 * the end of input on the input stream <code>in</code> 66 * 67 * @throws IOException may be thrown by <code>in</code> 68 */ 69 public int read() throws IOException { 70 return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF); 71 } 72 73 /** 74 * Decode into an array of bytes. 75 * <p> 76 * This calls <code>in.read(buf, off, len)</code> and defilters the 77 * returned data. 78 * 79 * @param buf target buffer for decoded data 80 * @param off start offset in <code>buf</code> 81 * @param len maximum number of bytes to read 82 * 83 * @return number of bytes read, or <code>-1</code> to indicate 84 * the end of the input stream <code>in</code> 85 * 86 * @throws XZIOException if the stream has been closed 87 * 88 * @throws IOException may be thrown by underlaying input 89 * stream <code>in</code> 90 */ 91 public int read(byte[] buf, int off, int len) throws IOException { 92 if (len == 0) 93 return 0; 94 95 if (in == null) 96 throw new XZIOException("Stream closed"); 97 98 if (exception != null) 99 throw exception; 100 101 int size; 102 try { 103 size = in.read(buf, off, len); 104 } catch (IOException e) { 105 exception = e; 106 throw e; 107 } 108 109 if (size == -1) 110 return -1; 111 112 delta.decode(buf, off, size); 113 return size; 114 } 115 116 /** 117 * Calls <code>in.available()</code>. 118 * 119 * @return the value returned by <code>in.available()</code> 120 */ 121 public int available() throws IOException { 122 if (in == null) 123 throw new XZIOException("Stream closed"); 124 125 if (exception != null) 126 throw exception; 127 128 return in.available(); 129 } 130 131 /** 132 * Closes the stream and calls <code>in.close()</code>. 133 * If the stream was already closed, this does nothing. 134 * 135 * @throws IOException if thrown by <code>in.close()</code> 136 */ 137 public void close() throws IOException { 138 if (in != null) { 139 try { 140 in.close(); 141 } finally { 142 in = null; 143 } 144 } 145 } 146 } 147