Index: lucene/src/java/org/apache/lucene/store/DirectNIOFSDirectory.java =================================================================== --- lucene/src/java/org/apache/lucene/store/DirectNIOFSDirectory.java (revision 0) +++ lucene/src/java/org/apache/lucene/store/DirectNIOFSDirectory.java (revision 0) @@ -0,0 +1,290 @@ +package org.apache.lucene.store; + +/** + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * An {@link FSDirectory} implementation that uses + * java.nio's FileChannel's positional read, which allows + * multiple threads to read from the same file without + * synchronizing. + * + *

This class only uses FileChannel when reading; writing + * is achieved with {@link FSDirectory.FSIndexOutput}. + * + *

NOTE: NIOFSDirectory is not recommended on Windows because of a bug + * in how FileChannel.read is implemented in Sun's JRE. + * Inside of the implementation the position is apparently + * synchronized. See here + * for details. + */ +public class DirectNIOFSDirectory extends FSDirectory { + + /** Create a new NIOFSDirectory for the named location. + * + * @param path the path of the directory + * @param lockFactory the lock factory to use, or null for the default + * ({@link NativeFSLockFactory}); + * @throws IOException + */ + public DirectNIOFSDirectory(File path, LockFactory lockFactory) throws IOException { + super(path, lockFactory); + } + + /** Create a new NIOFSDirectory for the named location and {@link NativeFSLockFactory}. + * + * @param path the path of the directory + * @throws IOException + */ + public DirectNIOFSDirectory(File path) throws IOException { + super(path, null); + } + + @Override + public IndexInput openInput(String name, int bufferSize) throws IOException { + ensureOpen(); + return new DirectNIOFSIndexInput(new File(getDirectory(), name), bufferSize); + } + + @Override + public IndexOutput createOutput(String name) throws IOException { + ensureOpen(); + ensureCanWrite(name); + return new DirectNIOFSIndexOutput(new File(getDirectory(), name), 4096); + } + + private final static class DirectNIOFSIndexOutput extends IndexOutput { + private final ByteBuffer buffer; + private final FileOutputStream fos; + private final FileChannel channel; + private final int bufferSize; + + private int bufferPos; + private long filePos; + private boolean isOpen; + + public DirectNIOFSIndexOutput(File path, int bufferSize) throws IOException { + fos = new FileOutputStream(path); + channel = fos.getChannel(); + buffer = ByteBuffer.allocateDirect(bufferSize); + this.bufferSize = bufferSize; + isOpen = true; + } + + @Override + public void writeByte(byte b) throws IOException { + assert bufferPos == buffer.position(): "bufferPos=" + bufferPos + " vs buffer.position()=" + buffer.position(); + buffer.put(b); + if (++bufferPos == bufferSize) { + flush(); + } + } + + @Override + public void writeBytes(byte[] src, int offset, int len) throws IOException { + int toWrite = len; + while(true) { + final int left = bufferSize - bufferPos; + if (left <= toWrite) { + buffer.put(src, offset, left); + toWrite -= left; + offset += left; + bufferPos = bufferSize; + flush(); + } else { + buffer.put(src, offset, toWrite); + bufferPos += toWrite; + break; + } + } + } + + @Override + public void flush() throws IOException { + buffer.flip(); + channel.write(buffer, filePos); + filePos += bufferPos; + bufferPos = 0; + buffer.clear(); + } + + @Override + public long getFilePointer() { + return filePos + bufferPos; + } + + @Override + public void seek(long pos) throws IOException { + if (pos != getFilePointer()) { + flush(); + filePos = pos; + bufferPos = 0; + } + } + + @Override + public long length() throws IOException { + return channel.size(); + } + + @Override + public void close() throws IOException { + if (isOpen) { + isOpen = false; + try { + flush(); + } finally { + try { + channel.close(); + } finally { + fos.close(); + } + } + } + } + } + + private final static class DirectNIOFSIndexInput extends IndexInput { + private final ByteBuffer buffer; + private final FileInputStream fis; + private final FileChannel channel; + private final int bufferSize; + + private boolean isOpen; + private boolean isClone; + private long filePos; + private int bufferPos; + + public DirectNIOFSIndexInput(File path, int bufferSize) throws IOException { + fis = new FileInputStream(path); + channel = fis.getChannel(); + this.bufferSize = bufferSize; + buffer = ByteBuffer.allocateDirect(bufferSize); + isOpen = true; + isClone = false; + filePos = -bufferSize; + bufferPos = bufferSize; + } + + public DirectNIOFSIndexInput(DirectNIOFSIndexInput other) throws IOException { + this.fis = null; + channel = other.channel; + this.bufferSize = other.bufferSize; + buffer = ByteBuffer.allocateDirect(bufferSize); + filePos = other.getFilePointer()-bufferSize; + bufferPos = bufferSize; + isOpen = true; + isClone = true; + } + + @Override + public void close() throws IOException { + if (isOpen && !isClone) { + try { + channel.close(); + } finally { + if (!isClone) { + fis.close(); + } + } + } + } + + @Override + public long getFilePointer() { + return filePos + bufferPos; + } + + @Override + public void seek(long pos) throws IOException { + if (pos != getFilePointer()) { + filePos = pos-bufferSize; + // invalidate buffer (TODO: handle seek-within-buffer + // case efficiently) + bufferPos = bufferSize; + } + } + + @Override + public long length() { + try { + return channel.size(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + + @Override + public byte readByte() throws IOException { + // NOTE: we don't guard against EOF here... ie the + // "final" buffer will typically be filled to less + // than bufferSize + if (bufferPos == bufferSize) { + refill(); + } + assert bufferPos == buffer.position() : "bufferPos=" + bufferPos + " vs buffer.position()=" + buffer.position(); + bufferPos++; + return buffer.get(); + } + + private void refill() throws IOException { + buffer.clear(); + filePos += bufferSize; + bufferPos = 0; + int n = channel.read(buffer, filePos); + if (n < 0) { + throw new IOException("eof"); + } + buffer.rewind(); + } + + @Override + public void readBytes(byte[] dst, int offset, int len) throws IOException { + int toRead = len; + while(true) { + final int left = bufferSize - bufferPos; + if (left < toRead) { + buffer.get(dst, offset, left); + toRead -= left; + offset += left; + refill(); + } else { + buffer.get(dst, offset, toRead); + bufferPos += toRead; + break; + } + } + } + + @Override + public Object clone() { + try { + return new DirectNIOFSIndexInput(this); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + } +} Property changes on: lucene/src/java/org/apache/lucene/store/DirectNIOFSDirectory.java ___________________________________________________________________ Added: svn:eol-style + native