2 * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.opendaylight.yangtools.triemap;
18 import static org.opendaylight.yangtools.triemap.Constants.LEVEL_BITS;
19 import static org.opendaylight.yangtools.triemap.LookupResult.RESTART;
20 import static org.opendaylight.yangtools.triemap.PresencePredicate.ABSENT;
21 import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
23 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
24 import java.util.Optional;
25 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
27 final class INode<K, V> extends BasicNode {
28 @SuppressWarnings("rawtypes")
29 private static final AtomicReferenceFieldUpdater<INode, MainNode> MAINNODE_UPDATER =
30 AtomicReferenceFieldUpdater.newUpdater(INode.class, MainNode.class, "mainnode");
32 private final Gen gen;
34 private volatile MainNode<K, V> mainnode;
36 INode(final Gen gen, final MainNode<K, V> mainnode) {
38 this.mainnode = mainnode;
41 MainNode<K, V> gcasRead(final TrieMap<?, ?> ct) {
45 private MainNode<K, V> GCAS_READ(final TrieMap<?, ?> ct) {
46 MainNode<K, V> m = /* READ */ mainnode;
47 MainNode<K, V> prevval = /* READ */ m.READ_PREV();
48 if (prevval == null) {
52 return GCAS_Complete(m, ct);
55 private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<?, ?> ct) {
62 final MainNode<K, V> prev = /* READ */ m.READ_PREV();
63 final Gen rdgen = ct.readRoot(true).gen;
68 if (prev instanceof FailedNode) {
69 // try to commit to previous value
70 FailedNode<K, V> fn = (FailedNode<K, V>) prev;
71 if (MAINNODE_UPDATER.compareAndSet(this, m, fn.READ_PREV())) {
72 return fn.READ_PREV();
75 // Tail recursion: return GCAS_Complete (/* READ */ mainnode, ct);
76 m = /* READ */ mainnode;
80 // Assume that you've read the root from the generation G.
81 // Assume that the snapshot algorithm is correct.
82 // ==> you can only reach nodes in generations <= G.
84 // We know that `ctr.gen` is >= G.
85 // ==> if `ctr.gen` = `gen` then they are both equal to G.
86 // ==> otherwise, we know that either `ctr.gen` > G, `gen` < G,
88 if (rdgen == gen && !ct.isReadOnly()) {
90 if (m.CAS_PREV(prev, null)) {
94 // Tail recursion: return GCAS_Complete (m, ct);
99 m.CAS_PREV(prev, new FailedNode<>(prev));
101 // Tail recursion: return GCAS_Complete(/* READ */ mainnode, ct);
102 m = /* READ */ mainnode;
106 private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<?, ?> ct) {
108 if (MAINNODE_UPDATER.compareAndSet(this, old, n)) {
109 GCAS_Complete(n, ct);
110 return /* READ */ n.READ_PREV() == null;
116 private INode<K, V> inode(final MainNode<K, V> cn) {
117 return new INode<>(gen, cn);
120 INode<K, V> copyToGen(final Gen ngen, final TrieMap<?, ?> ct) {
121 return new INode<>(ngen, GCAS_READ(ct));
125 * Inserts a key value pair, overwriting the old pair if the keys match.
127 * @return true if successful, false otherwise
129 boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
130 final TrieMap<K, V> ct) {
131 return rec_insert(k, v, hc, lev, parent, gen, ct);
134 private boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
135 final Gen startgen, final TrieMap<K, V> ct) {
137 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
139 if (m instanceof CNode) {
140 // 1) a multiway node
141 final CNode<K, V> cn = (CNode<K, V>) m;
142 final int idx = (hc >>> lev) & 0x1f;
143 final int flag = 1 << idx;
144 final int bmp = cn.bitmap;
145 final int mask = flag - 1;
146 final int pos = Integer.bitCount(bmp & mask);
148 if ((bmp & flag) != 0) {
150 final BasicNode cnAtPos = cn.array[pos];
151 if (cnAtPos instanceof INode) {
152 final INode<K, V> in = (INode<K, V>) cnAtPos;
153 if (startgen == in.gen) {
154 return in.rec_insert(k, v, hc, lev + LEVEL_BITS, this, startgen, ct);
156 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
157 // Tail recursion: return rec_insert (k, v, hc, lev, parent, startgen, ct);
162 } else if (cnAtPos instanceof SNode) {
163 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
164 if (sn.hc == hc && ct.equal(sn.k, k)) {
165 return GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct);
168 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
169 final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + LEVEL_BITS, gen)), gen);
170 return GCAS (cn, nn, ct);
173 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
174 final MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<>(k, v, hc), gen);
175 return GCAS (cn, ncnode, ct);
177 } else if (m instanceof TNode) {
178 clean(parent, ct, lev - LEVEL_BITS);
180 } else if (m instanceof LNode) {
181 final LNode<K, V> ln = (LNode<K, V>) m;
182 final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
183 return entry != null ? replaceln(ln, entry, v, ct) : insertln(ln, k, v, ct);
185 throw new IllegalStateException("Unhandled node " + m);
188 throw new RuntimeException ("Should not happen");
193 * Inserts a new key value pair, given that a specific condition is met.
196 * null - don't care if the key was there
197 * KEY_ABSENT - key wasn't there
198 * KEY_PRESENT - key was there
199 * other value `v` - key must be bound to `v`
200 * @return null if unsuccessful, Option[V] otherwise (indicating
201 * previous value bound to the key)
203 Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
204 final INode<K, V> parent, final TrieMap<K, V> ct) {
205 return rec_insertif(k, v, hc, cond, lev, parent, gen, ct);
208 @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
209 justification = "Returning null Optional indicates the need to restart.")
210 private Optional<V> insertDual(final TrieMap<K, V> ct, final CNode<K, V> cn, final int pos, final SNode<K, V> sn,
211 final K k, final V v, final int hc, final int lev) {
212 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
213 final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + LEVEL_BITS, gen)), gen);
214 return GCAS(cn, nn, ct) ? Optional.empty() : null;
217 @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
218 justification = "Returning null Optional indicates the need to restart.")
219 private Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
220 final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
222 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
224 if (m instanceof CNode) {
225 // 1) a multiway node
226 final CNode<K, V> cn = (CNode<K, V>) m;
227 final int idx = (hc >>> lev) & 0x1f;
228 final int flag = 1 << idx;
229 final int bmp = cn.bitmap;
230 final int mask = flag - 1;
231 final int pos = Integer.bitCount(bmp & mask);
233 if ((bmp & flag) != 0) {
235 final BasicNode cnAtPos = cn.array[pos];
236 if (cnAtPos instanceof INode) {
237 final INode<K, V> in = (INode<K, V>) cnAtPos;
238 if (startgen == in.gen) {
239 return in.rec_insertif(k, v, hc, cond, lev + LEVEL_BITS, this, startgen, ct);
242 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
243 // Tail recursion: return rec_insertif (k, v, hc, cond, lev, parent, startgen, ct);
248 } else if (cnAtPos instanceof SNode) {
249 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
251 if (sn.hc == hc && ct.equal(sn.k, k)) {
252 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
253 return Optional.of(sn.v);
259 return insertDual(ct, cn, pos, sn, k, v, hc, lev);
260 } else if (cond == ABSENT) {
261 if (sn.hc == hc && ct.equal(sn.k, k)) {
262 return Optional.of(sn.v);
265 return insertDual(ct, cn, pos, sn, k, v, hc, lev);
266 } else if (cond == PRESENT) {
267 if (sn.hc == hc && ct.equal(sn.k, k)) {
268 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
269 return Optional.of(sn.v);
274 return Optional.empty();
276 if (sn.hc == hc && ct.equal(sn.k, k) && cond.equals(sn.v)) {
277 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
278 return Optional.of(sn.v);
284 return Optional.empty();
287 } else if (cond == null || cond == ABSENT) {
288 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
289 final CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<>(k, v, hc), gen);
290 if (GCAS(cn, ncnode, ct)) {
291 return Optional.empty();
296 return Optional.empty();
298 } else if (m instanceof TNode) {
299 clean(parent, ct, lev - LEVEL_BITS);
301 } else if (m instanceof LNode) {
303 final LNode<K, V> ln = (LNode<K, V>) m;
304 final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
308 return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
311 return insertln(ln, k, v, ct) ? Optional.empty() : null;
312 } else if (cond == ABSENT) {
314 return Optional.of(entry.getValue());
317 return insertln(ln, k, v, ct) ? Optional.empty() : null;
318 } else if (cond == PRESENT) {
320 return Optional.empty();
323 return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
325 if (entry == null || !cond.equals(entry.getValue())) {
326 return Optional.empty();
329 return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
332 throw new IllegalStateException("Unhandled node " + m);
335 throw new RuntimeException("Should never happen");
339 private boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
340 return GCAS(ln, ln.insertChild(k, v), ct);
343 private boolean replaceln(final LNode<K, V> ln, final LNodeEntry<K, V> entry, final V v, final TrieMap<K, V> ct) {
344 return GCAS(ln, ln.replaceChild(entry, v), ct);
348 * Looks up the value associated with the key.
350 * @return null if no value has been found, RESTART if the operation
351 * wasn't successful, or any other value otherwise
353 Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final TrieMap<K, V> ct) {
354 return rec_lookup(k, hc, lev, parent, gen, ct);
357 private Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
358 final TrieMap<K, V> ct) {
360 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
362 if (m instanceof CNode) {
364 final CNode<K, V> cn = (CNode<K, V>) m;
365 final int idx = (hc >>> lev) & 0x1f;
366 final int flag = 1 << idx;
367 final int bmp = cn.bitmap;
369 if ((bmp & flag) == 0) {
370 // 1a) bitmap shows no binding
374 // 1b) bitmap contains a value - descend
375 final int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount(bmp & (flag - 1));
376 final BasicNode sub = cn.array[pos];
377 if (sub instanceof INode) {
378 final INode<K, V> in = (INode<K, V>) sub;
379 if (ct.isReadOnly() || (startgen == in.gen)) {
380 return in.rec_lookup(k, hc, lev + LEVEL_BITS, this, startgen, ct);
383 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
384 // Tail recursion: return rec_lookup(k, hc, lev, parent, startgen, ct);
389 } else if (sub instanceof SNode) {
391 final SNode<K, V> sn = (SNode<K, V>) sub;
392 if (sn.hc == hc && ct.equal(sn.k, k)) {
398 } else if (m instanceof TNode) {
400 return cleanReadOnly((TNode<K, V>) m, lev, parent, ct, k, hc);
401 } else if (m instanceof LNode) {
403 final LNodeEntry<K, V> entry = ((LNode<K, V>) m).get(ct.equiv(), k);
404 return entry != null ? entry.getValue() : null;
406 throw new IllegalStateException("Unhandled node " + m);
409 throw new RuntimeException ("Should not happen");
413 private Object cleanReadOnly(final TNode<K, V> tn, final int lev, final INode<K, V> parent,
414 final TrieMap<K, V> ct, final K k, final int hc) {
415 if (ct.isReadOnly()) {
416 if (tn.hc == hc && ct.equal(tn.k, k)) {
423 clean(parent, ct, lev - LEVEL_BITS);
428 * Removes the key associated with the given value.
431 * if null, will remove the key regardless of the value;
432 * otherwise removes only if binding contains that exact key
434 * @return null if not successful, an Optional indicating the previous
437 Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
438 final TrieMap<K, V> ct) {
439 return rec_remove(k, cond, hc, lev, parent, gen, ct);
442 @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
443 justification = "Returning null Optional indicates the need to restart.")
444 private Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
445 final Gen startgen, final TrieMap<K, V> ct) {
446 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
448 if (m instanceof CNode) {
449 final CNode<K, V> cn = (CNode<K, V>) m;
450 final int idx = (hc >>> lev) & 0x1f;
451 final int bmp = cn.bitmap;
452 final int flag = 1 << idx;
453 if ((bmp & flag) == 0) {
454 return Optional.empty();
457 final int pos = Integer.bitCount(bmp & (flag - 1));
458 final BasicNode sub = cn.array[pos];
459 Optional<V> res = null;
460 if (sub instanceof INode) {
461 final INode<K, V> in = (INode<K, V>) sub;
462 if (startgen == in.gen) {
463 res = in.rec_remove(k, cond, hc, lev + LEVEL_BITS, this, startgen, ct);
465 if (GCAS(cn, cn.renewed (startgen, ct), ct)) {
466 res = rec_remove(k, cond, hc, lev, parent, startgen, ct);
472 } else if (sub instanceof SNode) {
473 final SNode<K, V> sn = (SNode<K, V>) sub;
474 if (sn.hc == hc && ct.equal(sn.k, k) && (cond == null || cond.equals(sn.v))) {
475 final MainNode<K, V> ncn = cn.removedAt(pos, flag, gen).toContracted(lev);
476 if (GCAS(cn, ncn, ct)) {
477 res = Optional.of(sn.v);
482 res = Optional.empty();
486 if (res == null || !res.isPresent()) {
490 if (parent != null) {
491 // never tomb at root
492 final MainNode<K, V> n = GCAS_READ(ct);
493 if (n instanceof TNode) {
494 cleanParent(n, parent, ct, hc, lev, startgen);
499 } else if (m instanceof TNode) {
500 clean(parent, ct, lev - LEVEL_BITS);
502 } else if (m instanceof LNode) {
503 final LNode<K, V> ln = (LNode<K, V>) m;
504 final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
506 // Key was not found, hence no modification is needed
507 return Optional.empty();
510 final V value = entry.getValue();
511 if (cond != null && !cond.equals(value)) {
512 // Value does not match
513 return Optional.empty();
516 return GCAS(ln, ln.removeChild(entry, hc), ct) ? Optional.of(value) : null;
518 throw new IllegalStateException("Unhandled node " + m);
522 private void cleanParent(final Object nonlive, final INode<K, V> parent, final TrieMap<K, V> ct, final int hc,
523 final int lev, final Gen startgen) {
525 final MainNode<K, V> pm = parent.GCAS_READ(ct);
526 if ((!(pm instanceof CNode))) {
527 // parent is no longer a cnode, we're done
531 final CNode<K, V> cn = (CNode<K, V>) pm;
532 final int idx = (hc >>> (lev - LEVEL_BITS)) & 0x1f;
533 final int bmp = cn.bitmap;
534 final int flag = 1 << idx;
535 if ((bmp & flag) == 0) {
536 // somebody already removed this i-node, we're done
540 final int pos = Integer.bitCount(bmp & (flag - 1));
541 final BasicNode sub = cn.array[pos];
543 if (nonlive instanceof TNode) {
544 final TNode<?, ?> tn = (TNode<?, ?>) nonlive;
545 final MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - LEVEL_BITS);
546 if (!parent.GCAS(cn, ncn, ct)) {
547 if (ct.readRoot().gen == startgen) {
548 // Tail recursion: cleanParent(nonlive, parent, ct, hc, lev, startgen);
558 private void clean(final INode<K, V> nd, final TrieMap<K, V> ct, final int lev) {
559 final MainNode<K, V> m = nd.GCAS_READ(ct);
560 if (m instanceof CNode) {
561 final CNode<K, V> cn = (CNode<K, V>) m;
562 nd.GCAS(cn, cn.toCompressed(ct, lev, gen), ct);
566 int cachedSize(final TrieMap<?, ?> ct) {
567 return GCAS_READ(ct).cachedSize(ct);
570 // /* this is a quiescent method! */
571 // def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode
573 // case null => "<null>"
574 // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v,
576 // case cn: CNode[_, _] => cn.string(lev)
577 // case ln: LNode[_, _] => ln.string(lev)
578 // case x => "<elem: %s>".format(x)
582 String string(final int lev) {