/*
* Copyright 2004-2009 the original author or authors.
*
* 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.
*/
package org.compass.needle.gigaspaces.store;
import java.io.IOException;
import org.apache.lucene.store.IndexInput;
/**
* An index input using the {@link org.compass.needle.gigaspaces.store.FileEntry} as the
* "header" information and load buckets as needed.
*
* @author kimchy
*/
class GigaSpaceIndexInput extends IndexInput {
private GigaSpaceDirectory dir;
private FileEntry fileEntry;
private long position;
private FileBucketEntry bucketEntry;
private int currentBucketPosition;
public GigaSpaceIndexInput(GigaSpaceDirectory dir, FileEntry fileEntry) {
this.dir = dir;
this.fileEntry = fileEntry;
this.bucketEntry = new FileBucketEntry(fileEntry.indexName, fileEntry.fileName, -1, null);
}
public void close() throws IOException {
}
/**
* Returns the current position in this file, where the next read will
* occur.
*
* @see #seek(long)
*/
public long getFilePointer() {
return this.position;
}
/**
* The number of bytes in the file.
*/
public long length() {
return fileEntry.getSize();
}
/**
* Reads and returns a single byte.
*
* @see org.apache.lucene.store.IndexOutput#writeByte(byte)
*/
public byte readByte() throws IOException {
loadBucketIfNeeded();
position++;
return bucketEntry.data[currentBucketPosition++];
}
/**
* Reads a specified number of bytes into an array at the specified
* offset.
*
* @param b the array to read bytes into
* @param offset the offset in the array to start storing bytes
* @param len the number of bytes to read
* @see org.apache.lucene.store.IndexOutput#writeBytes(byte[],int)
*/
public void readBytes(byte[] b, int offset, int len) throws IOException {
loadBucketIfNeeded();
// if there is enough place to load at once
if (len <= (dir.getBucketSize() - currentBucketPosition)) {
if (len > 0) {
System.arraycopy(bucketEntry.data, currentBucketPosition, b, offset, len);
}
currentBucketPosition += len;
position += len;
return;
}
// cycle through the reads
while (true) {
int available = dir.getBucketSize() - currentBucketPosition;
int sizeToRead = (len <= available) ? len : available;
System.arraycopy(bucketEntry.data, currentBucketPosition, b, offset, sizeToRead);
len -= sizeToRead;
offset += sizeToRead;
position += sizeToRead;
currentBucketPosition += sizeToRead;
// check if we read enough, if we did, bail
if (len <= 0) {
break;
}
loadBucketIfNeeded();
}
}
/**
* Sets current position in this file, where the next read will occur.
*
* @see #getFilePointer()
*/
public void seek(long pos) throws IOException {
position = pos;
}
private void loadBucketIfNeeded() throws GigaSpaceDirectoryException {
currentBucketPosition = (int) position % dir.getBucketSize();
long bucketIndex = position / dir.getBucketSize();
// check if we need to load the bucket
if (bucketIndex == bucketEntry.bucketIndex) {
return;
}
// reuse the current bucket entry as the template
bucketEntry.data = null;
bucketEntry.bucketIndex = bucketIndex;
try {
bucketEntry = (FileBucketEntry) dir.getSpace().read(bucketEntry, null, 0);
} catch (Exception e) {
throw new GigaSpaceDirectoryException(fileEntry.indexName, fileEntry.fileName,
"Failed to read bucket [" + bucketIndex + "]", e);
}
if (bucketEntry == null) {
throw new GigaSpaceDirectoryException(fileEntry.indexName, fileEntry.fileName, "Bucket [" + bucketIndex
+ "] not found");
}
if (bucketEntry.data == null) {
throw new GigaSpaceDirectoryException(fileEntry.indexName, fileEntry.fileName, "Bucket [" + bucketIndex
+ "] has no data");
}
}
public Object clone() {
GigaSpaceIndexInput indexInput = (GigaSpaceIndexInput) super.clone();
indexInput.bucketEntry = new FileBucketEntry(fileEntry.indexName, fileEntry.fileName, -1, null);
return indexInput;
}
}