/* * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opendaylight.yangtools.triemap; import static org.opendaylight.yangtools.triemap.Constants.HASH_BITS; import static org.opendaylight.yangtools.triemap.Constants.LEVEL_BITS; import com.google.common.base.Verify; import com.google.common.base.VerifyException; import java.util.concurrent.ThreadLocalRandom; final class CNode<K, V> extends MainNode<K, V> { private static final BasicNode[] EMPTY_ARRAY = new BasicNode[0]; final int bitmap; final BasicNode[] array; final Gen gen; // Since concurrent computation should lead to same results we can update this field without any synchronization. private volatile int csize = NO_SIZE; private CNode(final Gen gen, final int bitmap, final BasicNode... array) { this.bitmap = bitmap; this.array = array; this.gen = gen; } CNode(final Gen gen) { this(gen, 0, EMPTY_ARRAY); } static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final K key, final V value, final int hc, final int lev, final Gen gen) { return dual(x, x.hc, new SNode<>(key, value, hc), hc, lev, gen); } private static <K, V> MainNode<K,V> dual(final SNode<K, V> x, final int xhc, final SNode<K, V> y, final int yhc, final int lev, final Gen gen) { if (lev >= HASH_BITS) { return new LNode<>(x.k, x.v, y.k, y.v); } final int xidx = (xhc >>> lev) & 0x1f; final int yidx = (yhc >>> lev) & 0x1f; final int bmp = (1 << xidx) | (1 << yidx); if (xidx == yidx) { return new CNode<>(gen, bmp, new INode<>(gen, dual(x, xhc, y, yhc, lev + LEVEL_BITS, gen))); } return xidx < yidx ? new CNode<>(gen, bmp, x, y) : new CNode<>(gen, bmp, y, x); } @Override int trySize() { return csize; } @Override int size(final ImmutableTrieMap<?, ?> ct) { int sz; return (sz = csize) != NO_SIZE ? sz : (csize = computeSize(ct)); } static VerifyException invalidElement(final BasicNode elem) { throw new VerifyException("A CNode can contain only CNodes and SNodes, not " + elem); } // lends itself towards being parallelizable by choosing // a random starting offset in the array // => if there are concurrent size computations, they start // at different positions, so they are more likely to // to be independent private int computeSize(final ImmutableTrieMap<?, ?> ct) { final int len = array.length; switch (len) { case 0: return 0; case 1: return elementSize(array[0], ct); default: final int offset = ThreadLocalRandom.current().nextInt(len); int sz = 0; for (int i = offset; i < len; ++i) { sz += elementSize(array[i], ct); } for (int i = 0; i < offset; ++i) { sz += elementSize(array[i], ct); } return sz; } } private static int elementSize(final BasicNode elem, final ImmutableTrieMap<?, ?> ct) { if (elem instanceof SNode) { return 1; } else if (elem instanceof INode) { return ((INode<?, ?>) elem).size(ct); } else { throw invalidElement(elem); } } CNode<K, V> updatedAt(final int pos, final BasicNode nn, final Gen gen) { int len = array.length; BasicNode[] narr = new BasicNode[len]; System.arraycopy(array, 0, narr, 0, len); narr[pos] = nn; return new CNode<>(gen, bitmap, narr); } CNode<K, V> removedAt(final int pos, final int flag, final Gen gen) { BasicNode[] arr = array; int len = arr.length; BasicNode[] narr = new BasicNode[len - 1]; System.arraycopy(arr, 0, narr, 0, pos); System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1); return new CNode<>(gen, bitmap ^ flag, narr); } CNode<K, V> insertedAt(final int pos, final int flag, final BasicNode nn, final Gen gen) { int len = array.length; BasicNode[] narr = new BasicNode[len + 1]; System.arraycopy(array, 0, narr, 0, pos); narr [pos] = nn; System.arraycopy(array, pos, narr, pos + 1, len - pos); return new CNode<>(gen, bitmap | flag, narr); } /** * Returns a copy of this cnode such that all the i-nodes below it are * copied to the specified generation `ngen`. */ CNode<K, V> renewed(final Gen ngen, final TrieMap<K, V> ct) { int i = 0; final BasicNode[] arr = array; final int len = arr.length; final BasicNode[] narr = new BasicNode[len]; while (i < len) { final BasicNode elem = arr[i]; if (elem instanceof INode) { narr[i] = ((INode<?, ?>) elem).copyToGen(ngen, ct); } else if (elem != null) { narr[i] = elem; } i += 1; } return new CNode<>(ngen, bitmap, narr); } MainNode<K, V> toContracted(final int lev) { if (array.length == 1 && lev > 0) { if (array[0] instanceof SNode) { return ((SNode<K, V>) array[0]).copyTombed(); } return this; } return this; } // - if the branching factor is 1 for this CNode, and the child // is a tombed SNode, returns its tombed version // - otherwise, if there is at least one non-null node below, // returns the version of this node with at least some null-inodes // removed (those existing when the op began) // - if there are only null-i-nodes below, returns null MainNode<K, V> toCompressed(final TrieMap<?, ?> ct, final int lev, final Gen gen) { int bmp = bitmap; int i = 0; BasicNode[] arr = array; BasicNode[] tmparray = new BasicNode[arr.length]; while (i < arr.length) { // construct new bitmap BasicNode sub = arr[i]; if (sub instanceof INode) { final INode<?, ?> in = (INode<?, ?>) sub; final MainNode<?, ?> inodemain = Verify.verifyNotNull(in.gcasRead(ct)); tmparray [i] = resurrect(in, inodemain); } else if (sub instanceof SNode) { tmparray [i] = sub; } i += 1; } return new CNode<K, V>(gen, bmp, tmparray).toContracted(lev); } private static BasicNode resurrect(final INode<?, ?> inode, final MainNode<?, ?> inodemain) { return inodemain instanceof TNode ? ((TNode<?, ?>) inodemain).copyUntombed() : inode; } @Override String string(final int lev) { // "CNode %x\n%s".format(bitmap, array.map(_.string(lev + // 1)).mkString("\n")); return "CNode"; } /* * quiescently consistent - don't call concurrently to anything * involving a GCAS!! */ // protected Seq<K,V> collectElems() { // array flatMap { // case sn: SNode[K, V] => Some(sn.kvPair) // case in: INode[K, V] => in.mainnode match { // case tn: TNode[K, V] => Some(tn.kvPair) // case ln: LNode[K, V] => ln.listmap.toList // case cn: CNode[K, V] => cn.collectElems // } // } // } // protected Seq<String> collectLocalElems() { // // array flatMap { // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString) // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen + // ")") // // } // return null; // } @Override public String toString() { // val elems = collectLocalElems // "CNode(sz: %d; %s)".format(elems.size, // elems.sorted.mkString(", ")) return "CNode"; } }