1 /*
2  * DeltaInputStream
3  *
4  * Author: Lasse Collin <lasse.collin@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      */
DeltaInputStream(InputStream in, int distance)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      */
read()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      */
read(byte[] buf, int off, int len)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      */
available()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      */
close()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