1 /* 2 * SimpleOutputStream 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.IOException; 13 import org.tukaani.xz.simple.SimpleFilter; 14 15 class SimpleOutputStream extends FinishableOutputStream { 16 private static final int FILTER_BUF_SIZE = 4096; 17 18 private FinishableOutputStream out; 19 private final SimpleFilter simpleFilter; 20 21 private final byte[] filterBuf = new byte[FILTER_BUF_SIZE]; 22 private int pos = 0; 23 private int unfiltered = 0; 24 25 private IOException exception = null; 26 private boolean finished = false; 27 28 private final byte[] tempBuf = new byte[1]; 29 30 static int getMemoryUsage() { 31 return 1 + FILTER_BUF_SIZE / 1024; 32 } 33 34 SimpleOutputStream(FinishableOutputStream out, 35 SimpleFilter simpleFilter) { 36 if (out == null) 37 throw new NullPointerException(); 38 39 this.out = out; 40 this.simpleFilter = simpleFilter; 41 } 42 43 public void write(int b) throws IOException { 44 tempBuf[0] = (byte)b; 45 write(tempBuf, 0, 1); 46 } 47 48 public void write(byte[] buf, int off, int len) throws IOException { 49 if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length) 50 throw new IndexOutOfBoundsException(); 51 52 if (exception != null) 53 throw exception; 54 55 if (finished) 56 throw new XZIOException("Stream finished or closed"); 57 58 while (len > 0) { 59 // Copy more unfiltered data into filterBuf. 60 int copySize = Math.min(len, FILTER_BUF_SIZE - (pos + unfiltered)); 61 System.arraycopy(buf, off, filterBuf, pos + unfiltered, copySize); 62 off += copySize; 63 len -= copySize; 64 unfiltered += copySize; 65 66 // Filter the data in filterBuf. 67 int filtered = simpleFilter.code(filterBuf, pos, unfiltered); 68 assert filtered <= unfiltered; 69 unfiltered -= filtered; 70 71 // Write out the filtered data. 72 try { 73 out.write(filterBuf, pos, filtered); 74 } catch (IOException e) { 75 exception = e; 76 throw e; 77 } 78 79 pos += filtered; 80 81 // If end of filterBuf was reached, move the pending unfiltered 82 // data to the beginning of the buffer so that more data can 83 // be copied into filterBuf on the next loop iteration. 84 if (pos + unfiltered == FILTER_BUF_SIZE) { 85 System.arraycopy(filterBuf, pos, filterBuf, 0, unfiltered); 86 pos = 0; 87 } 88 } 89 } 90 91 private void writePending() throws IOException { 92 assert !finished; 93 94 if (exception != null) 95 throw exception; 96 97 try { 98 out.write(filterBuf, pos, unfiltered); 99 } catch (IOException e) { 100 exception = e; 101 throw e; 102 } 103 104 finished = true; 105 } 106 107 public void flush() throws IOException { 108 throw new UnsupportedOptionsException("Flushing is not supported"); 109 } 110 111 public void finish() throws IOException { 112 if (!finished) { 113 // If it fails, don't call out.finish(). 114 writePending(); 115 116 try { 117 out.finish(); 118 } catch (IOException e) { 119 exception = e; 120 throw e; 121 } 122 } 123 } 124 125 public void close() throws IOException { 126 if (out != null) { 127 if (!finished) { 128 // out.close() must be called even if writePending() fails. 129 // writePending() saves the possible exception so we can 130 // ignore exceptions here. 131 try { 132 writePending(); 133 } catch (IOException e) {} 134 } 135 136 try { 137 out.close(); 138 } catch (IOException e) { 139 // If there is an earlier exception, the exception 140 // from out.close() is lost. 141 if (exception == null) 142 exception = e; 143 } 144 145 out = null; 146 } 147 148 if (exception != null) 149 throw exception; 150 } 151 } 152