package org.apache.lucene.store;
/**
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.IOException;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/** File-based {@link Directory} implementation that uses mmap for input.
*
* <p>To use this, invoke Java with the System property
* org.apache.lucene.FSDirectory.class set to
* org.apache.lucene.store.MMapDirectory. This will cause {@link
* FSDirectory#getDirectory(File,boolean)} to return instances of this class.
*/
public class MMapDirectory extends FSDirectory {
private static class MMapIndexInput extends IndexInput {
private ByteBuffer buffer;
private final long length;
private MMapIndexInput(RandomAccessFile raf) throws IOException {
this.length = raf.length();
this.buffer = raf.getChannel().map(MapMode.READ_ONLY, 0, length);
}
public byte readByte() throws IOException {
return buffer.get();
}
public void readBytes(byte[] b, int offset, int len)
throws IOException {
buffer.get(b, offset, len);
}
public long getFilePointer() {
return buffer.position();
}
public void seek(long pos) throws IOException {
buffer.position((int)pos);
}
public long length() {
return length;
}
public Object clone() {
MMapIndexInput clone = (MMapIndexInput)super.clone();
clone.buffer = buffer.duplicate();
return clone;
}
public void close() throws IOException {}
}
/* Added class MultiMMapIndexInput, Paul Elschot.
* Slightly adapted constructor of MMapIndexInput.
* Licensed under the Apache License, Version 2.0.
*/
private static class MultiMMapIndexInput extends IndexInput {
private ByteBuffer[] buffers;
private int[] bufSizes; // keep here, ByteBuffer.size() method is optional
private final long length;
private int curBufIndex;
private final int maxBufSize;
private ByteBuffer curBuf; // redundant for speed: buffers[curBufIndex]
private int curAvail; // redundant for speed: (bufSizes[curBufIndex] - curBuf.position())
public MultiMMapIndexInput(RandomAccessFile raf, int maxBufSize)
throws IOException {
this.length = raf.length();
this.maxBufSize = maxBufSize;
if (maxBufSize <= 0)
throw new IllegalArgumentException("Non positive maxBufSize: "
+ maxBufSize);
if ((length / maxBufSize) > Integer.MAX_VALUE)
throw new IllegalArgumentException
("RandomAccessFile too big for maximum buffer size: "
+ raf.toString());
int nrBuffers = (int) (length / maxBufSize);
if ((nrBuffers * maxBufSize) < length) nrBuffers++;
this.buffers = new ByteBuffer[nrBuffers];
this.bufSizes = new int[nrBuffers];
long bufferStart = 0;
FileChannel rafc = raf.getChannel();
for (int bufNr = 0; bufNr < nrBuffers; bufNr++) {
int bufSize = (length > (bufferStart + maxBufSize))
? maxBufSize
: (int) (length - bufferStart);
this.buffers[bufNr] = rafc.map(MapMode.READ_ONLY,bufferStart,bufSize);
this.bufSizes[bufNr] = bufSize;
bufferStart += bufSize;
}
seek(0L);
}
public byte readByte() throws IOException {
// Performance might be improved by reading ahead into an array of
// eg. 128 bytes and readByte() from there.
if (curAvail == 0) {
curBufIndex++;
curBuf = buffers[curBufIndex]; // index out of bounds when too many bytes requested
curBuf.position(0);
curAvail = bufSizes[curBufIndex];
}
curAvail--;
return curBuf.get();
}
public void readBytes(byte[] b, int offset, int len) throws IOException {
while (len > curAvail) {
curBuf.get(b, offset, curAvail);
len -= curAvail;
offset += curAvail;
curBufIndex++;
curBuf = buffers[curBufIndex]; // index out of bounds when too many bytes requested
curBuf.position(0);
curAvail = bufSizes[curBufIndex];
}
curBuf.get(b, offset, len);
curAvail -= len;
}
public long getFilePointer() {
return (curBufIndex * (long) maxBufSize) + curBuf.position();
}
public void seek(long pos) throws IOException {
curBufIndex = (int) (pos / maxBufSize);
curBuf = buffers[curBufIndex];
int bufOffset = (int) (pos - (curBufIndex * maxBufSize));
curBuf.position(bufOffset);
curAvail = bufSizes[curBufIndex] - bufOffset;
}
public long length() {
return length;
}
public Object clone() {
MultiMMapIndexInput clone = (MultiMMapIndexInput)super.clone();
clone.buffers = new ByteBuffer[buffers.length];
// No need to clone bufSizes.
// Since most clones will use only one buffer, duplicate() could also be
// done lazy in clones, eg. 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(ioe);
};
return clone;
}
public void close() throws IOException {}
}
private final int MAX_BBUF = Integer.MAX_VALUE;
public IndexInput openInput(String name) throws IOException {
File f = new File(getFile(), name);
RandomAccessFile raf = new RandomAccessFile(f, "r");
try {
return (raf.length() <= MAX_BBUF)
? (IndexInput) new MMapIndexInput(raf)
: (IndexInput) new MultiMMapIndexInput(raf, MAX_BBUF);
} finally {
raf.close();
}
}
}
|