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

com.signalfx.shaded.apache.commons.io.input.MemoryMappedFileInputStream Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.signalfx.shaded.apache.commons.io.input;

import static com.signalfx.shaded.apache.commons.io.IOUtils.EOF;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import com.signalfx.shaded.apache.commons.io.build.AbstractStreamBuilder;

/**
 * An {@link InputStream} that utilizes memory mapped files to improve performance. A sliding window of the file is
 * mapped to memory to avoid mapping the entire file to memory at one time. The size of the sliding buffer is
 * configurable.
 * 

* For most operating systems, mapping a file into memory is more expensive than reading or writing a few tens of * kilobytes of data. From the standpoint of performance. it is generally only worth mapping relatively large files into * memory. *

*

* Note: Use of this class does not necessarily obviate the need to use a {@link BufferedInputStream}. Depending on the * use case, the use of buffering may still further improve performance. For example: *

*

* To build an instance, use {@link Builder}. *

*
{@code
 * BufferedInputStream s = new BufferedInputStream(new GzipInputStream(
 *   MemoryMappedFileInputStream.builder()
 *     .setPath(path)
 *     .setBufferSize(256 * 1024)
 *     .get()));}
 * 
*

* should outperform: *

*
 * new GzipInputStream(new MemoryMappedFileInputStream(path))
 * 
*
{@code
 * GzipInputStream s = new GzipInputStream(
 *   MemoryMappedFileInputStream.builder()
 *     .setPath(path)
 *     .setBufferSize(256 * 1024)
 *     .get());}
 * 
* * @see Builder * @since 2.12.0 */ public final class MemoryMappedFileInputStream extends InputStream { // @formatter:off /** * Builds a new {@link MemoryMappedFileInputStream}. * *

* For example: *

*
{@code
     * MemoryMappedFileInputStream s = MemoryMappedFileInputStream.builder()
     *   .setPath(path)
     *   .setBufferSize(256 * 1024)
     *   .get();}
     * 
* * @see #get() * @since 2.12.0 */ // @formatter:on public static class Builder extends AbstractStreamBuilder { /** * Constructs a new {@link Builder}. */ public Builder() { setBufferSizeDefault(DEFAULT_BUFFER_SIZE); setBufferSize(DEFAULT_BUFFER_SIZE); } /** * Builds a new {@link MemoryMappedFileInputStream}. *

* You must set input that supports {@link #getPath()}, otherwise, this method throws an exception. *

*

* This builder use the following aspects: *

*
    *
  • {@link #getPath()}
  • *
  • {@link #getBufferSize()}
  • *
* * @return a new instance. * @throws IllegalStateException if the {@code origin} is {@code null}. * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Path}. * @throws IOException if an I/O error occurs. * @see #getPath() * @see #getBufferSize() */ @Override public MemoryMappedFileInputStream get() throws IOException { return new MemoryMappedFileInputStream(getPath(), getBufferSize()); } } /** * Default size of the sliding memory mapped buffer. We use 256K, equal to 65536 pages (given a 4K page size). * Increasing the value beyond the default size will generally not provide any increase in throughput. */ private static final int DEFAULT_BUFFER_SIZE = 256 * 1024; private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]).asReadOnlyBuffer(); /** * Constructs a new {@link Builder}. * * @return a new {@link Builder}. * @since 2.12.0 */ public static Builder builder() { return new Builder(); } private final int bufferSize; private final FileChannel channel; private ByteBuffer buffer = EMPTY_BUFFER; private boolean closed; /** * The starting position (within the file) of the next sliding buffer. */ private long nextBufferPosition; /** * Constructs a new instance. * * @param file The path of the file to open. * @param bufferSize Size of the sliding buffer. * @throws IOException If an I/O error occurs. */ private MemoryMappedFileInputStream(final Path file, final int bufferSize) throws IOException { this.bufferSize = bufferSize; this.channel = FileChannel.open(file, StandardOpenOption.READ); } @Override public int available() throws IOException { return buffer.remaining(); } private void cleanBuffer() { if (ByteBufferCleaner.isSupported() && buffer.isDirect()) { ByteBufferCleaner.clean(buffer); } } @Override public void close() throws IOException { if (!closed) { cleanBuffer(); buffer = null; channel.close(); closed = true; } } private void ensureOpen() throws IOException { if (closed) { throw new IOException("Stream closed"); } } int getBufferSize() { return bufferSize; } private void nextBuffer() throws IOException { final long remainingInFile = channel.size() - nextBufferPosition; if (remainingInFile > 0) { final long amountToMap = Math.min(remainingInFile, bufferSize); cleanBuffer(); buffer = channel.map(MapMode.READ_ONLY, nextBufferPosition, amountToMap); nextBufferPosition += amountToMap; } else { buffer = EMPTY_BUFFER; } } @Override public int read() throws IOException { ensureOpen(); if (!buffer.hasRemaining()) { nextBuffer(); if (!buffer.hasRemaining()) { return EOF; } } return Short.toUnsignedInt(buffer.get()); } @Override public int read(final byte[] b, final int off, final int len) throws IOException { ensureOpen(); if (!buffer.hasRemaining()) { nextBuffer(); if (!buffer.hasRemaining()) { return EOF; } } final int numBytes = Math.min(buffer.remaining(), len); buffer.get(b, off, numBytes); return numBytes; } @Override public long skip(final long n) throws IOException { ensureOpen(); if (n <= 0) { return 0; } if (n <= buffer.remaining()) { buffer.position((int) (buffer.position() + n)); return n; } final long remainingInFile = channel.size() - nextBufferPosition; final long skipped = buffer.remaining() + Math.min(remainingInFile, n - buffer.remaining()); nextBufferPosition += skipped - buffer.remaining(); nextBuffer(); return skipped; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy