Index: src/java/org/apache/lucene/store/FSDirectory.java
===================================================================
--- src/java/org/apache/lucene/store/FSDirectory.java	(revision 488905)
+++ src/java/org/apache/lucene/store/FSDirectory.java	(working copy)
@@ -25,6 +25,8 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Hashtable;
+import java.nio.channels.FileChannel;
+import java.nio.ByteBuffer;
 
 import org.apache.lucene.index.IndexFileNameFilter;
 
@@ -494,25 +496,27 @@
 }
 
 
-class FSIndexInput extends BufferedIndexInput {
+class FSIndexInput extends IndexInput {
 
   private static class Descriptor extends RandomAccessFile {
     // remember if the file is open, so that we don't try to close it
     // more than once
     private boolean isOpen;
-    long position;
     final long length;
-    
+    final FileChannel channel;
+
     public Descriptor(File file, String mode) throws IOException {
       super(file, mode);
       isOpen=true;
       length=length();
+      channel = this.getChannel();
     }
 
     public void close() throws IOException {
       if (isOpen) {
         isOpen=false;
         super.close();
+        // RandomAccessFile.close() will close the channel also
       }
     }
 
@@ -528,54 +532,145 @@
   private final Descriptor file;
   boolean isClone;
 
+  static final int BUFFER_SIZE = BufferedIndexOutput.BUFFER_SIZE;
+
+  private byte[] buffer;
+  private ByteBuffer bytebuf;         // wrapps the buffer for NIO
+  private long bufferStart = 0;			  // position in file of buffer
+  private int bufferLength = 0;			  // end of valid bytes
+  private int bufferPosition = 0;		  // next byte to read
+
   public FSIndexInput(File path) throws IOException {
     file = new Descriptor(path, "r");
+  }  
+
+  public byte readByte() throws IOException {
+    if (bufferPosition >= bufferLength)
+      refill();
+    return buffer[bufferPosition++];
   }
 
-  /** IndexInput methods */
-  protected void readInternal(byte[] b, int offset, int len)
-       throws IOException {
-    synchronized (file) {
-      long position = getFilePointer();
-      if (position != file.position) {
-        file.seek(position);
-        file.position = position;
+  public void readBytes(byte[] b, int offset, int len) throws IOException {
+    if(len <= (bufferLength-bufferPosition)){
+      // the buffer contains enough data to satistfy this request
+      if(len>0) // to allow b to be null if len is 0...
+        System.arraycopy(buffer, bufferPosition, b, offset, len);
+      bufferPosition+=len;
+    } else {
+      // the buffer does not have enough data. First serve all we've got.
+      int available = bufferLength - bufferPosition;
+      if(available > 0){
+        System.arraycopy(buffer, bufferPosition, b, offset, available);
+        offset += available;
+        len -= available;
+        bufferPosition += available;
       }
-      int total = 0;
-      do {
-        int i = file.read(b, offset+total, len-total);
-        if (i == -1)
+      // and now, read the remaining 'len' bytes:
+      if(len<BUFFER_SIZE){
+        // If the amount left to read is small enough, do it in the usual
+        // buffered way: fill the buffer and copy from it:
+        refill();
+        if(bufferLength<len){
+          // Throw an exception when refill() could not read len bytes:
+          System.arraycopy(buffer, 0, b, offset, bufferLength);
           throw new IOException("read past EOF");
-        file.position += i;
-        total += i;
-      } while (total < len);
+        } else {
+          System.arraycopy(buffer, 0, b, offset, len);
+          bufferPosition=len;
+        }
+      } else {
+        // The amount left to read is larger than the buffer - there's no
+        // performance reason not to read it all at once. Note that unlike
+        // the previous code of this function, there is no need to do a seek
+        // here, because there's no need to reread what we had in the buffer.
+        long after = bufferStart+bufferPosition+len;
+        readInternal(b, offset, len);
+        bufferStart = after;
+        bufferPosition = 0;
+        bufferLength = 0;                    // trigger refill() on read
+      }
     }
   }
 
-  public void close() throws IOException {
-    // only close the file if this is not a clone
-    if (!isClone) file.close();
+  private void refill() throws IOException {
+    long start = bufferStart + bufferPosition;
+    long end = start + BUFFER_SIZE;
+    if (end > length())
+      end = length();
+    bufferLength = (int)(end - start);
+    if (bufferLength <= 0)
+      throw new IOException("read past EOF");
+
+    if (buffer == null) {
+      buffer = new byte[BUFFER_SIZE];		  // allocate buffer lazily
+      bytebuf = ByteBuffer.wrap(buffer);
+    } else {
+      bytebuf.clear();
+    }
+
+    // since we try to read a complete buffer, we need to limit it if
+    // it's past the end of the file.
+    if (bufferLength != BUFFER_SIZE)
+      bytebuf.limit(bufferLength);
+
+    read(bytebuf);
+
+    bufferStart = start;
+    bufferPosition = 0;
   }
 
-  protected void seekInternal(long position) {
+  private void readInternal(byte[] b, int offset, int len) throws IOException {
+    read(ByteBuffer.wrap(b,offset,len));
   }
 
-  public long length() {
-    return file.length;
+  private void read(ByteBuffer bb)
+       throws IOException {
+    long pos = getFilePointer();
+    while (bb.hasRemaining()) {
+      int i = file.channel.read(bb, pos);
+      if (i == -1) throw new IOException("read past EOF");
+      pos += i;
+    }
   }
 
+  public long getFilePointer() { return bufferStart + bufferPosition; }
+
+  public long length() { return file.length; }
+
+  public void seek(long pos) throws IOException {
+    if (pos >= bufferStart && pos < (bufferStart + bufferLength))
+      bufferPosition = (int)(pos - bufferStart);  // seek within buffer
+    else {
+      bufferStart = pos;
+      bufferPosition = 0;
+      bufferLength = 0;				  // trigger refill() on read()
+      // seekInternal(pos);
+    }
+  }
+
   public Object clone() {
     FSIndexInput clone = (FSIndexInput)super.clone();
-    clone.isClone = true;
+    clone.isClone=true;
+    if (buffer != null) {
+      clone.buffer = new byte[buffer.length];
+      System.arraycopy(buffer, 0, clone.buffer, 0, bufferLength);
+      bytebuf = ByteBuffer.wrap(buffer);      
+    }
     return clone;
   }
 
+  public void close() throws IOException {
+    // only close the file if this is not a clone
+    if (!isClone) file.close();
+  }
+
   /** Method used for testing. Returns true if the underlying
    *  file descriptor is valid.
    */
   boolean isFDValid() throws IOException {
     return file.getFD().valid();
   }
+
 }
 
 
