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 java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
20 final class CNode<K, V> extends MainNode<K, V> {
21 @SuppressWarnings("rawtypes")
22 private static final AtomicIntegerFieldUpdater<CNode> CSIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(
23 CNode.class, "csize");
25 private static final BasicNode[] EMPTY_ARRAY = new BasicNode[0];
26 private static final int NO_SIZE = -1;
29 final BasicNode[] array;
32 private volatile int csize = NO_SIZE;
34 private CNode(final Gen gen, final int bitmap, final BasicNode... array) {
40 CNode(final Gen gen) {
41 this(gen, 0, EMPTY_ARRAY);
44 static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final int xhc, final SNode<K, V> y, final int yhc,
45 final int lev, final Gen gen) {
47 return new LNode<>(x.k, x.v, y.k, y.v);
50 final int xidx = (xhc >>> lev) & 0x1f;
51 final int yidx = (yhc >>> lev) & 0x1f;
52 final int bmp = (1 << xidx) | (1 << yidx);
55 INode<K, V> subinode = new INode<>(gen, dual(x, xhc, y, yhc, lev + 5, gen));
56 return new CNode<>(gen, bmp, subinode);
59 return xidx < yidx ? new CNode<>(gen, bmp, x, y) : new CNode<>(gen, bmp, y, x);
62 // this should only be called from within read-only snapshots
64 int cachedSize(final TrieMap<?, ?> ct) {
67 // We have not computed the size yet, do that now
69 if (!CSIZE_UPDATER.compareAndSet(this, NO_SIZE, sz)) {
70 // We have been pre-empted by some else: read the result
78 // lends itself towards being parallelizable by choosing
79 // a random starting offset in the array
80 // => if there are concurrent size computations, they start
81 // at different positions, so they are more likely to
83 private int computeSize(final TrieMap<?, ?> ct) {
86 // final int offset = (array.length > 0) ?
87 // // util.Random.nextInt(array.length) /* <-- benchmarks show that
88 // // this causes observable contention */
89 // scala.concurrent.forkjoin.ThreadLocalRandom.current.nextInt (0,
94 while (i < array.length) {
95 int pos = (i + offset) % array.length;
96 BasicNode elem = array [pos];
97 if (elem instanceof SNode) {
99 } else if (elem instanceof INode) {
100 sz += ((INode<?, ?>) elem).cachedSize(ct);
107 CNode<K, V> updatedAt(final int pos, final BasicNode nn, final Gen gen) {
108 int len = array.length;
109 BasicNode[] narr = new BasicNode[len];
110 System.arraycopy(array, 0, narr, 0, len);
112 return new CNode<>(gen, bitmap, narr);
115 CNode<K, V> removedAt(final int pos, final int flag, final Gen gen) {
116 BasicNode[] arr = array;
117 int len = arr.length;
118 BasicNode[] narr = new BasicNode[len - 1];
119 System.arraycopy(arr, 0, narr, 0, pos);
120 System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1);
121 return new CNode<>(gen, bitmap ^ flag, narr);
124 CNode<K, V> insertedAt(final int pos, final int flag, final BasicNode nn, final Gen gen) {
125 int len = array.length;
127 BasicNode[] narr = new BasicNode[len + 1];
128 System.arraycopy(array, 0, narr, 0, pos);
130 System.arraycopy(array, pos, narr, pos + 1, len - pos);
131 return new CNode<>(gen, bmp | flag, narr);
135 * Returns a copy of this cnode such that all the i-nodes below it are
136 * copied to the specified generation `ngen`.
138 CNode<K, V> renewed(final Gen ngen, final TrieMap<K, V> ct) {
140 BasicNode[] arr = array;
141 int len = arr.length;
142 BasicNode[] narr = new BasicNode[len];
144 BasicNode elem = arr[i];
145 if (elem instanceof INode) {
146 narr [i] = ((INode<?, ?>) elem).copyToGen(ngen, ct);
147 } else if (elem != null) {
152 return new CNode<>(ngen, bitmap, narr);
155 MainNode<K, V> toContracted(final int lev) {
156 if (array.length == 1 && lev > 0) {
157 if (array[0] instanceof SNode) {
158 return ((SNode<K, V>) array[0]).copyTombed();
166 // - if the branching factor is 1 for this CNode, and the child
167 // is a tombed SNode, returns its tombed version
168 // - otherwise, if there is at least one non-null node below,
169 // returns the version of this node with at least some null-inodes
170 // removed (those existing when the op began)
171 // - if there are only null-i-nodes below, returns null
172 MainNode<K, V> toCompressed(final TrieMap<?, ?> ct, final int lev, final Gen gen) {
175 BasicNode[] arr = array;
176 BasicNode[] tmparray = new BasicNode[arr.length];
177 while (i < arr.length) { // construct new bitmap
178 BasicNode sub = arr[i];
179 if (sub instanceof INode) {
180 final INode<?, ?> in = (INode<?, ?>) sub;
181 final MainNode<?, ?> inodemain = in.gcasRead(ct);
182 assert (inodemain != null);
183 tmparray [i] = resurrect(in, inodemain);
184 } else if (sub instanceof SNode) {
190 return new CNode<K, V>(gen, bmp, tmparray).toContracted(lev);
193 private static BasicNode resurrect(final INode<?, ?> inode, final MainNode<?, ?> inodemain) {
194 return inodemain instanceof TNode ? ((TNode<?, ?>) inodemain).copyUntombed() : inode;
198 String string(final int lev) {
199 // "CNode %x\n%s".format(bitmap, array.map(_.string(lev +
200 // 1)).mkString("\n"));
205 * quiescently consistent - don't call concurrently to anything
208 // protected Seq<K,V> collectElems() {
210 // case sn: SNode[K, V] => Some(sn.kvPair)
211 // case in: INode[K, V] => in.mainnode match {
212 // case tn: TNode[K, V] => Some(tn.kvPair)
213 // case ln: LNode[K, V] => ln.listmap.toList
214 // case cn: CNode[K, V] => cn.collectElems
219 // protected Seq<String> collectLocalElems() {
220 // // array flatMap {
221 // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString)
222 // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen +
229 public String toString () {
230 // val elems = collectLocalElems
231 // "CNode(sz: %d; %s)".format(elems.size,
232 // elems.sorted.mkString(", "))