*/
package org.opendaylight.yangtools.triemap;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
import static org.opendaylight.yangtools.triemap.PresencePredicate.ABSENT;
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.Optional;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
* @param <V> the type of mapped values
*/
@Beta
-final class MutableTrieMap<K, V> extends TrieMap<K, V> {
+public final class MutableTrieMap<K, V> extends TrieMap<K, V> {
+ private static final long serialVersionUID = 1L;
+
@SuppressWarnings("rawtypes")
private static final AtomicReferenceFieldUpdater<MutableTrieMap, Object> ROOT_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(MutableTrieMap.class, Object.class, "root");
MutableTrieMap(final Equivalence<? super K> equiv, final INode<K, V> root) {
super(equiv);
- this.root = checkNotNull(root);
+ this.root = requireNonNull(root);
}
@Override
public void clear() {
- boolean success;
+ INode<K, V> r;
do {
- final INode<K, V> r = RDCSS_READ_ROOT();
- success = RDCSS_ROOT(r, r.gcasRead(this), newRootNode());
- } while (!success);
+ r = RDCSS_READ_ROOT();
+ } while (!RDCSS_ROOT(r, r.gcasRead(this), newRootNode()));
}
@Override
public V put(final K key, final V value) {
- final K k = checkNotNull(key);
- return toNullable(insertifhc(k, computeHash(k), checkNotNull(value), null));
+ final K k = requireNonNull(key);
+ return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), null));
}
@Override
public V putIfAbsent(final K key, final V value) {
- final K k = checkNotNull(key);
- return toNullable(insertifhc(k, computeHash(k), checkNotNull(value), ABSENT));
+ final K k = requireNonNull(key);
+ return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), ABSENT));
}
@Override
public V remove(final Object key) {
@SuppressWarnings("unchecked")
- final K k = (K) checkNotNull(key);
+ final K k = (K) requireNonNull(key);
return toNullable(removehc(k, null, computeHash(k)));
}
+ @SuppressFBWarnings(value = "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE",
+ justification = "API contract allows null value, but we do not")
@Override
public boolean remove(final Object key, final Object value) {
@SuppressWarnings("unchecked")
- final K k = (K) checkNotNull(key);
- return removehc(k, checkNotNull(value), computeHash(k)).isPresent();
+ final K k = (K) requireNonNull(key);
+ return removehc(k, requireNonNull(value), computeHash(k)).isPresent();
}
@Override
public boolean replace(final K key, final V oldValue, final V newValue) {
- final K k = checkNotNull(key);
- return insertifhc(k, computeHash(k), checkNotNull(newValue), checkNotNull(oldValue)).isPresent();
+ final K k = requireNonNull(key);
+ return insertifhc(k, computeHash(k), requireNonNull(newValue), requireNonNull(oldValue)).isPresent();
}
@Override
public V replace(final K key, final V value) {
- final K k = checkNotNull(key);
- return toNullable(insertifhc(k, computeHash(k), checkNotNull(value), PRESENT));
+ final K k = requireNonNull(key);
+ return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), PRESENT));
}
@Override
return immutableSnapshot().size();
}
+ private INode<K, V> snapshot() {
+ INode<K, V> r;
+ do {
+ r = RDCSS_READ_ROOT();
+ } while (!RDCSS_ROOT(r, r.gcasRead(this), r.copyToGen(new Gen(), this)));
+
+ return r;
+ }
+
@Override
public ImmutableTrieMap<K, V> immutableSnapshot() {
- while (true) {
- final INode<K, V> r = RDCSS_READ_ROOT();
- final MainNode<K, V> expmain = r.gcasRead(this);
- if (RDCSS_ROOT(r, expmain, r.copyToGen (new Gen(), this))) {
- return new ImmutableTrieMap<>(r, equiv());
- }
-
- // Tail recursion: return readOnlySnapshot();
- }
+ return new ImmutableTrieMap<>(snapshot(), equiv());
}
@Override
public MutableTrieMap<K, V> mutableSnapshot() {
- while (true) {
- final INode<K, V> r = RDCSS_READ_ROOT();
- final MainNode<K, V> expmain = r.gcasRead(this);
- if (RDCSS_ROOT(r, expmain, r.copyToGen(new Gen(), this))) {
- return new MutableTrieMap<>(equiv(), r.copyToGen(new Gen(), this));
- }
+ return new MutableTrieMap<>(equiv(), snapshot().copyToGen(new Gen(), this));
+ }
- // Tail recursion: return snapshot();
- }
+ @Override
+ MutableEntrySet<K, V> createEntrySet() {
+ // FIXME: it would be nice to have a ReadWriteTrieMap with read-only iterator
+ // if (readOnlyEntrySet) return ImmutableEntrySet(this);
+ return new MutableEntrySet<>(this);
+ }
+
+ @Override
+ MutableKeySet<K> createKeySet() {
+ return new MutableKeySet<>(this);
+ }
+
+ @Override
+ MutableIterator<K, V> iterator() {
+ return new MutableIterator<>(this);
}
@Override
}
@Override
+ @SuppressWarnings("unchecked")
INode<K, V> RDCSS_READ_ROOT(final boolean abort) {
final Object r = /* READ */ root;
if (r instanceof INode) {
}
void add(final K key, final V value) {
- final K k = checkNotNull(key);
- inserthc(k, computeHash(k), checkNotNull(value));
+ final K k = requireNonNull(key);
+ inserthc(k, computeHash(k), requireNonNull(value));
}
private static <K,V> INode<K, V> newRootNode() {
return new INode<>(gen, new CNode<>(gen));
}
- private void inserthc(final K k, final int hc, final V v) {
+ private void inserthc(final K key, final int hc, final V value) {
// TODO: this is called from serialization only, which means we should not be observing any races,
// hence we should not need to pass down the entire tree, just equality (I think).
- final boolean success = RDCSS_READ_ROOT().rec_insert(k, v, hc, 0, null, this);
+ final boolean success = RDCSS_READ_ROOT().recInsert(key, value, hc, 0, null, this);
Verify.verify(success, "Concurrent modification during serialization of map %s", this);
}
- private Optional<V> insertifhc(final K k, final int hc, final V v, final Object cond) {
+ private Optional<V> insertifhc(final K key, final int hc, final V value, final Object cond) {
Optional<V> res;
do {
// Keep looping as long as we do not get a reply
- res = RDCSS_READ_ROOT().rec_insertif(k, v, hc, cond, 0, null, this);
+ res = RDCSS_READ_ROOT().recInsertIf(key, value, hc, cond, 0, null, this);
} while (res == null);
return res;
}
- private Optional<V> removehc(final K k, final Object cond, final int hc) {
+ private Optional<V> removehc(final K key, final Object cond, final int hc) {
Optional<V> res;
do {
// Keep looping as long as we do not get a reply
- res = RDCSS_READ_ROOT().rec_remove(k, cond, hc, 0, null, this);
+ res = RDCSS_READ_ROOT().recRemove(key, cond, hc, 0, null, this);
} while (res == null);
return res;
}
private boolean RDCSS_ROOT(final INode<K, V> ov, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
- final RDCSS_Descriptor<K, V> desc = new RDCSS_Descriptor<> (ov, expectedmain, nv);
+ final RDCSS_Descriptor<K, V> desc = new RDCSS_Descriptor<>(ov, expectedmain, nv);
if (CAS_ROOT(ov, desc)) {
RDCSS_Complete(false);
return /* READ */desc.committed;
return false;
}
+ @SuppressWarnings("unchecked")
private INode<K, V> RDCSS_Complete(final boolean abort) {
while (true) {
final Object r = /* READ */ root;
}
checkState(r instanceof RDCSS_Descriptor, "Unhandled root %s", r);
- @SuppressWarnings("unchecked")
final RDCSS_Descriptor<K, V> desc = (RDCSS_Descriptor<K, V>) r;
final INode<K, V> ov = desc.old;
final MainNode<K, V> exp = desc.expectedmain;
volatile boolean committed = false;
- RDCSS_Descriptor (final INode<K, V> old, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
+ RDCSS_Descriptor(final INode<K, V> old, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
this.old = old;
this.expectedmain = expectedmain;
this.nv = nv;