aboutsummaryrefslogtreecommitdiff
path: root/src/org/tukaani/xz/SimpleInputStream.java
blob: afd40c77e20854e61a175eb3981f93f0fdf7400f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * SimpleInputStream
 *
 * Author: Lasse Collin <lasse.collin@tukaani.org>
 *
 * This file has been put into the public domain.
 * You can do whatever you want with this file.
 */

package org.tukaani.xz;

import java.io.InputStream;
import java.io.IOException;
import org.tukaani.xz.simple.SimpleFilter;

class SimpleInputStream extends InputStream {
    private static final int FILTER_BUF_SIZE = 4096;

    private InputStream in;
    private final SimpleFilter simpleFilter;

    private final byte[] filterBuf = new byte[FILTER_BUF_SIZE];
    private int pos = 0;
    private int filtered = 0;
    private int unfiltered = 0;

    private boolean endReached = false;
    private IOException exception = null;

    private final byte[] tempBuf = new byte[1];

    static int getMemoryUsage() {
        return 1 + FILTER_BUF_SIZE / 1024;
    }

    SimpleInputStream(InputStream in, SimpleFilter simpleFilter) {
        // Check for null because otherwise null isn't detect
        // in this constructor.
        if (in == null)
            throw new NullPointerException();

        // The simpleFilter argument comes from this package
        // so it is known to be non-null already.
        assert simpleFilter != null;

        this.in = in;
        this.simpleFilter = simpleFilter;
    }

    public int read() throws IOException {
        return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
    }

    public int read(byte[] buf, int off, int len) throws IOException {
        if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
            throw new IndexOutOfBoundsException();

        if (len == 0)
            return 0;

        if (in == null)
            throw new XZIOException("Stream closed");

        if (exception != null)
            throw exception;

        try {
            int size = 0;

            while (true) {
                // Copy filtered data into the caller-provided buffer.
                int copySize = Math.min(filtered, len);
                System.arraycopy(filterBuf, pos, buf, off, copySize);
                pos += copySize;
                filtered -= copySize;
                off += copySize;
                len -= copySize;
                size += copySize;

                // If end of filterBuf was reached, move the pending data to
                // the beginning of the buffer so that more data can be
                // copied into filterBuf on the next loop iteration.
                if (pos + filtered + unfiltered == FILTER_BUF_SIZE) {
                    System.arraycopy(filterBuf, pos, filterBuf, 0,
                                     filtered + unfiltered);
                    pos = 0;
                }

                if (len == 0 || endReached)
                    return size > 0 ? size : -1;

                assert filtered == 0;

                // Get more data into the temporary buffer.
                int inSize = FILTER_BUF_SIZE - (pos + filtered + unfiltered);
                inSize = in.read(filterBuf, pos + filtered + unfiltered,
                                 inSize);

                if (inSize == -1) {
                    // Mark the remaining unfiltered bytes to be ready
                    // to be copied out.
                    endReached = true;
                    filtered = unfiltered;
                    unfiltered = 0;
                } else {
                    // Filter the data in filterBuf.
                    unfiltered += inSize;
                    filtered = simpleFilter.code(filterBuf, pos, unfiltered);
                    assert filtered <= unfiltered;
                    unfiltered -= filtered;
                }
            }
        } catch (IOException e) {
            exception = e;
            throw e;
        }
    }

    public int available() throws IOException {
        if (in == null)
            throw new XZIOException("Stream closed");

        if (exception != null)
            throw exception;

        return filtered;
    }

    public void close() throws IOException {
        if (in != null) {
            try {
                in.close();
            } finally {
                in = null;
            }
        }
    }
}