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

org.pkl.thirdparty.truffle.object.TrieNode Maven / Gradle / Ivy

Go to download

Shaded fat Jar for pkl-config-java, a Java config library based on the Pkl config language.

There is a newer version: 0.27.1
Show newest version
/*
 * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.pkl.thirdparty.truffle.object;

import java.util.Arrays;
import java.util.Map;
import java.util.function.Consumer;

abstract class TrieNode> {

    protected static final int HASH_SHIFT = 5; // t
    protected static final int HASH_RANGE = 32; // 2**t
    protected static final int HASH_MASK = HASH_RANGE - 1;
    private static final BitmapNode EMPTY_NODE = new BitmapNode<>();

    @SuppressWarnings("unchecked")
    static > TrieNode empty() {
        return (TrieNode) EMPTY_NODE;
    }

    final E find(K key, int hash) {
        assert key != null && hash(key) == hash;
        return find(key, hash, 0);
    }

    final TrieNode put(K key, int hash, E entry) {
        assert key != null && hash(key) == hash && key(entry).equals(key);
        return put(key, hash, entry, 0);
    }

    final TrieNode remove(K key, int hash) {
        assert key != null && hash(key) == hash;
        return remove(key, hash, 0);
    }

    abstract E find(K key, int hash, int shift);

    abstract TrieNode put(K key, int hash, E entry, int shift);

    abstract TrieNode remove(K key, int hash, int shift);

    final K key(E entry) {
        return entry.getKey();
    }

    final int hash(K key) {
        return key.hashCode();
    }

    final boolean isEmpty() {
        return this == empty();
    }

    static int pos(int hash, int shift) {
        return (hash >>> shift) & HASH_MASK;
    }

    static int bit(int pos) {
        return 1 << pos;
    }

    static int bit(int hash, int shift) {
        return bit(pos(hash, shift));
    }

    static  T[] copyAndSet(T[] original, int index, T newValue) {
        T[] copy = Arrays.copyOf(original, original.length);
        copy[index] = newValue;
        return copy;
    }

    @SuppressWarnings("unchecked")
    static  T[] copyAndRemove(T[] original, int index) {
        int newLength = original.length - 1;
        T[] copy = (T[]) new Object[newLength];
        System.arraycopy(original, 0, copy, 0, index);
        System.arraycopy(original, index + 1, copy, index, newLength - index);
        return copy;
    }

    @SuppressWarnings("unchecked")
    static  T[] copyAndInsert(T[] original, int index, T element) {
        int newLength = original.length + 1;
        T[] copy = (T[]) new Object[newLength];
        System.arraycopy(original, 0, copy, 0, index);
        copy[index] = element;
        System.arraycopy(original, index, copy, index + 1, original.length - index);
        return copy;
    }

    static  T[] copyAndAppend(T[] original, T element) {
        T[] newArray = Arrays.copyOf(original, original.length + 1);
        newArray[original.length] = element;
        return newArray;
    }

    abstract Object[] entries();

    final int count() {
        int count = 0;
        for (Object entry : entries()) {
            if (entry == null) {
                continue;
            } else if (entry instanceof TrieNode) {
                count += ((TrieNode) entry).count();
            } else {
                count += 1;
            }
        }
        return count;
    }

    @SuppressWarnings("unchecked")
    final void forEachEntry(Consumer consumer) {
        for (Object entry : entries()) {
            if (entry == null) {
                continue;
            } else if (entry instanceof TrieNode) {
                ((TrieNode) entry).forEachEntry(consumer);
            } else {
                consumer.accept((E) entry);
            }
        }
    }

    final boolean verify(int shift) {
        forEachEntry(new Consumer() {
            public void accept(E e) {
                K k = key(e);
                assert find(k, hash(k), shift) == e : k;
            }
        });
        return true;
    }

    @Override
    public String toString() {
        return toStringIndent(0);
    }

    private String toStringIndent(int indent) {
        StringBuilder sb = new StringBuilder(getClass().getSimpleName());
        sb.append("[");
        Object[] entries = entries();
        if (entries.length > 0) {
            for (Object entry : entries) {
                if (entry == null) {
                    continue;
                }
                sb.append("\n");
                for (int i = 0; i <= indent; i++) {
                    sb.append(" ");
                }
                if (entry instanceof TrieNode) {
                    sb.append(((TrieNode) entry).toStringIndent(indent + 1));
                } else {
                    sb.append(entry);
                }
            }
            sb.append("\n");
            for (int i = 0; i < indent; i++) {
                sb.append(" ");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    final TrieNode combine(K key1, int hash1, E entry1, K key2, int hash2, E entry2, int shift) {
        assert !key1.equals(key2);
        if (hash1 != hash2) {
            int pos1 = pos(hash1, shift);
            int pos2 = pos(hash2, shift);
            if (pos1 != pos2) {
                int bitmap = bit(pos1) | bit(pos2);
                if (pos1 < pos2) {
                    return new BitmapNode<>(bitmap, new Object[]{entry1, entry2});
                } else {
                    return new BitmapNode<>(bitmap, new Object[]{entry2, entry1});
                }
            } else {
                int bitmap = bit(pos1);
                return new BitmapNode<>(bitmap, new Object[]{combine(key1, hash1, entry1, key2, hash2, entry2, shift + HASH_SHIFT)});
            }
        } else {
            return new HashCollisionNode<>(hash1, new Object[]{entry1, entry2});
        }
    }

    static class BitmapNode> extends TrieNode {
        private final int bitmap;
        private final Object[] entries;

        BitmapNode() {
            this.bitmap = 0;
            this.entries = new Object[0];
        }

        BitmapNode(int bitmap, Object[] entries) {
            this.bitmap = bitmap;
            this.entries = entries;
            assert Integer.bitCount(bitmap) == entries.length;
        }

        private int index(int bit) {
            return Integer.bitCount(bitmap & (bit - 1));
        }

        @SuppressWarnings("unchecked")
        @Override
        E find(K key, int hash, int shift) {
            int bit = bit(hash, shift);
            if ((bitmap & bit) != 0) {
                int index = index(bit);
                Object entry = entries[index];
                assert entry != null;
                if (entry instanceof TrieNode) {
                    return ((TrieNode) entry).find(key, hash, shift + HASH_SHIFT);
                } else {
                    E e = (E) entry;
                    K k = key(e);
                    if (k.equals(key)) {
                        return e;
                    } else {
                        return null;
                    }
                }
            } else {
                return null;
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        TrieNode put(K key, int hash, E entry, int shift) {
            int bit = bit(hash, shift);
            int index = index(bit);
            if ((bitmap & bit) != 0) {
                Object nodeOrEntry = entries[index];
                assert nodeOrEntry != null;
                if (nodeOrEntry instanceof TrieNode) {
                    TrieNode newNode = ((TrieNode) nodeOrEntry).put(key, hash, entry, shift + HASH_SHIFT);
                    if (newNode == nodeOrEntry) {
                        return this;
                    } else {
                        assert newNode != null;
                        return new BitmapNode<>(bitmap, copyAndSet(entries, index, newNode));
                    }
                } else {
                    E e = (E) nodeOrEntry;
                    K k = key(e);
                    if (k.equals(key)) {
                        return new BitmapNode<>(bitmap, copyAndSet(entries, index, entry));
                    } else {
                        int h = hash(k);
                        assert bit(h, shift) == bit(hash, shift);
                        TrieNode newNode = combine(k, h, e, key, hash, entry, shift + HASH_SHIFT);
                        return new BitmapNode<>(bitmap, copyAndSet(entries, index, newNode));
                    }
                }
            } else {
                Object[] newArray = copyAndInsert(entries, index, entry);
                return new BitmapNode<>(bitmap | bit, newArray);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        TrieNode remove(K key, int hash, int shift) {
            int bit = bit(hash, shift);
            if ((bitmap & bit) != 0) {
                int index = index(bit);
                Object entry = entries[index];
                assert entry != null;
                if (entry instanceof TrieNode) {
                    TrieNode newNode = ((TrieNode) entry).remove(key, hash, shift + HASH_SHIFT);
                    if (newNode == entry) {
                        return this;
                    } else if (!newNode.isEmpty()) {
                        return new BitmapNode<>(bitmap, copyAndSet(entries, index, collapseSingletonNode(newNode)));
                    } else {
                        return removeBitAndIndex(bit, index);
                    }
                } else {
                    E e = (E) entry;
                    K k = key(e);
                    if (k.equals(key)) {
                        return removeBitAndIndex(bit, index);
                    } else {
                        return this;
                    }
                }
            } else {
                return this;
            }
        }

        private TrieNode removeBitAndIndex(int bit, int index) {
            if (entries.length > 1) {
                return new BitmapNode<>(bitmap & ~bit, copyAndRemove(entries, index));
            } else {
                return empty();
            }
        }

        private Object collapseSingletonNode(TrieNode node) {
            assert !node.isEmpty();
            // remove may return a single-entry node, collapse it into just the entry
            if (node instanceof BitmapNode) {
                BitmapNode bitmapNode = (BitmapNode) node;
                if (bitmapNode.entries.length == 1 && !(bitmapNode.entries[0] instanceof TrieNode)) {
                    return bitmapNode.entries[0];
                }
            }
            return node;
        }

        @Override
        Object[] entries() {
            return entries;
        }
    }

    static class HashCollisionNode> extends TrieNode {
        private final int hashcode;
        private final Object[] entries;

        HashCollisionNode(int hash, Object[] entries) {
            this.hashcode = hash;
            this.entries = entries;
            assert entries.length >= 2;
        }

        @SuppressWarnings("unchecked")
        private int findIndex(K key) {
            for (int i = 0; i < entries.length; i++) {
                E entry = (E) entries[i];
                if (key.equals(key(entry))) {
                    return i;
                }
            }
            return -1;
        }

        @SuppressWarnings("unchecked")
        @Override
        E find(K key, int hash, int shift) {
            int index = findIndex(key);
            if (index < 0) {
                return null;
            } else {
                E entry = (E) entries[index];
                assert entry != null && key(entry).equals(key);
                return entry;
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        TrieNode put(K key, int hash, E entry, int shift) {
            if (hash == this.hashcode) {
                int index = findIndex(key);
                if (index < 0) {
                    return new HashCollisionNode<>(hash, copyAndAppend(entries, entry));
                } else {
                    E e = (E) entries[index];
                    assert e != null && key(e).equals(key);
                    if (e.equals(entry)) {
                        return this;
                    } else {
                        return new HashCollisionNode<>(hash, copyAndSet(entries, index, entry));
                    }
                }
            } else {
                return new BitmapNode(bit(this.hashcode, shift), new Object[]{this}).put(key, hash, entry, shift);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        TrieNode remove(K key, int hash, int shift) {
            int index = findIndex(key);
            if (index < 0) {
                return this;
            } else {
                assert entries[index] != null && key((E) entries[index]).equals(key);
                assert entries.length >= 2;
                if (entries.length == 2) {
                    return new BitmapNode<>(bit(this.hashcode, shift), copyAndRemove(entries, index));
                } else {
                    return new HashCollisionNode<>(hash, copyAndRemove(entries, index));
                }
            }
        }

        @Override
        Object[] entries() {
            return entries;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy