BUG-7464: Refactor TrieMapIterator 83/49983/14
authorRobert Varga <rovarga@cisco.com>
Tue, 3 Jan 2017 16:41:08 +0000 (17:41 +0100)
committerRobert Varga <nite@hq.sk>
Tue, 10 Jan 2017 23:44:43 +0000 (23:44 +0000)
This eliminates unused code and re-organizes the type hierarchy
so we have an abstract base iterator specialized for mutable
and immutable cases. This allows the immutable iterator to not
track the last returned entry, making a bit lighter-weight.

Futhermore use of iterator in LNode is removed, as LNodeEntries
can simply be used the traverse the list without object allocation.

Finally it restores assumptions made by original Scala code, which
is that the base map for an iterator, used to access, is a read-only
one. This was probably damaged during initial Java porting, as
Scala iterators are read-only and the ability to mutate was added.

The was done without differentiating the iteration (read-only) map
from the mutation map (read-write).

Change-Id: I7949cde0712e3e02de1846a51b6dd0d3704bea58
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractIterator.java [new file with mode: 0644]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableIterator.java [new file with mode: 0644]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableTrieMap.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/LNode.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/LNodeEntries.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableIterator.java [new file with mode: 0644]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableTrieMap.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMap.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapIterator.java [deleted file]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapReadOnlyIterator.java [deleted file]
third-party/triemap/src/test/java/org/opendaylight/yangtools/triemap/TestMapIterator.java

diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractIterator.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractIterator.java
new file mode 100644 (file)
index 0000000..e13ddd6
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yangtools.triemap;
+
+import static org.opendaylight.yangtools.triemap.Constants.MAX_DEPTH;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract base class for iterators supporting {@link AbstractEntrySet} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> the type of entry keys
+ * @param <V> the type of entry values
+ */
+abstract class AbstractIterator<K, V> implements Iterator<Entry<K, V>> {
+    private final BasicNode[][] nodeStack = new BasicNode[MAX_DEPTH][];
+    private final int[] positionStack = new int[MAX_DEPTH];
+    private final TrieMap<K, V> map;
+
+    private LNodeEntries<K, V> lnode;
+    private EntryNode<K, V> current;
+    private int depth = -1;
+
+    AbstractIterator(final ImmutableTrieMap<K, V> map) {
+        this.map = map;
+        readin(map.RDCSS_READ_ROOT());
+    }
+
+    @Override
+    public final boolean hasNext() {
+        return current != null || lnode != null;
+    }
+
+    @Override
+    public final Entry<K, V> next() {
+        final Entry<K, V> entry;
+
+        // Check LNode iterator first
+        if (lnode != null) {
+            entry = lnode;
+            lnode = lnode.next();
+            if (lnode == null) {
+                advance();
+            }
+        } else {
+            entry = current;
+            advance();
+        }
+
+        if (entry == null) {
+            throw new NoSuchElementException();
+        }
+
+        return wrapEntry(entry);
+    }
+
+    /**
+     * Wrap entry so it can be presented to the user.
+     *
+     * @param entry An immutable entry, guaranteed to be non-null
+     * @return Wrapped entry, may not be null
+     */
+    abstract Entry<K, V> wrapEntry(Entry<K, V> entry);
+
+    /**
+     * Read the contents of an INode's main node.
+     *
+     * @param in INode to be read.
+     */
+    private void readin(final INode<K, V> in) {
+        final MainNode<K, V> m = in.gcasRead(map);
+        if (m instanceof CNode) {
+            // Enter the next level
+            final CNode<K, V> cn = (CNode<K, V>) m;
+            depth++;
+            nodeStack[depth] = cn.array;
+            positionStack[depth] = -1;
+            advance();
+        } else if (m instanceof TNode) {
+            current = (TNode<K, V>) m;
+        } else if (m instanceof LNode) {
+            lnode = ((LNode<K, V>) m).entries();
+        } else if (m == null) {
+            current = null;
+        }
+    }
+
+    private void advance() {
+        if (depth >= 0) {
+            int npos = positionStack[depth] + 1;
+            if (npos < nodeStack[depth].length) {
+                positionStack [depth] = npos;
+                BasicNode elem = nodeStack[depth][npos];
+                if (elem instanceof SNode) {
+                    current = (SNode<K, V>) elem;
+                } else if (elem instanceof INode) {
+                    readin((INode<K, V>) elem);
+                }
+            } else {
+                depth -= 1;
+                advance();
+            }
+        } else {
+            current = null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableIterator.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableIterator.java
new file mode 100644 (file)
index 0000000..e079adb
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yangtools.triemap;
+
+import java.util.Map.Entry;
+
+/**
+ * Specialized immutable iterator for use with {@link ImmutableEntrySet}.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> the type of entry keys
+ * @param <V> the type of entry values
+ */
+final class ImmutableIterator<K, V> extends AbstractIterator<K, V> {
+    ImmutableIterator(final ImmutableTrieMap<K, V> map) {
+        super(map);
+    }
+
+    @Override
+    Entry<K, V> wrapEntry(final Entry<K, V> entry) {
+        return entry;
+    }
+}
index 6f2982ad8d285691a2c399cb41cd37bee578817b..0949f99db66ddb344ad152336f02ceae11a88858 100644 (file)
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.annotations.Beta;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -129,6 +128,11 @@ public final class ImmutableTrieMap<K, V> extends TrieMap<K, V> {
         return true;
     }
 
+    @Override
+    ImmutableIterator<K, V> iterator() {
+        return immutableIterator();
+    }
+
     @Override
     INode<K, V> RDCSS_READ_ROOT(final boolean abort) {
         return root;
@@ -137,9 +141,4 @@ public final class ImmutableTrieMap<K, V> extends TrieMap<K, V> {
     static UnsupportedOperationException unsupported() {
         return new UnsupportedOperationException("Attempted to modify a read-only view");
     }
-
-    @Override
-    Iterator<Entry<K, V>> iterator() {
-        return immutableIterator();
-    }
 }
index ca28a06216a9962971da57e3e77a35146e170ac1..b4a32244466042debb13ed32e0a9019ed5747601 100644 (file)
  */
 package org.opendaylight.yangtools.triemap;
 
-import java.util.Iterator;
-import java.util.Map.Entry;
-
 final class LNode<K, V> extends MainNode<K, V> {
-    private final LNodeEntries<K, V> listmap;
+    // Internally-linked single list of of entries
+    private final LNodeEntries<K, V> entries;
+    private final int size;
 
-    private LNode(final LNodeEntries<K, V> listmap) {
-        this.listmap = listmap;
+    private LNode(final LNodeEntries<K, V> entries, final int size) {
+        this.entries = entries;
+        this.size = size;
     }
 
     LNode(final K k1, final V v1, final K k2, final V v2) {
-        this(LNodeEntries.map(k1, v1, k2, v2));
+        this(LNodeEntries.map(k1, v1, k2, v2), 2);
     }
 
     LNode<K, V> insertChild( final K k, final V v) {
-        return new LNode<>(listmap.insert(k, v));
+        return new LNode<>(entries.insert(k, v), size + 1);
     }
 
     MainNode<K, V> removeChild(final LNodeEntry<K, V> entry, final int hc) {
-        // We only ever create ListMaps with two or more entries,  and remove them as soon as they reach one element
-        // (below), so we cannot observe a null return here.
-        final LNodeEntries<K, V> map = listmap.remove(entry);
-        if (map.isSingle()) {
+        // While remove() can return null, that case will never happen here, as we are starting off with two entries
+        // so we cannot observe a null return here.
+        final LNodeEntries<K, V> map = entries.remove(entry);
+
+        // If the returned LNode would have only one element, we turn it into a TNode, hence above null return from
+        // remove() can never happen.
+        if (size == 2) {
             // create it tombed so that it gets compressed on subsequent accesses
             return new TNode<>(map.getKey(), map.getValue(), hc);
         }
 
-        return new LNode<>(map);
+        return new LNode<>(map, size - 1);
     }
 
     MainNode<K, V> replaceChild(final LNodeEntry<K, V> entry, final V v) {
-        return new LNode<>(listmap.replace(entry, v));
+        return new LNode<>(entries.replace(entry, v), size);
     }
 
     LNodeEntry<K, V> get(final Equivalence<? super K> equiv, final K k) {
-        return listmap.findEntry(equiv, k);
+        return entries.findEntry(equiv, k);
+    }
+
+    LNodeEntries<K, V> entries() {
+        return entries;
     }
 
     @Override
     int cachedSize(final TrieMap<?, ?> ct) {
-        return listmap.size();
+        return size;
     }
 
     @Override
@@ -63,8 +70,4 @@ final class LNode<K, V> extends MainNode<K, V> {
         // (" " * lev) + "LNode(%s)".format(listmap.mkString(", "))
         return "LNode";
     }
-
-    Iterator<Entry<K, V>> iterator() {
-        return listmap.iterator();
-    }
-}
\ No newline at end of file
+}
index 9f03dce653fdf8477ffeeec31fa17183b156d9d5..2fbfc7299575c3bd7646737431a4c83abc585734 100644 (file)
  */
 package org.opendaylight.yangtools.triemap;
 
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-
 /**
- * Similar to Scala's ListMap. Stores a linked set of entries, guaranteed to contain unique entry keys.
+ * Similar to Scala's ListMap, this is a single-linked list of set of map entries. Aside from the Set contract, this
+ * class fulfills the requirements for an immutable map entryset.
  *
  * @author Robert Varga
  *
@@ -33,11 +30,6 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
             super(k, v);
         }
 
-        @Override
-        boolean isSingle() {
-            return true;
-        }
-
         @Override
         LNodeEntries<K, V> next() {
             return null;
@@ -45,7 +37,7 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
     }
 
     private static final class Multiple<K, V> extends LNodeEntries<K, V> {
-        // Modified during remove only
+        // Modified during remove only, otherwise final
         LNodeEntries<K, V> next;
 
         // Used in remove() only
@@ -58,11 +50,6 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
             this.next = next;
         }
 
-        @Override
-        boolean isSingle() {
-            return next == null;
-        }
-
         @Override
         LNodeEntries<K, V> next() {
             return next;
@@ -77,8 +64,12 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
         return new Multiple<>(k1, v1, new Single<>(k2, v2));
     }
 
-    abstract boolean isSingle();
-
+    /**
+     * Return the remainder of this list. Useful for implementing Iterator-like contract. Null indicates there are no
+     * more entries.
+     *
+     * @return Remainder of this list, or null if nothing remains
+     */
     abstract LNodeEntries<K, V> next();
 
     final LNodeEntry<K, V> findEntry(final Equivalence<? super K> equiv, final K key) {
@@ -100,8 +91,9 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
     }
 
     final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V v) {
-        final LNodeEntries<K, V> removed = remove(entry);
-        return removed == null ? new Single<>(entry.getKey(), v) : new Multiple<>(entry.getKey(), v, removed);
+        final LNodeEntries<K, V> removed;
+        return (removed = remove(entry)) == null ? new Single<>(entry.getKey(), v)
+                : new Multiple<>(entry.getKey(), v, removed);
     }
 
     final LNodeEntries<K, V> remove(final LNodeEntry<K, V> entry) {
@@ -109,13 +101,16 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
             return next();
         }
 
+        // This will result in a list with a long tail, i.e last entry storing explicit null. Overhead is amortized
+        // against the number of entries. We do not retain chains shorter than two, so the worst-case overhead is
+        // half-a-reference for an entry.
         final Multiple<K, V> ret = new Multiple<>(this);
 
         Multiple<K, V> last = ret;
         LNodeEntries<K, V> cur = next();
         while (cur != null) {
-            // We cannot use equals() here, as it is wired to key/value equality,
-            // which we really do not want.
+            // We cannot use equals() here, as it is wired to key equality and we must never compare entries based on
+            // that property. This method is intended to remove a known reference, so identity is what we want.
             if (entry == cur) {
                 last.next = cur.next();
                 return ret;
@@ -129,42 +124,4 @@ abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
 
         throw new IllegalStateException(String.format("Entry %s not found", entry));
     }
-
-    // No need to specialize these two methods, as they are invoked only from a stable LNode, at which point it is
-    // guaranteed to be a Multiple.
-    final int size() {
-        int sz = 1;
-        for (LNodeEntries<?, ?> wlk = next(); wlk != null; wlk = wlk.next()) {
-            sz++;
-        }
-        return sz;
-    }
-
-    final Iterator<Entry<K, V>> iterator() {
-        return new NodeIterator<>(this);
-    }
-
-    static final class NodeIterator<K,V> implements Iterator<Entry<K, V>> {
-        private LNodeEntries<K, V> n;
-
-        NodeIterator(final LNodeEntries<K, V> n) {
-            this.n = n;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return n != null;
-        }
-
-        @Override
-        public Entry<K, V> next() {
-            if (n == null) {
-                throw new NoSuchElementException();
-            }
-
-            final Entry<K, V> res = n;
-            n = n.next();
-            return res;
-        }
-    }
 }
diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableIterator.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableIterator.java
new file mode 100644 (file)
index 0000000..fdd03bc
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yangtools.triemap;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Map.Entry;
+
+/**
+ * Specialized immutable iterator for use with {@link ImmutableEntrySet}.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> the type of entry keys
+ * @param <V> the type of entry values
+ */
+final class MutableIterator<K, V> extends AbstractIterator<K, V> {
+    private final MutableTrieMap<K, V> mutable;
+
+    private Entry<K, V> lastReturned;
+
+    MutableIterator(final MutableTrieMap<K, V> map) {
+        super(map.immutableSnapshot());
+        this.mutable = map;
+    }
+
+    @Override
+    public void remove() {
+        checkState(lastReturned != null);
+        mutable.remove(lastReturned.getKey());
+        lastReturned = null;
+    }
+
+    @Override
+    Entry<K, V> wrapEntry(final Entry<K, V> entry) {
+        lastReturned = entry;
+        return new MutableEntry<>(mutable, entry);
+    }
+
+    /**
+     * A mutable view of an entry in the map. Since the backing map is concurrent, its {@link #getValue()} and
+     * {@link #setValue(Object)} methods cannot guarantee consistency with the base map and may produce surprising
+     * results when the map is concurrently modified, either directly or via another entry/iterator.
+     *
+     * The behavior is similar to what Java 8's ConcurrentHashMap does, which is probably the most consistent handling
+     * of this case without requiring expensive and revalidation.
+     */
+    private static final class MutableEntry<K, V> implements Entry<K, V> {
+        private final MutableTrieMap<K, V> map;
+        private final Entry<K, V> delegate;
+
+        @SuppressWarnings("null")
+        private V newValue = null;
+
+        MutableEntry(final MutableTrieMap<K, V> map, final Entry<K, V> delegate) {
+            this.map = map;
+            this.delegate = delegate;
+        }
+
+        @Override
+        public K getKey() {
+            return delegate.getKey();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the most uptodate value we have observed via this entry. It does not reflect
+         * concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed.
+         */
+        @Override
+        public V getValue() {
+            return newValue != null ? newValue : delegate.getValue();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @implSpec
+         * This implementation returns the most uptodate value we have observed via this entry. It does not reflect
+         * concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed.
+         */
+        @Override
+        public V setValue(final V value) {
+            final V ret = getValue();
+            map.put(getKey(), value);
+            newValue = value;
+            return ret;
+        }
+
+        @Override
+        public int hashCode() {
+            return EntryUtil.hash(getKey(), getValue());
+        }
+
+        @SuppressFBWarnings(value = "EQ_UNUSUAL",  justification = "Equality handled by utility methods")
+        @Override
+        public boolean equals(final Object o) {
+            return EntryUtil.equal(o, getKey(), getValue());
+        }
+
+        @Override
+        public String toString() {
+            return EntryUtil.string(getKey(), getValue());
+        }
+    }
+}
index 9a46e2878e9e1105ab310192b3b59cfe567a9f8e..fa74bf0bb1ee52982affd9dea4ac065eb07dd984 100644 (file)
@@ -23,7 +23,6 @@ import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Verify;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.util.Iterator;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
@@ -141,6 +140,11 @@ final class MutableTrieMap<K, V> extends TrieMap<K, V> {
         return new MutableEntrySet<>(this);
     }
 
+    @Override
+    MutableIterator<K, V> iterator() {
+        return new MutableIterator<>(this);
+    }
+
     @Override
     boolean isReadOnly() {
         return false;
@@ -263,9 +267,4 @@ final class MutableTrieMap<K, V> extends TrieMap<K, V> {
             this.nv = nv;
         }
     }
-
-    @Override
-    Iterator<Entry<K, V>> iterator() {
-        return new TrieMapIterator<>(0, this);
-    }
 }
index b93fa4a367fea9b9043e0a4adcbb790a176e2c5b..2a0111af41b03e0ed2d3c7b901923d7f6b35ee86 100644 (file)
@@ -22,7 +22,6 @@ import com.google.common.annotations.Beta;
 import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.util.AbstractMap;
-import java.util.Iterator;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
@@ -149,7 +148,7 @@ public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements Concurr
      *
      * @return
      */
-    abstract Iterator<Entry<K, V>> iterator();
+    abstract AbstractIterator<K, V> iterator();
 
     /* internal methods provided for subclasses */
 
@@ -159,8 +158,8 @@ public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements Concurr
      *
      * @return
      */
-    final Iterator<Entry<K, V>> immutableIterator() {
-        return new TrieMapReadOnlyIterator<>(0, immutableSnapshot());
+    final ImmutableIterator<K, V> immutableIterator() {
+        return new ImmutableIterator<>(immutableSnapshot());
     }
 
     @SuppressWarnings("null")
diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapIterator.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapIterator.java
deleted file mode 100644 (file)
index 39479d6..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.opendaylight.yangtools.triemap;
-
-import static com.google.common.base.Preconditions.checkState;
-import static org.opendaylight.yangtools.triemap.Constants.MAX_DEPTH;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-
-class TrieMapIterator<K, V> implements Iterator<Entry<K, V>> {
-    private int level;
-    protected TrieMap<K, V> ct;
-    private final boolean mustInit;
-    private final BasicNode[][] stack = new BasicNode[MAX_DEPTH][];
-    private final int[] stackpos = new int[MAX_DEPTH];
-    private int depth = -1;
-    private Iterator<Entry<K, V>> subiter = null;
-    private EntryNode<K, V> current = null;
-    private Entry<K, V> lastReturned = null;
-
-    TrieMapIterator (final int level, final TrieMap<K, V> ct, final boolean mustInit) {
-        this.level = level;
-        this.ct = ct;
-        this.mustInit = mustInit;
-        if (this.mustInit) {
-            initialize ();
-        }
-    }
-
-    TrieMapIterator (final int level, final TrieMap<K, V> ct) {
-        this (level, ct, true);
-    }
-
-
-    @Override
-    public boolean hasNext() {
-        return (current != null) || (subiter != null);
-    }
-
-    @Override
-    public Entry<K, V> next() {
-        if (!hasNext()) {
-            throw new NoSuchElementException();
-        }
-
-        final Entry<K, V> r;
-        if (subiter != null) {
-            r = subiter.next();
-            checkSubiter();
-        } else {
-            r = current;
-            advance();
-        }
-
-        lastReturned = r;
-        if (r != null) {
-            final Entry<K, V> rr = r;
-            return nextEntry(rr);
-        }
-        return r;
-    }
-
-    Entry<K, V> nextEntry(final Entry<K, V> rr) {
-        return new Entry<K, V>() {
-            @SuppressWarnings("null")
-            private V updated = null;
-
-            @Override
-            public K getKey () {
-                return rr.getKey ();
-            }
-
-            @Override
-            public V getValue () {
-                return (updated == null) ? rr.getValue (): updated;
-            }
-
-            @Override
-            public V setValue (final V value) {
-                updated = value;
-                return ct.replace (getKey (), value);
-            }
-        };
-    }
-
-    private void readin (final INode<K, V> in) {
-        MainNode<K, V> m = in.gcasRead (ct);
-        if (m instanceof CNode) {
-            CNode<K, V> cn = (CNode<K, V>) m;
-            depth += 1;
-            stack [depth] = cn.array;
-            stackpos [depth] = -1;
-            advance ();
-        } else if (m instanceof TNode) {
-            current = (TNode<K, V>) m;
-        } else if (m instanceof LNode) {
-            subiter = ((LNode<K, V>) m).iterator();
-            checkSubiter ();
-        } else if (m == null) {
-            current = null;
-        }
-    }
-
-    // @inline
-    private void checkSubiter () {
-        if (!subiter.hasNext ()) {
-            subiter = null;
-            advance ();
-        }
-    }
-
-    // @inline
-    void initialize () {
-        // assert (ct.isReadOnly());
-        readin(ct.RDCSS_READ_ROOT());
-    }
-
-    void advance () {
-        if (depth >= 0) {
-            int npos = stackpos [depth] + 1;
-            if (npos < stack [depth].length) {
-                stackpos [depth] = npos;
-                BasicNode elem = stack [depth] [npos];
-                if (elem instanceof SNode) {
-                    current = (SNode<K, V>) elem;
-                } else if (elem instanceof INode) {
-                    readin ((INode<K, V>) elem);
-                }
-            } else {
-                depth -= 1;
-                advance ();
-            }
-        } else {
-            current = null;
-        }
-    }
-
-    protected TrieMapIterator<K, V> newIterator(final int _lev, final TrieMap<K, V> _ct, final boolean _mustInit) {
-        return new TrieMapIterator<> (_lev, _ct, _mustInit);
-    }
-
-    protected void dupTo(final TrieMapIterator<K, V> it) {
-        it.level = this.level;
-        it.ct = this.ct;
-        it.depth = this.depth;
-        it.current = this.current;
-
-        // these need a deep copy
-        System.arraycopy (this.stack, 0, it.stack, 0, 7);
-        System.arraycopy (this.stackpos, 0, it.stackpos, 0, 7);
-
-        // this one needs to be evaluated
-        if (this.subiter == null) {
-            it.subiter = null;
-        } else {
-            List<Entry<K, V>> lst = toList (this.subiter);
-            this.subiter = lst.iterator ();
-            it.subiter = lst.iterator ();
-        }
-    }
-
-    // /** Returns a sequence of iterators over subsets of this iterator.
-    // * It's used to ease the implementation of splitters for a parallel
-    // version of the TrieMap.
-    // */
-    // protected def subdivide(): Seq[Iterator[(K, V)]] = if (subiter ne
-    // null) {
-    // // the case where an LNode is being iterated
-    // val it = subiter
-    // subiter = null
-    // advance()
-    // this.level += 1
-    // Seq(it, this)
-    // } else if (depth == -1) {
-    // this.level += 1
-    // Seq(this)
-    // } else {
-    // var d = 0
-    // while (d <= depth) {
-    // val rem = stack(d).length - 1 - stackpos(d)
-    // if (rem > 0) {
-    // val (arr1, arr2) = stack(d).drop(stackpos(d) + 1).splitAt(rem / 2)
-    // stack(d) = arr1
-    // stackpos(d) = -1
-    // val it = newIterator(level + 1, ct, false)
-    // it.stack(0) = arr2
-    // it.stackpos(0) = -1
-    // it.depth = 0
-    // it.advance() // <-- fix it
-    // this.level += 1
-    // return Seq(this, it)
-    // }
-    // d += 1
-    // }
-    // this.level += 1
-    // Seq(this)
-    // }
-
-    private List<Entry<K, V>> toList (final Iterator<Entry<K, V>> it) {
-        ArrayList<Entry<K, V>> list = new ArrayList<> ();
-        while (it.hasNext ()) {
-            list.add (it.next());
-        }
-        return list;
-    }
-
-    @Override
-    public void remove() {
-        checkState(lastReturned != null);
-        ct.remove(lastReturned.getKey());
-        lastReturned = null;
-    }
-}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapReadOnlyIterator.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMapReadOnlyIterator.java
deleted file mode 100644 (file)
index 9899bef..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.opendaylight.yangtools.triemap;
-
-import java.util.Map.Entry;
-
-/**
- * This iterator is a read-only one and does not allow for any update
- * operations on the underlying data structure.
- *
- * @param <K> the type of keys
- * @param <V> the type of values
- */
-final class TrieMapReadOnlyIterator<K, V> extends TrieMapIterator<K, V> {
-    TrieMapReadOnlyIterator (final int level, final TrieMap<K, V> ct, final boolean mustInit) {
-        super (level, ct, mustInit);
-    }
-
-    TrieMapReadOnlyIterator (final int level, final TrieMap<K, V> ct) {
-        this (level, ct, true);
-    }
-    @Override
-    void initialize () {
-        assert (ct.isReadOnly ());
-        super.initialize ();
-    }
-
-    @Override
-    public void remove () {
-        throw new UnsupportedOperationException ("Operation not supported for read-only iterators");
-    }
-
-    @Override
-    Entry<K, V> nextEntry(final Entry<K, V> rr) {
-        // Return non-updatable entry
-        return rr;
-    }
-}
\ No newline at end of file
index ab6e9fdd1253da1dcfcb656fdf01dc9db969a0fe..764de0ae170ff0268b49ddf66407e71477f509ce 100644 (file)
@@ -32,7 +32,7 @@ import org.junit.Test;
 
 public class TestMapIterator {
     @Test
-    public void testMapIterator () {
+    public void testMapIterator() {
         final Random random = new Random();
 
         for (int i = 0; i < 60 * 1000; i+= 400 + random.nextInt(400)) {
@@ -77,6 +77,33 @@ public class TestMapIterator {
         }
     }
 
+    @Test
+    public void testMapImmutableIterator() {
+        final Random random = new Random();
+
+        for (int i = 0; i < 60 * 1000; i+= 400 + random.nextInt(400)) {
+            final Map<Integer, Integer> bt = TrieMap.create();
+            for (int j = 0; j < i; j++) {
+                assertNull(bt.put(Integer.valueOf(j), Integer.valueOf(j)));
+            }
+            int count = 0;
+            final Set<Integer> set = new HashSet<>();
+            for (final Entry<Integer, Integer> e : bt.entrySet()) {
+                set.add(e.getKey());
+                count++;
+            }
+            for (final Integer j : set) {
+                assertTrue(bt.containsKey(j));
+            }
+            for (final Integer j : bt.keySet()) {
+                assertTrue(set.contains(j));
+            }
+
+            assertEquals(i, count);
+            assertEquals(i, bt.size());
+        }
+    }
+
     private static void failAdvance(final Iterator<?> it) {
         assertFalse(it.hasNext());
         it.next();