SCViewer/com.minres.scviewer.databas.../src/org/apache/jdbm/BTreeNode.java

1551 lines
52 KiB
Java

/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
import java.util.ConcurrentModificationException;
import java.util.List;
/**
* Node of a BTree.
* <p/>
* The node contains a number of key-value pairs. Keys are ordered to allow
* dichotomic search. If value is too big, it is stored in separate record
* and only recid reference is stored
* <p/>
* If the node is a leaf node, the keys and values are user-defined and
* represent entries inserted by the user.
* <p/>
* If the node is non-leaf, each key represents the greatest key in the
* underlying BTreeNode and the values are recids pointing to the children BTreeNodes.
* The only exception is the rightmost BTreeNode, which is considered to have an
* "infinite" key value, meaning that any insert will be to the left of this
* pseudo-key
*
* @author Alex Boisvert
* @author Jan Kotek
*/
final class BTreeNode<K, V>
implements Serializer<BTreeNode<K, V>> {
private static final boolean DEBUG = false;
/**
* Parent B+Tree.
*/
transient BTree<K, V> _btree;
/**
* This BTreeNode's record ID in the DB.
*/
protected transient long _recid;
/**
* Flag indicating if this is a leaf BTreeNode.
*/
protected boolean _isLeaf;
/**
* Keys of children nodes
*/
protected K[] _keys;
/**
* Values associated with keys. (Only valid if leaf node)
*/
protected Object[] _values;
/**
* Children nodes (recids) associated with keys. (Only valid if non-leaf node)
*/
protected long[] _children;
/**
* Index of first used item at the node
*/
protected byte _first;
/**
* Previous leaf node (only if this node is a leaf)
*/
protected long _previous;
/**
* Next leaf node (only if this node is a leaf)
*/
protected long _next;
/**
* Return the B+Tree that is the owner of this {@link BTreeNode}.
*/
public BTree<K, V> getBTree() {
return _btree;
}
/**
* No-argument constructor used by serialization.
*/
public BTreeNode() {
// empty
}
/**
* Root node overflow constructor
*/
@SuppressWarnings("unchecked")
BTreeNode(BTree<K, V> btree, BTreeNode<K, V> root, BTreeNode<K, V> overflow)
throws IOException {
_btree = btree;
_isLeaf = false;
_first = BTree.DEFAULT_SIZE - 2;
_keys = (K[]) new Object[BTree.DEFAULT_SIZE];
_keys[BTree.DEFAULT_SIZE - 2] = overflow.getLargestKey();
_keys[BTree.DEFAULT_SIZE - 1] = root.getLargestKey();
_children = new long[BTree.DEFAULT_SIZE];
_children[BTree.DEFAULT_SIZE - 2] = overflow._recid;
_children[BTree.DEFAULT_SIZE - 1] = root._recid;
_recid = _btree._db.insert(this, this,false);
}
/**
* Root node (first insert) constructor.
*/
@SuppressWarnings("unchecked")
BTreeNode(BTree<K, V> btree, K key, V value)
throws IOException {
_btree = btree;
_isLeaf = true;
_first = BTree.DEFAULT_SIZE - 2;
_keys = (K[]) new Object[BTree.DEFAULT_SIZE];
_keys[BTree.DEFAULT_SIZE - 2] = key;
_keys[BTree.DEFAULT_SIZE - 1] = null; // I am the root BTreeNode for now
_values = new Object[BTree.DEFAULT_SIZE];
_values[BTree.DEFAULT_SIZE - 2] = value;
_values[BTree.DEFAULT_SIZE - 1] = null; // I am the root BTreeNode for now
_recid = _btree._db.insert(this, this,false);
}
/**
* Overflow node constructor. Creates an empty BTreeNode.
*/
@SuppressWarnings("unchecked")
BTreeNode(BTree<K, V> btree, boolean isLeaf){
_btree = btree;
_isLeaf = isLeaf;
// node will initially be half-full
_first = BTree.DEFAULT_SIZE / 2;
_keys = (K[]) new Object[BTree.DEFAULT_SIZE];
if (isLeaf) {
_values = new Object[BTree.DEFAULT_SIZE];
} else {
_children = new long[BTree.DEFAULT_SIZE];
}
try{
_recid = _btree._db.insert(this, this,false);
}catch(IOException e ){
throw new IOError(e);
}
}
/**
* Get largest key under this BTreeNode. Null is considered to be the
* greatest possible key.
*/
K getLargestKey() {
return _keys[BTree.DEFAULT_SIZE - 1];
}
/**
* Return true if BTreeNode is empty.
*/
boolean isEmpty() {
if (_isLeaf) {
return (_first == _values.length - 1);
} else {
return (_first == _children.length - 1);
}
}
/**
* Return true if BTreeNode is full.
*/
boolean isFull() {
return (_first == 0);
}
/**
* Find the object associated with the given key.
*
* @param height Height of the current BTreeNode (zero is leaf node)
* @param key The key
* @return TupleBrowser positionned just before the given key, or before
* next greater key if key isn't found.
*/
BTree.BTreeTupleBrowser<K, V> find(int height, final K key, final boolean inclusive)
throws IOException {
byte index = findChildren(key,inclusive);
height -= 1;
if (height == 0) {
// leaf node
return new Browser<K, V>(this, index);
} else {
// non-leaf node
BTreeNode<K, V> child = loadNode(_children[index]);
return child.find(height, key,inclusive);
}
}
/**
* Find value associated with the given key.
*
* @param height Height of the current BTreeNode (zero is leaf node)
* @param key The key
* @return TupleBrowser positionned just before the given key, or before
* next greater key if key isn't found.
*/
V findValue(int height, K key)
throws IOException {
byte index = findChildren(key,true);
height -= 1;
if (height == 0) {
K key2 = _keys[index];
// // get returns the matching key or the next ordered key, so we must
// // check if we have an exact match
if (key2 == null || compare(key, key2) != 0)
return null;
// leaf node
if (_values[index] instanceof BTreeLazyRecord)
return ((BTreeLazyRecord<V>) _values[index]).get();
else
return (V) _values[index];
} else {
// non-leaf node
BTreeNode<K, V> child = loadNode(_children[index]);
return child.findValue(height, key);
}
}
/**
* Find first entry and return a browser positioned before it.
*
* @return TupleBrowser positionned just before the first entry.
*/
BTree.BTreeTupleBrowser<K, V> findFirst()
throws IOException {
if (_isLeaf) {
return new Browser<K, V>(this, _first);
} else {
BTreeNode<K, V> child = loadNode(_children[_first]);
return child.findFirst();
}
}
/**
* Deletes this BTreeNode and all children nodes from the record manager
*/
void delete()
throws IOException {
if (_isLeaf) {
if (_next != 0) {
BTreeNode<K, V> nextNode = loadNode(_next);
if (nextNode._previous == _recid) { // this consistency check can be removed in production code
nextNode._previous = _previous;
_btree._db.update(nextNode._recid, nextNode, nextNode);
} else {
throw new Error("Inconsistent data in BTree");
}
}
if (_previous != 0) {
BTreeNode<K, V> previousNode = loadNode(_previous);
if (previousNode._next != _recid) { // this consistency check can be removed in production code
previousNode._next = _next;
_btree._db.update(previousNode._recid, previousNode, previousNode);
} else {
throw new Error("Inconsistent data in BTree");
}
}
} else {
int left = _first;
int right = BTree.DEFAULT_SIZE - 1;
for (int i = left; i <= right; i++) {
BTreeNode<K, V> childNode = loadNode(_children[i]);
childNode.delete();
}
}
_btree._db.delete(_recid);
}
/**
* Insert the given key and value.
* <p/>
* Since the Btree does not support duplicate entries, the caller must
* specify whether to replace the existing value.
*
* @param height Height of the current BTreeNode (zero is leaf node)
* @param key Insert key
* @param value Insert value
* @param replace Set to true to replace the existing value, if one exists.
* @return Insertion result containing existing value OR a BTreeNode if the key
* was inserted and provoked a BTreeNode overflow.
*/
InsertResult<K, V> insert(int height, K key, final V value, final boolean replace)
throws IOException {
InsertResult<K, V> result;
long overflow;
final byte index = findChildren(key,true);
height -= 1;
if (height == 0) {
//reuse InsertResult instance to avoid GC trashing on massive inserts
result = _btree.insertResultReuse;
_btree.insertResultReuse = null;
if (result == null)
result = new InsertResult<K, V>();
// inserting on a leaf BTreeNode
overflow = -1;
if (DEBUG) {
System.out.println("BTreeNode.insert() Insert on leaf node key=" + key
+ " value=" + value + " index=" + index);
}
if (compare(_keys[index], key) == 0) {
// key already exists
if (DEBUG) {
System.out.println("BTreeNode.insert() Key already exists.");
}
boolean isLazyRecord = _values[index] instanceof BTreeLazyRecord;
if (isLazyRecord)
result._existing = ((BTreeLazyRecord<V>) _values[index]).get();
else
result._existing = (V) _values[index];
if (replace) {
//remove old lazy record if necesarry
if (isLazyRecord)
((BTreeLazyRecord) _values[index]).delete();
_values[index] = value;
_btree._db.update(_recid, this, this);
}
// return the existing key
return result;
}
} else {
// non-leaf BTreeNode
BTreeNode<K, V> child = loadNode(_children[index]);
result = child.insert(height, key, value, replace);
if (result._existing != null) {
// return existing key, if any.
return result;
}
if (result._overflow == null) {
// no overflow means we're done with insertion
return result;
}
// there was an overflow, we need to insert the overflow node on this BTreeNode
if (DEBUG) {
System.out.println("BTreeNode.insert() Overflow node: " + result._overflow._recid);
}
key = result._overflow.getLargestKey();
overflow = result._overflow._recid;
// update child's largest key
_keys[index] = child.getLargestKey();
// clean result so we can reuse it
result._overflow = null;
}
// if we get here, we need to insert a new entry on the BTreeNode before _children[ index ]
if (!isFull()) {
if (height == 0) {
insertEntry(this, index - 1, key, value);
} else {
insertChild(this, index - 1, key, overflow);
}
_btree._db.update(_recid, this, this);
return result;
}
// node is full, we must divide the node
final byte half = BTree.DEFAULT_SIZE >> 1;
BTreeNode<K, V> newNode = new BTreeNode<K, V>(_btree, _isLeaf);
if (index < half) {
// move lower-half of entries to overflow node, including new entry
if (DEBUG) {
System.out.println("BTreeNode.insert() move lower-half of entries to overflow BTreeNode, including new entry.");
}
if (height == 0) {
copyEntries(this, 0, newNode, half, index);
setEntry(newNode, half + index, key, value);
copyEntries(this, index, newNode, half + index + 1, half - index - 1);
} else {
copyChildren(this, 0, newNode, half, index);
setChild(newNode, half + index, key, overflow);
copyChildren(this, index, newNode, half + index + 1, half - index - 1);
}
} else {
// move lower-half of entries to overflow node, new entry stays on this node
if (DEBUG) {
System.out.println("BTreeNode.insert() move lower-half of entries to overflow BTreeNode. New entry stays");
}
if (height == 0) {
copyEntries(this, 0, newNode, half, half);
copyEntries(this, half, this, half - 1, index - half);
setEntry(this, index - 1, key, value);
} else {
copyChildren(this, 0, newNode, half, half);
copyChildren(this, half, this, half - 1, index - half);
setChild(this, index - 1, key, overflow);
}
}
_first = half - 1;
// nullify lower half of entries
for (int i = 0; i < _first; i++) {
if (height == 0) {
setEntry(this, i, null, null);
} else {
setChild(this, i, null, -1);
}
}
if (_isLeaf) {
// link newly created node
newNode._previous = _previous;
newNode._next = _recid;
if (_previous != 0) {
BTreeNode<K, V> previous = loadNode(_previous);
previous._next = newNode._recid;
_btree._db.update(_previous, previous, this);
}
_previous = newNode._recid;
}
_btree._db.update(_recid, this, this);
_btree._db.update(newNode._recid, newNode, this);
result._overflow = newNode;
return result;
}
/**
* Remove the entry associated with the given key.
*
* @param height Height of the current BTreeNode (zero is leaf node)
* @param key Removal key
* @return Remove result object
*/
RemoveResult<K, V> remove(int height, K key)
throws IOException {
RemoveResult<K, V> result;
int half = BTree.DEFAULT_SIZE / 2;
byte index = findChildren(key,true);
height -= 1;
if (height == 0) {
// remove leaf entry
if (compare(_keys[index], key) != 0) {
throw new IllegalArgumentException("Key not found: " + key);
}
result = new RemoveResult<K, V>();
if (_values[index] instanceof BTreeLazyRecord) {
BTreeLazyRecord<V> r = (BTreeLazyRecord<V>) _values[index];
result._value = r.get();
r.delete();
} else {
result._value = (V) _values[index];
}
removeEntry(this, index);
// update this node
_btree._db.update(_recid, this, this);
} else {
// recurse into Btree to remove entry on a children node
BTreeNode<K, V> child = loadNode(_children[index]);
result = child.remove(height, key);
// update children
_keys[index] = child.getLargestKey();
_btree._db.update(_recid, this, this);
if (result._underflow) {
// underflow occured
if (child._first != half + 1) {
throw new IllegalStateException("Error during underflow [1]");
}
if (index < _children.length - 1) {
// exists greater brother node
BTreeNode<K, V> brother = loadNode(_children[index + 1]);
int bfirst = brother._first;
if (bfirst < half) {
// steal entries from "brother" node
int steal = (half - bfirst + 1) / 2;
brother._first += steal;
child._first -= steal;
if (child._isLeaf) {
copyEntries(child, half + 1, child, half + 1 - steal, half - 1);
copyEntries(brother, bfirst, child, 2 * half - steal, steal);
} else {
copyChildren(child, half + 1, child, half + 1 - steal, half - 1);
copyChildren(brother, bfirst, child, 2 * half - steal, steal);
}
for (int i = bfirst; i < bfirst + steal; i++) {
if (brother._isLeaf) {
setEntry(brother, i, null, null);
} else {
setChild(brother, i, null, -1);
}
}
// update child's largest key
_keys[index] = child.getLargestKey();
// no change in previous/next node
// update nodes
_btree._db.update(_recid, this, this);
_btree._db.update(brother._recid, brother, this);
_btree._db.update(child._recid, child, this);
} else {
// move all entries from node "child" to "brother"
if (brother._first != half) {
throw new IllegalStateException("Error during underflow [2]");
}
brother._first = 1;
if (child._isLeaf) {
copyEntries(child, half + 1, brother, 1, half - 1);
} else {
copyChildren(child, half + 1, brother, 1, half - 1);
}
_btree._db.update(brother._recid, brother, this);
// remove "child" from current node
if (_isLeaf) {
copyEntries(this, _first, this, _first + 1, index - _first);
setEntry(this, _first, null, null);
} else {
copyChildren(this, _first, this, _first + 1, index - _first);
setChild(this, _first, null, -1);
}
_first += 1;
_btree._db.update(_recid, this, this);
// re-link previous and next nodes
if (child._previous != 0) {
BTreeNode<K, V> prev = loadNode(child._previous);
prev._next = child._next;
_btree._db.update(prev._recid, prev, this);
}
if (child._next != 0) {
BTreeNode<K, V> next = loadNode(child._next);
next._previous = child._previous;
_btree._db.update(next._recid, next, this);
}
// delete "child" node
_btree._db.delete(child._recid);
}
} else {
// node "brother" is before "child"
BTreeNode<K, V> brother = loadNode(_children[index - 1]);
int bfirst = brother._first;
if (bfirst < half) {
// steal entries from "brother" node
int steal = (half - bfirst + 1) / 2;
brother._first += steal;
child._first -= steal;
if (child._isLeaf) {
copyEntries(brother, 2 * half - steal, child,
half + 1 - steal, steal);
copyEntries(brother, bfirst, brother,
bfirst + steal, 2 * half - bfirst - steal);
} else {
copyChildren(brother, 2 * half - steal, child,
half + 1 - steal, steal);
copyChildren(brother, bfirst, brother,
bfirst + steal, 2 * half - bfirst - steal);
}
for (int i = bfirst; i < bfirst + steal; i++) {
if (brother._isLeaf) {
setEntry(brother, i, null, null);
} else {
setChild(brother, i, null, -1);
}
}
// update brother's largest key
_keys[index - 1] = brother.getLargestKey();
// no change in previous/next node
// update nodes
_btree._db.update(_recid, this, this);
_btree._db.update(brother._recid, brother, this);
_btree._db.update(child._recid, child, this);
} else {
// move all entries from node "brother" to "child"
if (brother._first != half) {
throw new IllegalStateException("Error during underflow [3]");
}
child._first = 1;
if (child._isLeaf) {
copyEntries(brother, half, child, 1, half);
} else {
copyChildren(brother, half, child, 1, half);
}
_btree._db.update(child._recid, child, this);
// remove "brother" from current node
if (_isLeaf) {
copyEntries(this, _first, this, _first + 1, index - 1 - _first);
setEntry(this, _first, null, null);
} else {
copyChildren(this, _first, this, _first + 1, index - 1 - _first);
setChild(this, _first, null, -1);
}
_first += 1;
_btree._db.update(_recid, this, this);
// re-link previous and next nodes
if (brother._previous != 0) {
BTreeNode<K, V> prev = loadNode(brother._previous);
prev._next = brother._next;
_btree._db.update(prev._recid, prev, this);
}
if (brother._next != 0) {
BTreeNode<K, V> next = loadNode(brother._next);
next._previous = brother._previous;
_btree._db.update(next._recid, next, this);
}
// delete "brother" node
_btree._db.delete(brother._recid);
}
}
}
}
// underflow if node is more than half-empty
result._underflow = _first > half;
return result;
}
/**
* Find the first children node with a key equal or greater than the given
* key.
*
* @return index of first children with equal or greater key.
*/
private byte findChildren(final K key, final boolean inclusive) {
int left = _first;
int right = BTree.DEFAULT_SIZE - 1;
int middle;
final int D = inclusive?0:1;
// binary search
while (true) {
middle = (left + right) / 2;
if (compare(_keys[middle], key) < D) {
left = middle + 1;
} else {
right = middle;
}
if (left >= right) {
return (byte) right;
}
}
}
/**
* Insert entry at given position.
*/
private static <K, V> void insertEntry(BTreeNode<K, V> node, int index,
K key, V value) {
K[] keys = node._keys;
Object[] values = node._values;
int start = node._first;
int count = index - node._first + 1;
// shift entries to the left
System.arraycopy(keys, start, keys, start - 1, count);
System.arraycopy(values, start, values, start - 1, count);
node._first -= 1;
keys[index] = key;
values[index] = value;
}
/**
* Insert child at given position.
*/
private static <K, V> void insertChild(BTreeNode<K, V> node, int index,
K key, long child) {
K[] keys = node._keys;
long[] children = node._children;
int start = node._first;
int count = index - node._first + 1;
// shift entries to the left
System.arraycopy(keys, start, keys, start - 1, count);
System.arraycopy(children, start, children, start - 1, count);
node._first -= 1;
keys[index] = key;
children[index] = child;
}
/**
* Remove entry at given position.
*/
private static <K, V> void removeEntry(BTreeNode<K, V> node, int index) {
K[] keys = node._keys;
Object[] values = node._values;
int start = node._first;
int count = index - node._first;
System.arraycopy(keys, start, keys, start + 1, count);
keys[start] = null;
System.arraycopy(values, start, values, start + 1, count);
values[start] = null;
node._first++;
}
/**
* Set the entry at the given index.
*/
private static <K, V> void setEntry(BTreeNode<K, V> node, int index, K key, V value) {
node._keys[index] = key;
node._values[index] = value;
}
/**
* Set the child BTreeNode recid at the given index.
*/
private static <K, V> void setChild(BTreeNode<K, V> node, int index, K key, long recid) {
node._keys[index] = key;
node._children[index] = recid;
}
/**
* Copy entries between two nodes
*/
private static <K, V> void copyEntries(BTreeNode<K, V> source, int indexSource,
BTreeNode<K, V> dest, int indexDest, int count) {
System.arraycopy(source._keys, indexSource, dest._keys, indexDest, count);
System.arraycopy(source._values, indexSource, dest._values, indexDest, count);
}
/**
* Copy child node recids between two nodes
*/
private static <K, V> void copyChildren(BTreeNode<K, V> source, int indexSource,
BTreeNode<K, V> dest, int indexDest, int count) {
System.arraycopy(source._keys, indexSource, dest._keys, indexDest, count);
System.arraycopy(source._children, indexSource, dest._children, indexDest, count);
}
/**
* Load the node at the given recid.
*/
private BTreeNode<K, V> loadNode(long recid)
throws IOException {
BTreeNode<K, V> child = _btree._db.fetch(recid, this);
child._recid = recid;
child._btree = _btree;
return child;
}
private final int compare(final K value1, final K value2) {
if (value1 == null) {
return 1;
}
if (value2 == null) {
return -1;
}
if (_btree._comparator == null) {
return ((Comparable) value1).compareTo(value2);
} else {
return _btree._comparator.compare(value1, value2);
}
}
/**
* Dump the structure of the tree on the screen. This is used for debugging
* purposes only.
*/
private void dump(int height) {
String prefix = "";
for (int i = 0; i < height; i++) {
prefix += " ";
}
System.out.println(prefix + "-------------------------------------- BTreeNode recid=" + _recid);
System.out.println(prefix + "first=" + _first);
for (int i = 0; i < BTree.DEFAULT_SIZE; i++) {
if (_isLeaf) {
System.out.println(prefix + "BTreeNode [" + i + "] " + _keys[i] + " " + _values[i]);
} else {
System.out.println(prefix + "BTreeNode [" + i + "] " + _keys[i] + " " + _children[i]);
}
}
System.out.println(prefix + "--------------------------------------");
}
/**
* Recursively dump the state of the BTree on screen. This is used for
* debugging purposes only.
*/
void dumpRecursive(int height, int level)
throws IOException {
height -= 1;
level += 1;
if (height > 0) {
for (byte i = _first; i < BTree.DEFAULT_SIZE; i++) {
if (_keys[i] == null) break;
BTreeNode<K, V> child = loadNode(_children[i]);
child.dump(level);
child.dumpRecursive(height, level);
}
}
}
/**
* Deserialize the content of an object from a byte array.
*/
@SuppressWarnings("unchecked")
public BTreeNode<K, V> deserialize(DataInput ois2)
throws IOException {
DataInputOutput ois = (DataInputOutput) ois2;
BTreeNode<K, V> node = new BTreeNode<K, V>();
switch (ois.readUnsignedByte()) {
case SerializationHeader.BTREE_NODE_LEAF:
node._isLeaf = true;
break;
case SerializationHeader.BTREE_NODE_NONLEAF:
node._isLeaf = false;
break;
default:
throw new InternalError("wrong BTreeNode header");
}
if (node._isLeaf) {
node._previous = LongPacker.unpackLong(ois);
node._next = LongPacker.unpackLong(ois);
}
node._first = ois.readByte();
if (!node._isLeaf) {
node._children = new long[BTree.DEFAULT_SIZE];
for (int i = node._first; i < BTree.DEFAULT_SIZE; i++) {
node._children[i] = LongPacker.unpackLong(ois);
}
}
if (!_btree.loadValues)
return node;
try {
node._keys = readKeys(ois, node._first);
} catch (ClassNotFoundException except) {
throw new IOException(except.getMessage());
}
if (node._isLeaf) {
try {
readValues(ois, node);
} catch (ClassNotFoundException except) {
throw new IOException(except);
}
}
return node;
}
/**
* Serialize the content of an object into a byte array.
*
* @param obj Object to serialize
* @return a byte array representing the object's state
*/
public void serialize(DataOutput oos, BTreeNode<K, V> obj)
throws IOException {
// note: It is assumed that BTreeNode instance doing the serialization is the parent
// of the BTreeNode object being serialized.
BTreeNode<K, V> node = obj;
oos.writeByte(node._isLeaf ? SerializationHeader.BTREE_NODE_LEAF : SerializationHeader.BTREE_NODE_NONLEAF);
if (node._isLeaf) {
LongPacker.packLong(oos, node._previous);
LongPacker.packLong(oos, node._next);
}
oos.write(node._first);
if (!node._isLeaf) {
for (int i = node._first; i < BTree.DEFAULT_SIZE; i++) {
LongPacker.packLong(oos, node._children[i]);
}
}
writeKeys(oos, node._keys, node._first);
if (node._isLeaf && _btree.hasValues()) {
writeValues(oos, node);
}
}
private void readValues(DataInputOutput ois, BTreeNode<K, V> node) throws IOException, ClassNotFoundException {
node._values = new Object[BTree.DEFAULT_SIZE];
if(_btree.hasValues()){
Serializer<V> serializer = _btree.valueSerializer != null ? _btree.valueSerializer : (Serializer<V>) _btree.getRecordManager().defaultSerializer();
for (int i = node._first; i < BTree.DEFAULT_SIZE; i++) {
int header = ois.readUnsignedByte();
if (header == BTreeLazyRecord.NULL) {
node._values[i] = null;
} else if (header == BTreeLazyRecord.LAZY_RECORD) {
long recid = LongPacker.unpackLong(ois);
node._values[i] = new BTreeLazyRecord(_btree._db, recid, serializer);
} else {
node._values[i] = BTreeLazyRecord.fastDeser(ois, serializer, header);
}
}
}else{
//create fake values
for (int i = node._first; i < BTree.DEFAULT_SIZE; i++) {
if(node._keys[i]!=null)
node._values[i] = Utils.EMPTY_STRING;
}
}
}
private void writeValues(DataOutput oos, BTreeNode<K, V> node) throws IOException {
DataInputOutput output = null;
Serializer serializer = _btree.valueSerializer != null ? _btree.valueSerializer : _btree.getRecordManager().defaultSerializer();
for (int i = node._first; i < BTree.DEFAULT_SIZE; i++) {
if (node._values[i] instanceof BTreeLazyRecord) {
oos.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(oos, ((BTreeLazyRecord) node._values[i]).recid);
} else if (node._values[i] != null) {
if (output == null) {
output = new DataInputOutput();
} else {
output.reset();
}
serializer.serialize(output, node._values[i]);
if (output.getPos() > BTreeLazyRecord.MAX_INTREE_RECORD_SIZE) {
//write as separate record
long recid = _btree._db.insert(output.toByteArray(), BTreeLazyRecord.FAKE_SERIALIZER,true);
oos.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(oos, recid);
} else {
//write as part of btree
oos.write(output.getPos());
oos.write(output.getBuf(), 0, output.getPos());
}
} else {
oos.write(BTreeLazyRecord.NULL);
}
}
}
private static final int ALL_NULL = 0;
private static final int ALL_INTEGERS = 1 << 5;
private static final int ALL_INTEGERS_NEGATIVE = 2 << 5;
private static final int ALL_LONGS = 3 << 5;
private static final int ALL_LONGS_NEGATIVE = 4 << 5;
private static final int ALL_STRINGS = 5 << 5;
private static final int ALL_OTHER = 6 << 5;
private K[] readKeys(DataInput ois, final int firstUse) throws IOException, ClassNotFoundException {
Object[] ret = new Object[BTree.DEFAULT_SIZE];
final int type = ois.readUnsignedByte();
if (type == ALL_NULL) {
return (K[]) ret;
} else if (type == ALL_INTEGERS || type == ALL_INTEGERS_NEGATIVE) {
long first = LongPacker.unpackLong(ois);
if (type == ALL_INTEGERS_NEGATIVE)
first = -first;
ret[firstUse] = Integer.valueOf((int) first);
for (int i = firstUse + 1; i < BTree.DEFAULT_SIZE; i++) {
// ret[i] = Serialization.readObject(ois);
long v = LongPacker.unpackLong(ois);
if (v == 0) continue; //null
v = v + first;
ret[i] = Integer.valueOf((int) v);
first = v;
}
return (K[]) ret;
} else if (type == ALL_LONGS || type == ALL_LONGS_NEGATIVE) {
long first = LongPacker.unpackLong(ois);
if (type == ALL_LONGS_NEGATIVE)
first = -first;
ret[firstUse] = Long.valueOf(first);
for (int i = firstUse + 1; i < BTree.DEFAULT_SIZE; i++) {
//ret[i] = Serialization.readObject(ois);
long v = LongPacker.unpackLong(ois);
if (v == 0) continue; //null
v = v + first;
ret[i] = Long.valueOf(v);
first = v;
}
return (K[]) ret;
} else if (type == ALL_STRINGS) {
byte[] previous = null;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
byte[] b = leadingValuePackRead(ois, previous, 0);
if (b == null) continue;
ret[i] = new String(b,Serialization.UTF8);
previous = b;
}
return (K[]) ret;
} else if (type == ALL_OTHER) {
//TODO why this block is here?
if (_btree.keySerializer == null || _btree.keySerializer == _btree.getRecordManager().defaultSerializer()) {
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
ret[i] = _btree.getRecordManager().defaultSerializer().deserialize(ois);
}
return (K[]) ret;
}
Serializer ser = _btree.keySerializer != null ? _btree.keySerializer : _btree.getRecordManager().defaultSerializer();
DataInputOutput in2 = null;
byte[] previous = null;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
byte[] b = leadingValuePackRead(ois, previous, 0);
if (b == null) continue;
if (in2 == null) {
in2 = new DataInputOutput();
}
in2.reset(b);
ret[i] = ser.deserialize(in2);
previous = b;
}
return (K[]) ret;
} else {
throw new InternalError("unknown BTreeNode header type: " + type);
}
}
@SuppressWarnings("unchecked")
private void writeKeys(DataOutput oos, K[] keys, final int firstUse) throws IOException {
if (keys.length != BTree.DEFAULT_SIZE)
throw new IllegalArgumentException("wrong keys size");
//check if all items on key are null
boolean allNull = true;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] != null) {
allNull = false;
break;
}
}
if (allNull) {
oos.write(ALL_NULL);
return;
}
/**
* Special compression to compress Long and Integer
*/
if ((_btree._comparator == Utils.COMPARABLE_COMPARATOR || _btree._comparator == null) &&
(_btree.keySerializer == null || _btree.keySerializer == _btree.getRecordManager().defaultSerializer())) {
boolean allInteger = true;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] != null && keys[i].getClass() != Integer.class) {
allInteger = false;
break;
}
}
boolean allLong = true;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] != null && (keys[i].getClass() != Long.class ||
//special case to exclude Long.MIN_VALUE from conversion, causes problems to LongPacker
((Long) keys[i]).longValue() == Long.MIN_VALUE)
) {
allLong = false;
break;
}
}
if (allLong) {
//check that diff between MIN and MAX fits into PACKED_LONG
long max = Long.MIN_VALUE;
long min = Long.MAX_VALUE;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] == null) continue;
long v = (Long) keys[i];
if (v > max) max = v;
if (v < min) min = v;
}
//now convert to Double to prevent overflow errors
double max2 = max;
double min2 = min;
double maxDiff = Long.MAX_VALUE;
if (max2 - min2 > maxDiff / 2) // divide by two just to by sure
allLong = false;
}
if (allLong && allInteger)
throw new InternalError();
if (allLong || allInteger) {
long first = ((Number) keys[firstUse]).longValue();
//write header
if (allInteger) {
if (first > 0) oos.write(ALL_INTEGERS);
else oos.write(ALL_INTEGERS_NEGATIVE);
} else if (allLong) {
if (first > 0) oos.write(ALL_LONGS);
else oos.write(ALL_LONGS_NEGATIVE);
} else {
throw new InternalError();
}
//write first
LongPacker.packLong(oos, Math.abs(first));
//write others
for (int i = firstUse + 1; i < BTree.DEFAULT_SIZE; i++) {
// Serialization.writeObject(oos, keys[i]);
if (keys[i] == null)
LongPacker.packLong(oos, 0);
else {
long v = ((Number) keys[i]).longValue();
if (v <= first) throw new InternalError("not ordered");
LongPacker.packLong(oos, v - first);
first = v;
}
}
return;
} else {
//another special case for Strings
boolean allString = true;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] != null && (keys[i].getClass() != String.class)
) {
allString = false;
break;
}
}
if (allString) {
oos.write(ALL_STRINGS);
byte[] previous = null;
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] == null) {
leadingValuePackWrite(oos, null, previous, 0);
} else {
byte[] b = ((String) keys[i]).getBytes(Serialization.UTF8);
leadingValuePackWrite(oos, b, previous, 0);
previous = b;
}
}
return;
}
}
}
/**
* other case, serializer is provided or other stuff
*/
oos.write(ALL_OTHER);
if (_btree.keySerializer == null || _btree.keySerializer == _btree.getRecordManager().defaultSerializer()) {
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
_btree.getRecordManager().defaultSerializer().serialize(oos, keys[i]);
}
return;
}
//custom serializer is provided, use it
Serializer ser = _btree.keySerializer;
byte[] previous = null;
DataInputOutput out3 = new DataInputOutput();
for (int i = firstUse; i < BTree.DEFAULT_SIZE; i++) {
if (keys[i] == null) {
leadingValuePackWrite(oos, null, previous, 0);
} else {
out3.reset();
ser.serialize(out3, keys[i]);
byte[] b = out3.toByteArray();
leadingValuePackWrite(oos, b, previous, 0);
previous = b;
}
}
}
public void defrag(DBStore r1, DBStore r2) throws IOException {
if (_children != null)
for (long child : _children) {
if (child == 0) continue;
byte[] data = r1.fetchRaw(child);
r2.forceInsert(child, data);
BTreeNode t = deserialize(new DataInputOutput(data));
t._btree = _btree;
t.defrag(r1, r2);
}
}
/**
* STATIC INNER CLASS
* Result from insert() method call
*/
static final class InsertResult<K, V> {
/**
* Overflow node.
*/
BTreeNode<K, V> _overflow;
/**
* Existing value for the insertion key.
*/
V _existing;
}
/**
* STATIC INNER CLASS
* Result from remove() method call
*/
static final class RemoveResult<K, V> {
/**
* Set to true if underlying nodes underflowed
*/
boolean _underflow;
/**
* Removed entry value
*/
V _value;
}
/**
* PRIVATE INNER CLASS
* Browser to traverse leaf nodes.
*/
static final class Browser<K, V>
implements BTree.BTreeTupleBrowser<K, V> {
/**
* Current node.
*/
private BTreeNode<K, V> _node;
/**
* Current index in the node. The index positionned on the next
* tuple to return.
*/
private byte _index;
private int expectedModCount;
/**
* Create a browser.
*
* @param node Current node
* @param index Position of the next tuple to return.
*/
Browser(BTreeNode<K, V> node, byte index) {
_node = node;
_index = index;
expectedModCount = node._btree.modCount;
}
public boolean getNext(BTree.BTreeTuple<K, V> tuple)
throws IOException {
if (expectedModCount != _node._btree.modCount)
throw new ConcurrentModificationException();
if (_node == null) {
//last record in iterator was deleted, so iterator is at end of node
return false;
}
if (_index < BTree.DEFAULT_SIZE) {
if (_node._keys[_index] == null) {
// reached end of the tree.
return false;
}
} else if (_node._next != 0) {
// move to next node
_node = _node.loadNode(_node._next);
_index = _node._first;
}
tuple.key = _node._keys[_index];
if (_node._values[_index] instanceof BTreeLazyRecord)
tuple.value = ((BTreeLazyRecord<V>) _node._values[_index]).get();
else
tuple.value = (V) _node._values[_index];
_index++;
return true;
}
public boolean getPrevious(BTree.BTreeTuple<K, V> tuple)
throws IOException {
if (expectedModCount != _node._btree.modCount)
throw new ConcurrentModificationException();
if (_node == null) {
//deleted last record, but this situation is only supportedd on getNext
throw new InternalError();
}
if (_index == _node._first) {
if (_node._previous != 0) {
_node = _node.loadNode(_node._previous);
_index = BTree.DEFAULT_SIZE;
} else {
// reached beginning of the tree
return false;
}
}
_index--;
tuple.key = _node._keys[_index];
if (_node._values[_index] instanceof BTreeLazyRecord)
tuple.value = ((BTreeLazyRecord<V>) _node._values[_index]).get();
else
tuple.value = (V) _node._values[_index];
return true;
}
public void remove(K key) throws IOException {
if (expectedModCount != _node._btree.modCount)
throw new ConcurrentModificationException();
_node._btree.remove(key);
expectedModCount++;
//An entry was removed and this may trigger tree rebalance,
//This would change current node layout, so find our position again
BTree.BTreeTupleBrowser b = _node._btree.browse(key,true);
//browser is positioned just before value which was currently deleted, so find if we have new value
if (b.getNext(new BTree.BTreeTuple(null, null))) {
//next value value exists, copy its state
Browser b2 = (Browser) b;
this._node = b2._node;
this._index = b2._index;
} else {
this._node = null;
this._index = -1;
}
}
}
/**
* Used for debugging and testing only. Recursively obtains the recids of
* all child BTreeNodes and adds them to the 'out' list.
*
* @param out
* @param height
* @throws IOException
*/
void dumpChildNodeRecIDs(List out, int height)
throws IOException {
height -= 1;
if (height > 0) {
for (byte i = _first; i < BTree.DEFAULT_SIZE; i++) {
if (_children[i] == 0) continue;
BTreeNode child = loadNode(_children[i]);
out.add(new Long(child._recid));
child.dumpChildNodeRecIDs(out, height);
}
}
}
/**
* Read previously written data
*
* @author Kevin Day
*/
static byte[] leadingValuePackRead(DataInput in, byte[] previous, int ignoreLeadingCount) throws IOException {
int len = LongPacker.unpackInt(in) - 1; // 0 indicates null
if (len == -1)
return null;
int actualCommon = LongPacker.unpackInt(in);
byte[] buf = new byte[len];
if (previous == null) {
actualCommon = 0;
}
if (actualCommon > 0) {
in.readFully(buf, 0, ignoreLeadingCount);
System.arraycopy(previous, ignoreLeadingCount, buf, ignoreLeadingCount, actualCommon - ignoreLeadingCount);
}
in.readFully(buf, actualCommon, len - actualCommon);
return buf;
}
/**
* This method is used for delta compression for keys.
* Writes the contents of buf to the DataOutput out, with special encoding if
* there are common leading bytes in the previous group stored by this compressor.
*
* @author Kevin Day
*/
static void leadingValuePackWrite(DataOutput out, byte[] buf, byte[] previous, int ignoreLeadingCount) throws IOException {
if (buf == null) {
LongPacker.packInt(out, 0);
return;
}
int actualCommon = ignoreLeadingCount;
if (previous != null) {
int maxCommon = buf.length > previous.length ? previous.length : buf.length;
if (maxCommon > Short.MAX_VALUE) maxCommon = Short.MAX_VALUE;
for (; actualCommon < maxCommon; actualCommon++) {
if (buf[actualCommon] != previous[actualCommon])
break;
}
}
// there are enough common bytes to justify compression
LongPacker.packInt(out, buf.length + 1);// store as +1, 0 indicates null
LongPacker.packInt(out, actualCommon);
out.write(buf, 0, ignoreLeadingCount);
out.write(buf, actualCommon, buf.length - actualCommon);
}
BTreeNode<K, V> loadLastChildNode() throws IOException {
return loadNode(_children[BTree.DEFAULT_SIZE - 1]);
}
}