/* * 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 CompressedBlockReader extends BlockReader { public static final int PREFIX_OFFSET = 5 * Integer.SIZE / 8; private byte[] prefix; /** * Creates a reader for a compressed 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 CompressedBlockReader(ByteBuffer buf, int position, int limit, ByteRangeComparator comp) { super(true); this.buffer = buf; this.position = position; this.limit = limit; this.comp = comp; int valsOffset = position + buf.getInt(position); int keysOffset = position + buf.getInt(position + 4); numEntries = buf.getInt(position + 8); int keyEntrySize = buf.getInt(position + 12); int valEntrySize = buf.getInt(position + 16); // read in the prefix string int prefixSize = (keysOffset - position) - PREFIX_OFFSET; prefix = new byte[prefixSize]; if (prefixSize > 0) { // move to the position to perform the read buf.position(position + PREFIX_OFFSET); buf.get(prefix); // reset to original position buf.position(position); } 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); } /** * Creates a reader for a compressed 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 CompressedBlockReader(FileChannel channel, int position, int limit, ByteRangeComparator comp) throws IOException { super(false); this.readBuffer = BufferPool.allocate(limit - position); channel.read(readBuffer.getBuffer(), position); this.position = position; this.limit = limit; this.comp = comp; int valsOffset = readBuffer.getBuffer().getInt(0); int keysOffset = readBuffer.getBuffer().getInt(4); numEntries = readBuffer.getBuffer().getInt(8); int keyEntrySize = readBuffer.getBuffer().getInt(12); int valEntrySize = readBuffer.getBuffer().getInt(16); // read in the prefix string int prefixSize = keysOffset - PREFIX_OFFSET; prefix = new byte[prefixSize]; if (prefixSize > 0) { // move to the position to perform the read readBuffer.getBuffer().position(PREFIX_OFFSET); readBuffer.getBuffer().get(prefix); // reset to original position readBuffer.getBuffer().position(0); } 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); } /** * Returns null if the key is not matching the block prefix, otherwise the * suffix is returned, i.e. key - prefix. * * @param key * @return byte[] suffix of key. */ private byte[] usableSuffix(byte[] key) { // key cant contain the prefix if (key == null || prefix.length > key.length) return null; // no prefix exist, use the key as is if (prefix.length == 0) return key; byte[] prefixKey = new byte[prefix.length]; System.arraycopy(key, 0, prefixKey, 0, prefix.length); if (comp.compare(prefix, prefixKey) != 0) return null; byte[] suffixKey = new byte[key.length - prefix.length]; System.arraycopy(key, prefix.length, suffixKey, 0, key.length - prefix.length); return suffixKey; } public ByteRange lookup(byte[] key) { // if the key contains prefix check if the block // contains what remains after removing the prefix byte[] suffixKey = usableSuffix(key); if (suffixKey == null) return null; int index = keys.getPosition(suffixKey); if (index == -1) return null; return values.getEntry(index); } public ResultSet<ByteRange, ByteRange> rangeLookup(byte[] from, byte[] to, final boolean ascending) { final int startIndex; final int endIndex; { byte[] suffixFrom = usableSuffix(from); startIndex = ascending ? keys.getInclTopPosition(suffixFrom) : keys.getExclTopPosition(suffixFrom); assert (startIndex >= -1) : "invalid block start offset: " + startIndex; byte[] suffixTo = usableSuffix(to); endIndex = ascending ? keys.getExclBottomPosition(suffixTo) : keys .getInclBottomPosition(suffixTo); 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() { key.addPrefix(prefix); 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() {} }; } }