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.HASH_BITS;
19 import static org.opendaylight.yangtools.triemap.Constants.LEVEL_BITS;
21 import com.google.common.base.Verify;
22 import java.util.concurrent.ThreadLocalRandom;
23 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
25 final class CNode<K, V> extends MainNode<K, V> {
26 @SuppressWarnings("rawtypes")
27 private static final AtomicIntegerFieldUpdater<CNode> CSIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(
28 CNode.class, "csize");
30 private static final BasicNode[] EMPTY_ARRAY = new BasicNode[0];
31 private static final int NO_SIZE = -1;
34 final BasicNode[] array;
37 private volatile int csize = NO_SIZE;
39 private CNode(final Gen gen, final int bitmap, final BasicNode... array) {
45 CNode(final Gen gen) {
46 this(gen, 0, EMPTY_ARRAY);
49 static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final K k, final V v, final int hc, final int lev,
51 return dual(x, x.hc, new SNode<>(k, v, hc), hc, lev, gen);
54 private static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final int xhc, final SNode<K, V> y, final int yhc,
55 final int lev, final Gen gen) {
56 if (lev >= HASH_BITS) {
57 return new LNode<>(x.k, x.v, y.k, y.v);
60 final int xidx = (xhc >>> lev) & 0x1f;
61 final int yidx = (yhc >>> lev) & 0x1f;
62 final int bmp = (1 << xidx) | (1 << yidx);
65 return new CNode<>(gen, bmp, new INode<>(gen, dual(x, xhc, y, yhc, lev + LEVEL_BITS, gen)));
68 return xidx < yidx ? new CNode<>(gen, bmp, x, y) : new CNode<>(gen, bmp, y, x);
71 // this should only be called from within read-only snapshots
73 int cachedSize(final TrieMap<?, ?> ct) {
76 // We have not computed the size yet, do that now
78 if (!CSIZE_UPDATER.compareAndSet(this, NO_SIZE, sz)) {
79 // We have been pre-empted by some else: read the result
87 // lends itself towards being parallelizable by choosing
88 // a random starting offset in the array
89 // => if there are concurrent size computations, they start
90 // at different positions, so they are more likely to
92 private int computeSize(final TrieMap<?, ?> ct) {
93 final int len = array.length;
98 return elementSize(array[0], ct);
100 final int offset = ThreadLocalRandom.current().nextInt(len);
102 for (int i = offset; i < len; ++i) {
103 sz += elementSize(array[i], ct);
105 for (int i = 0; i < offset; ++i) {
106 sz += elementSize(array[i], ct);
112 private static int elementSize(final BasicNode elem, final TrieMap<?, ?> ct) {
113 if (elem instanceof SNode) {
115 } else if (elem instanceof INode) {
116 return ((INode<?, ?>) elem).cachedSize(ct);
118 throw new IllegalStateException("Unhandled element " + elem);
122 CNode<K, V> updatedAt(final int pos, final BasicNode nn, final Gen gen) {
123 int len = array.length;
124 BasicNode[] narr = new BasicNode[len];
125 System.arraycopy(array, 0, narr, 0, len);
127 return new CNode<>(gen, bitmap, narr);
130 CNode<K, V> removedAt(final int pos, final int flag, final Gen gen) {
131 BasicNode[] arr = array;
132 int len = arr.length;
133 BasicNode[] narr = new BasicNode[len - 1];
134 System.arraycopy(arr, 0, narr, 0, pos);
135 System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1);
136 return new CNode<>(gen, bitmap ^ flag, narr);
139 CNode<K, V> insertedAt(final int pos, final int flag, final BasicNode nn, final Gen gen) {
140 int len = array.length;
142 BasicNode[] narr = new BasicNode[len + 1];
143 System.arraycopy(array, 0, narr, 0, pos);
145 System.arraycopy(array, pos, narr, pos + 1, len - pos);
146 return new CNode<>(gen, bmp | flag, narr);
150 * Returns a copy of this cnode such that all the i-nodes below it are
151 * copied to the specified generation `ngen`.
153 CNode<K, V> renewed(final Gen ngen, final TrieMap<K, V> ct) {
155 final BasicNode[] arr = array;
156 final int len = arr.length;
157 final BasicNode[] narr = new BasicNode[len];
159 final BasicNode elem = arr[i];
160 if (elem instanceof INode) {
161 narr[i] = ((INode<?, ?>) elem).copyToGen(ngen, ct);
162 } else if (elem != null) {
167 return new CNode<>(ngen, bitmap, narr);
170 MainNode<K, V> toContracted(final int lev) {
171 if (array.length == 1 && lev > 0) {
172 if (array[0] instanceof SNode) {
173 return ((SNode<K, V>) array[0]).copyTombed();
181 // - if the branching factor is 1 for this CNode, and the child
182 // is a tombed SNode, returns its tombed version
183 // - otherwise, if there is at least one non-null node below,
184 // returns the version of this node with at least some null-inodes
185 // removed (those existing when the op began)
186 // - if there are only null-i-nodes below, returns null
187 MainNode<K, V> toCompressed(final TrieMap<?, ?> ct, final int lev, final Gen gen) {
190 BasicNode[] arr = array;
191 BasicNode[] tmparray = new BasicNode[arr.length];
192 while (i < arr.length) { // construct new bitmap
193 BasicNode sub = arr[i];
194 if (sub instanceof INode) {
195 final INode<?, ?> in = (INode<?, ?>) sub;
196 final MainNode<?, ?> inodemain = Verify.verifyNotNull(in.gcasRead(ct));
197 tmparray [i] = resurrect(in, inodemain);
198 } else if (sub instanceof SNode) {
204 return new CNode<K, V>(gen, bmp, tmparray).toContracted(lev);
207 private static BasicNode resurrect(final INode<?, ?> inode, final MainNode<?, ?> inodemain) {
208 return inodemain instanceof TNode ? ((TNode<?, ?>) inodemain).copyUntombed() : inode;
212 String string(final int lev) {
213 // "CNode %x\n%s".format(bitmap, array.map(_.string(lev +
214 // 1)).mkString("\n"));
219 * quiescently consistent - don't call concurrently to anything
222 // protected Seq<K,V> collectElems() {
224 // case sn: SNode[K, V] => Some(sn.kvPair)
225 // case in: INode[K, V] => in.mainnode match {
226 // case tn: TNode[K, V] => Some(tn.kvPair)
227 // case ln: LNode[K, V] => ln.listmap.toList
228 // case cn: CNode[K, V] => cn.collectElems
233 // protected Seq<String> collectLocalElems() {
234 // // array flatMap {
235 // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString)
236 // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen +
243 public String toString () {
244 // val elems = collectLocalElems
245 // "CNode(sz: %d; %s)".format(elems.size,
246 // elems.sorted.mkString(", "))