FileDocCategorySizeDatePackage
MemoryMappedFile.javaAPI DocAzureus 3.0.3.48186Thu Jan 11 07:45:38 GMT 2007com.aelitis.azureus.core.diskmanager

MemoryMappedFile

public class MemoryMappedFile extends Object
This class implements a virtual disk file backed by a pool of direct memory MappedByteBuffers, designed for high-speed random reads/writes. NOTE: Abandoning this code for now, as JRE 1.4 series does not provide an unmap() method for MappedByteBuffers, so we have to wait for the VM to lazily free the underlying direct memory regions, which means we eventually run out of direct memory when accessing files larger than the allowed direct memory space, since the unused buffers are not freed quickly enough. Forcing the memory unmap via sun.misc.Cleaner does not work properly under Windows, throwing the error described in the clean(x) method below.

Fields Summary
public static final int
MODE_READ_ONLY
public static final int
MODE_READ_WRITE
public static long
cache_hits
public static long
cache_misses
private static final int
BLOCK_SIZE
private final File
file
private int
access_mode
private FileChannel
channel
private Object[]
mapKeys
private int[]
counts
Constructors Summary
public MemoryMappedFile(File file)

  
       
    this.file = file;
    mapKeys = new Object[ new Long(file.length()/BLOCK_SIZE).intValue() + 1 ];
    Arrays.fill( counts, 0 );
  
Methods Summary
private java.nio.MappedByteBuffercreateMappedBuffer(long file_offset, int length)

    if ( channel == null ) {
      FileChannel fc = new RandomAccessFile( file, "rw" ).getChannel();
      MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE, file_offset, length );
      if ( access_mode == MODE_READ_ONLY ) fc.close();
      else channel = fc;
      return mbb;
    }
    
    return channel.map( FileChannel.MapMode.READ_WRITE, file_offset, length );
  
public intgetAccessMode()

  return access_mode;  
public voidsetAccessMode(int mode)

  	if ( mode == MODE_READ_ONLY && access_mode == MODE_READ_WRITE && channel != null ) {
  		try { channel.close(); } catch( Exception e ) { Debug.printStackTrace( e ); }
      channel = null;
  	}
    access_mode = mode;
  
public voidwrite(org.gudy.azureus2.core3.util.DirectByteBuffer buffer, int buffer_offset, long file_offset, int length)

  	if ( access_mode == MODE_READ_ONLY ) {
      throw new IOException( "cannot write to a read-only file" );
    }
        
    if ( buffer.limit(DirectByteBuffer.SS_OTHER) - buffer_offset < length ) {
      throw new IOException( "not enough buffer remaining to write given length" );
    }
    
    file.createNewFile();
    
    int key_pos = new Long( file_offset / BLOCK_SIZE ).intValue();
    int map_offset = new Long( file_offset % BLOCK_SIZE ).intValue();
    int written = 0;
    
    while( written < length ) {
      MappedByteBuffer mbb = null;
      long f_offset = file_offset + written;
      
      int length_to_write = BLOCK_SIZE - map_offset;
      if ( length - written < length_to_write ) length_to_write = length - written;
      
      //try grab the buffer from the pool
      if ( mapKeys.length > key_pos ) {
        Object key = mapKeys[ key_pos ];  
        if ( key != null ) {
          mbb = MemoryMapPool.getBuffer( key );
          //check if the current buff is too small
          if ( mbb != null && mbb.capacity() < (map_offset + length_to_write ) ) {
          	MemoryMapPool.clean( mbb );
          	mbb = null;
          }
        }
      }
      else {  //need to extend the key array
        mapKeys = new Object[ key_pos * 2 ];
      }
            
      //create new buffer, fully-sized, unless it's extending the file
      if ( mbb == null ) {
        int size = BLOCK_SIZE;
        if ( f_offset + length_to_write > file.length() ) {
          size = map_offset + length_to_write;
        }
        mbb = createMappedBuffer( f_offset - map_offset, size );
        cache_misses++;
      }
      else cache_hits++;
      
      //do the write
      buffer.position( DirectByteBuffer.SS_OTHER,buffer_offset + written );
      buffer.limit( DirectByteBuffer.SS_OTHER,buffer.position(DirectByteBuffer.SS_OTHER) + length_to_write );
      mbb.position( map_offset );
      mbb.put( buffer.getBuffer(DirectByteBuffer.SS_OTHER) );
      written += length_to_write;
      
      //add the buffer (back) to the pool
      mapKeys[ key_pos ] = MemoryMapPool.addBuffer( mbb );
      
      key_pos++;
      map_offset = 0;
    }