001: package org.apache.lucene.store;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import java.io.IOException;
021: import java.io.File;
022: import java.io.RandomAccessFile;
023: import java.nio.ByteBuffer;
024: import java.nio.channels.FileChannel;
025: import java.nio.channels.FileChannel.MapMode;
026:
027: /** File-based {@link Directory} implementation that uses mmap for input.
028: *
029: * <p>To use this, invoke Java with the System property
030: * org.apache.lucene.FSDirectory.class set to
031: * org.apache.lucene.store.MMapDirectory. This will cause {@link
032: * FSDirectory#getDirectory(File,boolean)} to return instances of this class.
033: */
034: public class MMapDirectory extends FSDirectory {
035:
036: private static class MMapIndexInput extends IndexInput {
037:
038: private ByteBuffer buffer;
039: private final long length;
040:
041: private MMapIndexInput(RandomAccessFile raf) throws IOException {
042: this .length = raf.length();
043: this .buffer = raf.getChannel().map(MapMode.READ_ONLY, 0,
044: length);
045: }
046:
047: public byte readByte() throws IOException {
048: return buffer.get();
049: }
050:
051: public void readBytes(byte[] b, int offset, int len)
052: throws IOException {
053: buffer.get(b, offset, len);
054: }
055:
056: public long getFilePointer() {
057: return buffer.position();
058: }
059:
060: public void seek(long pos) throws IOException {
061: buffer.position((int) pos);
062: }
063:
064: public long length() {
065: return length;
066: }
067:
068: public Object clone() {
069: MMapIndexInput clone = (MMapIndexInput) super .clone();
070: clone.buffer = buffer.duplicate();
071: return clone;
072: }
073:
074: public void close() throws IOException {
075: }
076: }
077:
078: private static class MultiMMapIndexInput extends IndexInput {
079:
080: private ByteBuffer[] buffers;
081: private int[] bufSizes; // keep here, ByteBuffer.size() method is optional
082:
083: private final long length;
084:
085: private int curBufIndex;
086: private final int maxBufSize;
087:
088: private ByteBuffer curBuf; // redundant for speed: buffers[curBufIndex]
089: private int curAvail; // redundant for speed: (bufSizes[curBufIndex] - curBuf.position())
090:
091: public MultiMMapIndexInput(RandomAccessFile raf, int maxBufSize)
092: throws IOException {
093: this .length = raf.length();
094: this .maxBufSize = maxBufSize;
095:
096: if (maxBufSize <= 0)
097: throw new IllegalArgumentException(
098: "Non positive maxBufSize: " + maxBufSize);
099:
100: if ((length / maxBufSize) > Integer.MAX_VALUE)
101: throw new IllegalArgumentException(
102: "RandomAccessFile too big for maximum buffer size: "
103: + raf.toString());
104:
105: int nrBuffers = (int) (length / maxBufSize);
106: if ((nrBuffers * maxBufSize) < length)
107: nrBuffers++;
108:
109: this .buffers = new ByteBuffer[nrBuffers];
110: this .bufSizes = new int[nrBuffers];
111:
112: long bufferStart = 0;
113: FileChannel rafc = raf.getChannel();
114: for (int bufNr = 0; bufNr < nrBuffers; bufNr++) {
115: int bufSize = (length > (bufferStart + maxBufSize)) ? maxBufSize
116: : (int) (length - bufferStart);
117: this .buffers[bufNr] = rafc.map(MapMode.READ_ONLY,
118: bufferStart, bufSize);
119: this .bufSizes[bufNr] = bufSize;
120: bufferStart += bufSize;
121: }
122: seek(0L);
123: }
124:
125: public byte readByte() throws IOException {
126: // Performance might be improved by reading ahead into an array of
127: // eg. 128 bytes and readByte() from there.
128: if (curAvail == 0) {
129: curBufIndex++;
130: curBuf = buffers[curBufIndex]; // index out of bounds when too many bytes requested
131: curBuf.position(0);
132: curAvail = bufSizes[curBufIndex];
133: }
134: curAvail--;
135: return curBuf.get();
136: }
137:
138: public void readBytes(byte[] b, int offset, int len)
139: throws IOException {
140: while (len > curAvail) {
141: curBuf.get(b, offset, curAvail);
142: len -= curAvail;
143: offset += curAvail;
144: curBufIndex++;
145: curBuf = buffers[curBufIndex]; // index out of bounds when too many bytes requested
146: curBuf.position(0);
147: curAvail = bufSizes[curBufIndex];
148: }
149: curBuf.get(b, offset, len);
150: curAvail -= len;
151: }
152:
153: public long getFilePointer() {
154: return (curBufIndex * (long) maxBufSize)
155: + curBuf.position();
156: }
157:
158: public void seek(long pos) throws IOException {
159: curBufIndex = (int) (pos / maxBufSize);
160: curBuf = buffers[curBufIndex];
161: int bufOffset = (int) (pos - (curBufIndex * maxBufSize));
162: curBuf.position(bufOffset);
163: curAvail = bufSizes[curBufIndex] - bufOffset;
164: }
165:
166: public long length() {
167: return length;
168: }
169:
170: public Object clone() {
171: MultiMMapIndexInput clone = (MultiMMapIndexInput) super
172: .clone();
173: clone.buffers = new ByteBuffer[buffers.length];
174: // No need to clone bufSizes.
175: // Since most clones will use only one buffer, duplicate() could also be
176: // done lazy in clones, eg. when adapting curBuf.
177: for (int bufNr = 0; bufNr < buffers.length; bufNr++) {
178: clone.buffers[bufNr] = buffers[bufNr].duplicate();
179: }
180: try {
181: clone.seek(getFilePointer());
182: } catch (IOException ioe) {
183: RuntimeException newException = new RuntimeException(
184: ioe);
185: newException.initCause(ioe);
186: throw newException;
187: }
188: ;
189: return clone;
190: }
191:
192: public void close() throws IOException {
193: }
194: }
195:
196: private final int MAX_BBUF = Integer.MAX_VALUE;
197:
198: public IndexInput openInput(String name) throws IOException {
199: File f = new File(getFile(), name);
200: RandomAccessFile raf = new RandomAccessFile(f, "r");
201: try {
202: return (raf.length() <= MAX_BBUF) ? (IndexInput) new MMapIndexInput(
203: raf)
204: : (IndexInput) new MultiMMapIndexInput(raf,
205: MAX_BBUF);
206: } finally {
207: raf.close();
208: }
209: }
210:
211: public IndexInput openInput(String name, int bufferSize)
212: throws IOException {
213: return openInput(name);
214: }
215: }
|