1 /*
2  * UncompressedLZMA2OutputStream
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.DataOutputStream;
13 import java.io.IOException;
14 
15 class UncompressedLZMA2OutputStream extends FinishableOutputStream {
16     private final ArrayCache arrayCache;
17 
18     private FinishableOutputStream out;
19     private final DataOutputStream outData;
20 
21     private final byte[] uncompBuf;
22     private int uncompPos = 0;
23     private boolean dictResetNeeded = true;
24 
25     private boolean finished = false;
26     private IOException exception = null;
27 
28     private final byte[] tempBuf = new byte[1];
29 
getMemoryUsage()30     static int getMemoryUsage() {
31         // uncompBuf + a little extra
32         return 70;
33     }
34 
UncompressedLZMA2OutputStream(FinishableOutputStream out, ArrayCache arrayCache)35     UncompressedLZMA2OutputStream(FinishableOutputStream out,
36                                   ArrayCache arrayCache) {
37         if (out == null)
38             throw new NullPointerException();
39 
40         this.out = out;
41         outData = new DataOutputStream(out);
42 
43         // We only allocate one array from the cache. We will call
44         // putArray directly in writeEndMarker and thus we don't use
45         // ResettableArrayCache here.
46         this.arrayCache = arrayCache;
47         uncompBuf = arrayCache.getByteArray(
48                 LZMA2OutputStream.COMPRESSED_SIZE_MAX, false);
49     }
50 
write(int b)51     public void write(int b) throws IOException {
52         tempBuf[0] = (byte)b;
53         write(tempBuf, 0, 1);
54     }
55 
write(byte[] buf, int off, int len)56     public void write(byte[] buf, int off, int len) throws IOException {
57         if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
58             throw new IndexOutOfBoundsException();
59 
60         if (exception != null)
61             throw exception;
62 
63         if (finished)
64             throw new XZIOException("Stream finished or closed");
65 
66         try {
67             while (len > 0) {
68                 int copySize = Math.min(LZMA2OutputStream.COMPRESSED_SIZE_MAX
69                                         - uncompPos, len);
70                 System.arraycopy(buf, off, uncompBuf, uncompPos, copySize);
71                 len -= copySize;
72                 uncompPos += copySize;
73 
74                 if (uncompPos == LZMA2OutputStream.COMPRESSED_SIZE_MAX)
75                     writeChunk();
76             }
77         } catch (IOException e) {
78             exception = e;
79             throw e;
80         }
81     }
82 
writeChunk()83     private void writeChunk() throws IOException {
84         outData.writeByte(dictResetNeeded ? 0x01 : 0x02);
85         outData.writeShort(uncompPos - 1);
86         outData.write(uncompBuf, 0, uncompPos);
87         uncompPos = 0;
88         dictResetNeeded = false;
89     }
90 
writeEndMarker()91     private void writeEndMarker() throws IOException {
92         if (exception != null)
93             throw exception;
94 
95         if (finished)
96             throw new XZIOException("Stream finished or closed");
97 
98         try {
99             if (uncompPos > 0)
100                 writeChunk();
101 
102             out.write(0x00);
103         } catch (IOException e) {
104             exception = e;
105             throw e;
106         }
107 
108         finished = true;
109         arrayCache.putArray(uncompBuf);
110     }
111 
flush()112     public void flush() throws IOException {
113         if (exception != null)
114             throw exception;
115 
116         if (finished)
117             throw new XZIOException("Stream finished or closed");
118 
119         try {
120             if (uncompPos > 0)
121                 writeChunk();
122 
123             out.flush();
124         } catch (IOException e) {
125             exception = e;
126             throw e;
127         }
128     }
129 
finish()130     public void finish() throws IOException {
131         if (!finished) {
132             writeEndMarker();
133 
134             try {
135                 out.finish();
136             } catch (IOException e) {
137                 exception = e;
138                 throw e;
139             }
140         }
141     }
142 
close()143     public void close() throws IOException {
144         if (out != null) {
145             if (!finished) {
146                 try {
147                     writeEndMarker();
148                 } catch (IOException e) {}
149             }
150 
151             try {
152                 out.close();
153             } catch (IOException e) {
154                 if (exception == null)
155                     exception = e;
156             }
157 
158             out = null;
159         }
160 
161         if (exception != null)
162             throw exception;
163     }
164 }
165