All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.pantheon.triemap.CNode Maven / Gradle / Ivy

Go to download

Java implementation of a concurrent trie hash map from Scala collections library

There is a newer version: 1.3.2
Show newest version
/*
 * (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 tech.pantheon.triemap;

import static tech.pantheon.triemap.Constants.HASH_BITS;
import static tech.pantheon.triemap.Constants.LEVEL_BITS;

import java.util.concurrent.ThreadLocalRandom;

final class CNode extends MainNode {
    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  MainNode dual(final SNode snode, final K key, final V value, final int hc, final int lev,
            final Gen gen) {
        return dual(snode, snode.hc, new SNode<>(key, value, hc), hc, lev, gen);
    }

    private static  MainNode dual(final SNode first, final int firstHash, final SNode second,
            final int secondHash, final int lev, final Gen gen) {
        if (lev >= HASH_BITS) {
            return new LNode<>(first.key, first.value, second.key, second.value);
        }

        final int xidx = firstHash >>> lev & 0x1f;
        final int yidx = secondHash >>> lev & 0x1f;
        final int bmp = 1 << xidx | 1 << yidx;

        if (xidx == yidx) {
            return new CNode<>(gen, bmp, new INode<>(gen, dual(first, firstHash, second, secondHash, lev + LEVEL_BITS,
                gen)));
        }

        return xidx < yidx ? new CNode<>(gen, bmp, first, second) : new CNode<>(gen, bmp, second, first);
    }

    @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 %s", 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 updatedAt(final int pos, final BasicNode nn, final Gen newGen) {
        int len = array.length;
        BasicNode[] narr = new BasicNode[len];
        System.arraycopy(array, 0, narr, 0, len);
        narr[pos] = nn;
        return new CNode<>(newGen, bitmap, narr);
    }

    CNode removedAt(final int pos, final int flag, final Gen newGen) {
        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<>(newGen, bitmap ^ flag, narr);
    }

    CNode insertedAt(final int pos, final int flag, final BasicNode nn, final Gen newGen) {
        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<>(newGen, 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 renewed(final Gen ngen, final TrieMap ct) {
        int idx = 0;
        final BasicNode[] arr = array;
        final int len = arr.length;
        final BasicNode[] narr = new BasicNode[len];
        while (idx < len) {
            final BasicNode elem = arr[idx];
            if (elem instanceof INode) {
                narr[idx] = ((INode) elem).copyToGen(ngen, ct);
            } else if (elem != null) {
                narr[idx] = elem;
            }
            idx += 1;
        }
        return new CNode<>(ngen, bitmap, narr);
    }

    @SuppressWarnings("unchecked")
    MainNode toContracted(final int lev) {
        if (array.length == 1 && lev > 0) {
            if (array[0] instanceof SNode) {
                return ((SNode) 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 toCompressed(final TrieMap ct, final int lev, final Gen newGen) {
        int bmp = bitmap;
        int idx = 0;
        BasicNode[] arr = array;
        BasicNode[] tmparray = new BasicNode[arr.length];
        while (idx < arr.length) { // construct new bitmap
            BasicNode sub = arr[idx];
            if (sub instanceof INode) {
                final INode in = (INode) sub;
                final MainNode inodemain = VerifyException.throwIfNull(in.gcasRead(ct));
                tmparray [idx] = resurrect(in, inodemain);
            } else if (sub instanceof SNode) {
                tmparray [idx] = sub;
            }
            idx += 1;
        }

        return new CNode(newGen, bmp, tmparray).toContracted(lev);
    }

    private static BasicNode resurrect(final INode inode, final MainNode inodemain) {
        return inodemain instanceof TNode ? ((TNode) inodemain).copyUntombed() : inode;
    }

    @Override
    public String toString() {
        // val elems = collectLocalElems
        // "CNode(sz: %d; %s)".format(elems.size,
        // elems.sorted.mkString(", "))
        return "CNode";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy