Home | History | Annotate | Download | only in xz
      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