/*
* Copyright (c) 2010, 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.util.Iterator;
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;
/**
* Merges an iterator for an in-memory overlay with an iterator for an on-disk
* index. <br/>
*
* The iterator either returns a byte array or a <code>ByteRange</code> object,
* depending on whether the current element is part of the overlay trees or the
* on-disk index. The returned keys and values are direct references to the
* internally used key-value pairs and should hence not be modified.
*
* @author stenjan
*
*/
public class InternalMergeIterator implements ResultSet<Object, Object> {
private Iterator<Entry<byte[], byte[]>> overlayIterator;
private InternalDiskIndexIterator diskIndexIterator;
private Entry<byte[], byte[]> nextOverlayEntry;
private Entry<ByteRange, ByteRange> nextDiskIndexEntry;
private Entry<Object, Object> nextEntry;
private ByteRangeComparator comp;
private byte[] nullValue;
private boolean ascending;
public InternalMergeIterator(Iterator<Entry<byte[], byte[]>> overlayIterator,
InternalDiskIndexIterator diskIndexIterator, ByteRangeComparator comp, byte[] nullValue,
boolean ascending) {
assert (overlayIterator != null);
this.overlayIterator = overlayIterator;
this.diskIndexIterator = diskIndexIterator;
this.comp = comp;
this.nullValue = nullValue;
this.ascending = ascending;
nextElement();
}
@Override
public boolean hasNext() {
return nextEntry != null;
}
@Override
public Entry<Object, Object> next() {
if (nextEntry == null)
throw new NoSuchElementException();
Entry<Object, Object> tmp = nextEntry;
nextElement();
return tmp;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
public void free() {
if (diskIndexIterator != null)
diskIndexIterator.free();
}
protected void nextElement() {
// find the smallest element in the 'rightmost' tree
for (;;) {
// find the next element in the overlay
if (nextOverlayEntry == null && overlayIterator.hasNext())
nextOverlayEntry = overlayIterator.next();
// find the next element in the disk index
if (nextDiskIndexEntry == null && diskIndexIterator != null && diskIndexIterator.hasNext())
nextDiskIndexEntry = diskIndexIterator.next();
// if the next overlay key is equal to the next disk index key,
// shift disk index element
if (nextOverlayEntry != null && nextDiskIndexEntry != null
&& comp.compare(nextDiskIndexEntry.getKey(), nextOverlayEntry.getKey()) == 0) {
// free the buffer if necessary
if(nextDiskIndexEntry.getValue().getReusableBuf() != null)
BufferPool.free(nextDiskIndexEntry.getValue().getReusableBuf());
if (diskIndexIterator.hasNext())
nextDiskIndexEntry = diskIndexIterator.next();
else
nextDiskIndexEntry = null;
}
// if no more element exists, set 'next' to 'empty' and return
if (nextDiskIndexEntry == null && nextOverlayEntry == null) {
nextEntry = null;
return;
}
// otherwise, choose the element with the smallest or largest key,
// depending on the iteration order
if (ascending) {
if (nextDiskIndexEntry == null) {
nextEntry = InternalBufferUtil.cast(nextOverlayEntry);
nextOverlayEntry = null;
}
else if (nextOverlayEntry == null) {
nextEntry = InternalBufferUtil.cast(nextDiskIndexEntry);
nextDiskIndexEntry = null;
}
else if (comp.compare(nextDiskIndexEntry.getKey(), nextOverlayEntry.getKey()) < 0) {
nextEntry = InternalBufferUtil.cast(nextDiskIndexEntry);
nextDiskIndexEntry = null;
}
else {
nextEntry = InternalBufferUtil.cast(nextOverlayEntry);
nextOverlayEntry = null;
}
} else {
if (nextDiskIndexEntry == null) {
nextEntry = InternalBufferUtil.cast(nextOverlayEntry);
nextOverlayEntry = null;
}
else if (nextOverlayEntry == null) {
nextEntry = InternalBufferUtil.cast(nextDiskIndexEntry);
nextDiskIndexEntry = null;
}
else if (comp.compare(nextDiskIndexEntry.getKey(), nextOverlayEntry.getKey()) > 0) {
nextEntry = InternalBufferUtil.cast(nextDiskIndexEntry);
nextDiskIndexEntry = null;
}
else {
InternalBufferUtil.cast(nextOverlayEntry);
nextOverlayEntry = null;
}
}
assert (nextEntry != null);
// if no tombstone value was defined or the next entry's value is
// not a tombstone value, return; otherwise, restart
if (nullValue == null || nextEntry.getValue() != nullValue)
return;
}
}
}