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 final class INode<K, V> extends INodeBase<K, V> {
19 static final Object KEY_PRESENT = new Object ();
20 static final Object KEY_ABSENT = new Object ();
23 * Virtual result for lookup methods indicating that the lookup needs to be restarted. This is a faster version
24 * of throwing a checked exception to control the restart.
26 static final Object RESTART = new Object();
28 INode(final Gen gen, final MainNode<K, V> bn) {
32 MainNode<K, V> gcasRead(final TrieMap<K, V> ct) {
36 MainNode<K, V> GCAS_READ(final TrieMap<K, V> ct) {
37 MainNode<K, V> m = /* READ */ READ();
38 MainNode<K, V> prevval = /* READ */ m.READ_PREV();
39 if (prevval == null) {
43 return GCAS_Complete(m, ct);
46 private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<K, V> ct) {
53 final MainNode<K, V> prev = /* READ */ m.READ_PREV();
54 final INode<K, V> ctr = ct.readRoot(true);
59 if (prev instanceof FailedNode) {
60 // try to commit to previous value
61 FailedNode<K, V> fn = (FailedNode<K, V>) prev;
62 if (CAS(m, fn.READ_PREV())) {
63 return fn.READ_PREV();
66 // Tail recursion: return GCAS_Complete (/* READ */ READ(), ct);
67 m = /* READ */ READ();
71 // Assume that you've read the root from the generation G.
72 // Assume that the snapshot algorithm is correct.
73 // ==> you can only reach nodes in generations <= G.
75 // We know that `ctr.gen` is >= G.
76 // ==> if `ctr.gen` = `gen` then they are both equal to G.
77 // ==> otherwise, we know that either `ctr.gen` > G, `gen` < G,
79 if ((ctr.gen == gen) && ct.nonReadOnly()) {
81 if (m.CAS_PREV(prev, null)) {
85 // Tail recursion: return GCAS_Complete (m, ct);
90 m.CAS_PREV(prev, new FailedNode<>(prev));
92 // Tail recursion: return GCAS_Complete(/* READ */ READ(), ct);
93 m = /* READ */ READ();
97 private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<K, V> ct) {
100 GCAS_Complete(n, ct);
101 return /* READ */ n.READ_PREV() == null;
107 private INode<K, V> inode(final MainNode<K, V> cn) {
108 return new INode<>(gen, cn);
111 INode<K, V> copyToGen(final Gen ngen, final TrieMap<K, V> ct) {
112 return new INode<>(ngen, GCAS_READ(ct));
116 * Inserts a key value pair, overwriting the old pair if the keys match.
118 * @return true if successful, false otherwise
120 boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
121 final TrieMap<K, V> ct) {
123 final MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
125 if (m instanceof CNode) {
126 // 1) a multiway node
127 final CNode<K, V> cn = (CNode<K, V>) m;
128 final int idx = (hc >>> lev) & 0x1f;
129 final int flag = 1 << idx;
130 final int bmp = cn.bitmap;
131 final int mask = flag - 1;
132 final int pos = Integer.bitCount(bmp & mask);
133 if ((bmp & flag) != 0) {
135 final BasicNode cnAtPos = cn.array[pos];
136 if (cnAtPos instanceof INode) {
137 final INode<K, V> in = (INode<K, V>) cnAtPos;
138 if (startgen == in.gen) {
139 return in.rec_insert(k, v, hc, lev + 5, this, startgen, ct);
141 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
142 // Tail recursion: return rec_insert (k, v, hc, lev, parent, startgen, ct);
147 } else if (cnAtPos instanceof SNode) {
148 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
149 if (sn.hc == hc && ct.equal(sn.k, k)) {
150 return GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct);
153 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
154 final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode<>(k, v, hc),
155 hc, lev + 5, gen)), gen);
156 return GCAS (cn, nn, ct);
159 CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
160 MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<> (k, v, hc), gen);
161 return GCAS (cn, ncnode, ct);
163 } else if (m instanceof TNode) {
164 clean(parent, ct, lev - 5);
166 } else if (m instanceof LNode) {
167 LNode<K, V> ln = (LNode<K, V>) m;
168 MainNode<K, V> nn = ln.inserted(k, v);
169 return GCAS(ln, nn, ct);
171 throw new IllegalStateException("Unhandled node " + m);
174 throw new RuntimeException ("Should not happen");
179 * Inserts a new key value pair, given that a specific condition is met.
182 * null - don't care if the key was there
183 * KEY_ABSENT - key wasn't there
184 * KEY_PRESENT - key was there
185 * other value `v` - key must be bound to `v`
186 * @return null if unsuccessful, Option[V] otherwise (indicating
187 * previous value bound to the key)
189 Option<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
190 final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
192 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
194 if (m instanceof CNode) {
195 // 1) a multiway node
196 final CNode<K, V> cn = (CNode<K, V>) m;
197 final int idx = (hc >>> lev) & 0x1f;
198 final int flag = 1 << idx;
199 final int bmp = cn.bitmap;
200 final int mask = flag - 1;
201 final int pos = Integer.bitCount(bmp & mask);
203 if ((bmp & flag) != 0) {
205 final BasicNode cnAtPos = cn.array[pos];
206 if (cnAtPos instanceof INode) {
207 final INode<K, V> in = (INode<K, V>) cnAtPos;
208 if (startgen == in.gen) {
209 return in.rec_insertif(k, v, hc, cond, lev + 5, this, startgen, ct);
212 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
213 // Tail recursion: return rec_insertif (k, v, hc, cond, lev, parent, startgen, ct);
218 } else if (cnAtPos instanceof SNode) {
219 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
221 if (sn.hc == hc && ct.equal(sn.k, k)) {
222 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
223 return Option.makeOption(sn.v);
229 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
230 final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
231 new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
232 if (GCAS(cn, nn, ct)) {
233 return Option.makeOption(); // None;
237 } else if (cond == INode.KEY_ABSENT) {
238 if (sn.hc == hc && ct.equal(sn.k, k)) {
239 return Option.makeOption(sn.v);
242 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
243 final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
244 new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
245 if (GCAS(cn, nn, ct)) {
246 return Option.makeOption(); // None
250 } else if (cond == INode.KEY_PRESENT) {
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 Option.makeOption(sn.v);
258 return Option.makeOption();// None;
260 if (sn.hc == hc && ct.equal(sn.k, k) && cond.equals(sn.v)) {
261 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
262 return Option.makeOption(sn.v);
268 return Option.makeOption(); // None
271 } else if (cond == null || cond == INode.KEY_ABSENT) {
272 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
273 final CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<>(k, v, hc), gen);
274 if (GCAS(cn, ncnode, ct)) {
275 return Option.makeOption(); // None
279 } else if (cond == INode.KEY_PRESENT) {
280 return Option.makeOption(); // None;
282 return Option.makeOption(); // None
284 } else if (m instanceof TNode) {
285 clean(parent, ct, lev - 5);
287 } else if (m instanceof LNode) {
289 final LNode<K, V> ln = (LNode<K, V>) m;
291 final Option<V> optv = ln.get(k);
292 if (insertln(ln, k, v, ct)) {
296 } else if (cond == INode.KEY_ABSENT) {
297 final Option<V> t = ln.get(k);
301 if (insertln(ln, k, v, ct)) {
302 return Option.makeOption();// None
305 } else if (cond == INode.KEY_PRESENT) {
306 final Option<V> t = ln.get(k);
310 if (insertln(ln, k, v, ct)) {
315 final Option<V> t = ln.get(k);
316 if (t instanceof Some) {
317 final Some<V> s = (Some<V>) t;
318 if (cond.equals(s.get())) {
319 if (insertln(ln, k, v, ct)) {
320 // Difference from Scala: we choose to reuse the object returned from LNode,
321 // as the identity of the value does not matter in this call graph.
329 return Option.makeOption();
332 throw new IllegalStateException("Unhandled node " + m);
335 throw new RuntimeException("Should never happen");
339 boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
340 final LNode<K, V> nn = ln.inserted (k, v);
341 return GCAS(ln, nn, ct);
345 * Looks up the value associated with the key.
347 * @return null if no value has been found, RESTART if the operation
348 * wasn't successful, or any other value otherwise
350 Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
351 final TrieMap<K, V> ct) {
353 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
355 if (m instanceof CNode) {
357 final CNode<K, V> cn = (CNode<K, V>) m;
358 final int idx = (hc >>> lev) & 0x1f;
359 final int flag = 1 << idx;
360 final int bmp = cn.bitmap;
361 if ((bmp & flag) == 0) {
362 // 1a) bitmap shows no binding
366 // 1b) bitmap contains a value - descend
367 final int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount(bmp & (flag - 1));
368 final BasicNode sub = cn.array[pos];
369 if (sub instanceof INode) {
370 final INode<K, V> in = (INode<K, V>) sub;
371 if (ct.isReadOnly() || (startgen == in.gen)) {
372 return in.rec_lookup(k, hc, lev + 5, this, startgen, ct);
375 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
376 // Tail recursion: return rec_lookup(k, hc, lev, parent, startgen, ct);
380 // used to be throw RestartException
382 } else if (sub instanceof SNode) {
384 final SNode<K, V> sn = (SNode<K, V>) sub;
385 if (sn.hc == hc && ct.equal(sn.k, k)) {
391 } else if (m instanceof TNode) {
393 return cleanReadOnly((TNode<K, V>) m, lev, parent, ct, k, hc);
394 } else if (m instanceof LNode) {
396 return ((LNode<K, V>) m).get(k);
398 throw new IllegalStateException("Unhandled node " + m);
401 throw new RuntimeException ("Should not happen");
405 private Object cleanReadOnly(final TNode<K, V> tn, final int lev, final INode<K, V> parent,
406 final TrieMap<K, V> ct, final K k, final int hc) {
407 if (ct.nonReadOnly()) {
408 // used to be throw RestartException
409 clean(parent, ct, lev - 5);
413 if (tn.hc == hc && ct.equal(tn.k, k)) {
421 * Removes the key associated with the given value.
424 * if null, will remove the key regardless of the value;
425 * otherwise removes only if binding contains that exact key
427 * @return null if not successful, an Option[V] indicating the previous
430 Option<V> rec_remove(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
431 final Gen startgen, final TrieMap<K, V> ct) {
432 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
434 if (m instanceof CNode) {
435 final CNode<K, V> cn = (CNode<K, V>) m;
436 final int idx = (hc >>> lev) & 0x1f;
437 final int bmp = cn.bitmap;
438 final int flag = 1 << idx;
439 if ((bmp & flag) == 0) {
440 return Option.makeOption();
443 final int pos = Integer.bitCount(bmp & (flag - 1));
444 final BasicNode sub = cn.array[pos];
445 Option<V> res = null;
446 if (sub instanceof INode) {
447 final INode<K, V> in = (INode<K, V>) sub;
448 if (startgen == in.gen) {
449 res = in.rec_remove(k, v, hc, lev + 5, this, startgen, ct);
451 if (GCAS(cn, cn.renewed (startgen, ct), ct)) {
452 res = rec_remove(k, v, hc, lev, parent, startgen, ct);
458 } else if (sub instanceof SNode) {
459 final SNode<K, V> sn = (SNode<K, V>) sub;
460 if (sn.hc == hc && ct.equal(sn.k, k) && (v == null || v.equals(sn.v))) {
461 final MainNode<K, V> ncn = cn.removedAt(pos, flag, gen).toContracted(lev);
462 if (GCAS(cn, ncn, ct)) {
463 res = Option.makeOption(sn.v);
468 res = Option.makeOption ();
472 if (res instanceof None || (res == null)) {
476 if (parent != null) {
477 // never tomb at root
478 final MainNode<K, V> n = GCAS_READ(ct);
479 if (n instanceof TNode) {
480 cleanParent(n, parent, ct, hc, lev, startgen);
485 } else if (m instanceof TNode) {
486 clean(parent, ct, lev - 5);
488 } else if (m instanceof LNode) {
489 final LNode<K, V> ln = (LNode<K, V>) m;
491 final Option<V> optv = ln.get(k);
492 final MainNode<K, V> nn = ln.removed(k, ct);
493 if (GCAS(ln, nn, ct)) {
500 final Option<V> tmp = ln.get(k);
501 if (tmp instanceof Some) {
502 final Some<V> tmp1 = (Some<V>) tmp;
503 if (v.equals(tmp1.get())) {
504 final MainNode<K, V> nn = ln.removed(k, ct);
505 if (GCAS(ln, nn, ct)) {
513 // Key not found or value does not match: we have not removed anything
514 return Option.makeOption();
516 throw new IllegalStateException("Unhandled node " + m);
520 private void cleanParent(final Object nonlive, final INode<K, V> parent, final TrieMap<K, V> ct, final int hc,
521 final int lev, final Gen startgen) {
523 final MainNode<K, V> pm = parent.GCAS_READ(ct);
524 if ((!(pm instanceof CNode))) {
525 // parent is no longer a cnode, we're done
529 final CNode<K, V> cn = (CNode<K, V>) pm;
530 final int idx = (hc >>> (lev - 5)) & 0x1f;
531 final int bmp = cn.bitmap;
532 final int flag = 1 << idx;
533 if ((bmp & flag) == 0) {
534 // somebody already removed this i-node, we're done
538 final int pos = Integer.bitCount(bmp & (flag - 1));
539 final BasicNode sub = cn.array[pos];
541 if (nonlive instanceof TNode) {
542 final TNode<K, V> tn = (TNode<K, V>) nonlive;
543 MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - 5);
544 if (!parent.GCAS(cn, ncn, ct)) {
545 if (ct.readRoot().gen == startgen) {
546 // Tail recursion: cleanParent(nonlive, parent, ct, hc, lev, startgen);
556 private void clean(final INode<K, V> nd, final TrieMap<K, V> ct, final int lev) {
557 final MainNode<K, V> m = nd.GCAS_READ(ct);
558 if (m instanceof CNode) {
559 final CNode<K, V> cn = (CNode<K, V>) m;
560 nd.GCAS(cn, cn.toCompressed(ct, lev, gen), ct);
564 int cachedSize(final TrieMap<K, V> ct) {
565 MainNode<K, V> m = GCAS_READ(ct);
566 return m.cachedSize(ct);
569 // /* this is a quiescent method! */
570 // def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode
572 // case null => "<null>"
573 // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v,
575 // case cn: CNode[_, _] => cn.string(lev)
576 // case ln: LNode[_, _] => ln.string(lev)
577 // case x => "<elem: %s>".format(x)
581 String string(final int lev) {