
org.zoodb.jdo.internal.server.index.LLIndexPage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parent Show documentation
Show all versions of parent Show documentation
ZooDB Java JDO Object Database.
The newest version!
/*
* Copyright 2009-2013 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZooDB. If not, see .
*
* See the README and COPYING files for further information.
*/
package org.zoodb.jdo.internal.server.index;
import java.util.Arrays;
import java.util.NoSuchElementException;
import javax.jdo.JDOFatalDataStoreException;
import org.zoodb.jdo.internal.server.index.PagedUniqueLongLong.LLEntry;
class LLIndexPage extends AbstractIndexPage {
private LLIndexPage parent;
private final long[] keys;
private final long[] values;
/** number of keys. There are nEntries+1 subPages in any leaf page. */
private short nEntries;
public LLIndexPage(AbstractPagedIndex ind, LLIndexPage parent, boolean isLeaf) {
super(ind, parent, isLeaf);
this.parent = parent;
if (isLeaf) {
nEntries = 0;
keys = new long[ind.maxLeafN];
values = new long[ind.maxLeafN];
} else {
nEntries = -1;
keys = new long[ind.maxInnerN];
if (ind.isUnique()) {
values = null;
} else {
values = new long[ind.maxInnerN];
}
}
}
public LLIndexPage(LLIndexPage p) {
super(p);
keys = p.keys.clone();
nEntries = p.nEntries;
parent = p.parent;
if (isLeaf) {
values = p.values.clone();
} else {
if (ind.isUnique()) {
values = null;
} else {
values = p.values.clone();
}
}
}
@Override
void readData() {
nEntries = ind.in.readShort();
readArrayFromRaf(ind.keySize, keys, nEntries);
readArrayFromRaf(ind.valSize, values, nEntries);
}
@Override
void writeData() {
ind.out.writeShort(nEntries);
writeArrayToRaf(ind.keySize, keys, nEntries);
writeArrayToRaf(ind.valSize, values, nEntries);
}
@Override
void writeKeys() {
ind.out.writeShort(nEntries);
writeArrayToRaf(ind.keySize, keys, nEntries);
if (!ind.isUnique()) {
writeArrayToRaf(ind.valSize, values, nEntries);
}
}
@Override
void readKeys() {
nEntries = ind.in.readShort();
readArrayFromRaf(ind.keySize, keys, nEntries);
if (!ind.isUnique()) {
readArrayFromRaf(ind.valSize, values, nEntries);
}
}
private void writeArrayToRaf(int bitWidth, long[] array, int nEntries) {
if (nEntries <= 0) {
return;
}
switch (bitWidth) {
case 8: ind.out.noCheckWrite(array); break;
// case 8:
// //writing ints using a normal loop
// for (int i = 0; i < nEntries; i++) {
// ind.paf.writeLong(array[i]);
// }
// break;
case 4:
ind.out.noCheckWriteAsInt(array, nEntries); break;
case 1:
//writing bytes using an array (different to int-write, see PerfByteArrayRW)
byte[] ba = new byte[nEntries];
for (int i = 0; i < ba.length; i++) {
ba[i] = (byte) array[i];
}
ind.out.noCheckWrite(ba);
break;
case 0: break;
default : throw new IllegalStateException("bit-width=" + bitWidth);
}
}
private void readArrayFromRaf(int bitWidth, long[] array, int nEntries) {
if (nEntries <= 0) {
return;
}
switch (bitWidth) {
case 8: ind.in.noCheckRead(array); break;
// case 8:
// //reading ints using a normal loop
// for (int i = 0; i < nEntries; i++) {
// array[i] = ind.paf.readLong();
// }
// break;
case 4:
ind.in.noCheckReadAsInt(array, nEntries); break;
case 1:
//reading bytes using an array (different to int-write, see PerfByteArrayRW)
byte[] ba = new byte[nEntries];
ind.in.noCheckRead(ba);
for (int i = 0; i < ba.length; i++) {
array[i] = ba[i];
}
break;
case 0: break;
default : throw new IllegalStateException("bit-width=" + bitWidth);
}
}
/**
* Locate the (first) page that could contain the given key.
* In the inner pages, the keys are the minimum values of the following page.
* @param key
* @return Page for that key
*/
public final LLIndexPage locatePageForKeyUnique(long key, boolean allowCreate) {
return locatePageForKey(key, -1, allowCreate);
}
/**
* Locate the (first) page that could contain the given key.
* In the inner pages, the keys are the minimum values of the sub-page. The value is
* the according minimum value of the first key of the sub-page.
* @param key
* @return Page for that key
*/
public LLIndexPage locatePageForKey(long key, long value, boolean allowCreate) {
if (isLeaf) {
return this;
}
if (nEntries == -1 && !allowCreate) {
return null;
}
//The stored value[i] is the min-values of the according page[i+1}
int pos = binarySearch(0, nEntries, key, value);
if (pos >= 0) {
//pos of matching key
pos++;
} else {
pos = -(pos+1);
}
//TODO use weak refs
//read page before that value
LLIndexPage page = (LLIndexPage) readOrCreatePage(pos, allowCreate);
return page.locatePageForKey(key, value, allowCreate);
}
public LLEntry getValueFromLeafUnique(long oid) {
if (!isLeaf) {
throw new JDOFatalDataStoreException();
}
int pos = binarySearchUnique(0, nEntries, oid);
if (pos >= 0) {
return new LLEntry( oid, values[pos]);
}
//Even if non-unique, if the value could is not on this page, it does not exist.
return null;
}
/**
* Add an entry at 'key'/'value'. If the PAIR already exists, nothing happens.
* @param key
* @param value
*/
public void insert(long key, long value) {
put(key, value);
}
/**
* Binary search.
*
* @param toIndex Exclusive, search stops at (toIndex-1).
* @param value For non-unique trees, the value is taken into account as well.
*/
int binarySearch(int fromIndex, int toIndex, long key, long value) {
if (ind.isUnique()) {
return binarySearchUnique(fromIndex, toIndex, key);
}
return binarySearchNonUnique(fromIndex, toIndex, key, value);
}
private int binarySearchUnique(int fromIndex, int toIndex, long key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
long midVal = keys[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else {
return mid; // key found
}
}
return -(low + 1); // key not found.
}
/**
* This effectively implements a binary search of a double-long array (128bit values).
*/
private int binarySearchNonUnique(int fromIndex, int toIndex, long key1, long key2) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
long midVal1 = keys[mid];
long midVal2 = values[mid];
if (midVal1 < key1) {
low = mid + 1;
} else if (midVal1 > key1) {
high = mid - 1;
} else {
if (midVal2 < key2) {
low = mid + 1;
} else if (midVal2 > key2) {
high = mid - 1;
} else {
return mid; // key found
}
}
}
return -(low + 1); // key not found.
}
/**
* Overwrite the entry at 'key'.
* @param key
* @param value
*/
public final void put(long key, long value) {
if (!isLeaf) {
throw new JDOFatalDataStoreException();
}
//in any case, check whether the key(+value) already exists
int pos = binarySearch(0, nEntries, key, value);
//key found? -> pos >=0
if (pos >= 0) {
//check if values changes
if (value != values[pos]) {
markPageDirtyAndClone();
values[pos] = value;
}
return;
}
if (nEntries < ind.maxLeafN) {
//okay so we add it locally
pos = -(pos+1);
markPageDirtyAndClone();
if (pos < nEntries) {
System.arraycopy(keys, pos, keys, pos+1, nEntries-pos);
System.arraycopy(values, pos, values, pos+1, nEntries-pos);
}
keys[pos] = key;
values[pos] = value;
nEntries++;
return;
} else {
//treat page overflow
LLIndexPage newP;
boolean isNew = false;
boolean isPrev = false;
//use ind.maxLeafN -1 to avoid pretty much pointless copying (and possible endless
//loops, see iterator tests)
LLIndexPage next = (LLIndexPage) parent.getNextLeafPage(this);
if (next != null && next.nEntries < ind.maxLeafN-1) {
//merge
newP = next;
newP.markPageDirtyAndClone();
isPrev = false;
} else {
//Merging with prev is not make a big difference, maybe we should remove it...
LLIndexPage prev = (LLIndexPage) parent.getPrevLeafPage(this);
if (prev != null && prev.nEntries < ind.maxLeafN-1) {
//merge
newP = prev;
newP.markPageDirtyAndClone();
isPrev = true;
} else {
newP = new LLIndexPage(ind, parent, true);
isNew = true;
}
}
markPageDirtyAndClone();
int nEntriesToKeep = (nEntries + newP.nEntries) >> 1;
if (isNew) {
if (ind.isUnique()) {
//This is an optimization for indices that add increasing unique numbers
//such as OIDs. For these, it increases the average fill-size.
//find split point such that pages can be completely full
int pos2 = binarySearch(0, nEntries, keys[0] + ind.maxLeafN, value);
if (pos2 < 0) {
pos2 = -(pos2+1);
}
if (pos2 > nEntriesToKeep) {
nEntriesToKeep = pos2;
}
} else {
//non-unique: we assume ascending keys.
//If they are not ascending, merging with subsequent page sorts it out.
nEntriesToKeep = (int) (ind.maxLeafN * 0.9);
}
}
int nEntriesToCopy = nEntries - nEntriesToKeep;
if (isNew) {
//works only if new page follows current page
System.arraycopy(keys, nEntriesToKeep, newP.keys, 0, nEntriesToCopy);
System.arraycopy(values, nEntriesToKeep, newP.values, 0, nEntriesToCopy);
} else if (isPrev) {
//copy element to previous page
System.arraycopy(keys, 0, newP.keys, newP.nEntries, nEntriesToCopy);
System.arraycopy(values, 0, newP.values, newP.nEntries, nEntriesToCopy);
//move element forward to beginning of page
System.arraycopy(keys, nEntriesToCopy, keys, 0, nEntries-nEntriesToCopy);
System.arraycopy(values, nEntriesToCopy, values, 0, nEntries-nEntriesToCopy);
} else {
//make space on next page
System.arraycopy(newP.keys, 0, newP.keys, nEntriesToCopy, newP.nEntries);
System.arraycopy(newP.values, 0, newP.values, nEntriesToCopy, newP.nEntries);
//insert element in next page
System.arraycopy(keys, nEntriesToKeep, newP.keys, 0, nEntriesToCopy);
System.arraycopy(values, nEntriesToKeep, newP.values, 0, nEntriesToCopy);
}
nEntries = (short) nEntriesToKeep;
newP.nEntries = (short) (nEntriesToCopy + newP.nEntries);
//New page and min key
if (isNew || !isPrev) {
if (ind.isUnique()) {
if (newP.keys[0] > key) {
put(key, value);
} else {
newP.put(key, value);
}
} else {
//why doesn't this work??? Because addSubPage needs the new keys already in the
//page
// parent.addSubPage(newP, newP.keys[0], newP.values[0]);
// locatePageForKey(key, value, false).put(key, value);
if (newP.keys[0] > key || (newP.keys[0]==key && newP.values[0] > value)) {
put(key, value);
} else {
newP.put(key, value);
}
}
} else {
if (ind.isUnique()) {
if (keys[0] > key) {
newP.put(key, value);
} else {
put(key, value);
}
} else {
if (keys[0] > key || (keys[0]==key && values[0] > value)) {
newP.put(key, value);
} else {
put(key, value);
}
}
}
if (isNew) {
parent.addSubPage(newP, newP.keys[0], newP.values[0]);
} else {
//TODO probably not necessary
newP.parent.updateKey(newP, newP.keys[0], newP.values[0]);
}
parent.updateKey(this, keys[0], values[0]);
}
}
void updateKey(LLIndexPage indexPage, long key, long value) {
//TODO do we need this whole key update business????
//-> surely not at the moment, where we only merge with pages that have the same
// immediate parent...
if (isLeaf) {
throw new JDOFatalDataStoreException();
}
int start = binarySearch(0, nEntries, key, value);
if (start < 0) {
start = -(start+1);
}
markPageDirtyAndClone();
for (int i = start; i <= nEntries; i++) {
if (subPages[i] == indexPage) {
if (i > 0) {
keys[i-1] = key;
if (!ind.isUnique()) {
values[i-1] = value;
}
} else {
//parent page could be affected
if (parent != null) {
parent.updateKey(this, key, value);
}
}
return;
}
}
// System.out.println("this:" + parent);
// this.printLocal();
// System.out.println("leaf: " + indexPage);
// indexPage.printLocal();
throw new JDOFatalDataStoreException("leaf page not found.");
}
void addSubPage(LLIndexPage newP, long minKey, long minValue) {
if (isLeaf) {
throw new JDOFatalDataStoreException();
}
markPageDirtyAndClone();
if (nEntries < ind.maxInnerN) {
//add page here
//TODO Should we store non-unique values more efficiently? Instead of always storing
// the key as well? -> Additional page type for values only? The local value only
// being a reference to the value page (inlc offs)? How would efficient insertion
// work (shifting values and references to value pages???) ?
//For now, we assume a unique index.
int i = binarySearch(0, nEntries, minKey, minValue);
//If the key (+val) has a perfect match then something went wrong. This should
//never happen so we don't need to check whether (i < 0).
i = -(i+1);
if (i > 0) {
System.arraycopy(keys, i, keys, i+1, nEntries-i);
System.arraycopy(subPages, i+1, subPages, i+2, nEntries-i);
System.arraycopy(subPageIds, i+1, subPageIds, i+2, nEntries-i);
if (!ind.isUnique()) {
System.arraycopy(values, i, values, i+1, nEntries-i);
values[i] = minValue;
}
keys[i] = minKey;
subPages[i+1] = newP;
newP.setParent( this );
subPageIds[i+1] = newP.pageId();
nEntries++;
} else {
//decide whether before or after first page (both will end up before the current
//first key).
int ii;
if (nEntries < 0) {
//can happen for empty root page
ii = 0;
} else {
System.arraycopy(keys, 0, keys, 1, nEntries);
long oldKey = subPages[0].getMinKey();
if (!ind.isUnique()) {
System.arraycopy(values, 0, values, 1, nEntries);
long oldValue = subPages[0].getMinKeyValue();
if ((minKey > oldKey) || (minKey==oldKey && minValue > oldValue)) {
ii = 1;
keys[0] = minKey;
values[0] = minValue;
} else {
ii = 0;
keys[0] = oldKey;
values[0] = oldValue;
}
} else {
if ( minKey > oldKey ) {
ii = 1;
keys[0] = minKey;
} else {
ii = 0;
keys[0] = oldKey;
}
}
System.arraycopy(subPages, ii, subPages, ii+1, nEntries-ii+1);
System.arraycopy(subPageIds, ii, subPageIds, ii+1, nEntries-ii+1);
}
subPages[ii] = newP;
newP.setParent( this );
subPageIds[ii] = newP.pageId();
nEntries++;
}
return;
} else {
//treat page overflow
LLIndexPage newInner = (LLIndexPage) ind.createPage(parent, false);
//TODO use optimized fill ration for unique values, just like for leaves?.
System.arraycopy(keys, ind.minInnerN+1, newInner.keys, 0, nEntries-ind.minInnerN-1);
if (!ind.isUnique()) {
System.arraycopy(values, ind.minInnerN+1, newInner.values, 0, nEntries-ind.minInnerN-1);
}
System.arraycopy(subPages, ind.minInnerN+1, newInner.subPages, 0, nEntries-ind.minInnerN);
System.arraycopy(subPageIds, ind.minInnerN+1, newInner.subPageIds, 0, nEntries-ind.minInnerN);
newInner.nEntries = (short) (nEntries-ind.minInnerN-1);
newInner.assignThisAsRootToLeaves();
if (parent == null) {
//create a parent
LLIndexPage newRoot = (LLIndexPage) ind.createPage(null, false);
newRoot.subPages[0] = this;
newRoot.nEntries = 0; // 0: indicates one leaf / zero keys
this.setParent( newRoot );
ind.updateRoot(newRoot);
}
if (ind.isUnique()) {
parent.addSubPage(newInner, keys[ind.minInnerN], -1);
} else {
parent.addSubPage(newInner, keys[ind.minInnerN], values[ind.minInnerN]);
}
nEntries = (short) (ind.minInnerN);
//finally add the leaf to the according page
LLIndexPage newHome;
long newInnerMinKey = newInner.getMinKey();
if (ind.isUnique()) {
if (minKey < newInnerMinKey) {
newHome = this;
} else {
newHome = newInner;
}
} else {
long newInnerMinValue = newInner.getMinKeyValue();
if (minKey < newInnerMinKey ||
(minKey == newInnerMinKey && minValue < newInnerMinValue)) {
newHome = this;
} else {
newHome = newInner;
}
}
newHome.addSubPage(newP, minKey, minValue);
return;
}
}
@Override
long getMinKey() {
if (isLeaf) {
return keys[0];
}
return readPage(0).getMinKey();
}
@Override
long getMinKeyValue() {
if (isLeaf) {
return values[0];
}
return readPage(0).getMinKeyValue();
}
@Override
public void print(String indent) {
// System.out.println("Java page ID: " + this); //TODO
if (isLeaf) {
System.out.println(indent + "Leaf page(id=" + pageId() + "): nK=" + nEntries + " keys=" +
Arrays.toString(keys));
System.out.println(indent + " " + Arrays.toString(values));
} else {
System.out.println(indent + "Inner page(id=" + pageId() + "): nK=" + nEntries + " keys=" +
Arrays.toString(keys));
System.out.println(indent + " " + nEntries + " page=" +
Arrays.toString(subPageIds));
if (!ind.isUnique()) {
System.out.println(indent + " " + nEntries + " values=" +
Arrays.toString(values));
}
// System.out.println(indent + " " + nEntries + " leaf=" +
// Arrays.toString(leaves));
System.out.print(indent + "[");
for (int i = 0; i <= nEntries; i++) {
if (subPages[i] != null) {
System.out.print(indent + "i=" + i + ": ");
subPages[i].print(indent + " ");
}
else System.out.println("Page not loaded: " + subPageIds[i]);
}
System.out.println(']');
}
}
@Override
public void printLocal() {
System.out.println("PrintLocal() for " + this);
if (isLeaf) {
System.out.println("Leaf page(id=" + pageId() + "): nK=" + nEntries + " oids=" +
Arrays.toString(keys));
System.out.println(" " + Arrays.toString(values));
} else {
System.out.println("Inner page(id=" + pageId() + "): nK=" + nEntries + " oids=" +
Arrays.toString(keys));
System.out.println(" " + Arrays.toString(subPageIds));
if (!ind.isUnique()) {
System.out.println(" " + Arrays.toString(values));
}
System.out.println(" " + Arrays.toString(subPages));
}
}
@Override
protected short getNKeys() {
return nEntries;
}
/**
* @param key
* @return the previous value
*/
protected long remove(long key) {
if (!ind.isUnique()) {
throw new IllegalStateException();
}
return remove(key, 0);
}
protected long remove(long oid, long value) {
int i = binarySearch(0, nEntries, oid, value);
if (i < 0) {
//key not found
throw new NoSuchElementException("Key not found: " + oid + "/" + value);
}
// first remove the element
markPageDirtyAndClone();
long prevValue = values[i];
System.arraycopy(keys, i+1, keys, i, nEntries-i-1);
System.arraycopy(values, i+1, values, i, nEntries-i-1);
nEntries--;
if (nEntries == 0) {
ind.statNLeaves--;
parent.removeLeafPage(this, oid, value);
} else if (nEntries < (ind.maxLeafN >> 1) && (nEntries % 8 == 0)) {
//The second term prevents frequent reading of previous and following pages.
//TODO Should we instead check for nEntries==MAx>>1 then == (MAX>>2) then <= (MAX>>3)?
//now attempt merging this page
LLIndexPage prevPage = (LLIndexPage) parent.getPrevLeafPage(this);
if (prevPage != null) {
//We merge only if they all fit on a single page. This means we may read
//the previous page unnecessarily, but we avoid writing it as long as
//possible. TODO find a balance, and do no read prev page in all cases
if (nEntries + prevPage.nEntries < ind.maxLeafN) {
//TODO for now this work only for leaves with the same root. We
//would need to update the min values in the inner nodes.
prevPage.markPageDirtyAndClone();
System.arraycopy(keys, 0, prevPage.keys, prevPage.nEntries, nEntries);
System.arraycopy(values, 0, prevPage.values, prevPage.nEntries, nEntries);
prevPage.nEntries += nEntries;
ind.statNLeaves--;
parent.removeLeafPage(this, keys[0], values[0]);
}
}
}
return prevValue;
}
protected void removeLeafPage(LLIndexPage indexPage, long key, long value) {
ind.statNInner--;
int start = binarySearch(0, nEntries, key, value);
if (start < 0) {
start = -(start+1);
}
for (int i = start; i <= nEntries; i++) {
if (subPages[i] == indexPage) {
markPageDirtyAndClone();
//remove sub page page from FSM.
ind.file.reportFreePage(subPageIds[i]);
if (nEntries > 0) { //otherwise we just delete this page
//remove entry
arraysRemoveInnerEntry(i);
nEntries--;
//Now try merging
if (parent == null) {
return;
}
LLIndexPage prev = (LLIndexPage) parent.getPrevInnerPage(this);
if (prev != null && !prev.isLeaf) {
//TODO this is only good for merging inside the same root.
if ((nEntries % 2 == 0) && (prev.nEntries + nEntries < ind.maxInnerN)) {
prev.markPageDirtyAndClone();
System.arraycopy(keys, 0, prev.keys, prev.nEntries+1, nEntries);
if (!ind.isUnique()) {
System.arraycopy(values, 0, prev.values, prev.nEntries+1, nEntries);
}
System.arraycopy(subPages, 0, prev.subPages, prev.nEntries+1, nEntries+1);
System.arraycopy(subPageIds, 0, prev.subPageIds, prev.nEntries+1, nEntries+1);
//find key for the first appended page -> go up or go down????? Up!
int pos = parent.getPagePosition(this)-1;
prev.keys[prev.nEntries] = parent.keys[pos];
if (!ind.isUnique()) {
prev.values[prev.nEntries] = parent.values[pos];
}
prev.nEntries += nEntries + 1; //for the additional key
prev.assignThisAsRootToLeaves();
parent.removeLeafPage(this, key, value);
}
return;
}
if (nEntries == 0) {
//only one element left, no merging occurred -> move sub-page up to parent
AbstractIndexPage child = readPage(0);
parent.replaceChildPage(this, key, value, child);
}
} else {
// nEntries == 0
if (parent != null) {
parent.removeLeafPage(this, key, value);
}
// else : No root and this is a leaf page... -> we do nothing.
subPageIds[0] = 0;
subPages[0] = null;
nEntries--; //down to -1 which indicates an empty root page
}
return;
}
}
// System.out.println("this:" + parent);
// this.printLocal();
// System.out.println("leaf: " + indexPage);
// indexPage.printLocal();
throw new JDOFatalDataStoreException("leaf page not found.");
}
private void arraysRemoveKey(int pos) {
System.arraycopy(keys, pos+1, keys, pos, nEntries-pos-1);
if (!ind.isUnique()) {
System.arraycopy(values, pos+1, values, pos, nEntries-pos-1);
}
}
private void arraysRemoveChild(int pos) {
System.arraycopy(subPages, pos+1, subPages, pos, nEntries-pos);
System.arraycopy(subPageIds, pos+1, subPageIds, pos, nEntries-pos);
subPageIds[nEntries] = 0;
subPages[nEntries] = null;
}
/**
*
* @param posEntry The pos in the subPage-array. The according keyPos may be -1.
*/
private void arraysRemoveInnerEntry(int posEntry) {
if (posEntry > 0) {
arraysRemoveKey(posEntry - 1);
} else {
arraysRemoveKey(0);
}
arraysRemoveChild(posEntry);
}
/**
* Replacing sub-pages occurs when the sub-page shrinks down to a single sub-sub-page, in which
* case we pull up the sub-sub-page to the local page, replacing the sub-page.
*/
protected void replaceChildPage(LLIndexPage indexPage, long key, long value,
AbstractIndexPage subChild) {
int start = binarySearch(0, nEntries, key, value);
if (start < 0) {
start = -(start+1);
}
for (int i = start; i <= nEntries; i++) {
if (subPages[i] == indexPage) {
markPageDirtyAndClone();
//remove page from FSM.
ind.file.reportFreePage(subPageIds[i]);
subPageIds[i] = subChild.pageId();
subPages[i] = subChild;
if (i>0) {
keys[i-1] = subChild.getMinKey();
if (!ind.isUnique()) {
values[i-1] = subChild.getMinKeyValue();
}
}
subChild.setParent(this);
return;
}
}
// System.out.println("this:" + parent);
// this.printLocal();
// System.out.println("sub-page:");
// indexPage.printLocal();
throw new JDOFatalDataStoreException("Sub-page not found.");
}
@Override
LLIndexPage getParent() {
return parent;
}
@Override
void setParent(AbstractIndexPage parent) {
this.parent = (LLIndexPage) parent;
}
public long getMax() {
if (isLeaf) {
if (nEntries == 0) {
return Long.MIN_VALUE;
}
return keys[nEntries-1];
}
//handle empty indices
if (nEntries == -1) {
return Long.MIN_VALUE;
}
long max = ((LLIndexPage)getPageByPos(nEntries)).getMax();
return max;
}
@Override
protected AbstractIndexPage newInstance() {
return new LLIndexPage(this);
}
/**
* Special method to remove entries. When removing the entry, it checks whether other entries
* in the given range exist. If none exist, the value is returned as free page to FSM.
* @param key
* @param min
* @param max
* @return The previous value
*/
public long deleteAndCheckRangeEmpty(long key, long min, long max) {
LLIndexPage pageKey = locatePageForKeyUnique(key, false);
int posKey = pageKey.binarySearchUnique(0, pageKey.nEntries, key);
//We assume that the key exists. Otherwise we get an exception anyway in the remove-method.
//-> no such calculation: posKey = -(posKey+1);
//First we cover the most frequent cases, which are also fastest to check.
long[] keys = pageKey.getKeys();
if (posKey > 0) {
if (keys[posKey-1] >= min) {
return pageKey.remove(key);
}
if (posKey < pageKey.nEntries-1) {
if (keys[posKey+1] <= max) {
return pageKey.remove(key);
}
//we are in the middle of the page surrounded by values outside the range
ind.file.reportFreePage(BitTools.getPage(key));
return pageKey.remove(key);
}
} else if (posKey == 0 && pageKey.nEntries > 1) {
if (keys[posKey+1] <= max) {
return pageKey.remove(key);
}
}
//brute force:
long pos = pageKey.remove(key);
LLIterator iter = new LLIterator(ind, min, max);
if (!iter.hasNextULL()) {
ind.file.reportFreePage(BitTools.getPage(key));
}
iter.close();
return pos;
// //If we get here, the key is on the border of the page and we need to search more.
// //If we get here, we also know that there are no values from the range on the page.
//
// LLIndexPage pageMin = locatePageForKeyUnique(min, false);
// if (pageKey != pageMin) {
//// System.out.println("X6");
// return pageKey.remove(key);
// }
// LLIndexPage pageMax = locatePageForKeyUnique(max, false);
// if (pageKey != pageMax) {
//// System.out.println("X7");
// return pageKey.remove(key);
// }
//
// //Now we know that there are no range-keys on other pages either. We can remove the page.
//
// System.out.println("X8");
// fsm.reportFreePage(BitTools.getPage(key));
// return pageKey.remove(key);
}
@Override
protected void incrementNEntries() {
nEntries++;
}
final long[] getKeys() {
return keys;
}
final long[] getValues() {
return values;
}
@Override
final void setNEntries(int n) {
nEntries = (short) n;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy