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

uk.ac.starlink.fits.BlockMappedInput Maven / Gradle / Ivy

package uk.ac.starlink.fits;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Random-access BasicInput implementation that maps a given region of a file
 * as a number of byte buffers.
 * The most recently-used buffer is always kept, but there is a choice of
 * what to do with less-recently used ones.  Concrete subclasses are
 * provided that either discard them automatically or keep them around
 * for a period of time before discarding them.
 * If and when a buffer is discarded, an attempt is made to release resources.
 *
 * 

Note: DO NOT use an instance * of this class from multiple threads - see {@link Unmapper}. * * @author Mark Taylor * @since 2 Dec 2014 */ public abstract class BlockMappedInput extends BlockInput { private final BlockManager blockManager_; private final long blockSize_; private final int nblock_; /** Default time in milliseconds after which buffers will be discarded. */ public static final long DEFAULT_EXPIRYMILLIS = 20 * 1000; /** * Constructor. * * @param blockManager manages file mapping using byte buffers */ protected BlockMappedInput( BlockManager blockManager ) { super( blockManager.getBlockCount() ); blockManager_ = blockManager; nblock_ = blockManager.getBlockCount(); blockSize_ = blockManager.getBlockSize(); } public int[] getBlockPos( long offset ) { return new int[] { (int) ( offset / blockSize_ ), (int) ( offset % blockSize_ ), }; } public long getBlockOffset( int iblock, int offsetInBlock ) { return iblock * blockSize_ + offsetInBlock; } /** * This does not close the BlockManager. */ public void close() { super.close(); } /** * Creates a new byte buffer for a given block. * * @param iblock block index * @return new buffer */ ByteBuffer createBuffer( int iblock ) throws IOException { return blockManager_.getBufferManager( iblock ).createBuffer(); } /** * Disposes of a buffer formerly obtained by {@link #createBuffer}. * The buffer must not be used following this call. * * @param iblock block index for which buffer was obtained * @param buf buffer which will no longer be used */ void disposeBuffer( int iblock, ByteBuffer buf ) { if ( iblock >= 0 ) { blockManager_.getBufferManager( iblock ).disposeBuffer( buf ); } } /** * Constructs an instance that does or does not support caching. * If caching is requested, recently used block buffers are kept around * for a while in case they are needed again. If not, as soon as * a new block is used, any others are discarded. * * @param blockManager manages buffer in blocks * @param caching whether buffers are cached * @return new instance */ public static BlockMappedInput createInput( BlockManager blockManager, boolean caching ) throws IOException { return createInput( blockManager, caching ? DEFAULT_EXPIRYMILLIS : 0 ); } /** * Constructs an instance with explicit configuration. * The expiryMillis parameter controls caching. * If zero, the current buffer is discarded as soon * as a different one is used. Otherwise, an attempt is made to * discard buffers only after they have been unused for a certain * number of milliseconds. * * @param blockManager manages buffer in blocks * @param expiryMillis buffer caching period in milliseconds * @return new instance */ public static BlockMappedInput createInput( BlockManager blockManager, long expiryMillis ) throws IOException { return expiryMillis > 0 ? new CachingBlockMappedInput( blockManager, expiryMillis ) : new UniqueBlockMappedInput( blockManager ); } /** * Instance that only keeps one buffer at a time. */ private static class UniqueBlockMappedInput extends BlockMappedInput { private int iblock_; private ByteBuffer buffer_; /** * Constructor. * * @param blockManager manages buffer in blocks */ public UniqueBlockMappedInput( BlockManager blockManager ) { super( blockManager ); } protected ByteBuffer acquireBlock( int iblock ) throws IOException { int oldIndex = iblock_; ByteBuffer oldBuffer = buffer_; if ( oldBuffer != null ) { disposeBuffer( oldIndex, oldBuffer ); } buffer_ = createBuffer( iblock ); iblock_ = iblock; return buffer_; } public void close() { super.close(); int oldIndex = iblock_; ByteBuffer oldBuffer = buffer_; if ( oldBuffer != null ) { iblock_ = -1; buffer_ = null; disposeBuffer( oldIndex, oldBuffer ); } } } /** * Instance that keeps buffers until a time period has expired. */ private static class CachingBlockMappedInput extends BlockMappedInput { private final int nblock_; private final long expiryMillis_; private final long tidyMillis_; private final ByteBuffer[] bufs_; private final long[] useEpochs_; private long lastTidy_; /** * Constructor. * * @param blockManager manages buffer in blocks * @param expiryMillis buffer caching period in milliseconds */ public CachingBlockMappedInput( BlockManager blockManager, long expiryMillis ) { super( blockManager ); expiryMillis_ = expiryMillis; tidyMillis_ = expiryMillis / 4; nblock_ = getBlockCount(); bufs_ = new ByteBuffer[ nblock_ ]; useEpochs_ = new long[ nblock_ ]; lastTidy_ = System.currentTimeMillis(); } protected ByteBuffer acquireBlock( int iblock ) throws IOException { ByteBuffer buf = bufs_[ iblock ]; if ( buf == null ) { buf = createBuffer( iblock ); long now = System.currentTimeMillis(); if ( now - lastTidy_ > tidyMillis_ ) { tidyCache( now - expiryMillis_ ); lastTidy_ = now; } bufs_[ iblock ] = buf; useEpochs_[ iblock ] = now; } else { buf.position( 0 ); } return buf; } public void close() { super.close(); tidyCache( Long.MAX_VALUE ); } /** * Attempts to unmap buffers that have not been used more recently * than a given epoch. * * @param lastOkUse latest usage epoch at which a buffer will * not be discarded */ private void tidyCache( long lastOkUse ) { for ( int i = 0; i < nblock_; i++ ) { ByteBuffer buf = bufs_[ i ]; long useEpoch = useEpochs_[ i ]; if ( buf != null && useEpoch < lastOkUse ) { bufs_[ i ] = null; disposeBuffer( i, buf ); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy