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

de.schlichtherle.io.rof.MemoryMappedReadOnlyFile 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) 2005-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.*;
import java.nio.*;
import java.nio.channels.*;

/**
 * A {@link ReadOnlyFile} implementation using channels to map the underlying
 * file into memory.
 * This class supports files larger than Integer.MAX_VALUE.
 *
 * @deprecated This class does not reliably work on the Windows platform,
 *             and hence its not used in TrueZIP.
 *             The reason is that the mapped file remains allocated until the
 *             garbage collector frees it even if the file channel and/or the
 *             RandomAccessFile has been closed. Subsequent delete/write
 *             operations on the file will then fail. For more information,
 *             please refer to
 *             
 *             http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154.
 * @author Christian Schlichtherle
 * @version $Id: MemoryMappedReadOnlyFile.java,v 1.3 2010/08/16 10:55:48 christian_schlichtherle Exp $
 */
public class MemoryMappedReadOnlyFile extends AbstractReadOnlyFile {

    /** The length of the mapped window. */
    private static final int WINDOW_LEN = Integer.MAX_VALUE;

    private FileChannel channel;
    private long windowOff = -1;
    private ByteBuffer window;

    public MemoryMappedReadOnlyFile(File file) throws FileNotFoundException {
        channel = new FileInputStream(file).getChannel();
        try {
            window(0);
        } catch (IOException ioe) {
            FileNotFoundException fnfe = new FileNotFoundException(ioe.toString());
            fnfe.initCause(ioe);
            throw fnfe;
        }
        assert window != null;
        assert windowOff == 0;
    }

    /**
     * Returns the number of bytes available in the floating window.
     * The window is positioned so that at least one byte is available
     * unless the end of the file has been reached.
     *
     * @return The number of bytes available in the floating window.
     *         If zero, the end of the file has been reached.
     */
    private final int available() throws IOException {
        ensureOpen();
        if (window.remaining() <= 0)
            window(windowOff + WINDOW_LEN);
        return window.remaining();
    }

    private void window(long newWindowOff) throws IOException {
        if (windowOff == newWindowOff)
            return;

        final long size = channel.size();
        if (newWindowOff > size)
            newWindowOff = size; // don't move past EOF

        window = channel.map(   FileChannel.MapMode.READ_ONLY, newWindowOff,
                                Math.min(size - newWindowOff, WINDOW_LEN));
        assert window != null;
        windowOff = newWindowOff;
    }

    public long length() throws IOException {
        ensureOpen();
        return channel.size();
    }

    public long getFilePointer() throws IOException {
        ensureOpen();
        return windowOff + window.position();
    }

    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 + ")");

        window(fp / WINDOW_LEN * WINDOW_LEN); // round down
        window.position((int) (fp % WINDOW_LEN));
    }

    public int read() throws IOException {
        return available() > 0 ? window.get() & 0xff : -1;
    }
    
    public int read(final byte[] buf, final int off, int len)
    throws IOException {
        if (len == 0)
            return 0; // be fault-tolerant and compatible to RandomAccessFile

        // Check state.
        final int avail = available();
        if (avail <= 0)
            return -1; // EOF

        // Check parameters.
        if (buf == null)
            throw new NullPointerException("buf");
        if (off < 0 || len < 0 || off + len > buf.length)
            throw new IndexOutOfBoundsException();

        if (len > avail)
            len = avail;
        window.get(buf, off, len);
        return len;
    }

    public void close() throws IOException {
        // Check state.
        if (channel == null)
            return;

        // Order is important here!
        window = null;
        try {
            channel.close();
        } finally {
            channel = null;
            // Workaround for garbage collection issue with memory mapped files.
            // Note that there's no guarantee that this works: Sometimes it
            // does, sometimes not!
            System.gc(); 
            System.runFinalization();
            // Thread.interrupted(); // cancel pending interrupt
            try {
                Thread.sleep(50);
            } catch (InterruptedException dontCare) {
            }
        }
    }

    /**
     * Ensures that this file is open.
     *
     * @throws IOException If the preconditions do not hold.
     */
    private final void ensureOpen() throws IOException {
        if (channel == null)
            throw new IOException("file is closed");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy