Package org.apache.lucene.store

Source Code of org.apache.lucene.store.LinkMMapDirectory$LinkMMapIndexInput

package org.apache.lucene.store;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;


public class LinkMMapDirectory extends LinkFSDirectory{
  private boolean useUnmapHack = MMapDirectory.UNMAP_SUPPORTED;
   private int chunkSizePower;
    public void setUseUnmap(final boolean useUnmapHack) {
      if (useUnmapHack && !MMapDirectory.UNMAP_SUPPORTED)
        throw new IllegalArgumentException("Unmap hack not supported on this platform!");
      this.useUnmapHack=useUnmapHack;
    }
   
    public boolean getUseUnmap() {
      return useUnmapHack;
    }

    final void cleanMapping(final ByteBuffer buffer) throws IOException {
      if (useUnmapHack) {
        try {
          AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
            public Object run() throws Exception {
              final Method getCleanerMethod = buffer.getClass()
                .getMethod("cleaner");
              getCleanerMethod.setAccessible(true);
              final Object cleaner = getCleanerMethod.invoke(buffer);
              if (cleaner != null) {
                cleaner.getClass().getMethod("clean")
                  .invoke(cleaner);
              }
              return null;
            }
          });
        } catch (PrivilegedActionException e) {
          final IOException ioe = new IOException("unable to unmap the mapped buffer");
          ioe.initCause(e.getCause());
          throw ioe;
        }
      }
    }
   
 
    public final void setMaxChunkSize(final int maxChunkSize) {
      if (maxChunkSize <= 0)
        throw new IllegalArgumentException("Maximum chunk size for mmap must be >0");
      this.chunkSizePower = 31 - Integer.numberOfLeadingZeros(maxChunkSize);
      assert this.chunkSizePower >= 0 && this.chunkSizePower <= 30;
    }
   
    public final int getMaxChunkSize() {
      return 1 << chunkSizePower;
    }
    public LinkMMapDirectory(File path, LockFactory lockFactory) throws IOException {
      super(path, lockFactory);
      setMaxChunkSize(MMapDirectory.DEFAULT_MAX_BUFF);
    }

    /** Create a new MMapDirectory for the named location and {@link NativeFSLockFactory}.
     *
     * @param path the path of the directory
     * @throws IOException
     */
    public LinkMMapDirectory(File path) throws IOException {
      super(path, null);
      setMaxChunkSize(MMapDirectory.DEFAULT_MAX_BUFF);
    }
   
   
    public IndexInput openInput(String name, int bufferSize) throws IOException {
      File file=null;
      if (!this.links.containsKey(name)) {
        file=new File(directory, name);
      }else{
      file = this.links.get(name);
      }
      RandomAccessFile raf = new RandomAccessFile(file, "r");
        try {
          return new LinkMMapIndexInput("MMapIndexInput(path=\"" + file + "\")", raf, chunkSizePower);
        } finally {
          raf.close();
        }
    }
   
   
    private final class LinkMMapIndexInput extends IndexInput {
     
        private ByteBuffer[] buffers;
     
        private final long length, chunkSizeMask, chunkSize;
        private final int chunkSizePower;
     
        private int curBufIndex;
     
        private ByteBuffer curBuf; // redundant for speed: buffers[curBufIndex]
     
        private boolean isClone = false;
       
        LinkMMapIndexInput(String resourceDescription, RandomAccessFile raf, int chunkSizePower) throws IOException {
          super(resourceDescription);
          this.length = raf.length();
          this.chunkSizePower = chunkSizePower;
          this.chunkSize = 1L << chunkSizePower;
          this.chunkSizeMask = chunkSize - 1L;
         
          if (chunkSizePower < 0 || chunkSizePower > 30)
            throw new IllegalArgumentException("Invalid chunkSizePower used for ByteBuffer size: " + chunkSizePower);
         
          if ((length >>> chunkSizePower) >= Integer.MAX_VALUE)
            throw new IllegalArgumentException("RandomAccessFile too big for chunk size: " + raf.toString());
         
          // we always allocate one more buffer, the last one may be a 0 byte one
          final int nrBuffers = (int) (length >>> chunkSizePower) + 1;
         
          //System.out.println("length="+length+", chunkSizePower=" + chunkSizePower + ", chunkSizeMask=" + chunkSizeMask + ", nrBuffers=" + nrBuffers);
         
          this.buffers = new ByteBuffer[nrBuffers];
         
          long bufferStart = 0L;
          FileChannel rafc = raf.getChannel();
          for (int bufNr = 0; bufNr < nrBuffers; bufNr++) {
            int bufSize = (int) ( (length > (bufferStart + chunkSize))
              ? chunkSize
              : (length - bufferStart)
            );
            this.buffers[bufNr] = rafc.map(MapMode.READ_ONLY, bufferStart, bufSize);
            bufferStart += bufSize;
          }
          seek(0L);
        }
     
        @Override
        public byte readByte() throws IOException {
          try {
            return curBuf.get();
          } catch (BufferUnderflowException e) {
            do {
              curBufIndex++;
              if (curBufIndex >= buffers.length) {
                throw new IOException("read past EOF: " + this);
              }
              curBuf = buffers[curBufIndex];
              curBuf.position(0);
            } while (!curBuf.hasRemaining());
            return curBuf.get();
          }
        }
     
        @Override
        public void readBytes(byte[] b, int offset, int len) throws IOException {
          try {
            curBuf.get(b, offset, len);
          } catch (BufferUnderflowException e) {
            int curAvail = curBuf.remaining();
            while (len > curAvail) {
              curBuf.get(b, offset, curAvail);
              len -= curAvail;
              offset += curAvail;
              curBufIndex++;
              if (curBufIndex >= buffers.length) {
                throw new IOException("read past EOF: " + this);
              }
              curBuf = buffers[curBufIndex];
              curBuf.position(0);
              curAvail = curBuf.remaining();
            }
            curBuf.get(b, offset, len);
          }
        }

        @Override
        public int readInt() throws IOException {
          try {
            return curBuf.getInt();
          } catch (BufferUnderflowException e) {
            return super.readInt();
          }
        }

        @Override
        public long readLong() throws IOException {
          try {
            return curBuf.getLong();
          } catch (BufferUnderflowException e) {
            return super.readLong();
          }
        }
       
        @Override
        public long getFilePointer() {
          return (((long) curBufIndex) << chunkSizePower) + curBuf.position();
        }
     
        @Override
        public void seek(long pos) throws IOException {
          // we use >> here to preserve negative, so we will catch AIOOBE:
          final int bi = (int) (pos >> chunkSizePower);
          try {
            final ByteBuffer b = buffers[bi];
            b.position((int) (pos & chunkSizeMask));
            // write values, on exception all is unchanged
            this.curBufIndex = bi;
            this.curBuf = b;
          } catch (ArrayIndexOutOfBoundsException aioobe) {
            if (pos < 0L) {
              throw new IllegalArgumentException("Seeking to negative position: " + this);
            }
            throw new IOException("seek past EOF");
          } catch (IllegalArgumentException iae) {
            if (pos < 0L) {
              throw new IllegalArgumentException("Seeking to negative position: " + this);
            }
            throw new IOException("seek past EOF: " + this);
          }
        }
     
        @Override
        public long length() {
          return length;
        }
     
        @Override
        public Object clone() {
          if (buffers == null) {
            throw new AlreadyClosedException("MMapIndexInput already closed: " + this);
          }
          final LinkMMapIndexInput clone = (LinkMMapIndexInput)super.clone();
          clone.isClone = true;
          clone.buffers = new ByteBuffer[buffers.length];
          // Since most clones will use only one buffer, duplicate() could also be
          // done lazy in clones, e.g. when adapting curBuf.
          for (int bufNr = 0; bufNr < buffers.length; bufNr++) {
            clone.buffers[bufNr] = buffers[bufNr].duplicate();
          }
          try {
            clone.seek(getFilePointer());
          } catch(IOException ioe) {
            throw new RuntimeException("Should never happen: " + this, ioe);
          }
          return clone;
        }
     
        @Override
        public void close() throws IOException {
          try {
            if (isClone || buffers == null) return;
            for (int bufNr = 0; bufNr < buffers.length; bufNr++) {
              try {
                cleanMapping(buffers[bufNr]);
              } finally {
                buffers[bufNr] = null;
              }
            }
          } finally {
            buffers = null;
          }
        }
      }

}
TOP

Related Classes of org.apache.lucene.store.LinkMMapDirectory$LinkMMapIndexInput

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.