Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2012 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.
*/
/*
* NOTE: some code (and javadoc) used in this class
* comes from Apache Harmony with following copyright:
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
*/
package org.mapdb;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.LockSupport;
/**
* A scalable concurrent {@link ConcurrentNavigableMap} implementation.
* The map is sorted according to the {@linkplain Comparable natural
* ordering} of its keys, or by a {@link Comparator} provided at map
* creation time.
*
* Insertion, removal,
* update, and access operations safely execute concurrently by
* multiple threads. Iterators are weakly consistent, returning
* elements reflecting the state of the map at some point at or since
* the creation of the iterator. They do not throw {@link
* ConcurrentModificationException}, and may proceed concurrently with
* other operations. Ascending key ordered views and their iterators
* are faster than descending ones.
*
* It is possible to obtain consistent iterator by using snapshot()
* method.
*
* All Map.Entry pairs returned by methods in this class
* and its views represent snapshots of mappings at the time they were
* produced. They do not support the Entry.setValue
* method. (Note however that it is possible to change mappings in the
* associated map using put, putIfAbsent, or
* replace, depending on exactly which effect you need.)
*
* This collection has optional size counter. If this is enabled Map size is
* kept in {@link Atomic.Long} variable. Keeping counter brings considerable
* overhead on inserts and removals.
* If the size counter is not enabled the size method is not a constant-time operation.
* Determining the current number of elements requires a traversal of the elements.
*
* Additionally, the bulk operations putAll, equals, and
* clear are not guaranteed to be performed
* atomically. For example, an iterator operating concurrently with a
* putAll operation might view only some of the added
* elements. NOTE: there is an optional
*
* This class and its views and iterators implement all of the
* optional methods of the {@link Map} and {@link Iterator}
* interfaces. Like most other concurrent collections, this class does
* not permit the use of null keys or values because some
* null return values cannot be reliably distinguished from the absence of
* elements.
*
* Theoretical design of BTreeMap is based on paper
* from Philip L. Lehman and S. Bing Yao. More practical aspects of BTreeMap implementation are based on notes
* and demo application from Thomas Dinsdale-Young.
* B-Linked-Tree used here does not require locking for read. Updates and inserts locks only one, two or three nodes.
* This B-Linked-Tree structure does not support removal well, entry deletion does not collapse tree nodes. Massive
* deletion causes empty nodes and performance lost. There is workaround in form of compaction process, but it is not
* implemented yet.
*
* @author Jan Kotek
* @author some parts by Doug Lea and JSR-166 group
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class BTreeMap extends AbstractMap
implements ConcurrentNavigableMap, Bind.MapWithModificationListener{
@SuppressWarnings("rawtypes")
public static final Comparator COMPARABLE_COMPARATOR = new Comparator() {
@Override
public int compare(Comparable o1, Comparable o2) {
return o1.compareTo(o2);
}
};
protected static final Object EMPTY = new Object();
protected static final int B_TREE_NODE_LEAF_LR = 180;
protected static final int B_TREE_NODE_LEAF_L = 181;
protected static final int B_TREE_NODE_LEAF_R = 182;
protected static final int B_TREE_NODE_LEAF_C = 183;
protected static final int B_TREE_NODE_DIR_LR = 184;
protected static final int B_TREE_NODE_DIR_L = 185;
protected static final int B_TREE_NODE_DIR_R = 186;
protected static final int B_TREE_NODE_DIR_C = 187;
/** recid under which reference to rootRecid is stored */
protected final long rootRecidRef;
/** Serializer used to convert keys from/into binary form. */
protected final BTreeKeySerializer keySerializer;
/** Serializer used to convert keys from/into binary form*/
protected final Serializer valueSerializer;
/** keys are sorted by this*/
protected final Comparator comparator;
/** holds node level locks*/
protected final LongConcurrentHashMap nodeLocks = new LongConcurrentHashMap();
/** maximal node size allowed in this BTree*/
protected final int maxNodeSize;
/** DB Engine in which entries are persisted */
protected final Engine engine;
/** is this a Map or Set? if false, entries do not have values, only keys are allowed*/
protected final boolean hasValues;
/** store values as part of BTree nodes */
protected final boolean valsOutsideNodes;
protected final List leftEdges;
private final KeySet keySet;
private final EntrySet entrySet = new EntrySet(this);
private final Values values = new Values(this);
private final ConcurrentNavigableMap descendingMap = new DescendingMap(this, null,true, null, false);
protected final Atomic.Long counter;
protected final int numberOfNodeMetas;
/** hack used for DB Catalog*/
protected static SortedMap preinitCatalog(DB db) {
Long rootRef = db.getEngine().get(Engine.CATALOG_RECID, Serializer.LONG);
if(rootRef==null){
if(db.getEngine().isReadOnly())
return Collections.unmodifiableSortedMap(new TreeMap());
NodeSerializer rootSerializer = new NodeSerializer(false,BTreeKeySerializer.STRING,
db.getDefaultSerializer(),COMPARABLE_COMPARATOR, 0);
BNode root = new LeafNode(new Object[]{null, null}, new Object[]{}, 0);
rootRef = db.getEngine().put(root, rootSerializer);
db.getEngine().update(Engine.CATALOG_RECID,rootRef, Serializer.LONG);
db.getEngine().commit();
}
return new BTreeMap(db.engine,Engine.CATALOG_RECID,32,false,0,
BTreeKeySerializer.STRING,
db.getDefaultSerializer(),
COMPARABLE_COMPARATOR,0);
}
/** if valsOutsideNodes is true, this class is used instead of values.
* It contains reference to actual value. It also supports assertions from preventing it to leak outside of Map*/
protected static final class ValRef{
/** reference to actual value */
final long recid;
public ValRef(long recid) {
this.recid = recid;
}
@Override
public boolean equals(Object obj) {
throw new IllegalAccessError();
}
@Override
public int hashCode() {
throw new IllegalAccessError();
}
@Override
public String toString() {
return "BTreeMap-ValRer["+recid+"]";
}
}
/** common interface for BTree node */
protected interface BNode{
boolean isLeaf();
Object[] keys();
Object[] vals();
Object highKey();
long[] child();
long next();
}
protected final static class DirNode implements BNode{
final Object[] keys;
final long[] child;
DirNode(Object[] keys, long[] child) {
this.keys = keys;
this.child = child;
}
DirNode(Object[] keys, List child) {
this.keys = keys;
this.child = new long[child.size()];
for(int i=0;i nodeSerializer;
protected static class NodeSerializer implements Serializer{
protected final boolean hasValues;
protected final boolean valsOutsideNodes;
protected final BTreeKeySerializer keySerializer;
protected final Serializer valueSerializer;
protected final Comparator comparator;
protected final int numberOfNodeMetas;
public NodeSerializer(boolean valsOutsideNodes, BTreeKeySerializer keySerializer, Serializer valueSerializer, Comparator comparator, int numberOfNodeMetas) {
assert(keySerializer!=null);
assert(comparator!=null);
this.hasValues = valueSerializer!=null;
this.valsOutsideNodes = valsOutsideNodes;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.comparator = comparator;
this.numberOfNodeMetas = numberOfNodeMetas;
}
@Override
public void serialize(DataOutput out, BNode value) throws IOException {
final boolean isLeaf = value.isLeaf();
//first byte encodes if is leaf (first bite) and length (last seven bites)
assert(value.keys().length<=255);
assert(!(!isLeaf && value.child().length!= value.keys().length));
assert(!(isLeaf && hasValues && value.vals().length!= value.keys().length-2));
assert(!(!isLeaf && value.highKey()!=null && value.child()[value.child().length-1]==0));
//check node integrity in paranoid mode
if(CC.PARANOID){
int len = value.keys().length;
for(int i=value.keys()[0]==null?2:1;
i<(value.keys()[len-1]==null?len-1:len);
i++){
int comp = comparator.compare(value.keys()[i-1], value.keys()[i]);
int limit = i==len-1 ? 1:0 ;
if(comp>=limit){
throw new AssertionError("BTreeNode format error, wrong key order at #"+i+"\n"+value);
}
}
}
final boolean left = value.keys()[0] == null;
final boolean right = value.keys()[value.keys().length-1] == null;
final int header;
if(isLeaf){
if(right){
if(left)
header = B_TREE_NODE_LEAF_LR;
else
header = B_TREE_NODE_LEAF_R;
}else{
if(left)
header = B_TREE_NODE_LEAF_L;
else
header = B_TREE_NODE_LEAF_C;
}
}else{
if(right){
if(left)
header = B_TREE_NODE_DIR_LR;
else
header = B_TREE_NODE_DIR_R;
}else{
if(left)
header = B_TREE_NODE_DIR_L;
else
header = B_TREE_NODE_DIR_C;
}
}
out.write(header);
out.write(value.keys().length);
//write node metas, right now this is ignored, but in future it could be used for counted btrees or aggregations
for(int i=0;i keySerializer, Serializer valueSerializer, Comparator comparator,
int numberOfNodeMetas) {
if(maxNodeSize%2!=0) throw new IllegalArgumentException("maxNodeSize must be dividable by 2");
if(maxNodeSize<6) throw new IllegalArgumentException("maxNodeSize too low");
if(maxNodeSize>126) throw new IllegalArgumentException("maxNodeSize too high");
if(rootRecidRef<=0||counterRecid<0 || numberOfNodeMetas<0) throw new IllegalArgumentException();
if(keySerializer==null) throw new NullPointerException();
if(comparator==null) throw new NullPointerException();
SerializerBase.assertSerializable(keySerializer);
SerializerBase.assertSerializable(valueSerializer);
SerializerBase.assertSerializable(comparator);
this.rootRecidRef = rootRecidRef;
this.hasValues = valueSerializer!=null;
this.valsOutsideNodes = valsOutsideNodes;
this.engine = engine;
this.maxNodeSize = maxNodeSize;
this.comparator = comparator;
this.numberOfNodeMetas = numberOfNodeMetas;
{
Comparator requiredComparator = keySerializer.getComparator();
if(requiredComparator!=null && !requiredComparator.equals(comparator))
throw new IllegalArgumentException("KeySerializers requires its own comparator");
}
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.nodeSerializer = new NodeSerializer(valsOutsideNodes,keySerializer,valueSerializer,comparator,numberOfNodeMetas);
this.keySet = new KeySet(this, hasValues);
if(counterRecid!=0){
this.counter = new Atomic.Long(engine,counterRecid);
Bind.size(this,counter);
}else{
this.counter = null;
}
//load left edge refs
ArrayList leftEdges2 = new ArrayList();
long r = engine.get(rootRecidRef,Serializer.LONG);
for(;;){
BNode n= engine.get(r,nodeSerializer);
leftEdges2.add(r);
if(n.isLeaf()) break;
r = n.child()[0];
}
Collections.reverse(leftEdges2);
leftEdges = new CopyOnWriteArrayList(leftEdges2);
}
/** creates empty root node and returns recid of its reference*/
static protected long createRootRef(Engine engine, BTreeKeySerializer keySer, Serializer valueSer, Comparator comparator, int numberOfNodeMetas){
final LeafNode emptyRoot = new LeafNode(new Object[]{null, null}, new Object[]{}, 0);
//empty root is serializer simpler way, so we can use dummy values
long rootRecidVal = engine.put(emptyRoot, new NodeSerializer(false,keySer, valueSer, comparator, numberOfNodeMetas));
return engine.put(rootRecidVal,Serializer.LONG);
}
/**
* Find the first children node with a key equal or greater than the given key.
* If all items are smaller it returns `keys.length`
*/
protected final int findChildren(final Object key, final Object[] keys) {
int left = 0;
if(keys[0] == null) left++;
int right = keys[keys.length-1] == null ? keys.length-1 : keys.length;
int middle;
// binary search
while (true) {
middle = (left + right) / 2;
if(keys[middle]==null) return middle; //null is positive infinitive
if (comparator.compare(keys[middle], key) < 0) {
left = middle + 1;
} else {
right = middle;
}
if (left >= right) {
return right;
}
}
}
@Override
public V get(Object key){
if(key==null) throw new NullPointerException();
K v = (K) key;
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode A = engine.get(current, nodeSerializer);
//dive until leaf
while(!A.isLeaf()){
current = nextDir((DirNode) A, v);
A = engine.get(current, nodeSerializer);
}
//now at leaf level
LeafNode leaf = (LeafNode) A;
int pos = findChildren(v, leaf.keys);
while(pos == leaf.keys.length){
//follow next link on leaf until necessary
leaf = (LeafNode) engine.get(leaf.next, nodeSerializer);
pos = findChildren(v, leaf.keys);
}
if(pos==leaf.keys.length-1){
return null; //last key is always deleted
}
//finish search
if(leaf.keys[pos]!=null && 0==comparator.compare(v,leaf.keys[pos])){
Object ret = leaf.vals[pos-1];
return valExpand(ret);
}else
return null;
}
protected V valExpand(Object ret) {
if(valsOutsideNodes && ret!=null) {
long recid = ((ValRef)ret).recid;
ret = engine.get(recid, valueSerializer);
}
return (V) ret;
}
protected long nextDir(DirNode d, Object key) {
int pos = findChildren(key, d.keys) - 1;
if(pos<0) pos = 0;
return d.child[pos];
}
@Override
public V put(K key, V value){
if(key==null||value==null) throw new NullPointerException();
return put2(key,value, false);
}
protected V put2(K v, V value2, final boolean putOnlyIfAbsent){
if(v == null) throw new IllegalArgumentException("null key");
if(value2 == null) throw new IllegalArgumentException("null value");
V value = value2;
if(valsOutsideNodes){
long recid = engine.put(value2, valueSerializer);
value = (V) new ValRef(recid);
}
int stackPos = -1;
long[] stackVals = new long[4];
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode A = engine.get(current, nodeSerializer);
while(!A.isLeaf()){
long t = current;
current = nextDir((DirNode) A, v);
assert(current>0) : A;
if(current == A.child()[A.child().length-1]){
//is link, do nothing
}else{
//stack push t
stackPos++;
if(stackVals.length == stackPos) //grow if needed
stackVals = Arrays.copyOf(stackVals, stackVals.length*2);
stackVals[stackPos] = t;
}
A = engine.get(current, nodeSerializer);
}
int level = 1;
long p=0;
try{
while(true){
boolean found;
do{
lock(nodeLocks, current);
found = true;
A = engine.get(current, nodeSerializer);
int pos = findChildren(v, A.keys());
//check if keys is already in tree
if(pos highvalue(a)
if(A.highKey() != null && comparator.compare(v, A.highKey())>0){
//follow link until necessary
unlock(nodeLocks, current);
found = false;
int pos2 = findChildren(v, A.keys());
while(A!=null && pos2 == A.keys().length){
//TODO lock?
long next = A.next();
if(next==0) break;
current = next;
A = engine.get(current, nodeSerializer);
pos2 = findChildren(v, A.keys());
}
}
}while(!found);
// can be new item inserted into A without splitting it?
if(A.keys().length - (A.isLeaf()?2:1)0);
}else{
BNode R = new DirNode(
new Object[]{A.keys()[0], A.highKey(), B.isLeaf()?null:B.highKey()},
new long[]{current,q, 0});
lock(nodeLocks, rootRecidRef);
unlock(nodeLocks, current);
long newRootRecid = engine.put(R, nodeSerializer);
assert(nodeLocks.get(rootRecidRef)==Thread.currentThread());
engine.update(rootRecidRef, newRootRecid, Serializer.LONG);
//add newRootRecid into leftEdges
leftEdges.add(newRootRecid);
notify(v, null, value2);
unlock(nodeLocks, rootRecidRef);
if(CC.PARANOID) assertNoLocks(nodeLocks);
return null;
}
}
}
}catch(RuntimeException e){
unlockAll(nodeLocks);
throw e;
}catch(Exception e){
unlockAll(nodeLocks);
throw new RuntimeException(e);
}
}
protected static class BTreeIterator{
final BTreeMap m;
LeafNode currentLeaf;
Object lastReturnedKey;
int currentPos;
final Object hi;
final boolean hiInclusive;
/** unbounded iterator*/
BTreeIterator(BTreeMap m){
this.m = m;
hi=null;
hiInclusive=false;
pointToStart();
}
/** bounder iterator, args may be null for partially bounded*/
BTreeIterator(BTreeMap m, Object lo, boolean loInclusive, Object hi, boolean hiInclusive){
this.m = m;
if(lo==null){
pointToStart();
}else{
Fun.Tuple2 l = m.findLargerNode(lo, loInclusive);
currentPos = l!=null? l.a : -1;
currentLeaf = l!=null ? l.b : null;
}
this.hi = hi;
this.hiInclusive = hiInclusive;
if(hi!=null && currentLeaf!=null){
//check in bounds
Object key = currentLeaf.keys[currentPos];
int c = m.comparator.compare(key, hi);
if (c > 0 || (c == 0 && !hiInclusive)){
//out of high bound
currentLeaf=null;
currentPos=-1;
}
}
}
private void pointToStart() {
//find left-most leaf
final long rootRecid = m.engine.get(m.rootRecidRef, Serializer.LONG);
BNode node = (BNode) m.engine.get(rootRecid, m.nodeSerializer);
while(!node.isLeaf()){
node = (BNode) m.engine.get(node.child()[0], m.nodeSerializer);
}
currentLeaf = (LeafNode) node;
currentPos = 1;
while(currentLeaf.keys.length==2){
//follow link until leaf is not empty
if(currentLeaf.next == 0){
currentLeaf = null;
return;
}
currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer);
}
}
public boolean hasNext(){
return currentLeaf!=null;
}
public void remove(){
if(lastReturnedKey==null) throw new IllegalStateException();
m.remove(lastReturnedKey);
lastReturnedKey = null;
}
protected void advance(){
if(currentLeaf==null) return;
lastReturnedKey = currentLeaf.keys[currentPos];
currentPos++;
if(currentPos == currentLeaf.keys.length-1){
//move to next leaf
if(currentLeaf.next==0){
currentLeaf = null;
currentPos=-1;
return;
}
currentPos = 1;
currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer);
while(currentLeaf.keys.length==2){
if(currentLeaf.next ==0){
currentLeaf = null;
currentPos=-1;
return;
}
currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer);
}
}
if(hi!=null && currentLeaf!=null){
//check in bounds
Object key = currentLeaf.keys[currentPos];
int c = m.comparator.compare(key, hi);
if (c > 0 || (c == 0 && !hiInclusive)){
//out of high bound
currentLeaf=null;
currentPos=-1;
}
}
}
}
@Override
public V remove(Object key) {
return remove2(key, null);
}
private V remove2(Object key, Object value) {
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode A = engine.get(current, nodeSerializer);
while(!A.isLeaf()){
current = nextDir((DirNode) A, key);
A = engine.get(current, nodeSerializer);
}
try{
while(true){
lock(nodeLocks, current);
A = engine.get(current, nodeSerializer);
int pos = findChildren(key, A.keys());
if(pos0){
int pos2 = findChildren(key, A.keys());
while(pos2 == A.keys().length){
//TODO lock?
current = ((LeafNode)A).next;
A = engine.get(current, nodeSerializer);
}
}else{
return null;
}
}
}
}catch(RuntimeException e){
unlockAll(nodeLocks);
throw e;
}catch(Exception e){
unlockAll(nodeLocks);
throw new RuntimeException(e);
}
}
@Override
public void clear() {
Iterator iter = keyIterator();
while(iter.hasNext()){
iter.next();
iter.remove();
}
}
static class BTreeKeyIterator extends BTreeIterator implements Iterator{
BTreeKeyIterator(BTreeMap m) {
super(m);
}
BTreeKeyIterator(BTreeMap m, Object lo, boolean loInclusive, Object hi, boolean hiInclusive) {
super(m, lo, loInclusive, hi, hiInclusive);
}
@Override
public K next() {
if(currentLeaf == null) throw new NoSuchElementException();
K ret = (K) currentLeaf.keys[currentPos];
advance();
return ret;
}
}
static class BTreeValueIterator extends BTreeIterator implements Iterator{
BTreeValueIterator(BTreeMap m) {
super(m);
}
BTreeValueIterator(BTreeMap m, Object lo, boolean loInclusive, Object hi, boolean hiInclusive) {
super(m, lo, loInclusive, hi, hiInclusive);
}
@Override
public V next() {
if(currentLeaf == null) throw new NoSuchElementException();
Object ret = currentLeaf.vals[currentPos-1];
advance();
return (V) m.valExpand(ret);
}
}
static class BTreeEntryIterator extends BTreeIterator implements Iterator>{
BTreeEntryIterator(BTreeMap m) {
super(m);
}
BTreeEntryIterator(BTreeMap m, Object lo, boolean loInclusive, Object hi, boolean hiInclusive) {
super(m, lo, loInclusive, hi, hiInclusive);
}
@Override
public Entry next() {
if(currentLeaf == null) throw new NoSuchElementException();
K ret = (K) currentLeaf.keys[currentPos];
Object val = currentLeaf.vals[currentPos-1];
advance();
return m.makeEntry(ret, m.valExpand(val));
}
}
protected Entry makeEntry(Object key, Object value) {
assert(!(value instanceof ValRef));
return new SimpleImmutableEntry((K)key, (V)value);
}
@Override
public boolean isEmpty() {
return !keyIterator().hasNext();
}
@Override
public int size() {
long size = sizeLong();
if(size>Integer.MAX_VALUE) return Integer.MAX_VALUE;
return (int) size;
}
@Override
public long sizeLong() {
if(counter!=null)
return counter.get();
long size = 0;
BTreeIterator iter = new BTreeIterator(this);
while(iter.hasNext()){
iter.advance();
size++;
}
return size;
}
@Override
public V putIfAbsent(K key, V value) {
if(key == null || value == null) throw new NullPointerException();
return put2(key, value, true);
}
@Override
public boolean remove(Object key, Object value) {
if(key == null) throw new NullPointerException();
if(value == null) return false;
return remove2(key, value)!=null;
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
if(key == null || oldValue == null || newValue == null ) throw new NullPointerException();
long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode node = engine.get(current, nodeSerializer);
//dive until leaf is found
while(!node.isLeaf()){
current = nextDir((DirNode) node, key);
node = engine.get(current, nodeSerializer);
}
lock(nodeLocks, current);
LeafNode leaf = (LeafNode) engine.get(current, nodeSerializer);
int pos = findChildren(key, node.keys());
try{
while(pos==leaf.keys.length){
//follow leaf link until necessary
lock(nodeLocks, leaf.next);
unlock(nodeLocks, current);
current = leaf.next;
leaf = (LeafNode) engine.get(current, nodeSerializer);
pos = findChildren(key, node.keys());
}
boolean ret = false;
if( key!=null && leaf.keys()[pos]!=null &&
0==comparator.compare(key,leaf.keys[pos])){
Object val = leaf.vals[pos-1];
val = valExpand(val);
if(oldValue.equals(val)){
Object[] vals = Arrays.copyOf(leaf.vals, leaf.vals.length);
notify(key, oldValue, newValue);
if(valsOutsideNodes){
long recid = engine.put(newValue, valueSerializer);
newValue = (V) new ValRef(recid);
}
vals[pos-1] = newValue;
leaf = new LeafNode(Arrays.copyOf(leaf.keys, leaf.keys.length), vals, leaf.next);
assert(nodeLocks.get(current)==Thread.currentThread());
engine.update(current, leaf, nodeSerializer);
ret = true;
}
}
unlock(nodeLocks, current);
return ret;
}catch(RuntimeException e){
unlockAll(nodeLocks);
throw e;
}catch(Exception e){
unlockAll(nodeLocks);
throw new RuntimeException(e);
}
}
@Override
public V replace(K key, V value) {
if(key == null || value == null) throw new NullPointerException();
final long rootRecid = engine.get(rootRecidRef,Serializer.LONG);
long current = rootRecid;
BNode node = engine.get(current, nodeSerializer);
//dive until leaf is found
while(!node.isLeaf()){
current = nextDir((DirNode) node, key);
node = engine.get(current, nodeSerializer);
}
lock(nodeLocks, current);
LeafNode leaf = (LeafNode) engine.get(current, nodeSerializer);
try{
int pos = findChildren(key, node.keys());
while(pos==leaf.keys.length){
//follow leaf link until necessary
lock(nodeLocks, leaf.next);
unlock(nodeLocks, current);
current = leaf.next;
leaf = (LeafNode) engine.get(current, nodeSerializer);
pos = findChildren(key, node.keys());
}
Object ret = null;
if( key!=null && leaf.keys()[pos]!=null &&
0==comparator.compare(key,leaf.keys[pos])){
Object[] vals = Arrays.copyOf(leaf.vals, leaf.vals.length);
Object oldVal = vals[pos-1];
ret = valExpand(oldVal);
notify(key, (V)ret, value);
if(valsOutsideNodes && value!=null){
long recid = engine.put(value, valueSerializer);
value = (V) new ValRef(recid);
}
vals[pos-1] = value;
leaf = new LeafNode(Arrays.copyOf(leaf.keys, leaf.keys.length), vals, leaf.next);
assert(nodeLocks.get(current)==Thread.currentThread());
engine.update(current, leaf, nodeSerializer);
}
unlock(nodeLocks, current);
return (V)ret;
}catch(RuntimeException e){
unlockAll(nodeLocks);
throw e;
}catch(Exception e){
unlockAll(nodeLocks);
throw new RuntimeException(e);
}
}
@Override
public Comparator super K> comparator() {
return comparator;
}
@Override
public Map.Entry firstEntry() {
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
BNode n = engine.get(rootRecid, nodeSerializer);
while(!n.isLeaf()){
n = engine.get(n.child()[0], nodeSerializer);
}
LeafNode l = (LeafNode) n;
//follow link until necessary
while(l.keys.length==2){
if(l.next==0) return null;
l = (LeafNode) engine.get(l.next, nodeSerializer);
}
return makeEntry(l.keys[1], valExpand(l.vals[0]));
}
@Override
public Entry pollFirstEntry() {
while(true){
Entry e = firstEntry();
if(e==null || remove(e.getKey(),e.getValue())){
return e;
}
}
}
@Override
public Entry pollLastEntry() {
while(true){
Entry e = lastEntry();
if(e==null || remove(e.getKey(),e.getValue())){
return e;
}
}
}
protected Entry findSmaller(K key,boolean inclusive){
if(key==null) throw new NullPointerException();
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
BNode n = engine.get(rootRecid, nodeSerializer);
Entry k = findSmallerRecur(n, key, inclusive);
if(k==null || (k.getValue()==null)) return null;
return k;
}
private Entry findSmallerRecur(BNode n, K key, boolean inclusive) {
final boolean leaf = n.isLeaf();
final int start = leaf ? n.keys().length-2 : n.keys().length-1;
final int end = leaf?1:0;
final int res = inclusive? 1 : 0;
for(int i=start;i>=end; i--){
final Object key2 = n.keys()[i];
int comp = (key2==null)? -1 : comparator.compare(key2, key);
if(comp ret = findSmallerRecur(n2, key, inclusive);
if(ret!=null) return ret;
}
}
}
return null;
}
@Override
public Map.Entry lastEntry() {
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
BNode n = engine.get(rootRecid, nodeSerializer);
Entry e = lastEntryRecur(n);
if(e!=null && e.getValue()==null) return null;
return e;
}
private Map.Entry lastEntryRecur(BNode n){
if(n.isLeaf()){
//follow next node if available
if(n.next()!=0){
BNode n2 = engine.get(n.next(), nodeSerializer);
Map.Entry ret = lastEntryRecur(n2);
if(ret!=null)
return ret;
}
//iterate over keys to find last non null key
for(int i=n.keys().length-2; i>0;i--){
Object k = n.keys()[i];
if(k!=null && n.vals().length>0) {
Object val = valExpand(n.vals()[i-1]);
if(val!=null){
return makeEntry(k, val);
}
}
}
}else{
//dir node, dive deeper
for(int i=n.child().length-1; i>=0;i--){
long childRecid = n.child()[i];
if(childRecid==0) continue;
BNode n2 = engine.get(childRecid, nodeSerializer);
Entry ret = lastEntryRecur(n2);
if(ret!=null)
return ret;
}
}
return null;
}
@Override
public Map.Entry lowerEntry(K key) {
if(key==null) throw new NullPointerException();
return findSmaller(key, false);
}
@Override
public K lowerKey(K key) {
Entry n = lowerEntry(key);
return (n == null)? null : n.getKey();
}
@Override
public Map.Entry floorEntry(K key) {
if(key==null) throw new NullPointerException();
return findSmaller(key, true);
}
@Override
public K floorKey(K key) {
Entry n = floorEntry(key);
return (n == null)? null : n.getKey();
}
@Override
public Map.Entry ceilingEntry(K key) {
if(key==null) throw new NullPointerException();
return findLarger(key, true);
}
protected Entry findLarger(K key, boolean inclusive) {
if(key==null) return null;
K v = (K) key;
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode A = engine.get(current, nodeSerializer);
//dive until leaf
while(!A.isLeaf()){
current = nextDir((DirNode) A, v);
A = engine.get(current, nodeSerializer);
}
//now at leaf level
LeafNode leaf = (LeafNode) A;
//follow link until first matching node is found
final int comp = inclusive?1:0;
while(true){
for(int i=1;i findLargerNode(K key, boolean inclusive) {
if(key==null) return null;
K v = (K) key;
final long rootRecid = engine.get(rootRecidRef, Serializer.LONG);
long current = rootRecid;
BNode A = engine.get(current, nodeSerializer);
//dive until leaf
while(!A.isLeaf()){
current = nextDir((DirNode) A, v);
A = engine.get(current, nodeSerializer);
}
//now at leaf level
LeafNode leaf = (LeafNode) A;
//follow link until first matching node is found
final int comp = inclusive?1:0;
while(true){
for(int i=1;i n = ceilingEntry(key);
return (n == null)? null : n.getKey();
}
@Override
public Map.Entry higherEntry(K key) {
if(key==null) throw new NullPointerException();
return findLarger(key, false);
}
@Override
public K higherKey(K key) {
if(key==null) throw new NullPointerException();
Entry n = higherEntry(key);
return (n == null)? null : n.getKey();
}
@Override
public boolean containsKey(Object key) {
if(key==null) throw new NullPointerException();
return get(key)!=null;
}
@Override
public boolean containsValue(Object value){
if(value ==null) throw new NullPointerException();
Iterator valueIter = valueIterator();
while(valueIter.hasNext()){
if(value.equals(valueIter.next()))
return true;
}
return false;
}
@Override
public K firstKey() {
Entry e = firstEntry();
if(e==null) throw new NoSuchElementException();
return e.getKey();
}
@Override
public K lastKey() {
Entry e = lastEntry();
if(e==null) throw new NoSuchElementException();
return e.getKey();
}
@Override
public ConcurrentNavigableMap subMap(K fromKey,
boolean fromInclusive,
K toKey,
boolean toInclusive) {
if (fromKey == null || toKey == null)
throw new NullPointerException();
return new SubMap
( this, fromKey, fromInclusive, toKey, toInclusive);
}
@Override
public ConcurrentNavigableMap headMap(K toKey,
boolean inclusive) {
if (toKey == null)
throw new NullPointerException();
return new SubMap
(this, null, false, toKey, inclusive);
}
@Override
public ConcurrentNavigableMap tailMap(K fromKey,
boolean inclusive) {
if (fromKey == null)
throw new NullPointerException();
return new SubMap
(this, fromKey, inclusive, null, false);
}
@Override
public ConcurrentNavigableMap subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
@Override
public ConcurrentNavigableMap headMap(K toKey) {
return headMap(toKey, false);
}
@Override
public ConcurrentNavigableMap tailMap(K fromKey) {
return tailMap(fromKey, true);
}
Iterator keyIterator() {
return new BTreeKeyIterator(this);
}
Iterator valueIterator() {
return new BTreeValueIterator(this);
}
Iterator> entryIterator() {
return new BTreeEntryIterator(this);
}
/* ---------------- View methods -------------- */
@Override
public NavigableSet keySet() {
return keySet;
}
@Override
public NavigableSet navigableKeySet() {
return keySet;
}
@Override
public Collection values() {
return values;
}
@Override
public Set> entrySet() {
return entrySet;
}
@Override
public ConcurrentNavigableMap descendingMap() {
return descendingMap;
}
@Override
public NavigableSet descendingKeySet() {
return descendingMap.keySet();
}
static final List toList(Collection c) {
// Using size() here would be a pessimization.
List list = new ArrayList();
for (E e : c){
list.add(e);
}
return list;
}
static final class KeySet extends AbstractSet implements NavigableSet {
protected final ConcurrentNavigableMap m;
private final boolean hasValues;
KeySet(ConcurrentNavigableMap map, boolean hasValues) {
m = map;
this.hasValues = hasValues;
}
@Override
public int size() { return m.size(); }
@Override
public boolean isEmpty() { return m.isEmpty(); }
@Override
public boolean contains(Object o) { return m.containsKey(o); }
@Override
public boolean remove(Object o) { return m.remove(o) != null; }
@Override
public void clear() { m.clear(); }
@Override
public E lower(E e) { return m.lowerKey(e); }
@Override
public E floor(E e) { return m.floorKey(e); }
@Override
public E ceiling(E e) { return m.ceilingKey(e); }
@Override
public E higher(E e) { return m.higherKey(e); }
@Override
public Comparator super E> comparator() { return m.comparator(); }
@Override
public E first() { return m.firstKey(); }
@Override
public E last() { return m.lastKey(); }
@Override
public E pollFirst() {
Map.Entry e = m.pollFirstEntry();
return e == null? null : e.getKey();
}
@Override
public E pollLast() {
Map.Entry e = m.pollLastEntry();
return e == null? null : e.getKey();
}
@Override
public Iterator iterator() {
if (m instanceof BTreeMap)
return ((BTreeMap)m).keyIterator();
else if(m instanceof SubMap)
return ((BTreeMap.SubMap)m).keyIterator();
else
return ((BTreeMap.DescendingMap)m).keyIterator();
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection> c = (Collection>) o;
try {
return containsAll(c) && c.containsAll(this);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
@Override
public Object[] toArray() { return toList(this).toArray(); }
@Override
public T[] toArray(T[] a) { return toList(this).toArray(a); }
@Override
public Iterator descendingIterator() {
return descendingSet().iterator();
}
@Override
public NavigableSet subSet(E fromElement,
boolean fromInclusive,
E toElement,
boolean toInclusive) {
return new KeySet(m.subMap(fromElement, fromInclusive,
toElement, toInclusive),hasValues);
}
@Override
public NavigableSet headSet(E toElement, boolean inclusive) {
return new KeySet(m.headMap(toElement, inclusive),hasValues);
}
@Override
public NavigableSet tailSet(E fromElement, boolean inclusive) {
return new KeySet(m.tailMap(fromElement, inclusive),hasValues);
}
@Override
public NavigableSet subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
}
@Override
public NavigableSet headSet(E toElement) {
return headSet(toElement, false);
}
@Override
public NavigableSet tailSet(E fromElement) {
return tailSet(fromElement, true);
}
@Override
public NavigableSet descendingSet() {
return new KeySet(m.descendingMap(),hasValues);
}
@Override
public boolean add(E k) {
if(hasValues)
throw new UnsupportedOperationException();
else
return m.put(k, EMPTY ) == null;
}
}
static final class Values extends AbstractCollection {
private final ConcurrentNavigableMap