
org.javersion.util.AbstractHashTrie Maven / Gradle / Ivy
/*
* Copyright 2013 Samppa Saarela
*
* 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.javersion.util;
import static java.lang.System.arraycopy;
import java.util.Objects;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.function.Consumer;
import org.javersion.util.AbstractHashTrie.EntryNode;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
public abstract class AbstractHashTrie, This extends AbstractHashTrie> {
public abstract int size();
public boolean isEmpty() {
return size() == 0;
}
@SuppressWarnings("unchecked")
protected This self() {
return (This) this;
}
protected abstract This doReturn(Node newRoot, int newSize);
protected abstract Node root();
public boolean containsKey(Object key) {
return root().find(key) != null;
}
protected final Iterator doIterator() {
return root().iterator();
}
private This commitAndReturn(UpdateContext super E> updateContext, Node newRoot, int newSize) {
commit(updateContext);
return doReturn(newRoot, newSize);
}
protected final This doAdd(UpdateContext super E> updateContext, E newEntry) {
Node newRoot = root().assoc(updateContext, newEntry);
return commitAndReturn(updateContext, newRoot, size() + updateContext.getChangeAndReset());
}
@SuppressWarnings("rawtypes")
protected final This doAddAll(UpdateContext super E> updateContext, Iterator entries) {
Node newRoot = root();
int size = size();
while (entries.hasNext()) {
@SuppressWarnings("unchecked")
E entry = (E) entries.next();
newRoot = newRoot.assoc(updateContext, entry);
size += updateContext.getChangeAndReset();
}
return commitAndReturn(updateContext, newRoot, size);
}
protected final This doRemove(UpdateContext super E> updateContext, Object key) {
Node newRoot = root().dissoc(updateContext, key);
return commitAndReturn(updateContext, newRoot, size() + updateContext.getChangeAndReset());
}
@SuppressWarnings("rawtypes")
protected final This doRemoveAll(UpdateContext super E> updateContext, Iterator keys) {
Node newRoot = root();
int size = size();
while (keys.hasNext()) {
newRoot = newRoot.dissoc(updateContext, keys.next());
size += updateContext.getChangeAndReset();
}
return commitAndReturn(updateContext, newRoot, size);
}
protected void commit(UpdateContext> updateContext) {
updateContext.commit();
}
static abstract class Node> implements Iterable {
static final int SHIFT_INCREMENT = 5;
E find(Object key) {
return findInternal(0, hash(key), key);
}
Node assoc(UpdateContext super E> currentContext, E newEntry) {
return assocInternal(currentContext, 0, newEntry.getHash(), newEntry);
}
Node dissoc(UpdateContext super E> currentContext, Object key) {
return dissocInternal(currentContext, 0, hash(key), key);
}
static int hash(Object key) {
return key == null ? 0 : key.hashCode();
}
static int index(int bitmap, int bit){
return Integer.bitCount(bitmap & (bit - 1));
}
static int bit(int hash, int shift) {
return 1 << bitIndex(hash, shift);
}
static int bitIndex(int hash, int shift) {
// xx xxxxx xxxxx xxxxx xxxxx NNNNN xxxxx >>> 5
// 00 00000 00000 00000 00000 00000 NNNNN & 0x01f
// return number (NNNNN) between 0..31
return (hash >>> shift) & 0x01f;
}
abstract E findInternal(int shift, int hash, Object key);
abstract Node assocInternal(UpdateContext super E> currentContext, int shift, int hash, E newEntry);
abstract Node dissocInternal(UpdateContext super E> currentContext, int shift, int hash, Object key);
protected abstract Node[] getChildren();
}
@SuppressWarnings("rawtypes")
private static final Iterator EMTPY_ITER = Collections.emptyIterator();
@SuppressWarnings("rawtypes")
static final Node EMPTY_NODE = new Node() {
@Override
public Iterator iterator() {
return EMTPY_ITER;
}
@Override
EntryNode findInternal(int shift, int hash, Object key) {
return null;
}
@Override
Node dissocInternal(UpdateContext currentContext, int shift, int hash, Object key) {
return this;
}
@SuppressWarnings("unchecked")
@Override
Node assocInternal(UpdateContext currentContext, int shift, int hash, EntryNode newEntryNode) {
currentContext.insert(newEntryNode);
if (currentContext.expectedUpdates() == 1) {
return newEntryNode;
} else {
Node node = new HashNode(currentContext);
return node.assocInternal(currentContext, shift, hash, newEntryNode);
}
}
@Override
protected Node[] getChildren() {
return null;
}
};
protected static abstract class EntryNode> extends Node {
final K key;
public EntryNode(K key) {
this.key = key;
}
public int getHash() {
return hash(key);
}
@SuppressWarnings("unchecked")
protected E self() {
return (E) this;
}
@SuppressWarnings("unchecked")
protected Node split(final UpdateContext super E> currentContext, final int shift, final int hash, final E newEntry) {
int thisHash = getHash();
if (hash == thisHash) {
currentContext.insert(newEntry);
return new CollisionNode((E) this, newEntry);
}
else {
@SuppressWarnings("rawtypes")
Node[] newChildren = new Node[HashNode.newSizeForInsert(currentContext, 1)];
newChildren[0] = this;
return new HashNode(currentContext, bit(thisHash, shift), newChildren)
.assocInternal(currentContext, shift, hash, newEntry);
}
}
@Override
Node dissocInternal(UpdateContext super E> currentContext, int shift, int hash, Object key) {
if (Objects.equals(key, this.key)) {
currentContext.delete(self());
return null;
}
return this;
}
@Override
E findInternal(int shift, int hash, Object key) {
if (Objects.equals(this.key, key)) {
return self();
}
return null;
}
@Override
public Iterator iterator() {
return Iterators.singletonIterator(self());
}
@Override
protected Node[] getChildren() {
return null;
}
}
static final class HashNode> extends Node {
final UpdateContext super E> updateContext;
private int bitmap;
private Node[] children;
HashNode(UpdateContext super E> contextReference) {
this(contextReference, contextReference.expectedUpdates());
}
@SuppressWarnings("unchecked")
HashNode(UpdateContext super E> contextReference, int expectedSize) {
this(contextReference, 0, new Node[expectedSize < 32 ? expectedSize : 32]);
}
HashNode(UpdateContext super E> contextReference, int bitmap, Node[] children) {
this.updateContext = contextReference;
this.bitmap = bitmap;
this.children = children;
}
@Override
Node assocInternal(final UpdateContext super E> currentContext, final int shift, int hash, final E newEntry) {
int bit = bit(hash, shift);
int index = index(bitmap, bit);
if ((bitmap & bit) != 0) {
Node oldNode = children[index];
Node newNode = oldNode.assocInternal(currentContext, shift + SHIFT_INCREMENT, hash, newEntry);
if (newNode == oldNode) {
return this;
} else {
HashNode editable = cloneForReplace(currentContext);
editable.children[index] = newNode;
return editable;
}
} else {
currentContext.insert(newEntry);
return insert(currentContext, index, newEntry, bit);
}
}
@Override
Node dissocInternal(UpdateContext super E> currentContext, int shift, int hash, Object key) {
int bit = bit(hash, shift);
if ((bitmap & bit) == 0) {
return this;
}
int index = index(bitmap, bit);
Node oldNode = children[index];
Node newNode = oldNode.dissocInternal(currentContext, shift + SHIFT_INCREMENT, hash, key);
if (newNode == oldNode) {
return this;
} else if (newNode == null) {
if (bitmap == bit) {
return null;
} else {
return cloneForDelete(currentContext, index, bit);
}
} else {
HashNode editable = cloneForReplace(currentContext);
editable.children[index] = newNode;
return editable;
}
}
@Override
public E findInternal(int shift, int hash, Object key) {
int bit = bit(hash, shift);
if ((bitmap & bit) == 0) {
return null;
}
int index = index(bitmap, bit);
Node nodeOrEntry = children[index];
return nodeOrEntry.findInternal(shift + SHIFT_INCREMENT, hash, key);
}
@SuppressWarnings("unchecked")
private Node insert(UpdateContext super E> currentContext, int index, E newEntry, int bit) {
int childCount = childCount();
boolean editInPlace = updateContext.isSameAs(currentContext);
Node[] newChildren;
if (editInPlace && childCount < children.length) {
newChildren = this.children;
} else {
newChildren = new Node[newSizeForInsert(currentContext, childCount)];
if (index > 0) {
arraycopy(children, 0, newChildren, 0, index);
}
}
// make room for insertion
if (index < childCount) {
arraycopy(children, index, newChildren, index + 1, childCount - index);
}
newChildren[index] = newEntry;
if (childCount == 31) {
// Convert to ArrayNode as it can be done here practically with no extra cost
return new ArrayNode<>(currentContext, newChildren, childCount + 1);
}
else if (editInPlace) {
this.bitmap |= bit;
this.children = newChildren;
return this;
}
else {
return new HashNode<>(currentContext, bitmap | bit, newChildren);
}
}
private int childCount() {
return Integer.bitCount(bitmap);
}
@SuppressWarnings("unchecked")
private Node cloneForDelete(UpdateContext super E> currentContext, int index, int bit) {
int childCount = childCount();
boolean editInPlace = updateContext.isSameAs(currentContext);
Node[] newChildren;
if (editInPlace) {
newChildren = this.children;
} else {
newChildren = new Node[childCount - 1];
if (index > 0) {
arraycopy(children, 0, newChildren, 0, index);
}
}
// Delete given node
if (index + 1 < childCount) {
arraycopy(children, index + 1, newChildren, index, childCount - index - 1);
if (newChildren.length >= childCount) {
newChildren[childCount - 1] = null;
}
}
if (editInPlace) {
this.bitmap = bitmap ^ bit;
return this;
}
else {
return new HashNode<>(currentContext, bitmap ^ bit, newChildren);
}
}
static int newSizeForInsert(UpdateContext> currentContext, int currentChildCount) {
if (currentContext.expectedUpdates() == 1) {
return currentChildCount + 1;
} else {
return currentChildCount < 16 ? 2*(currentChildCount + 1) : 32;
}
}
private HashNode cloneForReplace(UpdateContext super E> currentContext) {
if (this.updateContext.isSameAs(currentContext)) {
return this;
} else {
return new HashNode<>(currentContext, bitmap, children.clone());
}
}
@Override
public Iterator iterator() {
return new ArrayIterator<>(children);
}
@Override
public Node[] getChildren() {
return children;
}
}
static final class ArrayNode> extends Node {
final UpdateContext super E> updateContext;
private Node[] children;
private int childCount;
ArrayNode(UpdateContext super E> contextReference, Node[] children, int childCount) {
this.updateContext = contextReference;
this.children = children;
this.childCount = childCount;
}
@Override
Node assocInternal(final UpdateContext super E> currentContext, final int shift, int hash, final E newEntry) {
int index = bitIndex(hash, shift);
Node node = children[index];
int newChildCount = childCount;
Node newChild;
if (node != null) {
newChild = node.assocInternal(currentContext, shift + SHIFT_INCREMENT, hash, newEntry);
if (newChild == node) {
return this;
}
} else {
currentContext.insert(newEntry);
newChildCount++;
newChild = newEntry;
}
if (isEditInPlace(currentContext)) {
this.children[index] = newChild;
this.childCount = newChildCount;
return this;
} else {
Node[] newChildren = this.children.clone();
newChildren[index] = newChild;
return new ArrayNode<>(currentContext, newChildren, newChildCount);
}
}
@Override
Node dissocInternal(UpdateContext super E> currentContext, int shift, int hash, Object key) {
int index = bitIndex(hash, shift);
Node node = children[index];
if (node == null) {
return this;
}
int newChildCount = childCount;
Node newChild = node.dissocInternal(currentContext, shift + SHIFT_INCREMENT, hash, key);
if (newChild == node) {
return this;
} else if (newChild == null) {
newChildCount--;
if (newChildCount < 16) {
return toBitmapNode(currentContext, newChildCount, index);
}
}
if (isEditInPlace(currentContext)) {
this.children[index] = newChild;
this.childCount = newChildCount;
return this;
} else {
Node[] newChildren = this.children.clone();
newChildren[index] = newChild;
return new ArrayNode<>(currentContext, newChildren, newChildCount);
}
}
private Node toBitmapNode(UpdateContext super E> currentContext, int newChildCount, int removedIndex) {
@SuppressWarnings("unchecked")
Node[] newChildren = new Node[newChildCount];
int bitmap = 0;
for (int i=0, j=0; i < children.length; i++) {
if (children[i] != null && i != removedIndex) {
newChildren[j] = children[i];
bitmap |= 1 << i;
j++;
}
}
return new HashNode<>(currentContext, bitmap, newChildren);
}
@Override
E findInternal(int shift, int hash, Object key) {
int index = bitIndex(hash, shift);
Node node = children[index];
if (node != null) {
return node.findInternal(shift + SHIFT_INCREMENT, hash, key);
} else {
return null;
}
}
private boolean isEditInPlace(UpdateContext super E> currentContext) {
return this.updateContext.isSameAs(currentContext);
}
@Override
public Iterator iterator() {
return new ArrayIterator<>(children);
}
@Override
public Node[] getChildren() {
return children;
}
}
static final class CollisionNode> extends Node {
final int hash;
private E[] entries;
@SuppressWarnings("unchecked")
public CollisionNode(E first, E second) {
this.hash = first.getHash();
this.entries = (E[]) new EntryNode[] { first, second };
}
@SuppressWarnings("unchecked")
private CollisionNode(EntryNode extends K, ? extends E>[] entries) {
this.hash = entries[0].getHash();
this.entries = (E[]) entries;
}
@Override
public E findInternal(int shift, int hash, Object key) {
for (E entry : entries) {
if (Objects.equals(entry.key, key)) {
return entry;
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public Node assocInternal(final UpdateContext super E> currentContext, final int shift, int hash, final E newEntry) {
if (hash == this.hash) {
for (int i=0; i < entries.length; i++) {
if (Objects.equals(entries[i], newEntry)) {
return this;
}
else if (Objects.equals(entries[i].key, newEntry.key)) {
if (currentContext.merge(entries[i], newEntry)) {
E[] newEntries = entries.clone();
newEntries[i] = newEntry;
return new CollisionNode<>(newEntries);
} else {
return this;
}
}
}
currentContext.insert(newEntry);
E[] newEntries = (E[]) new EntryNode[entries.length + 1];
arraycopy(entries, 0, newEntries, 0, entries.length);
newEntries[entries.length] = newEntry;
return new CollisionNode<>(newEntries);
}
Node[] newChildren = (currentContext.expectedUpdates() == 1
? new Node[] { this, null } : new Node[] { this, null, null, null });
Node newNode = new HashNode<>(currentContext, bit(this.hash, shift), newChildren);
return newNode.assocInternal(currentContext, shift, hash, newEntry);
}
@Override
Node dissocInternal(UpdateContext super E> currentContext, int shift, int hash, Object key) {
if (hash == this.hash) {
for (int i=0; i < entries.length; i++) {
if (Objects.equals(entries[i].key, key)) {
currentContext.delete(entries[i]);
if (entries.length == 2) {
if (i == 1) {
return entries[0];
} else {
return entries[1];
}
}
@SuppressWarnings("unchecked")
E[] newEntries = (E[]) new EntryNode[entries.length - 1];
arraycopy(entries, 0, newEntries, 0, i);
if (i + 1 < entries.length) {
arraycopy(entries, i + 1, newEntries, i, entries.length - i - 1);
}
return new CollisionNode<>(newEntries);
}
}
}
return this;
}
@Override
public Iterator iterator() {
return new ArrayIterator<>(entries);
}
@Override
protected Node[] getChildren() {
return entries;
}
}
static class ArrayIterator> extends UnmodifiableIterator {
@SuppressWarnings("unchecked")
private final Node[][] nodeStack = new Node[7][];
private final int[] nodeIndices = new int[7];
private int stackIndex = 0;
public ArrayIterator(Node[] array) {
nodeStack[0] = array;
}
@Override
public boolean hasNext() {
while (stackIndex >= 0) {
while (nodeIndices[stackIndex] < nodeStack[stackIndex].length) {
if (nodeStack[stackIndex][nodeIndices[stackIndex]] != null) {
return true;
}
nodeIndices[stackIndex]++;
}
stackIndex--;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Node node = nodeStack[stackIndex][nodeIndices[stackIndex]];
nodeIndices[stackIndex]++;
if (node instanceof EntryNode) {
return (E) node;
} else {
stackIndex++;
nodeStack[stackIndex] = node.getChildren();
nodeIndices[stackIndex] = 0;
return next();
}
}
}
static abstract class NodeSpliterator> implements Spliterator {
private Node[] array;
private int pos;
private int limit;
private int sizeEstimate;
private final int characteristics;
private NodeSpliterator subSpliterator;
@SuppressWarnings("unchecked")
protected NodeSpliterator(Node node, int sizeEstimate, int additionalCharacteristics) {
if (node instanceof EntryNode) {
this.array = (Node[]) new Node[] { node };
pos = 0;
limit = 1;
this.sizeEstimate = 1;
}
else {
array = node.getChildren();
if (array == null) {
pos = limit = this.sizeEstimate = 0;
} else {
pos = 0;
limit = array.length;
this.sizeEstimate = sizeEstimate;
}
}
this.characteristics = ORDERED | SIZED | additionalCharacteristics;
}
protected NodeSpliterator(Node[] array, int pos, int limit, int sizeEstimate, int additionalCharacteristics) {
this.array = array;
this.pos = pos;
this.limit = limit;
this.sizeEstimate = sizeEstimate;
this.characteristics = ORDERED | additionalCharacteristics;
}
@Override
public final boolean tryAdvance(Consumer super T> action) {
if (subSpliterator != null) {
if (subSpliterator.tryAdvance(action)) {
return true;
}
subSpliterator = null;
}
if (pos >= limit) {
return false;
}
Node node = array[pos++];
if (node instanceof EntryNode) {
@SuppressWarnings("unchecked")
T value = apply((E) node);
action.accept(value);
return true;
} else {
Node[] children = node.getChildren();
subSpliterator = newSubSpliterator(children, 0, children.length, sizeEstimate / (limit - pos));
return subSpliterator.tryAdvance(action);
}
}
@Override
public final void forEachRemaining(Consumer super T> action) {
if (subSpliterator != null) {
subSpliterator.forEachRemaining(action);
subSpliterator = null;
}
forEach(array, pos, limit, action);
}
private void forEach(Node[] nodes, Consumer super T> action) {
forEach(nodes, 0, nodes.length, action);
}
private void forEach(Node[] nodes, int pos, int limit, Consumer super T> action) {
for (; pos < limit; pos++) {
if (nodes[pos] != null) {
forEach(nodes[pos], action);
}
}
}
private void forEach(Node node, Consumer super T> action) {
if (node instanceof EntryNode) {
@SuppressWarnings("unchecked")
T value = apply((E) node);
action.accept(value);
} else {
Node[] children = node.getChildren();
if (children != null) {
forEach(children, action);
}
}
}
private void trim() {
while (pos < limit && array[pos] == null) {
pos++;
}
while (limit > pos && array[limit-1] == null) {
limit--;
}
}
@Override
public NodeSpliterator trySplit() {
trim();
NodeSpliterator prefix;
if (subSpliterator != null) {
return trySplitSubSpliterator();
}
else if (pos >= limit) {
return null;
}
else if (pos + 1 == limit) {
return trySplitLastNode();
}
int mid = (pos + limit) >>> 1;
prefix = newSubSpliterator(array, pos, mid, sizeEstimate >>> 1);
this.pos = mid;
return prefix;
}
private NodeSpliterator trySplitLastNode() {
Node[] children = array[pos].getChildren();
if (children == null) {
return null;
}
array = children;
pos = 0;
limit = array.length;
return trySplit();
}
private NodeSpliterator trySplitSubSpliterator() {
NodeSpliterator prefix;
if (pos >= limit) {
// Array is already consumed, recurse split into subIterator
prefix = subSpliterator.trySplit();
this.sizeEstimate = subSpliterator.sizeEstimate;
} else {
// Split subIterator off
prefix = subSpliterator;
this.sizeEstimate -= subSpliterator.sizeEstimate;
subSpliterator = null;
}
return prefix;
}
@Override
public final long estimateSize() {
return sizeEstimate;
}
@Override
public int characteristics() {
return characteristics;
}
protected abstract NodeSpliterator newSubSpliterator(Node[] array, int pos, int limit, int sizeEstimate);
protected abstract T apply(E entry);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy