All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.io.rof.BufferedReadOnlyFile Maven / Gradle / Ivy

Go to download

TrueZIP is a Java based Virtual File System (VFS) to enable transparent, multi-threaded read/write access to archive files (ZIP, TAR etc.) as if they were directories. Archive files may be arbitrarily nested and the nesting level is only limited by heap and file system size.

The newest version!
/*
 * Copyright (C) 2006-2010 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.schlichtherle.io.rof;

import java.io.*;

/**
 * A {@link ReadOnlyFile} implementation which provides buffered random read
 * only access to another {@code ReadOnlyFile}.
 * 

* Note: This class implements its own virtual file pointer. * Thus, if you would like to access the underlying {@code ReadOnlyFile} * again after you have finished working with an instance of this class, * you should synchronize their file pointers using the pattern as described * in {@link FilterReadOnlyFile}. * * @author Christian Schlichtherle * @version $Id: BufferedReadOnlyFile.java,v 1.4 2010/08/20 13:09:47 christian_schlichtherle Exp $ */ public class BufferedReadOnlyFile extends FilterReadOnlyFile { /** * The default buffer length of the window to the file. */ public static final int WINDOW_LEN = 4096; /** Returns the smaller parameter. */ protected static final long min(long a, long b) { return a < b ? a : b; } /** Returns the greater parameter. */ protected static final long max(long a, long b) { return a < b ? b : a; } private long length; /** * The virtual file pointer in the file data. * This is relative to the start of the file. */ private long fp; /** * The current offset in the read only file where the buffer window starts. * This is always a multiple of the buffer window size. */ private long windowOff; /** * The buffer window to the file data. */ private final byte[] window; private boolean closed; /** * Creates a new instance of {@code BufferedReadOnlyFile}. * * @param file The file to read. * @throws NullPointerException If any of the parameters is {@code null}. * @throws FileNotFoundException If the file cannot get opened for reading. * @throws IOException On any other I/O related issue. */ public BufferedReadOnlyFile( final File file) throws NullPointerException, FileNotFoundException, IOException { this(null, file, WINDOW_LEN); } /** * Creates a new instance of {@code BufferedReadOnlyFile}. * * @param file The file to read. * @param windowLen The size of the buffer window in bytes. * @throws NullPointerException If any of the parameters is {@code null}. * @throws FileNotFoundException If the file cannot get opened for reading. * @throws IOException On any other I/O related issue. */ public BufferedReadOnlyFile( final File file, final int windowLen) throws NullPointerException, FileNotFoundException, IOException { this(null, file, windowLen); } /** * Creates a new instance of {@code BufferedReadOnlyFile}. * * @param rof The read only file to read. * @throws NullPointerException If any of the parameters is {@code null}. * @throws FileNotFoundException If the file cannot get opened for reading. * @throws IOException On any other I/O related issue. */ public BufferedReadOnlyFile( final ReadOnlyFile rof) throws NullPointerException, FileNotFoundException, IOException { this(rof, null, WINDOW_LEN); } /** * Creates a new instance of {@code BufferedReadOnlyFile}. * * @param rof The read only file to read. * @param windowLen The size of the buffer window in bytes. * @throws NullPointerException If any of the parameters is {@code null}. * @throws FileNotFoundException If the file cannot get opened for reading. * @throws IOException On any other I/O related issue. */ public BufferedReadOnlyFile( final ReadOnlyFile rof, final int windowLen) throws NullPointerException, FileNotFoundException, IOException { this(rof, null, windowLen); } private BufferedReadOnlyFile( ReadOnlyFile rof, final File file, final int windowLen) throws NullPointerException, FileNotFoundException, IOException { super(rof); // Check parameters (fail fast). if (rof == null) { if (file == null) throw new NullPointerException(); rof = createReadOnlyFile(file); } else { // rof != null assert file == null; } if (windowLen <= 0) throw new IllegalArgumentException(); super.rof = rof; length = rof.length(); fp = rof.getFilePointer(); window = new byte[windowLen]; invalidateWindow(); assert window.length > 0; } /** * A factory method called by the constructor to get a read only file * to access the contents of the read only file. * This method is only used if the constructor isn't called with a read * only file as its parameter. * * @throws FileNotFoundException If the file cannot get opened for reading. * @throws IOException On any other I/O related issue. */ protected ReadOnlyFile createReadOnlyFile(File file) throws IOException { return new SimpleReadOnlyFile(file); //return new FastReadOnlyFile(file); //return new ChannelReadOnlyFile(file); } public long length() throws IOException { final long newLength = rof.length(); if (newLength != length) { length = newLength; invalidateWindow(); } return length; } public long getFilePointer() throws IOException { ensureOpen(); return fp; } public void seek(final long fp) throws IOException { ensureOpen(); if (fp < 0) throw new IOException("file pointer must not be negative"); final long length = length(); if (fp > length) throw new IOException("file pointer (" + fp + ") is larger than file length (" + length + ")"); this.fp = fp; } public int read() throws IOException { // Check state. ensureOpen(); if (fp >= length()) return -1; // Position window and return its data. positionWindow(); return window[(int) (fp++ % window.length)] & 0xff; } public int read(final byte[] buf, final int off, final int len) throws IOException { if (len == 0) return 0; // be fault-tolerant and compatible to RandomAccessFile // Check state. ensureOpen(); final long length = length(); if (fp >= length) return -1; // Check parameters. if (buf == null) throw new NullPointerException("buf"); final int offPlusLen = off + len; if ((off | len | offPlusLen | buf.length - offPlusLen) < 0) throw new IndexOutOfBoundsException(); // Setup. final int windowLen = window.length; int read = 0; // amount of decrypted data copied to buf { // Partial read of window data at the start. final int o = (int) (fp % windowLen); if (o != 0) { // The file pointer is not on a window boundary. positionWindow(); read = (int) min(len, windowLen - o); read = (int) min(read, length - fp); System.arraycopy(window, o, buf, off, read); fp += read; } } { // Full read of window data in the center. while (read + windowLen < len && fp + windowLen <= length) { // The file pointer is starting and ending on window boundaries. positionWindow(); System.arraycopy(window, 0, buf, off + read, windowLen); read += windowLen; fp += windowLen; } } // Partial read of window data at the end. if (read < len && fp < length) { // The file pointer is not on a window boundary. positionWindow(); final int n = (int) min(len - read, length - fp); System.arraycopy(window, 0, buf, off + read, n); read += n; fp += n; } // Assert that at least one byte has been read if len isn't zero. // Note that EOF has been tested before. assert read > 0; return read; } /** * Closes this read only file. * As a side effect, this will set the reference to the underlying read * only file ({@link #rof} to {@code null}. */ public void close() throws IOException { if (closed) return; // Order is important here! closed = true; rof.close(); } /** * Ensures that this file is open. * * @throws IOException If the preconditions do not hold. */ private final void ensureOpen() throws IOException { if (closed) throw new IOException("file is closed"); } // // Buffer window operations. // /** * Ensures that the window is positioned so that the block containing * the current virtual file pointer in the encrypted file is entirely * contained in it. * * @throws IOException On any I/O related issue. * The window is invalidated in this case. */ private void positionWindow() throws IOException { // Check window position. final long fp = this.fp; final int windowLen = window.length; final long nextWindowOff = windowOff + windowLen; if (windowOff <= fp && fp < nextWindowOff) return; try { // Move window in the buffered file. windowOff = (fp / windowLen) * windowLen; // round down to multiple of window size if (windowOff != nextWindowOff) rof.seek(windowOff); // Fill window until end of file or buffer. // This should normally complete in one loop cycle, but we do not // depend on this as it would be a violation of ReadOnlyFile's // contract. int n = 0; do { int read = rof.read(window, n, windowLen - n); if (read < 0) break; n += read; } while (n < windowLen); } catch (IOException ioe) { windowOff = -windowLen - 1; // force seek() at next positionWindow() throw ioe; } } /** * Forces the window to be reloaded on the next call to * {@link #positionWindow()}. */ private final void invalidateWindow() { windowOff = Long.MIN_VALUE; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy