org.jhotdraw8.icollection.impl.champ.ChampSpliterator Maven / Gradle / Ivy
/*
* @(#)KeySpliterator.java
* Copyright © 2023 The authors and contributors of JHotDraw. MIT License.
*/
package org.jhotdraw8.icollection.impl.champ;
import org.jspecify.annotations.Nullable;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Data iterator over a CHAMP trie.
*
* References:
*
* This class has been derived from 'The Capsule Hash Trie Collections Library'.
*
* - The Capsule Hash Trie Collections Library.
*
Copyright (c) Michael Steindorfer. BSD-2-Clause License
* - github.com
*
*
*
* @param the data type of the trie node
* @param the element type of the iterator
*/
public class ChampSpliterator extends Spliterators.AbstractSpliterator {
private final Function mappingFunction;
private static final int MAX_DEPTH = 7;
protected int currentValueCursor;
protected int currentValueLength;
protected Node currentValueNode;
private int currentStackLevel = -1;
/**
* Even indexes: node index
* Odd indexes: node length
*/
private final int[] indexAndArity = new int[MAX_DEPTH * 2];
@SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"})
final Node[] nodes = new Node[MAX_DEPTH];
private K current;
@SuppressWarnings("unchecked")
public ChampSpliterator(Node rootNode, @Nullable Function mappingFunction, long size, int characteristics) {
super(size, characteristics);
this.mappingFunction = mappingFunction == null ? k -> (E) k : mappingFunction;
if (rootNode.hasNodes()) {
currentStackLevel = 0;
nodes[0] = rootNode;
indexAndArity[0] = 0;
indexAndArity[1] = rootNode.nodeArity();
}
if (rootNode.hasData()) {
currentValueNode = rootNode;
currentValueCursor = 0;
currentValueLength = rootNode.dataArity();
}
}
private boolean searchNextValueNode() {
// For inlining, it is essential that this method has a very small amount of byte code!
while (currentStackLevel >= 0) {
var index = currentStackLevel << 1;
if (indexAndArity[index] < indexAndArity[index + 1]) {
var nextNode = nodes[currentStackLevel].getNode(indexAndArity[index]);
indexAndArity[index]++;
if (nextNode.hasNodes()) {
++currentStackLevel;
index += 2;
nodes[currentStackLevel] = nextNode;
indexAndArity[index] = 0;
indexAndArity[index + 1] = nextNode.nodeArity();
}
if (nextNode.hasData()) {
currentValueNode = nextNode;
currentValueCursor = 0;
currentValueLength = nextNode.dataArity();
return true;
}
} else {
currentStackLevel--;
}
}
return false;
}
@Override
public boolean tryAdvance(Consumer super E> action) {
// For inlining, it is essential that this method has a very small amount of byte code!
// Specifically, do not inline searchNextValueNode() into this method!
if (currentValueCursor < currentValueLength || searchNextValueNode()) {
action.accept(mappingFunction.apply(currentValueNode.getData(currentValueCursor++)));
return true;
}
return false;
}
}