BUG-7464: clean up CNode.dual() call convention
[yangtools.git] / third-party / triemap / src / main / java / org / opendaylight / yangtools / triemap / CNode.java
1 /*
2  * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.opendaylight.yangtools.triemap;
17
18 import com.google.common.base.Verify;
19 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
20
21 final class CNode<K, V> extends MainNode<K, V> {
22     @SuppressWarnings("rawtypes")
23     private static final AtomicIntegerFieldUpdater<CNode> CSIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(
24         CNode.class, "csize");
25
26     private static final BasicNode[] EMPTY_ARRAY = new BasicNode[0];
27     private static final int NO_SIZE = -1;
28
29     final int bitmap;
30     final BasicNode[] array;
31     final Gen gen;
32
33     private volatile int csize = NO_SIZE;
34
35     private CNode(final Gen gen, final int bitmap, final BasicNode... array) {
36         this.bitmap = bitmap;
37         this.array = array;
38         this.gen = gen;
39     }
40
41     CNode(final Gen gen) {
42         this(gen, 0, EMPTY_ARRAY);
43     }
44
45     static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final K k, final V v, final int hc, final int lev,
46             final Gen gen) {
47         return dual(x, x.hc, new SNode<>(k, v, hc), hc, lev, gen);
48     }
49
50     private static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final int xhc, final SNode<K, V> y, final int yhc,
51             final int lev, final Gen gen) {
52         if (lev >= 35) {
53             return new LNode<>(x.k, x.v, y.k, y.v);
54         }
55
56         final int xidx = (xhc >>> lev) & 0x1f;
57         final int yidx = (yhc >>> lev) & 0x1f;
58         final int bmp = (1 << xidx) | (1 << yidx);
59
60         if (xidx == yidx) {
61             return new CNode<>(gen, bmp, new INode<>(gen, dual(x, xhc, y, yhc, lev + 5, gen)));
62         }
63
64         return xidx < yidx ? new CNode<>(gen, bmp, x, y) : new CNode<>(gen, bmp, y, x);
65     }
66
67     // this should only be called from within read-only snapshots
68     @Override
69     int cachedSize(final TrieMap<?, ?> ct) {
70         int sz = csize;
71         if (sz == NO_SIZE) {
72             // We have not computed the size yet, do that now
73             sz = computeSize(ct);
74             if (!CSIZE_UPDATER.compareAndSet(this, NO_SIZE, sz)) {
75                 // We have been pre-empted by some else: read the result
76                 sz = csize;
77             }
78         }
79
80         return sz;
81     }
82
83     // lends itself towards being parallelizable by choosing
84     // a random starting offset in the array
85     // => if there are concurrent size computations, they start
86     // at different positions, so they are more likely to
87     // to be independent
88     private int computeSize(final TrieMap<?, ?> ct) {
89         int i = 0;
90         int sz = 0;
91         // final int offset = (array.length > 0) ?
92         // // util.Random.nextInt(array.length) /* <-- benchmarks show that
93         // // this causes observable contention */
94         // scala.concurrent.forkjoin.ThreadLocalRandom.current.nextInt (0,
95         // array.length)
96         // : 0;
97
98         final int offset = 0;
99         while (i < array.length) {
100             int pos = (i + offset) % array.length;
101             BasicNode elem = array [pos];
102             if (elem instanceof SNode) {
103                 sz += 1;
104             } else if (elem instanceof INode) {
105                 sz += ((INode<?, ?>) elem).cachedSize(ct);
106             }
107             i += 1;
108         }
109         return sz;
110     }
111
112     CNode<K, V> updatedAt(final int pos, final BasicNode nn, final Gen gen) {
113         int len = array.length;
114         BasicNode[] narr = new BasicNode[len];
115         System.arraycopy(array, 0, narr, 0, len);
116         narr[pos] = nn;
117         return new CNode<>(gen, bitmap, narr);
118     }
119
120     CNode<K, V> removedAt(final int pos, final int flag, final Gen gen) {
121         BasicNode[] arr = array;
122         int len = arr.length;
123         BasicNode[] narr = new BasicNode[len - 1];
124         System.arraycopy(arr, 0, narr, 0, pos);
125         System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1);
126         return new CNode<>(gen, bitmap ^ flag, narr);
127     }
128
129     CNode<K, V> insertedAt(final int pos, final int flag, final BasicNode nn, final Gen gen) {
130         int len = array.length;
131         int bmp = bitmap;
132         BasicNode[] narr = new BasicNode[len + 1];
133         System.arraycopy(array, 0, narr, 0, pos);
134         narr [pos] = nn;
135         System.arraycopy(array, pos, narr, pos + 1, len - pos);
136         return new CNode<>(gen, bmp | flag, narr);
137     }
138
139     /**
140      * Returns a copy of this cnode such that all the i-nodes below it are
141      * copied to the specified generation `ngen`.
142      */
143     CNode<K, V> renewed(final Gen ngen, final TrieMap<K, V> ct) {
144         int i = 0;
145         final BasicNode[] arr = array;
146         final int len = arr.length;
147         final BasicNode[] narr = new BasicNode[len];
148         while (i < len) {
149             final BasicNode elem = arr[i];
150             if (elem instanceof INode) {
151                 narr[i] = ((INode<?, ?>) elem).copyToGen(ngen, ct);
152             } else if (elem != null) {
153                 narr[i] = elem;
154             }
155             i += 1;
156         }
157         return new CNode<>(ngen, bitmap, narr);
158     }
159
160     MainNode<K, V> toContracted(final int lev) {
161         if (array.length == 1 && lev > 0) {
162             if (array[0] instanceof SNode) {
163                 return ((SNode<K, V>) array[0]).copyTombed();
164             }
165             return this;
166         }
167
168         return this;
169     }
170
171     // - if the branching factor is 1 for this CNode, and the child
172     // is a tombed SNode, returns its tombed version
173     // - otherwise, if there is at least one non-null node below,
174     // returns the version of this node with at least some null-inodes
175     // removed (those existing when the op began)
176     // - if there are only null-i-nodes below, returns null
177     MainNode<K, V> toCompressed(final TrieMap<?, ?> ct, final int lev, final Gen gen) {
178         int bmp = bitmap;
179         int i = 0;
180         BasicNode[] arr = array;
181         BasicNode[] tmparray = new BasicNode[arr.length];
182         while (i < arr.length) { // construct new bitmap
183             BasicNode sub = arr[i];
184             if (sub instanceof INode) {
185                 final INode<?, ?> in = (INode<?, ?>) sub;
186                 final MainNode<?, ?> inodemain = Verify.verifyNotNull(in.gcasRead(ct));
187                 tmparray [i] = resurrect(in, inodemain);
188             } else if (sub instanceof SNode) {
189                 tmparray [i] = sub;
190             }
191             i += 1;
192         }
193
194         return new CNode<K, V>(gen, bmp, tmparray).toContracted(lev);
195     }
196
197     private static BasicNode resurrect(final INode<?, ?> inode, final MainNode<?, ?> inodemain) {
198         return inodemain instanceof TNode ? ((TNode<?, ?>) inodemain).copyUntombed() : inode;
199     }
200
201     @Override
202     String string(final int lev) {
203         // "CNode %x\n%s".format(bitmap, array.map(_.string(lev +
204         // 1)).mkString("\n"));
205         return "CNode";
206     }
207
208     /*
209      * quiescently consistent - don't call concurrently to anything
210      * involving a GCAS!!
211      */
212     // protected Seq<K,V> collectElems() {
213     // array flatMap {
214     // case sn: SNode[K, V] => Some(sn.kvPair)
215     // case in: INode[K, V] => in.mainnode match {
216     // case tn: TNode[K, V] => Some(tn.kvPair)
217     // case ln: LNode[K, V] => ln.listmap.toList
218     // case cn: CNode[K, V] => cn.collectElems
219     // }
220     // }
221     // }
222
223     // protected Seq<String> collectLocalElems() {
224     // // array flatMap {
225     // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString)
226     // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen +
227     // ")")
228     // // }
229     // return null;
230     // }
231
232     @Override
233     public String toString () {
234         // val elems = collectLocalElems
235         // "CNode(sz: %d; %s)".format(elems.size,
236         // elems.sorted.mkString(", "))
237         return "CNode";
238     }
239 }