All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mapdb.BTreeMap Maven / Gradle / Ivy

Go to download

MapDB provides concurrent Maps, Sets and Queues backed by disk storage or off-heap memory. It is a fast, scalable and easy to use embedded Java database.

The newest version!
/*
 *  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 JSR-166 group 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.*;
import java.util.*;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
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 1986 paper * * Concurrent operations on B∗-trees with overtaking * written by Yehoshua Sagiv. * More practical aspects of BTreeMap implementation are based on * demo application from Thomas Dinsdale-Young. * Also more work from Thomas: A Simple Abstraction for Complex Concurrent Indexes *

* * 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, Closeable, Serializable { /** 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 values from/into binary form*/ protected final Serializer valueSerializer; /** Serializer used to convert values inside nodes from/into binary form * If maps has external serializer, this is ValRef serializer*/ protected final Serializer valueNodeSerializer; /** 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; 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; /** * Indicates if this collection collection was not made by DB by user. * If user can not access DB object, we must shutdown Executor and close Engine ourself in close() method. */ protected final boolean closeEngine; /* hack used for DB Catalog*/ protected static SortedMap preinitCatalog(DB db) { Long rootRef = db.getEngine().get(Engine.RECID_NAME_CATALOG, Serializer.RECID); ; //$DELAY$ if(rootRef==null){ if(db.getEngine().isReadOnly()) return Collections.unmodifiableSortedMap(new TreeMap()); NodeSerializer rootSerializer = new NodeSerializer(false,BTreeKeySerializer.STRING, db.getDefaultSerializer(), 0); BNode root = new LeafNode(BTreeKeySerializer.STRING.emptyKeys(), true,true,false, new Object[]{}, 0); rootRef = db.getEngine().put(root, rootSerializer); //$DELAY$ db.getEngine().update(Engine.RECID_NAME_CATALOG,rootRef, Serializer.RECID); db.getEngine().commit(); } Serializer valser = db.getDefaultSerializer(); if(CC.ASSERT && valser == null) throw new AssertionError(); return new BTreeMap( db.engine, false, Engine.RECID_NAME_CATALOG, 32, false, 0, BTreeKeySerializer.STRING, valser, 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-ValRef["+recid+"]"; } } protected static final Serializer VALREF_SERIALIZER = new Serializer.EightByteSerializer(){ @Override public void serialize(DataOutput out, ValRef value) throws IOException { DataIO.packLong(out,value.recid); } @Override public ValRef deserialize(DataInput in, int available) throws IOException { return new ValRef(DataIO.unpackLong(in)); } @Override public int fixedSize() { return -1; } @Override public boolean equals(ValRef a1, ValRef a2) { throw new IllegalAccessError(); } @Override public int hashCode(ValRef valRef, int seed) { throw new IllegalAccessError(); } @Override protected ValRef unpack(long l) { return new ValRef(l); } @Override protected long pack(ValRef l) { return l.recid; } @Override public void valueArraySerialize(DataOutput out, Object vals) throws IOException { for(long o:(long[]) vals){ DataIO.packLong(out, o); } } @Override public Object valueArrayDeserialize(DataInput in, int size) throws IOException { //PERF six-byte long[] long[] ret = new long[size]; for(int i=0;i BOOLEAN_PACKED = new Serializer.BooleanSer() { @Override public void valueArraySerialize(DataOutput out, Object vals) throws IOException { SerializerBase.writeBooleanArray(out,(boolean[]) vals); } @Override public Object valueArrayDeserialize(DataInput in, int size) throws IOException { return SerializerBase.readBooleanArray(size, in); } }; /** common interface for BTree node */ public abstract static class BNode{ static final int LEFT_MASK = 1; static final int RIGHT_MASK = 1<<1; static final int TOO_LARGE_MASK = 1<<2; final Object keys; final byte flags; public BNode(Object keys, boolean leftEdge, boolean rightEdge, boolean tooLarge){ this.keys = keys; this.flags = (byte)( (leftEdge?LEFT_MASK:0)| (rightEdge?RIGHT_MASK:0)| (tooLarge?TOO_LARGE_MASK:0) ); } final public Object key(BTreeKeySerializer keyser, int pos) { if(isLeftEdge()){ if(pos--==0) return null; } if(pos==keyser.length(keys) && isRightEdge()) return null; return keyser.getKey(keys,pos); } final public int keysLen(BTreeKeySerializer keyser) { return keyser.length(keys) + leftEdgeInc() + rightEdgeInc(); } final public boolean isLeftEdge(){ return (flags&LEFT_MASK)!=0; } final public boolean isRightEdge(){ return (flags&RIGHT_MASK)!=0; } /** @return 1 if is left edge, or 0*/ final public int leftEdgeInc(){ return flags&LEFT_MASK; } /** @return 1 if is right edge, or 0*/ final public int rightEdgeInc(){ return (flags&RIGHT_MASK)>>>1; } final public boolean isTooLarge(){ return (flags&TOO_LARGE_MASK)!=0; } public abstract boolean isLeaf(); public abstract Object val(int pos, Serializer valueSerializer); final public Object highKey(BTreeKeySerializer keyser) { if(isRightEdge()) return null; return keyser.getKey(keys,keyser.length(keys)-1); } public abstract Object childArray(); public abstract long child(int i); public abstract long next(); public final int compare(final BTreeKeySerializer keyser, int pos1, int pos2){ if(pos1==pos2) return 0; //$DELAY$ if(isLeftEdge()){ //first position is negative infinity, so everything else is bigger //first keys is missing in array, so adjust positions if(pos1--==0) return -1; if(pos2--==0) return 1; } //$DELAY$ if(isRightEdge()){ int keysLen = keyser.length(keys); //last position is positive infinity, so everything else is smaller if(pos1==keysLen) return 1; if(pos2==keysLen) return -1; } return keyser.compare(keys,pos1,pos2); } public final int compare(final BTreeKeySerializer keyser, int pos, Object second){ if(isLeftEdge()) { //first position is negative infinity, so everything else is bigger //first keys is missing in array, so adjust positions if (pos-- == 0) return -1; } //$DELAY$ if(isRightEdge() && pos==keyser.length(keys)){ //last position is positive infinity, so everything else is smaller return 1; } return keyser.compare(keys,pos,second); } public void checkStructure(BTreeKeySerializer keyser, Serializer valser){ //check all keys are sorted; if(keyser==null) return; int keylen = keyser.length(keys); int end = keylen-2+rightEdgeInc(); if(end>1){ for(int i = 1;i<=end;i++){ if(keyser.compare(keys,i-1, i)>=0) throw new DBException.DataCorruption("keys are not sorted: "+Arrays.toString(keyser.keysToArray(keys))); } } //check last key is sorted or null if(!isRightEdge() && keylen>2){ if(keyser.compare(keys,keylen-2, keylen-1)>0){ throw new DBException.DataCorruption("Last key is not sorted: "+Arrays.toString(keyser.keysToArray(keys))); } } } public abstract BNode copyAddKey(BTreeKeySerializer keyser, Serializer valser, int pos, Object newKey, long newChild, Object newValue); public abstract BNode copySplitRight(BTreeKeySerializer keyser, Serializer valser, int splitPos); public abstract BNode copySplitLeft(BTreeKeySerializer keyser, Serializer valser, int splitPos, long newNext); public abstract int valSize(Serializer valueSerializer); public abstract int childArrayLength(); public int childIndexOf(long child){ for(int i=0;i0){ throw new AssertionError(); } } return new LeafNode(keys2, isLeftEdge(), false, false, vals2, newNext); } @Override public int valSize(Serializer valueSerializer) { return valueSerializer.valueArraySize(vals); } @Override public int childArrayLength() { return -1; } public LeafNode copyChangeValue(Serializer valser, int pos, Object value) { Object vals2 = valser.valueArrayUpdateVal(vals, pos - 1, value); //$DELAY$ return new LeafNode(keys, isLeftEdge(), isRightEdge(), false, vals2, next); } public LeafNode copyRemoveKey(BTreeKeySerializer keyser, Serializer valser, int pos) { int keyPos = pos -leftEdgeInc(); Object keys2 = keyser.deleteKey(keys,keyPos); //$DELAY$ Object vals2 = valser.valueArrayDeleteValue(vals,pos); //$DELAY$ return new LeafNode(keys2, isLeftEdge(), isRightEdge(), false, vals2, next); } public LeafNode copyClear(BTreeKeySerializer keyser, Serializer valser) { Object[] keys2 = new Object[2-leftEdgeInc()-rightEdgeInc()]; if(!isLeftEdge()) keys2[0] = key(keyser,0); //$DELAY$ if(!isRightEdge()) keys2[1-leftEdgeInc()] = highKey(keyser); //$DELAY$ return new LeafNode (keyser.arrayToKeys(keys2), isLeftEdge(), isRightEdge(), false, valser.valueArrayEmpty(), next); } } protected final Serializer nodeSerializer; protected static final class NodeSerializer extends Serializer{ protected static final int LEAF_MASK = 1<<15; protected static final int LEFT_SHIFT = 14; protected static final int LEFT_MASK = 1<< LEFT_SHIFT; protected static final int RIGHT_SHIFT = 13; protected static final int RIGHT_MASK = 1<< RIGHT_SHIFT; protected static final int SIZE_MASK = RIGHT_MASK - 1; protected final boolean hasValues; protected final boolean valsOutsideNodes; protected final BTreeKeySerializer keySerializer; protected final Serializer valueSerializer; protected final int numberOfNodeMetas; public NodeSerializer(boolean valsOutsideNodes, BTreeKeySerializer keySerializer, Serializer valueSerializer, int numberOfNodeMetas) { if(keySerializer==null) throw new NullPointerException("keySerializer not set"); this.hasValues = valueSerializer!=null; this.valsOutsideNodes = valsOutsideNodes; this.keySerializer = keySerializer; this.valueSerializer = (Serializer) (hasValues? (valsOutsideNodes? VALREF_SERIALIZER : valueSerializer): BTreeMap.BOOLEAN_PACKED); this.numberOfNodeMetas = numberOfNodeMetas; } @Override public void serialize(DataOutput out, BNode value) throws IOException { final boolean isLeaf = value.isLeaf(); //check node integrity in paranoid mode if(CC.PARANOID){ value.checkStructure(keySerializer,valueSerializer); } //$DELAY$ final int header = (isLeaf ? LEAF_MASK : 0) | (value.isLeftEdge() ? LEFT_MASK : 0) | (value.isRightEdge() ? RIGHT_MASK : 0) | value.keysLen(keySerializer); out.writeShort(header); //$DELAY$ //write node metas, right now this is ignored, but in future it could be used for counted btrees or aggregations for(int i=0;i0) keySerializer.serialize(out,value.keys); //$DELAY$ if(isLeaf){ valueSerializer.valueArraySerialize(out,((LeafNode)value).vals); } } protected void serializeChildArray(DataOutput out, Object childArray) throws IOException { if(childArray instanceof int[]){ int[] cc = (int[]) childArray; DataIO.packLong(out, (((long)cc[0]) << 1) | 1L); //pack first value mixed with int flag for(int i=1;i>LEFT_SHIFT; final int right = (header& RIGHT_MASK) >>RIGHT_SHIFT; DataIO.DataInputInternal in2 = (DataIO.DataInputInternal) in; //TODO fallback option if cast fails BNode node; if(isLeaf){ node = deserializeLeaf(in2, size, left, right); }else{ node = deserializeDir(in2, size, left, right); } //$DELAY$ if(CC.PARANOID){ node.checkStructure(keySerializer,valueSerializer); } return node; } private BNode deserializeDir(final DataIO.DataInputInternal in, final int size, final int left, final int right) throws IOException { Object child; long firstChild = in.unpackLong(); if((firstChild&1) == 0){ //deserialize as long[] long[] child_ = new long[size]; child = child_; child_[0] = firstChild>>>1; in.unpackLongArray(child_,1,size); }else{ //deserialize as long[] int[] child_ = new int[size]; child = child_; child_[0] = (int) (firstChild>>>1); in.unpackIntArray(child_,1,size); } int keysize = size - left- right; //$DELAY$ final Object keys = keysize==0? keySerializer.emptyKeys(): keySerializer.deserialize(in, keysize); //$DELAY$ return new DirNode(keys, left!=0, right!=0, false ,child); } private BNode deserializeLeaf(final DataIO.DataInputInternal in, final int size, final int left, final int right) throws IOException { final long next = in.unpackLong(); int keysize = size - left- right; //$DELAY$ final Object keys = keysize==0? keySerializer.emptyKeys(): keySerializer.deserialize(in, keysize); //$DELAY$ Object vals = valueSerializer.valueArrayDeserialize(in,size-2); return new LeafNode(keys, left!=0, right!=0, false , vals, next); } @Override public boolean isTrusted() { return keySerializer.isTrusted() && valueSerializer.isTrusted(); } } /** Constructor used to create new BTreeMap. * * @param engine used for persistence * @param closeEngine if this object was created without DB. If true shutdown everything on close method, otherwise DB takes care of shutdown * @param rootRecidRef reference to root recid * @param maxNodeSize maximal BTree Node size. Node will split if number of entries is higher * @param valsOutsideNodes Store Values outside of BTree Nodes in separate record? * @param counterRecid recid under which {@code Atomic.Long} is stored, or {@code 0} for no counter * @param keySerializer Serializer used for keys. May be null for default value. * @param valueSerializer Serializer used for values. May be null for default value * @param numberOfNodeMetas number of meta records associated with each BTree node */ public BTreeMap( Engine engine, boolean closeEngine, long rootRecidRef, int maxNodeSize, boolean valsOutsideNodes, long counterRecid, BTreeKeySerializer keySerializer, final Serializer valueSerializer, int numberOfNodeMetas ) { this.closeEngine = closeEngine; if(maxNodeSize%2!=0) throw new IllegalArgumentException("maxNodeSize must be dividable by 2"); if(maxNodeSize<6) throw new IllegalArgumentException("maxNodeSize too low"); if((maxNodeSize& NodeSerializer.SIZE_MASK) !=maxNodeSize) throw new IllegalArgumentException("maxNodeSize too high"); if(rootRecidRef<=0||counterRecid<0 || numberOfNodeMetas<0) throw new IllegalArgumentException(); if(keySerializer==null) throw new NullPointerException(); this.rootRecidRef = rootRecidRef; this.hasValues = valueSerializer!=null; this.valsOutsideNodes = valsOutsideNodes; this.engine = engine; this.maxNodeSize = maxNodeSize; this.numberOfNodeMetas = numberOfNodeMetas; this.keySerializer = keySerializer; this.valueSerializer = valueSerializer!=null? valueSerializer: (Serializer) BTreeMap.BOOLEAN_PACKED; this.valueNodeSerializer = valsOutsideNodes ? VALREF_SERIALIZER : this.valueSerializer; entrySet = new EntrySet(this, this.valueSerializer); this.nodeSerializer = new NodeSerializer(valsOutsideNodes,keySerializer,valueNodeSerializer,numberOfNodeMetas); this.keySet = new KeySet(this, hasValues); //$DELAY$ 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.RECID); for(;;){ if(CC.ASSERT && r<=0) throw new DBException.DataCorruption("wrong recid"); //$DELAY$ BNode n= engine.get(r,nodeSerializer); leftEdges2.add(r); if(n.isLeaf()) break; r = n.child(0); } //$DELAY$ Collections.reverse(leftEdges2); leftEdges = Collections.synchronizedList(leftEdges2); } /* creates empty root node and returns recid of its reference*/ static protected long createRootRef(Engine engine, BTreeKeySerializer keySer, Serializer valueSer, boolean valuesOutsideNodes, int numberOfNodeMetas){ if(valuesOutsideNodes) valueSer = BTreeMap.VALREF_SERIALIZER; else if(valueSer==null) valueSer = BTreeMap.BOOLEAN_PACKED; Object emptyArray = valueSer.valueArrayEmpty(); final LeafNode emptyRoot = new LeafNode(keySer.emptyKeys(), true,true, false,emptyArray, 0); //empty root is serializer simpler way, so we can use dummy values long rootRecidVal = engine.put(emptyRoot, new NodeSerializer(false,keySer, valueSer, numberOfNodeMetas)); return engine.put(rootRecidVal,Serializer.RECID); } @Override public V get(Object key){ return (V) get(key, true); } protected Object get(Object key, boolean expandValue) { if(key==null) throw new NullPointerException(); K v = (K) key; long current = engine.get(rootRecidRef, Serializer.RECID); //get root //$DELAY$ BNode A = engine.get(current, nodeSerializer); //dive until leaf while(!A.isLeaf()){ //$DELAY$ current = nextDir((DirNode) A, v); //$DELAY$ A = engine.get(current, nodeSerializer); } for(;;) { int pos = keySerializer.findChildren2(A, key); //$DELAY$ if (pos > 0 && pos != A.keysLen(keySerializer) - 1) { //found Object val = A.val(pos - 1,valueNodeSerializer); //$DELAY$ if(expandValue) val = valExpand(val); return val; } else if( pos<=0 && -pos> A.keysLen(keySerializer)){ //move to next link current = A.next(); //$DELAY$ if (current == 0) { return null; } A = engine.get(current, nodeSerializer); } else { //$DELAY$ //not found return null; } } } protected V valExpand(Object ret) { if(valsOutsideNodes && ret!=null) { long recid = ((ValRef)ret).recid; //$DELAY$ ret = engine.get(recid, valueSerializer); } return (V) ret; } protected final long nextDir(DirNode d, Object key) { int pos = keySerializer.findChildren(d, key) - 1; //$DELAY$ 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(final K key, final V value2, final boolean putOnlyIfAbsent){ K v = key; int stackPos = -1; long[] stackVals = new long[4]; final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); long current = rootRecid; //$DELAY$ BNode A = engine.get(current, nodeSerializer); while(!A.isLeaf()){ //$DELAY$ long t = current; current = nextDir((DirNode) A, v); //$DELAY$ if(CC.ASSERT && ! (current>0) ) throw new DBException.DataCorruption("wrong recid"); //if is not link if (current != A.next()) { //stack push t stackPos++; if(stackVals.length == stackPos) //grow if needed stackVals = Arrays.copyOf(stackVals, stackVals.length*2); //$DELAY$ stackVals[stackPos] = t; } //$DELAY$ A = engine.get(current, nodeSerializer); } int level = 0; long p=0; try{ while(true){ //$DELAY$ boolean found; do{ //$DELAY$ lock(nodeLocks, current); //$DELAY$ found = true; A = engine.get(current, nodeSerializer); int pos = keySerializer.findChildren(A, v); //check if keys is already in tree //$DELAY$ if(pos highvalue(a) if(!A.isRightEdge() && A.compare(keySerializer,A.keysLen(keySerializer)-1,v)<0){ //$DELAY$ //follow link until necessary unlock(nodeLocks, current); found = false; //$DELAY$ int pos2 = keySerializer.findChildren(A, v); while(A!=null && pos2 == A.keysLen(keySerializer)){ //TODO lock? long next = A.next(); //$DELAY$ if(next==0) break; current = next; A = engine.get(current, nodeSerializer); //$DELAY$ pos2 = keySerializer.findChildren(A, v); } } }while(!found); V value = value2; if(valsOutsideNodes){ long recid = engine.put(value2, valueSerializer); //$DELAY$ value = (V) new ValRef(recid); } int pos = keySerializer.findChildren(A, v); //$DELAY$ A = A.copyAddKey(keySerializer,valueNodeSerializer, pos,v,p,value); //$DELAY$ // can be new item inserted into A without splitting it? if(A.keysLen(keySerializer) - (A.isLeaf()?1:0)0)) throw new DBException.DataCorruption("wrong recid"); }else{ Object rootChild = (current l = m.findLargerNode(lo, loInclusive); currentPos = l!=null? l.a : -1; currentLeaf = l!=null ? l.b : null; } this.hi = hi; this.hiInclusive = hiInclusive; //$DELAY$ if(hi!=null && currentLeaf!=null){ //check in bounds int c = currentLeaf.compare(m.keySerializer,currentPos,hi); if (c > 0 || (c == 0 && !hiInclusive)){ //out of high bound currentLeaf=null; currentPos=-1; //$DELAY$ } } } private void pointToStart() { //find left-most leaf final long rootRecid = m.engine.get(m.rootRecidRef, Serializer.RECID); BNode node = (BNode) m.engine.get(rootRecid, m.nodeSerializer); //$DELAY$ while(!node.isLeaf()){ //$DELAY$ node = (BNode) m.engine.get(node.child(0), m.nodeSerializer); } currentLeaf = (LeafNode) node; currentPos = 1; //$DELAY$ while(currentLeaf.keysLen(m.keySerializer)==2){ //follow link until leaf is not empty if(currentLeaf.next == 0){ //$DELAY$ currentLeaf = null; return; } //$DELAY$ 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); //$DELAY$ lastReturnedKey = null; } protected void advance(){ if(currentLeaf==null) return; lastReturnedKey = currentLeaf.key(m.keySerializer,currentPos); currentPos++; //$DELAY$ if(currentPos == currentLeaf.keysLen(m.keySerializer)-1){ //move to next leaf if(currentLeaf.next==0){ currentLeaf = null; currentPos=-1; return; } //$DELAY$ currentPos = 1; currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer); while(currentLeaf.keysLen(m.keySerializer)==2){ if(currentLeaf.next ==0){ currentLeaf = null; currentPos=-1; return; } currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer); //$DELAY$ } } if(hi!=null && currentLeaf!=null){ //check in bounds int c = currentLeaf.compare(m.keySerializer,currentPos,hi); if (c > 0 || (c == 0 && !hiInclusive)){ //$DELAY$ //out of high bound currentLeaf=null; currentPos=-1; } } } } protected static class BTreeDescendingIterator{ final BTreeMap m; LeafNode currentLeaf; Object lastReturnedKey; int currentPos; final Object lo; final boolean loInclusive; /** unbounded iterator*/ BTreeDescendingIterator(BTreeMap m){ this.m = m; lo=null; loInclusive=false; pointToStart(); } /** bounder iterator, args may be null for partially bounded*/ BTreeDescendingIterator( BTreeMap m, Object lo, boolean loInclusive, Object hi, boolean hiInclusive){ this.m = m; if(hi==null){ //$DELAY$ pointToStart(); }else{ //$DELAY$ Fun.Pair l = m.findSmallerNode(hi, hiInclusive); currentPos = l!=null? l.a : -1; currentLeaf = l!=null ? l.b : null; } this.lo = lo; this.loInclusive = loInclusive; //$DELAY$ if(lo!=null && currentLeaf!=null){ //check in bounds int c = -currentLeaf.compare(m.keySerializer,currentPos,lo); if (c > 0 || (c == 0 && !loInclusive)){ //out of high bound currentLeaf=null; currentPos=-1; //$DELAY$ } } } private void pointToStart() { //find right-most leaf final long rootRecid = m.engine.get(m.rootRecidRef, Serializer.RECID); BNode node = (BNode) m.engine.get(rootRecid, m.nodeSerializer); //descend and follow link until possible for(;;){ long next = node.next(); if(next==0){ if(node.isLeaf()){ //end currentLeaf = (LeafNode) node; int len = currentLeaf.keysLen(m.keySerializer); if(len==2){ currentLeaf=null; currentPos=-1; }else { currentPos = len - 2; } return; } //follow last children in directory Object children = node.childArray(); next = children instanceof int[] ? ((int[])children)[((int[])children).length-2] : ((long[])children)[((long[])children).length-2]; } node = (BNode) m.engine.get(next,m.nodeSerializer); } } public boolean hasNext(){ return currentLeaf!=null; } public void remove(){ if(lastReturnedKey==null) throw new IllegalStateException(); m.remove(lastReturnedKey); //$DELAY$ lastReturnedKey = null; } protected void advance(){ if(currentLeaf==null) return; lastReturnedKey = currentLeaf.key(m.keySerializer,currentPos); currentPos--; //$DELAY$ if(currentPos == 0){ //$DELAY$ Object nextKey = currentLeaf.key(m.keySerializer,0); Fun.Pair prevPair = nextKey==null?null: m.findSmallerNode(nextKey,false); if(prevPair==null){ currentLeaf = null; currentPos=-1; return; } currentLeaf = (LeafNode) prevPair.b; currentPos = currentLeaf.keysLen(m.keySerializer)-2; while(currentLeaf.keysLen(m.keySerializer)==2){ if(currentLeaf.next ==0){ currentLeaf = null; currentPos=-1; return; } currentLeaf = (LeafNode) m.engine.get(currentLeaf.next, m.nodeSerializer); //$DELAY$ } } if(lo!=null && currentLeaf!=null){ //check in bounds int c = -currentLeaf.compare(m.keySerializer,currentPos,lo); if (c > 0 || (c == 0 && !loInclusive)){ //$DELAY$ //out of high bound currentLeaf=null; currentPos=-1; } } } } @Override public V remove(Object key) { return removeOrReplace(key, null, null); } private V removeOrReplace(final Object key, final Object value, final Object putNewValue) { if(key==null) throw new NullPointerException("null key"); long current = engine.get(rootRecidRef, Serializer.RECID); BNode A = engine.get(current, nodeSerializer); //$DELAY$ while(!A.isLeaf()){ //$DELAY$ current = nextDir((DirNode) A, key); A = engine.get(current, nodeSerializer); } long old =0; try{for(;;){ //$DELAY$ if(old!=0) { //$DELAY$ unlock(nodeLocks, old); } //$DELAY$ lock(nodeLocks, current); A = engine.get(current, nodeSerializer); //$DELAY$ int pos = keySerializer.findChildren2(A, key); // System.out.println(key+" - "+pos+" - "+A); if(pos>0 && pos!=A.keysLen(keySerializer)-1){ //found, delete from node //$DELAY$ Object oldValNotExpanded = A.val(pos-1, valueNodeSerializer); Object oldVal = valExpand(oldValNotExpanded); if(value!=null && valueSerializer!=null && !valueSerializer.equals((V)value,(V)oldVal)){ unlock(nodeLocks, current); //$DELAY$ return null; } if(valsOutsideNodes){ long recid = ((ValRef)oldValNotExpanded).recid; engine.update(recid, (V) putNewValue,valueSerializer); } if(putNewValue==null || !valsOutsideNodes){ //if existing item is updated outside of node, there is no need to modify node A = putNewValue!=null? ((LeafNode)A).copyChangeValue(valueNodeSerializer,pos,putNewValue): ((LeafNode)A).copyRemoveKey(keySerializer,valueNodeSerializer,pos); //$DELAY$ engine.update(current, A, nodeSerializer); } if(CC.ASSERT && ! (nodeLocks.get(current)==Thread.currentThread())) throw new AssertionError(); notify((K)key, (V)oldVal, (V)putNewValue); unlock(nodeLocks, current); return (V) oldVal; }else if(pos<=0 && -pos-1!=A.keysLen(keySerializer)-1){ //not found unlock(nodeLocks, current); //$DELAY$ return null; }else{ //move to next link old = current; current = A.next(); //$DELAY$ if(current==0){ //end reached unlock(nodeLocks,old); return null; } } } }catch(RuntimeException e){ unlockAll(nodeLocks); throw e; }catch(Exception e){ unlockAll(nodeLocks); throw new RuntimeException(e); } } @Override public void clear() { boolean hasListeners = modListeners.length>0; long current = engine.get(rootRecidRef, Serializer.RECID); BNode A = engine.get(current, nodeSerializer); //$DELAY$ while(!A.isLeaf()){ current = A.child(0); //$DELAY$ A = engine.get(current, nodeSerializer); } long old =0; try{for(;;) { //$DELAY$ //lock nodes lock(nodeLocks, current); if (old != 0) { //$DELAY$ unlock(nodeLocks, old); } //$DELAY$ //notify about deletion int size = A.keysLen(keySerializer)-1; if(hasListeners) { //$DELAY$ for (int i = 1; i < size; i++) { Object val = A.val(i - 1, valueNodeSerializer); val = valExpand(val); //$DELAY$ notify((K) A.key(keySerializer,i),(V) val, null); } } //remove all node content A = ((LeafNode) A).copyClear(keySerializer,valueNodeSerializer); //$DELAY$ engine.update(current, A, nodeSerializer); //move to next link old = current; //$DELAY$ current = A.next(); if (current == 0) { //end reached //$DELAY$ unlock(nodeLocks, old); //$DELAY$ return; } //$DELAY$ A = engine.get(current, nodeSerializer); } }catch(RuntimeException e){ unlockAll(nodeLocks); throw e; }catch(Exception e){ unlockAll(nodeLocks); throw new RuntimeException(e); } } 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.key(m.keySerializer,currentPos); //$DELAY$ advance(); //$DELAY$ return ret; } } static class BTreeValueIterator extends BTreeIterator implements Iterator{ 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.val(currentPos-1,m.valueNodeSerializer); //$DELAY$ advance(); //$DELAY$ 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.key(m.keySerializer,currentPos); Object val = currentLeaf.val(currentPos-1,m.valueNodeSerializer); //$DELAY$ advance(); //$DELAY$ return m.makeEntry(ret, m.valExpand(val)); } } static class BTreeDescendingKeyIterator extends BTreeDescendingIterator implements Iterator{ BTreeDescendingKeyIterator(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.key(m.keySerializer,currentPos); //$DELAY$ advance(); //$DELAY$ return ret; } } static class BTreeDescendingValueIterator extends BTreeDescendingIterator implements Iterator{ BTreeDescendingValueIterator(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.val(currentPos-1,m.valueNodeSerializer); //$DELAY$ advance(); //$DELAY$ return (V) m.valExpand(ret); } } static class BTreeDescendingEntryIterator extends BTreeDescendingIterator implements Iterator>{ BTreeDescendingEntryIterator(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.key(m.keySerializer,currentPos); Object val = currentLeaf.val(currentPos - 1, m.valueNodeSerializer); //$DELAY$ advance(); //$DELAY$ return m.makeEntry(ret, m.valExpand(val)); } } protected Entry makeEntry(Object key, Object value) { if(CC.ASSERT && ! (!(value instanceof ValRef))) throw new AssertionError(); return new SimpleImmutableEntry((K)key, (V)value); } @Override public boolean isEmpty() { return !keyIterator().hasNext(); } @Override public int size() { return (int) Math.min(sizeLong(), Integer.MAX_VALUE); } @Override public long sizeLong() { if(counter!=null) return counter.get(); long size = 0; BTreeIterator iter = new BTreeIterator(this); //$DELAY$ while(iter.hasNext()){ //$DELAY$ iter.advance(); size++; } return size; } public long mappingCount(){ //method added in java 8 return sizeLong(); } @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(); return value != null && removeOrReplace(key, value, null) != null; } @Override public boolean replace(final K key, final V oldValue, final V newValue) { if(key == null || oldValue == null || newValue == null ) throw new NullPointerException(); return removeOrReplace(key,oldValue,newValue)!=null; } @Override public V replace(final K key, final V value) { if(key == null || value == null) throw new NullPointerException(); return removeOrReplace(key, null, value); } @Override public Comparator comparator() { return keySerializer.comparator(); } @Override public Map.Entry firstEntry() { final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); BNode n = engine.get(rootRecid, nodeSerializer); //$DELAY$ while(!n.isLeaf()){ //$DELAY$ n = engine.get(n.child(0), nodeSerializer); } LeafNode l = (LeafNode) n; //follow link until necessary while(l.keysLen(keySerializer)==2){ if(l.next==0) return null; //$DELAY$ l = (LeafNode) engine.get(l.next, nodeSerializer); } //$DELAY$ return makeEntry(l.key(keySerializer,1), valExpand(l.val(0, valueNodeSerializer))); } @Override public K firstKey() { final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); BNode n = engine.get(rootRecid, nodeSerializer); //$DELAY$ while(!n.isLeaf()){ //$DELAY$ n = engine.get(n.child(0), nodeSerializer); } LeafNode l = (LeafNode) n; //follow link until necessary while(l.keysLen(keySerializer)==2){ if(l.next==0) throw new NoSuchElementException(); //$DELAY$ l = (LeafNode) engine.get(l.next, nodeSerializer); } //$DELAY$ return (K) l.key(keySerializer, 1); } @Override public Entry pollFirstEntry() { //$DELAY$ while(true){ //$DELAY$ Entry e = firstEntry(); //$DELAY$ if(e==null || remove(e.getKey(),e.getValue())){ return e; } } } @Override public Entry pollLastEntry() { //$DELAY$ while(true){ Entry e = lastEntry(); //$DELAY$ 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.RECID); //$DELAY$ BNode n = engine.get(rootRecid, nodeSerializer); //$DELAY$ Entry k = findSmallerRecur(n, key, inclusive); //$DELAY$ if(k==null || (k.getValue()==null)) return null; return k; } private Entry findSmallerRecur(BNode n, K key, boolean inclusive) { //PERF optimize comparation in this method final boolean leaf = n.isLeaf(); final int start = leaf ? n.keysLen(keySerializer)-2 : n.keysLen(keySerializer)-1; final int end = leaf?1:0; final int res = inclusive && leaf? 1 : 0; //$DELAY$ for(int i=start;i>=end; i--){ //$DELAY$ final Object key2 = n.key(keySerializer,i); int comp = (key2==null)? -1 : keySerializer.comparator().compare(key2, key); if(comp2 && keySerializer.comparator().compare( n2.key(keySerializer,1), key)>=(inclusive ? 1 : 0)) { continue; } } //$DELAY$ Entry ret = findSmallerRecur(n2, key, inclusive); if(ret!=null) return ret; } } } return null; } protected Fun.Pair findSmallerNode(K key,boolean inclusive){ if(key==null) throw new NullPointerException(); final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); //$DELAY$ BNode n = engine.get(rootRecid, nodeSerializer); //$DELAY$ return findSmallerNodeRecur(n, key, inclusive); } protected Fun.Pair findSmallerNodeRecur( BNode n, K key, boolean inclusive) { //PERF optimize comparation in this method final boolean leaf = n.isLeaf(); final int start = leaf ? n.keysLen(keySerializer)-2 : n.keysLen(keySerializer)-1; final int end = leaf?1:0; final int res = inclusive && leaf? 1 : 0; //$DELAY$ for(int i=start;i>=end; i--){ //$DELAY$ final Object key2 = n.key(keySerializer,i); int comp = (key2==null)? -1 : keySerializer.comparator().compare(key2, key); if(comp2 && keySerializer.comparator().compare( n2.key(keySerializer,1), key)>=(inclusive ? 1 : 0)) { continue; } } //$DELAY$ return findSmallerNodeRecur(n2, key, inclusive); } } } return null; } @Override public Map.Entry lastEntry() { final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); BNode n = engine.get(rootRecid, nodeSerializer); //$DELAY$ 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); //$DELAY$ if(ret!=null) return ret; } //iterate over keys to find last non null key for(int i=n.keysLen(keySerializer)-2; i>0;i--){ Object k = n.key(keySerializer,i); if(k!=null && n.valSize(valueNodeSerializer)>0) { Object val = valExpand(n.val(i-1,valueNodeSerializer)); //$DELAY$ if(val!=null){ //$DELAY$ return makeEntry(k, val); } } } }else{ //dir node, dive deeper for(int i=n.childArrayLength()-1; i>=0;i--){ long childRecid = n.child(i); if(childRecid==0) continue; BNode n2 = engine.get(childRecid, nodeSerializer); //$DELAY$ Entry ret = lastEntryRecur(n2); //$DELAY$ 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(final K key, boolean inclusive) { if(key==null) return null; long current = engine.get(rootRecidRef, Serializer.RECID); BNode A = engine.get(current, nodeSerializer); //dive until leaf //$DELAY$ while(!A.isLeaf()){ current = nextDir((DirNode) A, key); //$DELAY$ 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; //$DELAY$ while(true){ //$DELAY$ for(int i=1;i findLargerNode(final K key, boolean inclusive) { if(key==null) return null; long current = engine.get(rootRecidRef, Serializer.RECID); //$DELAY$ BNode A = engine.get(current, nodeSerializer); //dive until leaf while(!A.isLeaf()){ current = nextDir((DirNode) A, key); 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){ //$DELAY$ 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, false)!=null; } @Override public boolean containsValue(Object value){ if(value ==null) throw new NullPointerException(); Iterator valueIter = valueIterator(); //$DELAY$ while(valueIter.hasNext()){ //$DELAY$ if(valueSerializer.equals((V)value,valueIter.next())) return true; } return false; } @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,null,false,null,false); } 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 List toList(Collection c) { // Using size() here would be a pessimization. List list = new ArrayList(); for (E e : c){ list.add(e); } return list; } public static final class KeySet extends AbstractSet implements NavigableSet, Closeable, Serializable{ 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(); } public long sizeLong(){ if (m instanceof BTreeMap) return ((BTreeMap)m).sizeLong(); else return ((SubMap)m).sizeLong(); } @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 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, Boolean.TRUE ) == null; } @Override public void close() { if(m instanceof BTreeMap) ((BTreeMap)m).close(); } Object writeReplace() throws ObjectStreamException { Set ret = new ConcurrentSkipListSet(); for(Object e:this){ ret.add(e); } return ret; } } static final class Values extends AbstractCollection { private final ConcurrentNavigableMap m; Values(ConcurrentNavigableMap map) { m = map; } @Override public Iterator iterator() { if (m instanceof BTreeMap) return ((BTreeMap)m).valueIterator(); else return ((SubMap)m).valueIterator(); } @Override public boolean isEmpty() { return m.isEmpty(); } @Override public int size() { return m.size(); } @Override public boolean contains(Object o) { return m.containsValue(o); } @Override public void clear() { m.clear(); } @Override public Object[] toArray() { return toList(this).toArray(); } @Override public T[] toArray(T[] a) { return toList(this).toArray(a); } } static final class EntrySet extends AbstractSet> { private final ConcurrentNavigableMap m; private final Serializer valueSerializer; EntrySet(ConcurrentNavigableMap map, Serializer valueSerializer) { m = map; this.valueSerializer = valueSerializer; } @Override public Iterator> iterator() { if (m instanceof BTreeMap) return ((BTreeMap)m).entryIterator(); else if(m instanceof SubMap) return ((SubMap)m).entryIterator(); else return ((DescendingMap)m).entryIterator(); } @Override public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; K1 key = e.getKey(); if(key == null) return false; V1 v = m.get(key); //$DELAY$ return v != null && valueSerializer.equals(v,e.getValue()); } @Override public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; K1 key = e.getKey(); if(key == null) return false; return m.remove(key, e.getValue()); } @Override public boolean isEmpty() { return m.isEmpty(); } @Override public int size() { return m.size(); } @Override public void clear() { m.clear(); } @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); } } static protected class SubMap extends AbstractMap implements ConcurrentNavigableMap { protected final BTreeMap m; protected final K lo; protected final boolean loInclusive; protected final K hi; protected final boolean hiInclusive; public SubMap(BTreeMap m, K lo, boolean loInclusive, K hi, boolean hiInclusive) { this.m = m; this.lo = lo; this.loInclusive = loInclusive; this.hi = hi; this.hiInclusive = hiInclusive; if(lo!=null && hi!=null && m.keySerializer.comparator().compare(lo, hi)>0){ throw new IllegalArgumentException(); } } /* ---------------- Map API methods -------------- */ @Override public boolean containsKey(Object key) { if (key == null) throw new NullPointerException(); K k = (K)key; return inBounds(k) && m.containsKey(k); } @Override public V get(Object key) { if (key == null) throw new NullPointerException(); K k = (K)key; return ((!inBounds(k)) ? null : m.get(k)); } @Override public V put(K key, V value) { checkKeyBounds(key); return m.put(key, value); } @Override public V remove(Object key) { if(key==null) throw new NullPointerException("key null"); K k = (K)key; return (!inBounds(k))? null : m.remove(k); } @Override public int size() { return (int) Math.min(sizeLong(), Integer.MAX_VALUE); } public long sizeLong() { //PERF use counted btrees once they become available if(hi==null && lo==null) return m.sizeLong(); Iterator i = keyIterator(); long counter = 0; while(i.hasNext()){ counter++; i.next(); } return counter; } @Override public boolean isEmpty() { return !keyIterator().hasNext(); } @Override public boolean containsValue(Object value) { if(value==null) throw new NullPointerException(); Iterator i = valueIterator(); while(i.hasNext()){ if(m.valueSerializer.equals((V)value,i.next())) return true; } return false; } @Override public void clear() { Iterator i = keyIterator(); while(i.hasNext()){ i.next(); i.remove(); } } /* ---------------- ConcurrentMap API methods -------------- */ @Override public V putIfAbsent(K key, V value) { checkKeyBounds(key); return m.putIfAbsent(key, value); } @Override public boolean remove(Object key, Object value) { K k = (K)key; return inBounds(k) && m.remove(k, value); } @Override public boolean replace(K key, V oldValue, V newValue) { checkKeyBounds(key); return m.replace(key, oldValue, newValue); } @Override public V replace(K key, V value) { checkKeyBounds(key); return m.replace(key, value); } /* ---------------- SortedMap API methods -------------- */ @Override public Comparator comparator() { return m.comparator(); } /* ---------------- Relational methods -------------- */ @Override public Map.Entry lowerEntry(K key) { if(key==null)throw new NullPointerException(); if(tooLow(key))return null; if(tooHigh(key)) return lastEntry(); Entry r = m.lowerEntry(key); return r!=null && !tooLow(r.getKey()) ? r :null; } @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(); if(tooLow(key)) return null; if(tooHigh(key)){ return lastEntry(); } Entry ret = m.floorEntry(key); if(ret!=null && tooLow(ret.getKey())) return null; return ret; } @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(); if(tooHigh(key)) return null; if(tooLow(key)){ return firstEntry(); } Entry ret = m.ceilingEntry(key); if(ret!=null && tooHigh(ret.getKey())) return null; return ret; } @Override public K ceilingKey(K key) { Entry k = ceilingEntry(key); return k!=null? k.getKey():null; } @Override public Entry higherEntry(K key) { Entry r = m.higherEntry(key); return r!=null && inBounds(r.getKey()) ? r : null; } @Override public K higherKey(K key) { Entry k = higherEntry(key); return k!=null? k.getKey():null; } @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 Map.Entry firstEntry() { Entry k = lo==null ? m.firstEntry(): m.findLarger(lo, loInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Map.Entry lastEntry() { Entry k = hi==null ? m.lastEntry(): m.findSmaller(hi, hiInclusive); return k!=null && inBounds(k.getKey())? k : null; } @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; } } } /** * Utility to create submaps, where given bounds override * unbounded(null) ones and/or are checked against bounded ones. */ private SubMap newSubMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { // if(fromKey!=null && toKey!=null){ // int comp = m.comparator.compare(fromKey, toKey); // if((fromInclusive||!toInclusive) && comp==0) // throw new IllegalArgumentException(); // } if (lo != null) { if (fromKey == null) { fromKey = lo; fromInclusive = loInclusive; } else { int c = m.keySerializer.comparator().compare(fromKey, lo); if (c < 0 || (c == 0 && !loInclusive && fromInclusive)) throw new IllegalArgumentException("key out of range"); } } if (hi != null) { if (toKey == null) { toKey = hi; toInclusive = hiInclusive; } else { int c = m.keySerializer.comparator().compare(toKey, hi); if (c > 0 || (c == 0 && !hiInclusive && toInclusive)) throw new IllegalArgumentException("key out of range"); } } return new SubMap(m, fromKey, fromInclusive, toKey, toInclusive); } @Override public SubMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { if (fromKey == null || toKey == null) throw new NullPointerException(); return newSubMap(fromKey, fromInclusive, toKey, toInclusive); } @Override public SubMap headMap(K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); return newSubMap(null, false, toKey, inclusive); } @Override public SubMap tailMap(K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); return newSubMap(fromKey, inclusive, null, false); } @Override public SubMap subMap(K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @Override public SubMap headMap(K toKey) { return headMap(toKey, false); } @Override public SubMap tailMap(K fromKey) { return tailMap(fromKey, true); } @Override public ConcurrentNavigableMap descendingMap() { return new DescendingMap(m, lo,loInclusive, hi,hiInclusive); } @Override public NavigableSet navigableKeySet() { return new KeySet((ConcurrentNavigableMap) this,m.hasValues); } /* ---------------- Utilities -------------- */ private boolean tooLow(K key) { if (lo != null) { int c = m.keySerializer.comparator().compare(key, lo); if (c < 0 || (c == 0 && !loInclusive)) return true; } return false; } private boolean tooHigh(K key) { if (hi != null) { int c = m.keySerializer.comparator().compare(key, hi); if (c > 0 || (c == 0 && !hiInclusive)) return true; } return false; } private boolean inBounds(K key) { return !tooLow(key) && !tooHigh(key); } private void checkKeyBounds(K key) throws IllegalArgumentException { if (key == null) throw new NullPointerException(); if (!inBounds(key)) throw new IllegalArgumentException("key out of range"); } @Override public NavigableSet keySet() { return new KeySet((ConcurrentNavigableMap) this, m.hasValues); } @Override public NavigableSet descendingKeySet() { return new DescendingMap(m,lo,loInclusive, hi, hiInclusive).keySet(); } @Override public Set> entrySet() { return new EntrySet(this,m.valueSerializer); } Iterator keyIterator() { return new BTreeKeyIterator(m,lo,loInclusive,hi,hiInclusive); } Iterator valueIterator() { return new BTreeValueIterator(m,lo,loInclusive,hi,hiInclusive); } Iterator> entryIterator() { return new BTreeEntryIterator(m,lo,loInclusive,hi,hiInclusive); } } static protected class DescendingMap extends AbstractMap implements ConcurrentNavigableMap { protected final BTreeMap m; protected final K lo; protected final boolean loInclusive; protected final K hi; protected final boolean hiInclusive; public DescendingMap(BTreeMap m, K lo, boolean loInclusive, K hi, boolean hiInclusive) { this.m = m; this.lo = lo; this.loInclusive = loInclusive; this.hi = hi; this.hiInclusive = hiInclusive; if(lo!=null && hi!=null && m.keySerializer.comparator().compare(lo, hi)>0){ throw new IllegalArgumentException(); } } /* ---------------- Map API methods -------------- */ @Override public boolean containsKey(Object key) { if (key == null) throw new NullPointerException(); K k = (K)key; return inBounds(k) && m.containsKey(k); } @Override public V get(Object key) { if (key == null) throw new NullPointerException(); K k = (K)key; return ((!inBounds(k)) ? null : m.get(k)); } @Override public V put(K key, V value) { checkKeyBounds(key); return m.put(key, value); } @Override public V remove(Object key) { K k = (K)key; return (!inBounds(k))? null : m.remove(k); } @Override public int size() { if(hi==null && lo==null) return m.size(); Iterator i = keyIterator(); long counter = 0; while(i.hasNext()){ counter++; i.next(); } return (int) Math.min(counter, Integer.MAX_VALUE); } @Override public boolean isEmpty() { return !keyIterator().hasNext(); } @Override public boolean containsValue(Object value) { if(value==null) throw new NullPointerException(); Iterator i = valueIterator(); while(i.hasNext()){ if(m.valueSerializer.equals((V) value,i.next())) return true; } return false; } @Override public void clear() { Iterator i = keyIterator(); while(i.hasNext()){ i.next(); i.remove(); } } /* ---------------- ConcurrentMap API methods -------------- */ @Override public V putIfAbsent(K key, V value) { checkKeyBounds(key); return m.putIfAbsent(key, value); } @Override public boolean remove(Object key, Object value) { K k = (K)key; return inBounds(k) && m.remove(k, value); } @Override public boolean replace(K key, V oldValue, V newValue) { checkKeyBounds(key); return m.replace(key, oldValue, newValue); } @Override public V replace(K key, V value) { checkKeyBounds(key); return m.replace(key, value); } /* ---------------- SortedMap API methods -------------- */ @Override public Comparator comparator() { return m.comparator(); } /* ---------------- Relational methods -------------- */ @Override public Map.Entry higherEntry(K key) { if(key==null)throw new NullPointerException(); if(tooLow(key))return null; if(tooHigh(key)) return firstEntry(); Entry r = m.lowerEntry(key); return r!=null && !tooLow(r.getKey()) ? r :null; } @Override public K lowerKey(K key) { Entry n = lowerEntry(key); return (n == null)? null : n.getKey(); } @Override public Map.Entry ceilingEntry(K key) { if(key==null) throw new NullPointerException(); if(tooLow(key)) return null; if(tooHigh(key)){ return firstEntry(); } Entry ret = m.floorEntry(key); if(ret!=null && tooLow(ret.getKey())) return null; return ret; } @Override public K floorKey(K key) { Entry n = floorEntry(key); return (n == null)? null : n.getKey(); } @Override public Map.Entry floorEntry(K key) { if(key==null) throw new NullPointerException(); if(tooHigh(key)) return null; if(tooLow(key)){ return lastEntry(); } Entry ret = m.ceilingEntry(key); if(ret!=null && tooHigh(ret.getKey())) return null; return ret; } @Override public K ceilingKey(K key) { Entry k = ceilingEntry(key); return k!=null? k.getKey():null; } @Override public Entry lowerEntry(K key) { Entry r = m.higherEntry(key); return r!=null && inBounds(r.getKey()) ? r : null; } @Override public K higherKey(K key) { Entry k = higherEntry(key); return k!=null? k.getKey():null; } @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 Map.Entry lastEntry() { Entry k = lo==null ? m.firstEntry(): m.findLarger(lo, loInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Map.Entry firstEntry() { Entry k = hi==null ? m.lastEntry(): m.findSmaller(hi, hiInclusive); return k!=null && inBounds(k.getKey())? k : null; } @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; } } } /** * Utility to create submaps, where given bounds override * unbounded(null) ones and/or are checked against bounded ones. */ private DescendingMap newSubMap( K toKey, boolean toInclusive, K fromKey, boolean fromInclusive) { // if(fromKey!=null && toKey!=null){ // int comp = m.comparator.compare(fromKey, toKey); // if((fromInclusive||!toInclusive) && comp==0) // throw new IllegalArgumentException(); // } if (lo != null) { if (fromKey == null) { fromKey = lo; fromInclusive = loInclusive; } else { int c = m.keySerializer.comparator().compare(fromKey, lo); if (c < 0 || (c == 0 && !loInclusive && fromInclusive)) throw new IllegalArgumentException("key out of range"); } } if (hi != null) { if (toKey == null) { toKey = hi; toInclusive = hiInclusive; } else { int c = m.keySerializer.comparator().compare(toKey, hi); if (c > 0 || (c == 0 && !hiInclusive && toInclusive)) throw new IllegalArgumentException("key out of range"); } } return new DescendingMap(m, fromKey, fromInclusive, toKey, toInclusive); } @Override public DescendingMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { if (fromKey == null || toKey == null) throw new NullPointerException(); return newSubMap(fromKey, fromInclusive, toKey, toInclusive); } @Override public DescendingMap headMap(K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); return newSubMap(null, false, toKey, inclusive); } @Override public DescendingMap tailMap(K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); return newSubMap(fromKey, inclusive, null, false); } @Override public DescendingMap subMap(K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @Override public DescendingMap headMap(K toKey) { return headMap(toKey, false); } @Override public DescendingMap tailMap(K fromKey) { return tailMap(fromKey, true); } @Override public ConcurrentNavigableMap descendingMap() { if(lo==null && hi==null) return m; return m.subMap(lo,loInclusive,hi,hiInclusive); } @Override public NavigableSet navigableKeySet() { return new KeySet((ConcurrentNavigableMap) this,m.hasValues); } /* ---------------- Utilities -------------- */ private boolean tooLow(K key) { if (lo != null) { int c = m.keySerializer.comparator().compare(key, lo); if (c < 0 || (c == 0 && !loInclusive)) return true; } return false; } private boolean tooHigh(K key) { if (hi != null) { int c = m.keySerializer.comparator().compare(key, hi); if (c > 0 || (c == 0 && !hiInclusive)) return true; } return false; } private boolean inBounds(K key) { return !tooLow(key) && !tooHigh(key); } private void checkKeyBounds(K key) throws IllegalArgumentException { if (key == null) throw new NullPointerException(); if (!inBounds(key)) throw new IllegalArgumentException("key out of range"); } @Override public NavigableSet keySet() { return new KeySet((ConcurrentNavigableMap) this, m.hasValues); } @Override public NavigableSet descendingKeySet() { return new KeySet((ConcurrentNavigableMap) descendingMap(), m.hasValues); } @Override public Set> entrySet() { return new EntrySet(this,m.valueSerializer); } /* * ITERATORS */ Iterator keyIterator() { return new BTreeDescendingKeyIterator(m,lo,loInclusive,hi,hiInclusive); } Iterator valueIterator() { return new BTreeDescendingValueIterator(m,lo,loInclusive,hi,hiInclusive); } Iterator> entryIterator() { return new BTreeDescendingEntryIterator(m,lo,loInclusive,hi,hiInclusive); } } /** * Make readonly snapshot view of current Map. Snapshot is immutable and not affected by modifications made by other threads. * Useful if you need consistent view on Map. * * Maintaining snapshot have some overhead, underlying Engine is closed after Map view is GCed. * Please make sure to release reference to this Map view, so snapshot view can be garbage collected. * * @return snapshot */ public NavigableMap snapshot(){ Engine snapshot = TxEngine.createSnapshotFor(engine); return new BTreeMap( snapshot, closeEngine, rootRecidRef, maxNodeSize, valsOutsideNodes, counter==null?0L:counter.recid, keySerializer, valueSerializer, numberOfNodeMetas ); } protected final Object modListenersLock = new Object(); protected Bind.MapListener[] modListeners = new Bind.MapListener[0]; @Override public void modificationListenerAdd(Bind.MapListener listener) { synchronized (modListenersLock){ Bind.MapListener[] modListeners2 = Arrays.copyOf(modListeners,modListeners.length+1); modListeners2[modListeners2.length-1] = listener; modListeners = modListeners2; } } @Override public void modificationListenerRemove(Bind.MapListener listener) { synchronized (modListenersLock){ for(int i=0;i[] modListeners2 = modListeners; for(Bind.MapListener listener:modListeners2){ if(listener!=null) listener.update(key, oldValue, newValue); } } public Engine getEngine(){ return engine; } public void printTreeStructure() { final long rootRecid = engine.get(rootRecidRef, Serializer.RECID); printRecur(this, rootRecid, ""); } private static void printRecur(BTreeMap m, long recid, String s) { BTreeMap.BNode n = (BTreeMap.BNode) m.engine.get(recid, m.nodeSerializer); System.out.println(s + recid + "-" + n); if(!n.isLeaf()){ int childArrayLen = n.childArrayLength()-1; for(int i=0;i locks){ LongConcurrentHashMap.LongMapIterator i = locks.longMapIterator(); Thread t =null; while(i.moveToNext()){ if(t==null) t = Thread.currentThread(); if(i.value()==t){ throw new AssertionError("Node "+i.key()+" is still locked"); } } } protected static void unlock(LongConcurrentHashMap locks,final long recid) { final Thread t = locks.remove(recid); if(CC.ASSERT && ! (t==Thread.currentThread())) throw new AssertionError("unlocked wrong thread"); } protected static void unlockAll(LongConcurrentHashMap locks) { final Thread t = Thread.currentThread(); LongConcurrentHashMap.LongMapIterator iter = locks.longMapIterator(); while(iter.moveToNext()) if(iter.value()==t) iter.remove(); } protected static void lock(LongConcurrentHashMap locks, long recid){ //feel free to rewrite, if you know better (more efficient) way final Thread currentThread = Thread.currentThread(); //check node is not already locked by this thread if(CC.ASSERT && ! (locks.get(recid)!= currentThread)) throw new AssertionError("node already locked by current thread: "+recid); while(locks.putIfAbsent(recid, currentThread) != null){ LockSupport.parkNanos(10); } } public void checkStructure(){ Store.LongObjectMap recids = new Store.LongObjectMap(); final long recid = engine.get(rootRecidRef, Serializer.RECID); checkNodeRecur(recid,recids); } private void checkNodeRecur(long rootRecid, Store.LongObjectMap recids) { BNode n = engine.get(rootRecid, nodeSerializer); n.checkStructure(keySerializer,valueNodeSerializer); if(recids.get(rootRecid)!=null){ throw new DBException.DataCorruption("Duplicate recid: "+rootRecid); } recids.put(rootRecid,this); if(n.next()!=0L && recids.get(n.next())==null){ throw new DBException.DataCorruption("Next link was not found: "+n); } if(n.next()==rootRecid){ throw new DBException.DataCorruption("Recursive next: "+n); } if(!n.isLeaf()){ for(int i=n.childArrayLength()-1;i>=0;i--){ long recid = n.child(i); if(recid==rootRecid){ throw new DBException.DataCorruption("Recursive recid: "+n); } if(recid==0 || recid==n.next()){ continue; } checkNodeRecur(recid, recids); } } } @Override public void close(){ if(closeEngine) { engine.close(); } } void compactLevel(int level){ int k = maxNodeSize * 3/4; // current := pointer to leftmost node at level (i + 1) long current = leftEdges.get(level+1); //TODO check level // one := nil long one = 0; // while ( current != nil ) // { while(current!=0) { // lock(current) lock(nodeLocks, current); // F := get(current) BNode F = engine.get(current, nodeSerializer); // pos := the index of pointer one in F int pos = F.childIndexOf(one); //TODO verify // oldone := one long olddone = one; // if (( one == nil ) or (pos > -1 and F.i > pos)) // { if (((one == 0)) || (pos > -1 && F.keysLen(keySerializer) > pos)) { // if ( one == nil ) // { if (one == 0) { // one := F.p[0] one = F.child(0); // } // else // { } else { // one := F.p[(pos + 1)] one = F.child(pos + 1); // } } // lock(one) lock(nodeLocks, one); // A := get(one) BNode A = engine.get(one, nodeSerializer); // two := link of A long two = A.next(); // if ( two == nil ) // { if (two == 0) { // return return; // } } // lock(two) lock(nodeLocks, two); // B := get(two) BNode B = engine.get(two, nodeSerializer); // if the index of pointer two in F > -1 // { if (F.childIndexOf(two) > -1) { //two is in F // if (k > A.i or k > B.i) // { if (k > A.keysLen(keySerializer) || k > B.keysLen(keySerializer)) { // rearrange A and B //TODO ?? // root delete is not implemented yet, so skip that branch // if B.deleted // { // delete link to two from F // } // else { // F.v[pos] = highvalue(A) F = F; // TODO copy and modify?? } // put(A, one) engine.update(one, A, nodeSerializer); // unlock(one) unlock(nodeLocks, one); // put(F, current) engine.update(current, F, nodeSerializer); // unlock(current) unlock(nodeLocks, current); // put(B, two) engine.update(two, B, nodeSerializer); // unlock(two) unlock(nodeLocks, two); // } } // root delete is not implemented yet, so skip that branch // if ( B.deleted == false ) // { // unlock(current) // unlock(one) // unlock(two) // one := two // } // } // else // { } else { // unlock(current) unlock(nodeLocks, current); // unlock(one) unlock(nodeLocks, one); // unlock(two) unlock(nodeLocks, two); // if highvalue(B) > highvalue(F) // { if (keySerializer.comparator().compare(B.highKey(keySerializer), F.highKey(keySerializer)) > 0) { // current := link of F current = F.next(); // one := nil one = 0; // } // else // { } else { // if (k > A.i or k > B.i) // { if (k > A.keysLen(keySerializer) || k > B.keysLen(keySerializer)) { // one := oldone one = olddone; // } } // } } // } } // } // else // { } else { // unlock(current) unlock(nodeLocks, current); // current := link of F current = F.next(); // one := nil one = 0; // } } // } } } Object writeReplace() throws ObjectStreamException { Map ret = new ConcurrentSkipListMap(); for(Map.Entry e:entrySet()){ ret.put(e.getKey(), e.getValue()); } return ret; } }