/*
* Copyright (c) 2008 - 2011, Jan Stender, Bjoern Kolbeck, Mikael Hoegqvist,
* Felix Hupfeld, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
package de.mxro.thrd.babudb05.index.reader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.NoSuchElementException;
import java.util.Map.Entry;
import de.mxro.thrd.babudb05.api.database.ResultSet;
import de.mxro.thrd.babudb05.api.index.ByteRangeComparator;
import de.mxro.thrd.babudb05.index.ByteRange;
import de.mxro.thrd.xstreemfs.foundation.buffer.BufferPool;
public class DefaultBlockReader extends BlockReader {
public static final int KEYS_OFFSET = 4 * Integer.SIZE / 8;
/**
* Creates a reader for a buffered block.
*
* @param buf
* the buffer
* @param position
* the position of the block in the buffer
* @param limit
* the limit of the block in the buffer
* @param comp
* the byte range comparator
*/
public DefaultBlockReader(ByteBuffer buf, int position, int limit, ByteRangeComparator comp) {
super(true);
this.buffer = buf;
this.position = position;
this.limit = limit;
this.comp = comp;
// with limit <= 0 there are no entries in the buffer
if (limit > 0) {
int keysOffset = position + KEYS_OFFSET;
int valsOffset = position + buf.getInt(position);
numEntries = buf.getInt(position + 4);
int keyEntrySize = buf.getInt(position + 8);
int valEntrySize = buf.getInt(position + 12);
keys = keyEntrySize == -1 ? new VarLenMiniPage(numEntries, buf, keysOffset, valsOffset, comp)
: new FixedLenMiniPage(keyEntrySize, numEntries, buf, keysOffset, valsOffset, comp);
values = valEntrySize == -1 ? new VarLenMiniPage(numEntries, buf, valsOffset, limit, comp)
: new FixedLenMiniPage(valEntrySize, numEntries, buf, valsOffset, limit, comp);
} else {
numEntries = 0;
keys = new FixedLenMiniPage(0, 0, null, 0, 0, comp);
values = new FixedLenMiniPage(0, 0, null, 0, 0, comp);
}
}
/**
* Creates a reader for a streamed block.
*
* @param channel
* the channel to the block file
* @param position
* the position of the block
* @param limit
* the limit of the block
* @param comp
* the byte range comparator
*/
public DefaultBlockReader(FileChannel channel, int position, int limit, ByteRangeComparator comp)
throws IOException {
super(false);
this.position = position;
this.limit = limit;
this.comp = comp;
this.readBuffer = BufferPool.allocate(limit - position);
channel.read(readBuffer.getBuffer(), position);
// with limit <= 0 there are no entries in the buffer
if (limit > 0) {
int keysOffset = KEYS_OFFSET;
int valsOffset = readBuffer.getBuffer().getInt(0);
numEntries = readBuffer.getBuffer().getInt(4);
int keyEntrySize = readBuffer.getBuffer().getInt(8);
int valEntrySize = readBuffer.getBuffer().getInt(12);
keys = keyEntrySize == -1 ? new VarLenMiniPage(numEntries, readBuffer.getBuffer(), keysOffset,
valsOffset, comp) : new FixedLenMiniPage(keyEntrySize, numEntries, readBuffer.getBuffer(),
keysOffset, valsOffset, comp);
values = valEntrySize == -1 ? new VarLenMiniPage(numEntries, readBuffer.getBuffer(), valsOffset,
limit - position, comp) : new FixedLenMiniPage(valEntrySize, numEntries, readBuffer
.getBuffer(), valsOffset, limit - position, comp);
} else {
numEntries = 0;
keys = new FixedLenMiniPage(0, 0, null, 0, 0, comp);
values = new FixedLenMiniPage(0, 0, null, 0, 0, comp);
}
}
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.index.reader.BlockReader#lookup(byte[])
*/
public ByteRange lookup(byte[] key) {
int index = keys.getPosition(key);
if (index == -1)
return null;
return values.getEntry(index);
}
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.index.reader.BlockReader#rangeLookup(byte[],
* byte[], boolean)
*/
@Override
public ResultSet<ByteRange, ByteRange> rangeLookup(byte[] from, byte[] to, final boolean ascending) {
final int startIndex;
final int endIndex;
{
startIndex = ascending ? keys.getInclTopPosition(from) : keys.getExclTopPosition(from);
assert (startIndex >= -1) : "invalid block start offset: " + startIndex;
endIndex = ascending ? keys.getExclBottomPosition(to) : keys.getInclBottomPosition(to);
assert (endIndex >= -1) : "invalid block end offset: " + endIndex;
}
return new ResultSet<ByteRange, ByteRange>() {
int currentIndex = ascending ? startIndex : endIndex;
@Override
public boolean hasNext() {
return ascending ? currentIndex <= endIndex : currentIndex >= startIndex;
}
@Override
public Entry<ByteRange, ByteRange> next() {
if (!hasNext())
throw new NoSuchElementException();
Entry<ByteRange, ByteRange> entry = new Entry<ByteRange, ByteRange>() {
final ByteRange key = keys.getEntry(currentIndex);
final ByteRange value = values.getEntry(currentIndex);
{
// attach the buffer to the last key-value pair, so that
// it can be freed automatically
boolean last = !(ascending ? currentIndex < endIndex : currentIndex > startIndex);
if (last)
value.setReusableBuf(readBuffer);
}
@Override
public ByteRange getValue() {
return value;
}
@Override
public ByteRange getKey() {
return key;
}
@Override
public ByteRange setValue(ByteRange value) {
throw new UnsupportedOperationException();
}
};
if (ascending)
currentIndex++;
else
currentIndex--;
return entry;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void free() {
}
};
}
}