RegionFileInputStream.java 9.85 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.day.io;

import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.ZipException;

public class RegionFileInputStream
extends InputStream {
    static final String CVS_ID = "$URL: http://svn.day.com/repos/commons/tags/day-commons-io-1.1.8/src/java/com/day/io/RegionFileInputStream.java $ $Rev: 25879 $ $Date: 2007-03-27 16:35:13 +0200 (Tue, 27 Mar 2007) $";
    static final int MAX_BUFFER = 8192;
    private byte[] buffer;
    private final byte[] singleByteBuf = new byte[1];
    private int pos = 0;
    private int end = 0;
    private final long regionStart;
    private final long regionEnd;
    private long lastSyncPoint = -1;
    private long lastSyncPos = 0;
    private long length;
    private long position;
    private long markedPosition = 0;
    private File file;
    private RandomAccessFile raf;
    private Inflater inflater;
    private boolean isInflating;
    private boolean reachEOF = false;
    private IOException closedBy = null;
    private boolean hasMultiStreams = true;

    public RegionFileInputStream(File file) throws IOException, FileNotFoundException {
        this(file, false);
    }

    public RegionFileInputStream(File file, boolean inflate) throws IOException, FileNotFoundException {
        this(file, 0, file.length(), inflate);
    }

    public RegionFileInputStream(File file, long off, long len) throws IOException, FileNotFoundException {
        this(file, off, len, false);
    }

    public RegionFileInputStream(File file, long off, long len, boolean inflate) throws FileNotFoundException, IOException {
        this.file = file;
        this.regionStart = off;
        this.regionEnd = off + len;
        this.length = Integer.MAX_VALUE;
        if (this.regionEnd > file.length()) {
            throw new EOFException("Region overlaps.");
        }
        if (!file.canRead()) {
            throw new FileNotFoundException(file.getPath());
        }
        this.isInflating = inflate;
        if (!this.isInflating) {
            this.length = this.regionEnd - this.regionStart;
        }
    }

    private RegionFileInputStream(File file, long start, long end, long pos, long len) throws IOException, FileNotFoundException, EOFException {
        this(file, start, end - start, true);
        if (this.skip(pos) < 0) {
            throw new EOFException("error while seeking to " + pos);
        }
        this.length = this.position + len;
    }

    private void ensureOpen() throws IOException {
        if (this.closedBy != null) {
            throw this.closedBy;
        }
        if (this.raf == null) {
            this.raf = new RandomAccessFile(this.file, "r");
            if (this.isInflating) {
                this.inflater = new Inflater();
            }
            this.raf.seek(this.regionStart);
            this.buffer = new byte[(int)Math.min(this.regionEnd - this.regionStart, 8192)];
        }
    }

    public int available() throws IOException {
        if (this.reachEOF) {
            return 0;
        }
        return 1;
    }

    public void close() throws IOException {
        if (this.raf != null) {
            this.raf.close();
            this.raf = null;
        }
        try {
            throw new IOException("RegionFileInputStream already closed. Stack trace is the one of the closer. (file=" + this.file.getPath() + ", s=" + this.regionStart + ", e=" + this.regionEnd);
        }
        catch (IOException e) {
            this.closedBy = e;
            return;
        }
    }

    public synchronized void reset() throws IOException {
        if (!this.markSupported()) {
            throw new IOException("mark not supported or not called.");
        }
        this.pos = 0;
        this.end = 0;
        this.position = this.markedPosition;
        this.raf.seek(this.regionStart + this.position);
    }

    public boolean markSupported() {
        return !this.isInflating;
    }

    public synchronized void mark(int readlimit) {
        if (!this.isInflating) {
            try {
                this.ensureOpen();
                this.markedPosition = this.position;
            }
            catch (IOException e) {
                // empty catch block
            }
        }
    }

    public long skip(long n) throws IOException {
        int len;
        int total;
        this.ensureOpen();
        byte[] b = new byte[8192];
        int max = (int)Math.min(n, Integer.MAX_VALUE);
        for (total = 0; total < max; total += len) {
            len = max - total;
            if (len > b.length) {
                len = b.length;
            }
            if ((len = this.read(b, 0, len)) != -1) continue;
            this.reachEOF = true;
            break;
        }
        return total;
    }

    public int read() throws IOException {
        return this.read(this.singleByteBuf, 0, 1) == -1 ? -1 : this.singleByteBuf[0] & 255;
    }

    public int read(byte[] b) throws IOException {
        return this.read(b, 0, b.length);
    }

    public int read(byte[] b, int off, int len) throws IOException {
        int read;
        this.ensureOpen();
        len = Math.min(len, (int)(this.length - this.position));
        if (len == 0) {
            return -1;
        }
        int n = read = this.inflater == null ? this.readBuffered(b, off, len) : this.readCompressed(b, off, len);
        if (read > 0) {
            this.position += (long)read;
        }
        return read;
    }

    private int readBuffered(byte[] b, int off, int len) throws IOException {
        if (this.fill() < 0) {
            return -1;
        }
        int read = 0;
        while (len > 0 && this.fill() >= 0) {
            int d = Math.min(this.end - this.pos, len);
            System.arraycopy(this.buffer, this.pos, b, off, d);
            this.pos += d;
            off += d;
            len -= d;
            read += d;
        }
        return read;
    }

    private int readCompressed(byte[] b, int off, int len) throws IOException {
        try {
            int n;
            while ((n = this.inflater.inflate(b, off, len)) == 0) {
                if (this.inflater.finished() || this.inflater.needsDictionary()) {
                    if (this.hasMultiStreams && this.getRemaining() > 0) {
                        int r = this.inflater.getRemaining();
                        this.inflater.reset();
                        this.inflater.setInput(this.buffer, this.end - r, r);
                        this.lastSyncPoint = this.raf.getFilePointer() - (long)r;
                        this.lastSyncPos = this.position;
                    } else {
                        this.reachEOF = true;
                        return -1;
                    }
                }
                if (!this.inflater.needsInput()) continue;
                this.feed();
            }
            return n;
        }
        catch (DataFormatException e) {
            String s = e.getMessage();
            throw new ZipException(s != null ? s : "Invalid ZLIB data format");
        }
    }

    private int fill() throws IOException {
        int d;
        int read;
        if (this.end > this.pos) {
            return this.end - this.pos;
        }
        this.pos = 0;
        this.end = 0;
        if (d == 0) {
            this.reachEOF = true;
            return -1;
        }
        for (d = (int)Math.min((long)((long)this.buffer.length), (long)(this.regionEnd - this.raf.getFilePointer())); d > 0; d -= read) {
            read = this.raf.read(this.buffer, this.end, d);
            if (read < 0) {
                throw new EOFException("error while reading past region boundaries");
            }
            this.end += read;
        }
        return this.end - this.pos;
    }

    private int feed() throws IOException {
        int d;
        int read;
        this.pos = 0;
        this.end = 0;
        if (d == 0) {
            this.reachEOF = true;
            return -1;
        }
        for (d = (int)Math.min((long)((long)this.buffer.length), (long)(this.regionEnd - this.raf.getFilePointer())); d > 0; d -= read) {
            read = this.raf.read(this.buffer, this.end, d);
            if (read < 0) {
                throw new EOFException("error while reading past region boundaries");
            }
            this.end += read;
        }
        this.inflater.setInput(this.buffer, 0, this.end);
        return this.end;
    }

    public File getFile() {
        return this.file;
    }

    public long getAbsolutePosition() throws IOException {
        this.ensureOpen();
        return this.regionStart + this.position;
    }

    public long getPosition() throws IOException {
        this.ensureOpen();
        return this.position;
    }

    public long getRemaining() throws IOException {
        this.ensureOpen();
        if (this.length == Integer.MAX_VALUE) {
            return this.regionEnd - this.raf.getFilePointer() + (long)this.inflater.getRemaining();
        }
        return this.length - this.position;
    }

    public RegionFileInputStream substream(long off, long len) throws IOException {
        this.ensureOpen();
        if (this.inflater == null) {
            return new RegionFileInputStream(this.file, this.getAbsolutePosition() + off, len);
        }
        if (this.lastSyncPoint < 0) {
            return new RegionFileInputStream(this.file, this.regionStart, this.regionEnd, this.position + off, len);
        }
        return new RegionFileInputStream(this.file, this.lastSyncPoint, this.regionEnd, this.position + off - this.lastSyncPos, len);
    }

    public RegionFileInputStream duplicate() throws IOException {
        if (this.raf != null || this.inflater != null) {
            return null;
        }
        return new RegionFileInputStream(this.file, this.regionStart, this.regionEnd - this.regionStart, false);
    }
}