package org.mapdb; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.mapdb.serializer.GroupSerializer; import java.io.Closeable; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.*; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListSet; /** * Java code for BTreeMap. Mostly performance sensitive code. */ public class BTreeMapJava { static final int DIR = 1<<3; static final int LEFT = 1<<2; static final int RIGHT = 1<<1; static final int LAST_KEY_DOUBLE = 1; public static class Node{ /** bit flags (dir, left most, right most, next key equal to last...) */ final byte flags; /** link to next node */ final long link; /** represents keys */ final Object keys; /** represents values for leaf node, or ArrayLong of children for dir node */ final Object values; Node(int flags, long link, Object keys, Object values){ this.flags = (byte)flags; this.link = link; this.keys = keys; this.values = values; if(CC.ASSERT && isLastKeyDouble() && isDir()) throw new AssertionError(); if(CC.ASSERT && isRightEdge() && (link!=0L)) throw new AssertionError(); if(CC.ASSERT && !isRightEdge() && (link==0L)) throw new AssertionError(); if(CC.ASSERT && isDir() && getChildren().length==0) throw new AssertionError(); } int intDir(){ return (flags>>>3)&1; } int intLeftEdge(){ return (flags>>>2)&1; } int intRightEdge(){ return (flags>>>1)&1; } int intLastKeyTwice(){ return flags&1; } boolean isDir(){ return ((flags>>>3)&1)==1; } boolean isLeftEdge(){ return ((flags>>>2)&1)==1; } boolean isRightEdge(){ return ((flags>>>1)&1)==1; } boolean isLastKeyDouble(){ return ((flags)&1)==1; } boolean isEmpty(GroupSerializer keySerializer){ int keySize = keySerializer.valueArraySize(keys); return !isLastKeyDouble() && keySize == 2-intLeftEdge()-intRightEdge(); } @Nullable public <K> K highKey(GroupSerializer<K> keySerializer) { int keysLen = keySerializer.valueArraySize(keys); return keySerializer.valueArrayGet(keys, keysLen-1); } public long[] getChildren(){ return (long[]) values; } public void verifyNode(GroupSerializer keySerializer, Comparator comparator, GroupSerializer valueSerializer) { int keysLen = keySerializer.valueArraySize(keys); if (isDir()){ // compare directory size if( keysLen - 1 + intLeftEdge() + intRightEdge() != ((long[]) values).length) { throw new AssertionError(); } } else{ // compare leaf size if (keysLen != valueSerializer.valueArraySize(values) + 2 - intLeftEdge() - intRightEdge() - intLastKeyTwice()) { throw new AssertionError(); } } //ensure keys are sorted if(keysLen>1) { for (int i = 1; i < keysLen; i++){ int c = comparator.compare( keySerializer.valueArrayGet(keys, i-1), keySerializer.valueArrayGet(keys, i)); if(c>0) throw new AssertionError(); if(c==0 && i!=keysLen-1) throw new AssertionError(); } } } } public static class NodeSerializer implements Serializer<Node>{ final GroupSerializer keySerializer; final Comparator comparator; final GroupSerializer valueSerializer; NodeSerializer(GroupSerializer keySerializer, Comparator comparator, GroupSerializer valueSerializer) { this.keySerializer = keySerializer; this.comparator = comparator; this.valueSerializer = valueSerializer; } @Override public void serialize(@NotNull DataOutput2 out, @NotNull Node value) throws IOException { if(CC.ASSERT && value.flags>>>4!=0) throw new AssertionError(); if(CC.PARANOID) value.verifyNode(keySerializer, comparator, valueSerializer); int keysLenOrig = keySerializer.valueArraySize(value.keys); int keysLen = keySerializer.valueArraySize(value.keys)<<4; keysLen += value.flags; keysLen = DataIO.parity1Set(keysLen<<1); //keysLen and flags are combined into single packed long, that saves a byte for small nodes out.packInt(keysLen); if(!value.isRightEdge()) out.packLong(value.link); if(keysLenOrig>0) keySerializer.valueArraySerialize(out, value.keys); if(value.isDir()) { long[] child = (long[]) value.values; out.packLongArray(child, 0, child.length ); }else valueSerializer.valueArraySerialize(out, value.values); } @Override public Node deserialize(@NotNull DataInput2 input, int available) throws IOException { int keysLen = DataIO.parity1Get(input.unpackInt())>>>1; int flags = keysLen & 0xF; keysLen = keysLen>>>4; long link = (flags&RIGHT)!=0 ? 0L : input.unpackLong(); Object keys = keysLen==0? keySerializer.valueArrayEmpty() : keySerializer.valueArrayDeserialize(input, keysLen); if(CC.ASSERT && keysLen!=keySerializer.valueArraySize(keys)) throw new AssertionError(); Object values; if((flags&DIR)!=0){ keysLen = keysLen - 1 + (flags>>2&1) +(flags>>1&1); long[] c = new long[keysLen]; values = c; input.unpackLongArray(c, 0, keysLen); }else{ values = valueSerializer.valueArrayDeserialize(input, keysLen - 2 + ((flags >>> 2) & 1) + ((flags >>> 1) & 1) + (flags & 1)); } Node ret = new Node(flags, link, keys, values); if(CC.PARANOID) ret.verifyNode(keySerializer, comparator, valueSerializer); return ret; } @Override public boolean isTrusted() { return keySerializer.isTrusted() && valueSerializer.isTrusted(); } } public static final Comparator COMPARABLE_COMPARATOR = new Comparator<Comparable>() { @Override public int compare(Comparable o1, Comparable o2) { return o1.compareTo(o2); } }; static long findChild(GroupSerializer keySerializer, Node node, Comparator comparator, Object key){ if(CC.ASSERT && !node.isDir()) throw new AssertionError(); //find an index int pos = keySerializer.valueArraySearch(node.keys, key, comparator); if(pos<0) pos = -pos-1; pos += -1+node.intLeftEdge(); pos = Math.max(0, pos); long[] children = (long[]) node.values; if(pos>=children.length) { if(CC.ASSERT && node.isRightEdge()) throw new AssertionError(); return node.link; } return children[pos]; } static final Object LINK = new Object(){ @Override public String toString() { return "BTreeMap.LINK"; } }; static Object leafGet(Node node, Comparator comparator, Object key, GroupSerializer keySerializer, GroupSerializer valueSerializer){ int pos = keySerializer.valueArraySearch(node.keys, key, comparator); return leafGet(node, pos, keySerializer, valueSerializer); } static Object leafGet(Node node, int pos, GroupSerializer keySerializer, GroupSerializer valueSerializer){ if(pos<0+1-node.intLeftEdge()) { if(!node.isRightEdge() && pos<-keySerializer.valueArraySize(node.keys)) return LINK; else return null; } int valsLen = valueSerializer.valueArraySize(node.values); if(!node.isRightEdge() && pos==valsLen+1) return null; else if(pos>=valsLen+1){ return LINK; } pos = pos-1+node.intLeftEdge(); if(pos>=valsLen) return null; return valueSerializer.valueArrayGet(node.values, pos); } /* expand array size by 1, and put value at given position. No items from original array are lost*/ protected static Object[] arrayPut(final Object[] array, final int pos, final Object value){ final Object[] ret = Arrays.copyOf(array, array.length+1); if(pos<array.length){ System.arraycopy(array, pos, ret, pos+1, array.length-pos); } ret[pos] = value; return ret; } /* expand array size by 1, and put value at given position. No items from original array are lost*/ protected static long[] arrayPut(final long[] array, final int pos, final long value){ final long[] ret = Arrays.copyOf(array, array.length+1); if(pos<array.length){ System.arraycopy(array, pos, ret, pos+1, array.length-pos); } ret[pos] = value; return ret; } public static class BinaryGet<K, V> implements StoreBinaryGetLong { final GroupSerializer<K> keySerializer; final GroupSerializer<V> valueSerializer; final Comparator<K> comparator; final K key; V value = null; public BinaryGet( @NotNull GroupSerializer<K> keySerializer, @NotNull GroupSerializer<V> valueSerializer, @NotNull Comparator<K> comparator, @NotNull K key ) { this.keySerializer = keySerializer; this.valueSerializer = valueSerializer; this.comparator = comparator; this.key = key; } @Override public long get(DataInput2 input, int size) throws IOException { //read size and flags int keysLen = DataIO.parity1Get(input.unpackInt())>>>1; int flags = keysLen&0xF; keysLen = keysLen>>>4; if(keysLen==0) return -1L; long link = (flags&RIGHT)!=0 ? 0L : input.unpackLong(); int intLeft = ((flags >>> 2) & 1); int intRight = ((flags >>> 1) & 1); int pos = keySerializer.valueArrayBinarySearch(key, input, keysLen, comparator); if((flags&DIR)!=0){ //is directory, return related children if(pos<0) pos = -pos-1; pos += -1 + intLeft; // plus left edge pos = Math.max(0, pos); keysLen = keysLen - 1 + intLeft + intRight; if(pos>=keysLen) { if(CC.ASSERT && intRight==1) throw new AssertionError(); return link; } if(pos>0) input.unpackLongSkip(pos-1); return input.unpackLong(); } //is leaf, get value from leaf if(pos<0+1-intLeft) { if(intRight==0 && pos<-keysLen) return link; else return -1; } int valsLen = keysLen - 2 + intLeft + intRight + (flags & 1); if(intRight==0 /*is not right edge*/ && pos==valsLen+1) { //return null return -1; }else if(pos>=valsLen+1){ return link; } pos = pos-1+((flags >>> 2) & 1); if(pos>=valsLen) { //return null return -1; } //found value, return it value = valueSerializer.valueArrayBinaryGet(input, valsLen, pos); return -1L; } } static <E> List<E> toList(Collection<E> c) { // Using size() here would be a pessimization. List<E> list = new ArrayList<E>(); for (E e : c){ list.add(e); } return list; } public static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E>, Closeable, Serializable { protected final ConcurrentNavigableMap2<E,Object> m; private final boolean hasValues; private final Serializer<E> serializer; KeySet(ConcurrentNavigableMap2<E, Object> map, boolean hasValues, Serializer<E> serializer) { m = map; this.hasValues = hasValues; this.serializer = serializer; } @Override public int size() { return m.size(); } public long sizeLong(){ if (m instanceof BTreeMap) return ((BTreeMap<Object,E>)m).sizeLong(); else return ((SubMap<Object,E>)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) { if(m instanceof MapExtra) return ((MapExtra) m).removeBoolean(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() { while(true){ E e = m.firstKey2(); if(e==null || m.remove(e)!=null){ return e; } } } @Override public E pollLast() { while(true){ E e = m.lastKey2(); if(e==null || m.remove(e)!=null){ return e; } } } @Override public Iterator<E> iterator() { if (m instanceof ConcurrentNavigableMapExtra) return ((ConcurrentNavigableMapExtra<E,Object>)m).keyIterator(); else if(m instanceof SubMap) return ((BTreeMapJava.SubMap<E,Object>)m).keyIterator(); else return ((BTreeMapJava.DescendingMap<E,Object>)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 int hashCode() { return Utils.iterableHashCode(serializer, this); } @Override public Object[] toArray() { return toList(this).toArray(); } @Override public <T> T[] toArray(T[] a) { return toList(this).toArray(a); } @Override public Iterator<E> descendingIterator() { return descendingSet().iterator(); } @Override public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { return new KeySet<E>((ConcurrentNavigableMap2)m.subMap(fromElement, fromInclusive, toElement, toInclusive),hasValues, serializer); } @Override public NavigableSet<E> headSet(E toElement, boolean inclusive) { return new KeySet<E>((ConcurrentNavigableMap2)m.headMap(toElement, inclusive),hasValues, serializer); } @Override public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { return new KeySet<E>((ConcurrentNavigableMap2)m.tailMap(fromElement, inclusive),hasValues, serializer); } @Override public NavigableSet<E> subSet(E fromElement, E toElement) { return subSet(fromElement, true, toElement, false); } @Override public NavigableSet<E> headSet(E toElement) { return headSet(toElement, false); } @Override public NavigableSet<E> tailSet(E fromElement) { return tailSet(fromElement, true); } @Override public NavigableSet<E> descendingSet() { return new KeySet((ConcurrentNavigableMap2)m.descendingMap(),hasValues, serializer); } @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 EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> { private final ConcurrentNavigableMap<K1, V1> m; private final Serializer valueSerializer; EntrySet(ConcurrentNavigableMap<K1, V1> map, Serializer valueSerializer) { m = map; this.valueSerializer = valueSerializer; } @Override public Iterator<Map.Entry<K1,V1>> iterator() { if (m instanceof BTreeMap) return ((BTreeMap<K1,V1>)m).entryIterator(); else if(m instanceof SubMap) return ((SubMap<K1,V1>)m).entryIterator(); else return ((DescendingMap<K1,V1>)m).entryIterator(); } @Override public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)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<K1,V1> e = (Map.Entry<K1,V1>)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> T[] toArray(T[] a) { return toList(this).toArray(a); } } static protected class SubMap<K,V> extends AbstractMap<K,V> implements ConcurrentNavigableMap2<K,V> { protected final ConcurrentNavigableMapExtra<K,V> m; protected final K lo; protected final boolean loInclusive; protected final K hi; protected final boolean hiInclusive; public SubMap(ConcurrentNavigableMapExtra<K,V> 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.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<K> 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<V> i = valueIterator(); while(i.hasNext()){ if(m.getValueSerializer().equals((V)value,i.next())) return true; } return false; } @Override public void clear() { Iterator<K> 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<? super K> comparator() { return m.comparator(); } /* ---------------- Relational methods -------------- */ @Override public Map.Entry<K,V> lowerEntry(K key) { if(key==null)throw new NullPointerException(); if(tooLow(key))return null; if(tooHigh(key)) return lastEntry(); Entry<K,V> r = m.lowerEntry(key); return r!=null && !tooLow(r.getKey()) ? r :null; } @Override public K lowerKey(K key) { if(key==null)throw new NullPointerException(); if(tooLow(key))return null; if(tooHigh(key)) return lastKey2(); K r = m.lowerKey(key); return r!=null && !tooLow(r) ? r :null; } @Override public Map.Entry<K,V> floorEntry(K key) { if(key==null) throw new NullPointerException(); if(tooLow(key)) return null; if(tooHigh(key)){ return lastEntry(); } Entry<K,V> ret = m.floorEntry(key); if(ret!=null && tooLow(ret.getKey())) return null; return ret; } @Override public K floorKey(K key) { if(key==null) throw new NullPointerException(); if(tooLow(key)) return null; if(tooHigh(key)){ return lastKey2(); } K ret = m.floorKey(key); if(ret!=null && tooLow(ret)) return null; return ret; } @Override public Map.Entry<K,V> ceilingEntry(K key) { if(key==null) throw new NullPointerException(); if(tooHigh(key)) return null; if(tooLow(key)){ return firstEntry(); } Entry<K,V> ret = m.ceilingEntry(key); if(ret!=null && tooHigh(ret.getKey())) return null; return ret; } @Override public K ceilingKey(K key) { if(key==null) throw new NullPointerException(); if(tooHigh(key)) return null; if(tooLow(key)){ return firstKey2(); } K ret = m.ceilingKey(key); if(ret!=null && tooHigh(ret)) return null; return ret; } @Override public Entry<K, V> higherEntry(K key) { Entry<K,V> r = m.higherEntry(key); return r!=null && inBounds(r.getKey()) ? r : null; } @Override public K higherKey(K key) { K r = m.higherKey(key); return r!=null && inBounds(r) ? r : null; } public K firstKey2() { K k = lo==null ? m.firstKey2(): m.findHigherKey(lo, loInclusive); return k!=null && inBounds(k)? k : null; } public K lastKey2() { K k = hi==null ? m.lastKey2(): m.findLowerKey(hi, hiInclusive); return k!=null && inBounds(k)? k : null; } @Override public K firstKey() { K ret = firstKey2(); if(ret==null) throw new NoSuchElementException(); return ret; } @Override public K lastKey() { K ret = lastKey2(); if(ret==null) throw new NoSuchElementException(); return ret; } @Override public Map.Entry<K,V> firstEntry() { Entry<K,V> k = lo==null ? m.firstEntry(): m.findHigher(lo, loInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Map.Entry<K,V> lastEntry() { Entry<K,V> k = hi==null ? m.lastEntry(): m.findLower(hi, hiInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Entry<K, V> pollFirstEntry() { while(true){ Entry<K, V> e = firstEntry(); if(e==null || remove(e.getKey(),e.getValue())){ return e; } } } @Override public Entry<K, V> pollLastEntry() { while(true){ Entry<K, V> 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<K,V> 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.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.comparator().compare(toKey, hi); if (c > 0 || (c == 0 && !hiInclusive && toInclusive)) throw new IllegalArgumentException("key out of range"); } } return new SubMap<K,V>(m, fromKey, fromInclusive, toKey, toInclusive); } @Override public SubMap<K,V> 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<K,V> headMap(K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); return newSubMap(null, false, toKey, inclusive); } @Override public SubMap<K,V> tailMap(K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); return newSubMap(fromKey, inclusive, null, false); } @Override public SubMap<K,V> subMap(K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @Override public SubMap<K,V> headMap(K toKey) { return headMap(toKey, false); } @Override public SubMap<K,V> tailMap(K fromKey) { return tailMap(fromKey, true); } @Override public ConcurrentNavigableMap<K,V> descendingMap() { return new DescendingMap(m, lo,loInclusive, hi,hiInclusive); } @Override public NavigableSet<K> navigableKeySet() { return new KeySet<K>((ConcurrentNavigableMap2<K,Object>) this,m.getHasValues(), m.getKeySerializer()); } /* ---------------- Utilities -------------- */ private boolean tooLow(K key) { if (lo != null) { int c = m.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.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<K> keySet() { return new KeySet<K>((ConcurrentNavigableMap2<K,Object>) this, m.getHasValues(), m.getKeySerializer()); } @Override public NavigableSet<K> descendingKeySet() { return new DescendingMap<K,V>(m,lo,loInclusive, hi, hiInclusive).keySet(); } @Override public Set<Entry<K, V>> entrySet() { return new EntrySet<K, V>(this,m.getValueSerializer()); } Iterator<K> keyIterator() { return m.keyIterator(lo,loInclusive,hi,hiInclusive); } Iterator<V> valueIterator() { return m.valueIterator(lo,loInclusive,hi,hiInclusive); } Iterator<Map.Entry<K,V>> entryIterator() { return m.entryIterator(lo,loInclusive,hi,hiInclusive); } } public interface ConcurrentNavigableMap2<K,V> extends ConcurrentNavigableMap<K,V>{ /** returns first key in map or null, does not throw `NoSuchElementException` as `firstKey()` does */ K firstKey2(); /** returns last key in map or null, does not throw `NoSuchElementException` as `lastKey()` does */ K lastKey2(); } static protected class DescendingMap<K,V> extends AbstractMap<K,V> implements ConcurrentNavigableMap2<K,V> { protected final ConcurrentNavigableMapExtra<K,V> m; protected final K lo; protected final boolean loInclusive; protected final K hi; protected final boolean hiInclusive; public DescendingMap(ConcurrentNavigableMapExtra<K,V> 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.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<K> i = m.keyIterator(lo, loInclusive, hi, hiInclusive); 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<V> i = valueIterator(); while(i.hasNext()){ if(m.getValueSerializer().equals((V) value,i.next())) return true; } return false; } @Override public void clear() { Iterator<K> 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<? super K> comparator() { return m.comparator(); } /* ---------------- Relational methods -------------- */ @Override public Map.Entry<K,V> higherEntry(K key) { if(key==null)throw new NullPointerException(); if(tooLow(key))return null; if(tooHigh(key)) return firstEntry(); Entry<K,V> r = m.lowerEntry(key); return r!=null && !tooLow(r.getKey()) ? r :null; } @Override public K lowerKey(K key) { Entry<K,V> n = lowerEntry(key); return (n == null)? null : n.getKey(); } @Override public Map.Entry<K,V> ceilingEntry(K key) { if(key==null) throw new NullPointerException(); if(tooLow(key)) return null; if(tooHigh(key)){ return firstEntry(); } Entry<K,V> ret = m.floorEntry(key); if(ret!=null && tooLow(ret.getKey())) return null; return ret; } @Override public K floorKey(K key) { Entry<K,V> n = floorEntry(key); return (n == null)? null : n.getKey(); } @Override public Map.Entry<K,V> floorEntry(K key) { if(key==null) throw new NullPointerException(); if(tooHigh(key)) return null; if(tooLow(key)){ return lastEntry(); } Entry<K,V> ret = m.ceilingEntry(key); if(ret!=null && tooHigh(ret.getKey())) return null; return ret; } @Override public K ceilingKey(K key) { Entry<K,V> k = ceilingEntry(key); return k!=null? k.getKey():null; } @Override public Entry<K, V> lowerEntry(K key) { Entry<K,V> r = m.higherEntry(key); return r!=null && inBounds(r.getKey()) ? r : null; } @Override public K higherKey(K key) { Entry<K,V> k = higherEntry(key); return k!=null? k.getKey():null; } @Override public K firstKey2() { Entry<K,V> e = firstEntry(); if(e==null) return null; return e.getKey(); } @Override public K lastKey2() { Entry<K,V> e = lastEntry(); if(e==null) return null; return e.getKey(); } @Override public K firstKey() { K key = firstKey2(); if(key==null) throw new NoSuchElementException(); return key; } @Override public K lastKey() { K key = lastKey2(); if(key==null) throw new NoSuchElementException(); return key; } @Override public Map.Entry<K,V> lastEntry() { Entry<K,V> k = lo==null ? m.firstEntry(): m.findHigher(lo, loInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Map.Entry<K,V> firstEntry() { Entry<K,V> k = hi==null ? m.lastEntry(): m.findLower(hi, hiInclusive); return k!=null && inBounds(k.getKey())? k : null; } @Override public Entry<K, V> pollFirstEntry() { while(true){ Entry<K, V> e = firstEntry(); if(e==null || remove(e.getKey(),e.getValue())){ return e; } } } @Override public Entry<K, V> pollLastEntry() { while(true){ Entry<K, V> 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<K,V> 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.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.comparator().compare(toKey, hi); if (c > 0 || (c == 0 && !hiInclusive && toInclusive)) throw new IllegalArgumentException("key out of range"); } } return new DescendingMap<K,V>(m, fromKey, fromInclusive, toKey, toInclusive); } @Override public DescendingMap<K,V> 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<K,V> headMap(K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); return newSubMap(null, false, toKey, inclusive); } @Override public DescendingMap<K,V> tailMap(K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); return newSubMap(fromKey, inclusive, null, false); } @Override public DescendingMap<K,V> subMap(K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @Override public DescendingMap<K,V> headMap(K toKey) { return headMap(toKey, false); } @Override public DescendingMap<K,V> tailMap(K fromKey) { return tailMap(fromKey, true); } @Override public ConcurrentNavigableMap<K,V> descendingMap() { if(lo==null && hi==null) return m; return m.subMap(lo,loInclusive,hi,hiInclusive); } @Override public NavigableSet<K> navigableKeySet() { return new KeySet<K>((ConcurrentNavigableMap2<K,Object>) this,m.getHasValues(), m.getKeySerializer()); } /* ---------------- Utilities -------------- */ private boolean tooLow(K key) { if (lo != null) { int c = m.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.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<K> keySet() { return new KeySet<K>((ConcurrentNavigableMap2<K,Object>) this, m.getHasValues(), m.getKeySerializer()); } @Override public NavigableSet<K> descendingKeySet() { return new KeySet<K>((ConcurrentNavigableMap2<K,Object>) descendingMap(), m.getHasValues(), m.getKeySerializer()); } @Override public Set<Entry<K, V>> entrySet() { return new EntrySet<K, V>(this,m.getValueSerializer()); } /* * ITERATORS */ Iterator<K> keyIterator() { if(lo==null && hi==null ) return m.descendingKeyIterator(); else return m.descendingKeyIterator(lo, loInclusive, hi, hiInclusive); } Iterator<V> valueIterator() { if(lo==null && hi==null ) return m.descendingValueIterator(); else return m.descendingValueIterator(lo, loInclusive, hi, hiInclusive); } Iterator<Map.Entry<K,V>> entryIterator() { if(lo==null && hi==null ) return m.descendingEntryIterator(); else return m.descendingEntryIterator(lo, loInclusive, hi, hiInclusive); } } }