long maxCount,
RangeConstraint rangeConstraint,
CursorImpl finalPositionCursor) {
/* Start with the entry at the cursor position. */
final Tree tree = databaseImpl.getTree();
BIN curBin = latchBIN();
int prevIndex = getIndex();
long count = 0;
try {
while (true) {
/* Skip entries in the current BIN. */
count = skipEntries
(forward, maxCount, rangeConstraint, finalPositionCursor,
curBin, prevIndex, count);
if (count < 0) {
return (- count);
}
/*
* Get the parent IN at level two. The BIN is unlatched by
* getParentINForChildIN. Before releasing the BIN latch, get
* the search key for the last entry.
*/
final byte[] idKey = (curBin.getNEntries() == 0) ?
curBin.getIdentifierKey() :
(forward ?
curBin.getKey(curBin.getNEntries() - 1) :
curBin.getKey(0));
final BIN binToFind = curBin;
curBin = null; // BIN latch will be released.
final SearchResult result = tree.getParentINForChildIN
(binToFind, true /*requireExactMatch*/,
CacheMode.UNCHANGED);
final IN parent = result.parent;
boolean fetchOrWait = false;
try {
if (!result.exactParentFound) {
throw EnvironmentFailureException.unexpectedState
("Cannot get parent of BIN id=" +
binToFind.getNodeId() + " key=" +
Arrays.toString(idKey));
}
/*
* Find previous child BIN by matching idKey rather than
* using result.index, as in getNextBinInternal (see
* comments there).
*/
int parentIndex = parent.findEntry(idKey, false, false);
if (forward ?
(parentIndex == parent.getNEntries() - 1) :
(parentIndex == 0)) {
/*
* This is the last entry in the parent. Fetch and
* latch it, in preparation for getNextBin below.
*/
curBin = (BIN) parent.fetchTargetWithExclusiveLatch
(parentIndex);
curBin.latch();
} else {
/*
* Skip entries for child BINs that are resident and
* can be latched no-wait.
*/
final int incr = forward ? 1 : (-1);
for (parentIndex += incr;; parentIndex += incr) {
if (fetchOrWait ||
(forward ?
(parentIndex >= parent.getNEntries()) :
(parentIndex < 0))) {
break;
}
/* Release previous child BIN. */
if (curBin != null) {
curBin.releaseLatch();
curBin = null;
}
/* Fetch and latch next child BIN. */
curBin = (BIN) parent.getTarget(parentIndex);
if (curBin == null) {
fetchOrWait = true;
curBin =
(BIN) parent.fetchTargetWithExclusiveLatch
(parentIndex);
}
if (!curBin.latchNoWait(CacheMode.UNCHANGED)) {
fetchOrWait = true;
curBin.latch();
}
/* Position at new BIN to prevent compression. */
setPosition(curBin, -1);
/* Skip entries in new child BIN. */
count = skipEntries
(forward, maxCount, rangeConstraint,
finalPositionCursor, curBin,
forward ? (-1) : curBin.getNEntries(), count);
if (count < 0) {
return (- count);
}
}
}
} finally {
if (parent != null) {
parent.releaseLatch();
}
}
/*
* Continue after releasing the parent latch. The current BIN
* is still latched.
*/
if (fetchOrWait) {
/*
* A child BIN was not resident or we could not get a
* no-wait latch. Skip over the current BIN, which has
* already been processed, and continue the loop within the
* same parent IN.
*/
prevIndex = forward ? (curBin.getNEntries() - 1) : 0;
} else {
/*
* There are no more entries in the parent IN. Move to the
* next BIN, which will be in the next parent IN, and
* continue at the beginning of that BIN.
*/
curBin = forward ?
tree.getNextBin(curBin, CacheMode.UNCHANGED) :
tree.getPrevBin(curBin, CacheMode.UNCHANGED);
if (curBin == null) {
return count;
}
prevIndex = forward ? (-1) : curBin.getNEntries();