// no concurrent read/write is assumed public V put(double key, V value) { if (value == null) throw new NullPointerException(); // not incrementing b's refcnt, so need not release it. Node b = skipListMap.findGrandPredecessor(key); if (b == null) b = head; Node n = b.next; if (n == null) { n = Node.alloc(); n.put(key, value, this); n.next = null; b.next = n; return null; } Node f = n.next; if (f == null) { // put (key,value) into node n Object val = n.put(key, value, this); return (V) val; } else { assert f.len() > 0 && f.first() >= key; if (f.first() == key) { Object val = f.put(key, value, this); return (V) val; } else { Object val = n.put(key, value, this); return (V) val; } } }
int findBeginIndex(Node n, double key, boolean inclusive) { boolean exclusive = !inclusive; if (key <= n.first()) { if (key == n.first() && exclusive) return 1; return 0; } int pos = n.findKeyIndex(key); if (pos < 0) pos = -(pos + 1); else if (!inclusive) pos++; return pos; }
V putAtomic(double key, V value, boolean onlyIfAbsent) { if (value == null) throw new NullPointerException(); for (; ; ) { Node b = skipListMap.findGrandPredecessor(key, refCntCallback); if (b == null) b = head; if (b != head && b.first() <= key && key <= b.last()) { release(b); continue; } assert b == head || b.last() < key; Node n = safeNext(b); if (n == null) { if (b.appendNewAtomic(key, value, this)) { release(b); return null; } else { release(b); continue; } } else if (n.first() <= key && key <= n.last()) { Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n); if (val == Retry) continue; return (V) val; } Node f = safeNext(n); if (f == null) { Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n); if (val == Retry) continue; return (V) val; } else { if (f.first() == key) { Object val = f.putAtomic(key, value, n, onlyIfAbsent, this); release(b, n, f); if (val == Retry) continue; return (V) val; } else { if (key > f.first()) { // inconsistent read, retry release(b, n, f); continue; } Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n, f); if (val == Retry) continue; return (V) val; } } } }
private boolean isInconsistentNextNode(Node f, double key) { /** * With skipListMap.findGrandPredecessor, we expect the key of the target node (next of next * node of returned node) to be greater than or equal to the given key. If not, there is * inconsistency between skipListMap and this ordered list map. * * @see Node#putAtomicReally for the cause of this inconsistency */ return f.next != null && f.first() < key; }
V doReplace(double key, V old, V value) { for (; ; ) { Node b = skipListMap.findGrandPredecessor(key, refCntCallback); if (b == null) b = head; if (b != head && b.first() <= key && key <= b.last()) { // can happen if there's inconsistency between skipListMap and orderedMap(this). // The inconsistency can happen from Node.putAtomicReally(), // where it adds to, and removes from skiplistMap. Object val = b.replace(key, old, value); release(b); if (val == Retry) continue; return (V) val; } assert b == head || b.last() < key; Node n = safeNext(b); release(b); if (n == null) { return null; } else if (n.first() <= key && key <= n.last()) { Object val = n.replace(key, old, value); release(n); if (val == Retry) continue; return (V) val; } Node f = safeNext(n); release(n); if (f == null) { return null; } else { if (isInconsistentNextNode(f, key)) { release(f); continue; } Object val = f.replace(key, old, value); release(f); if (val == Retry) continue; return (V) val; } } }
public void doSanityCheck() { int count = 0; double prevKey = Double.MIN_VALUE; Node n = safeNext(head); while (n != null) { count++; if (!skipListMap.containsKey(n.first())) { System.out.println("Node n not in skiplistMap:" + n.first()); n._print(); } if (n.first() < prevKey) { System.out.println("Error in ordering, prevKey:" + prevKey + ", n:" + n); } prevKey = n.first(); Node next = safeNext(n); release(n); n = next; } if (skipListMap.size() != count) { System.out.println("skipListMap.size:" + skipListMap.size() + ", count:" + count); } }
int findEndIndex(Node n, double key, boolean inclusive) { boolean exclusive = !inclusive; if (key < n.first()) return -1; if (key >= n.last()) { if (key == n.last() && exclusive) return n.len() - 2; return n.len() - 1; } int pos = n.findKeyIndex(key); if (pos < 0) pos = -(pos + 1); else if (!inclusive) pos--; if (pos == n.len()) pos--; return pos; }
protected Node first() { if (left == nullNode) return this; return left.first(); }
public K first() { if (isEmpty()) return null; if (from == null) return root.first().key; if (fromInclusive) return ceiling(from); return higher(from); }