|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package java.util.stream; |
|
|
|
import java.util.ArrayDeque; |
|
import java.util.Arrays; |
|
import java.util.Collection; |
|
import java.util.Deque; |
|
import java.util.List; |
|
import java.util.Objects; |
|
import java.util.Spliterator; |
|
import java.util.Spliterators; |
|
import java.util.concurrent.CountedCompleter; |
|
import java.util.function.BinaryOperator; |
|
import java.util.function.Consumer; |
|
import java.util.function.DoubleConsumer; |
|
import java.util.function.IntConsumer; |
|
import java.util.function.IntFunction; |
|
import java.util.function.LongConsumer; |
|
import java.util.function.LongFunction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class Nodes { |
|
|
|
private Nodes() { |
|
throw new Error("no instances"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; |
|
|
|
|
|
static final String BAD_SIZE = "Stream size exceeds max array size"; |
|
|
|
@SuppressWarnings("rawtypes") |
|
private static final Node EMPTY_NODE = new EmptyNode.OfRef(); |
|
private static final Node.OfInt EMPTY_INT_NODE = new EmptyNode.OfInt(); |
|
private static final Node.OfLong EMPTY_LONG_NODE = new EmptyNode.OfLong(); |
|
private static final Node.OfDouble EMPTY_DOUBLE_NODE = new EmptyNode.OfDouble(); |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
static <T> IntFunction<T[]> castingArray() { |
|
return size -> (T[]) new Object[size]; |
|
} |
|
|
|
// General shape-based node creation methods |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
static <T> Node<T> emptyNode(StreamShape shape) { |
|
return (Node<T>) switch (shape) { |
|
case REFERENCE -> EMPTY_NODE; |
|
case INT_VALUE -> EMPTY_INT_NODE; |
|
case LONG_VALUE -> EMPTY_LONG_NODE; |
|
case DOUBLE_VALUE -> EMPTY_DOUBLE_NODE; |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
static <T> Node<T> conc(StreamShape shape, Node<T> left, Node<T> right) { |
|
return (Node<T>) switch (shape) { |
|
case REFERENCE -> new ConcNode<>(left, right); |
|
case INT_VALUE -> new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right); |
|
case LONG_VALUE -> new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right); |
|
case DOUBLE_VALUE -> new ConcNode.OfDouble((Node.OfDouble) left, (Node.OfDouble) right); |
|
}; |
|
} |
|
|
|
// Reference-based node methods |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static <T> Node<T> node(T[] array) { |
|
return new ArrayNode<>(array); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static <T> Node<T> node(Collection<T> c) { |
|
return new CollectionNode<>(c); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static <T> Node.Builder<T> builder(long exactSizeIfKnown, IntFunction<T[]> generator) { |
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE) |
|
? new FixedNodeBuilder<>(exactSizeIfKnown, generator) |
|
: builder(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static <T> Node.Builder<T> builder() { |
|
return new SpinedNodeBuilder<>(); |
|
} |
|
|
|
// Int nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.OfInt node(int[] array) { |
|
return new IntArrayNode(array); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfInt intBuilder(long exactSizeIfKnown) { |
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE) |
|
? new IntFixedNodeBuilder(exactSizeIfKnown) |
|
: intBuilder(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfInt intBuilder() { |
|
return new IntSpinedNodeBuilder(); |
|
} |
|
|
|
// Long nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.OfLong node(final long[] array) { |
|
return new LongArrayNode(array); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfLong longBuilder(long exactSizeIfKnown) { |
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE) |
|
? new LongFixedNodeBuilder(exactSizeIfKnown) |
|
: longBuilder(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfLong longBuilder() { |
|
return new LongSpinedNodeBuilder(); |
|
} |
|
|
|
// Double nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.OfDouble node(final double[] array) { |
|
return new DoubleArrayNode(array); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfDouble doubleBuilder(long exactSizeIfKnown) { |
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE) |
|
? new DoubleFixedNodeBuilder(exactSizeIfKnown) |
|
: doubleBuilder(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Node.Builder.OfDouble doubleBuilder() { |
|
return new DoubleSpinedNodeBuilder(); |
|
} |
|
|
|
// Parallel evaluation of pipelines to nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <P_IN, P_OUT> Node<P_OUT> collect(PipelineHelper<P_OUT> helper, |
|
Spliterator<P_IN> spliterator, |
|
boolean flattenTree, |
|
IntFunction<P_OUT[]> generator) { |
|
long size = helper.exactOutputSizeIfKnown(spliterator); |
|
if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
P_OUT[] array = generator.apply((int) size); |
|
new SizedCollectorTask.OfRef<>(spliterator, helper, array).invoke(); |
|
return node(array); |
|
} else { |
|
Node<P_OUT> node = new CollectorTask.OfRef<>(helper, generator, spliterator).invoke(); |
|
return flattenTree ? flatten(node, generator) : node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <P_IN> Node.OfInt collectInt(PipelineHelper<Integer> helper, |
|
Spliterator<P_IN> spliterator, |
|
boolean flattenTree) { |
|
long size = helper.exactOutputSizeIfKnown(spliterator); |
|
if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
int[] array = new int[(int) size]; |
|
new SizedCollectorTask.OfInt<>(spliterator, helper, array).invoke(); |
|
return node(array); |
|
} |
|
else { |
|
Node.OfInt node = new CollectorTask.OfInt<>(helper, spliterator).invoke(); |
|
return flattenTree ? flattenInt(node) : node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <P_IN> Node.OfLong collectLong(PipelineHelper<Long> helper, |
|
Spliterator<P_IN> spliterator, |
|
boolean flattenTree) { |
|
long size = helper.exactOutputSizeIfKnown(spliterator); |
|
if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
long[] array = new long[(int) size]; |
|
new SizedCollectorTask.OfLong<>(spliterator, helper, array).invoke(); |
|
return node(array); |
|
} |
|
else { |
|
Node.OfLong node = new CollectorTask.OfLong<>(helper, spliterator).invoke(); |
|
return flattenTree ? flattenLong(node) : node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <P_IN> Node.OfDouble collectDouble(PipelineHelper<Double> helper, |
|
Spliterator<P_IN> spliterator, |
|
boolean flattenTree) { |
|
long size = helper.exactOutputSizeIfKnown(spliterator); |
|
if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
double[] array = new double[(int) size]; |
|
new SizedCollectorTask.OfDouble<>(spliterator, helper, array).invoke(); |
|
return node(array); |
|
} |
|
else { |
|
Node.OfDouble node = new CollectorTask.OfDouble<>(helper, spliterator).invoke(); |
|
return flattenTree ? flattenDouble(node) : node; |
|
} |
|
} |
|
|
|
// Parallel flattening of nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <T> Node<T> flatten(Node<T> node, IntFunction<T[]> generator) { |
|
if (node.getChildCount() > 0) { |
|
long size = node.count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
T[] array = generator.apply((int) size); |
|
new ToArrayTask.OfRef<>(node, array, 0).invoke(); |
|
return node(array); |
|
} else { |
|
return node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Node.OfInt flattenInt(Node.OfInt node) { |
|
if (node.getChildCount() > 0) { |
|
long size = node.count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
int[] array = new int[(int) size]; |
|
new ToArrayTask.OfInt(node, array, 0).invoke(); |
|
return node(array); |
|
} else { |
|
return node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Node.OfLong flattenLong(Node.OfLong node) { |
|
if (node.getChildCount() > 0) { |
|
long size = node.count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
long[] array = new long[(int) size]; |
|
new ToArrayTask.OfLong(node, array, 0).invoke(); |
|
return node(array); |
|
} else { |
|
return node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Node.OfDouble flattenDouble(Node.OfDouble node) { |
|
if (node.getChildCount() > 0) { |
|
long size = node.count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
double[] array = new double[(int) size]; |
|
new ToArrayTask.OfDouble(node, array, 0).invoke(); |
|
return node(array); |
|
} else { |
|
return node; |
|
} |
|
} |
|
|
|
// Implementations |
|
|
|
private abstract static class EmptyNode<T, T_ARR, T_CONS> implements Node<T> { |
|
EmptyNode() { } |
|
|
|
@Override |
|
public T[] asArray(IntFunction<T[]> generator) { |
|
return generator.apply(0); |
|
} |
|
|
|
public void copyInto(T_ARR array, int offset) { } |
|
|
|
@Override |
|
public long count() { |
|
return 0; |
|
} |
|
|
|
public void forEach(T_CONS consumer) { } |
|
|
|
private static class OfRef<T> extends EmptyNode<T, T[], Consumer<? super T>> { |
|
private OfRef() { |
|
super(); |
|
} |
|
|
|
@Override |
|
public Spliterator<T> spliterator() { |
|
return Spliterators.emptySpliterator(); |
|
} |
|
} |
|
|
|
private static final class OfInt |
|
extends EmptyNode<Integer, int[], IntConsumer> |
|
implements Node.OfInt { |
|
|
|
OfInt() { } |
|
|
|
@Override |
|
public Spliterator.OfInt spliterator() { |
|
return Spliterators.emptyIntSpliterator(); |
|
} |
|
|
|
@Override |
|
public int[] asPrimitiveArray() { |
|
return EMPTY_INT_ARRAY; |
|
} |
|
} |
|
|
|
private static final class OfLong |
|
extends EmptyNode<Long, long[], LongConsumer> |
|
implements Node.OfLong { |
|
|
|
OfLong() { } |
|
|
|
@Override |
|
public Spliterator.OfLong spliterator() { |
|
return Spliterators.emptyLongSpliterator(); |
|
} |
|
|
|
@Override |
|
public long[] asPrimitiveArray() { |
|
return EMPTY_LONG_ARRAY; |
|
} |
|
} |
|
|
|
private static final class OfDouble |
|
extends EmptyNode<Double, double[], DoubleConsumer> |
|
implements Node.OfDouble { |
|
|
|
OfDouble() { } |
|
|
|
@Override |
|
public Spliterator.OfDouble spliterator() { |
|
return Spliterators.emptyDoubleSpliterator(); |
|
} |
|
|
|
@Override |
|
public double[] asPrimitiveArray() { |
|
return EMPTY_DOUBLE_ARRAY; |
|
} |
|
} |
|
} |
|
|
|
|
|
private static class ArrayNode<T> implements Node<T> { |
|
final T[] array; |
|
int curSize; |
|
|
|
@SuppressWarnings("unchecked") |
|
ArrayNode(long size, IntFunction<T[]> generator) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
this.array = generator.apply((int) size); |
|
this.curSize = 0; |
|
} |
|
|
|
ArrayNode(T[] array) { |
|
this.array = array; |
|
this.curSize = array.length; |
|
} |
|
|
|
// Node |
|
|
|
@Override |
|
public Spliterator<T> spliterator() { |
|
return Arrays.spliterator(array, 0, curSize); |
|
} |
|
|
|
@Override |
|
public void copyInto(T[] dest, int destOffset) { |
|
System.arraycopy(array, 0, dest, destOffset, curSize); |
|
} |
|
|
|
@Override |
|
public T[] asArray(IntFunction<T[]> generator) { |
|
if (array.length == curSize) { |
|
return array; |
|
} else { |
|
throw new IllegalStateException(); |
|
} |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return curSize; |
|
} |
|
|
|
@Override |
|
public void forEach(Consumer<? super T> consumer) { |
|
for (int i = 0; i < curSize; i++) { |
|
consumer.accept(array[i]); |
|
} |
|
} |
|
|
|
// |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("ArrayNode[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
|
|
private static final class CollectionNode<T> implements Node<T> { |
|
private final Collection<T> c; |
|
|
|
CollectionNode(Collection<T> c) { |
|
this.c = c; |
|
} |
|
|
|
// Node |
|
|
|
@Override |
|
public Spliterator<T> spliterator() { |
|
return c.stream().spliterator(); |
|
} |
|
|
|
@Override |
|
public void copyInto(T[] array, int offset) { |
|
for (T t : c) |
|
array[offset++] = t; |
|
} |
|
|
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public T[] asArray(IntFunction<T[]> generator) { |
|
return c.toArray(generator.apply(c.size())); |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return c.size(); |
|
} |
|
|
|
@Override |
|
public void forEach(Consumer<? super T> consumer) { |
|
c.forEach(consumer); |
|
} |
|
|
|
// |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("CollectionNode[%d][%s]", c.size(), c); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private abstract static class AbstractConcNode<T, T_NODE extends Node<T>> implements Node<T> { |
|
protected final T_NODE left; |
|
protected final T_NODE right; |
|
private final long size; |
|
|
|
AbstractConcNode(T_NODE left, T_NODE right) { |
|
this.left = left; |
|
this.right = right; |
|
// The Node count will be required when the Node spliterator is |
|
// obtained and it is cheaper to aggressively calculate bottom up |
|
// as the tree is built rather than later on from the top down |
|
|
|
this.size = left.count() + right.count(); |
|
} |
|
|
|
@Override |
|
public int getChildCount() { |
|
return 2; |
|
} |
|
|
|
@Override |
|
public T_NODE getChild(int i) { |
|
if (i == 0) return left; |
|
if (i == 1) return right; |
|
throw new IndexOutOfBoundsException(); |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return size; |
|
} |
|
} |
|
|
|
static final class ConcNode<T> |
|
extends AbstractConcNode<T, Node<T>> |
|
implements Node<T> { |
|
|
|
ConcNode(Node<T> left, Node<T> right) { |
|
super(left, right); |
|
} |
|
|
|
@Override |
|
public Spliterator<T> spliterator() { |
|
return new Nodes.InternalNodeSpliterator.OfRef<>(this); |
|
} |
|
|
|
@Override |
|
public void copyInto(T[] array, int offset) { |
|
Objects.requireNonNull(array); |
|
left.copyInto(array, offset); |
|
// Cast to int is safe since it is the callers responsibility to |
|
|
|
right.copyInto(array, offset + (int) left.count()); |
|
} |
|
|
|
@Override |
|
public T[] asArray(IntFunction<T[]> generator) { |
|
long size = count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
T[] array = generator.apply((int) size); |
|
copyInto(array, 0); |
|
return array; |
|
} |
|
|
|
@Override |
|
public void forEach(Consumer<? super T> consumer) { |
|
left.forEach(consumer); |
|
right.forEach(consumer); |
|
} |
|
|
|
@Override |
|
public Node<T> truncate(long from, long to, IntFunction<T[]> generator) { |
|
if (from == 0 && to == count()) |
|
return this; |
|
long leftCount = left.count(); |
|
if (from >= leftCount) |
|
return right.truncate(from - leftCount, to - leftCount, generator); |
|
else if (to <= leftCount) |
|
return left.truncate(from, to, generator); |
|
else { |
|
return Nodes.conc(getShape(), left.truncate(from, leftCount, generator), |
|
right.truncate(0, to - leftCount, generator)); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
if (count() < 32) { |
|
return String.format("ConcNode[%s.%s]", left, right); |
|
} else { |
|
return String.format("ConcNode[size=%d]", count()); |
|
} |
|
} |
|
|
|
private abstract static class OfPrimitive<E, T_CONS, T_ARR, |
|
T_SPLITR extends Spliterator.OfPrimitive<E, T_CONS, T_SPLITR>, |
|
T_NODE extends Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE>> |
|
extends AbstractConcNode<E, T_NODE> |
|
implements Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE> { |
|
|
|
OfPrimitive(T_NODE left, T_NODE right) { |
|
super(left, right); |
|
} |
|
|
|
@Override |
|
public void forEach(T_CONS consumer) { |
|
left.forEach(consumer); |
|
right.forEach(consumer); |
|
} |
|
|
|
@Override |
|
public void copyInto(T_ARR array, int offset) { |
|
left.copyInto(array, offset); |
|
// Cast to int is safe since it is the callers responsibility to |
|
|
|
right.copyInto(array, offset + (int) left.count()); |
|
} |
|
|
|
@Override |
|
public T_ARR asPrimitiveArray() { |
|
long size = count(); |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
T_ARR array = newArray((int) size); |
|
copyInto(array, 0); |
|
return array; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
if (count() < 32) |
|
return String.format("%s[%s.%s]", this.getClass().getName(), left, right); |
|
else |
|
return String.format("%s[size=%d]", this.getClass().getName(), count()); |
|
} |
|
} |
|
|
|
static final class OfInt |
|
extends ConcNode.OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> |
|
implements Node.OfInt { |
|
|
|
OfInt(Node.OfInt left, Node.OfInt right) { |
|
super(left, right); |
|
} |
|
|
|
@Override |
|
public Spliterator.OfInt spliterator() { |
|
return new InternalNodeSpliterator.OfInt(this); |
|
} |
|
} |
|
|
|
static final class OfLong |
|
extends ConcNode.OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> |
|
implements Node.OfLong { |
|
|
|
OfLong(Node.OfLong left, Node.OfLong right) { |
|
super(left, right); |
|
} |
|
|
|
@Override |
|
public Spliterator.OfLong spliterator() { |
|
return new InternalNodeSpliterator.OfLong(this); |
|
} |
|
} |
|
|
|
static final class OfDouble |
|
extends ConcNode.OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble> |
|
implements Node.OfDouble { |
|
|
|
OfDouble(Node.OfDouble left, Node.OfDouble right) { |
|
super(left, right); |
|
} |
|
|
|
@Override |
|
public Spliterator.OfDouble spliterator() { |
|
return new InternalNodeSpliterator.OfDouble(this); |
|
} |
|
} |
|
} |
|
|
|
|
|
private abstract static class InternalNodeSpliterator<T, |
|
S extends Spliterator<T>, |
|
N extends Node<T>> |
|
implements Spliterator<T> { |
|
// Node we are pointing to |
|
|
|
N curNode; |
|
|
|
|
|
int curChildIndex; |
|
|
|
// The spliterator of the curNode if that node is last and has no children. |
|
// This spliterator will be delegated to for splitting and traversing. |
|
|
|
S lastNodeSpliterator; |
|
|
|
// spliterator used while traversing with tryAdvance |
|
|
|
S tryAdvanceSpliterator; |
|
|
|
// node stack used when traversing to search and find leaf nodes |
|
|
|
Deque<N> tryAdvanceStack; |
|
|
|
InternalNodeSpliterator(N curNode) { |
|
this.curNode = curNode; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
protected final Deque<N> initStack() { |
|
// Bias size to the case where leaf nodes are close to this node |
|
|
|
Deque<N> stack = new ArrayDeque<>(8); |
|
for (int i = curNode.getChildCount() - 1; i >= curChildIndex; i--) |
|
stack.addFirst((N) curNode.getChild(i)); |
|
return stack; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
protected final N findNextLeafNode(Deque<N> stack) { |
|
N n = null; |
|
while ((n = stack.pollFirst()) != null) { |
|
if (n.getChildCount() == 0) { |
|
if (n.count() > 0) |
|
return n; |
|
} else { |
|
for (int i = n.getChildCount() - 1; i >= 0; i--) |
|
stack.addFirst((N) n.getChild(i)); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
protected final boolean initTryAdvance() { |
|
if (curNode == null) |
|
return false; |
|
|
|
if (tryAdvanceSpliterator == null) { |
|
if (lastNodeSpliterator == null) { |
|
|
|
tryAdvanceStack = initStack(); |
|
N leaf = findNextLeafNode(tryAdvanceStack); |
|
if (leaf != null) |
|
tryAdvanceSpliterator = (S) leaf.spliterator(); |
|
else { |
|
// A non-empty leaf node was not found |
|
|
|
curNode = null; |
|
return false; |
|
} |
|
} |
|
else |
|
tryAdvanceSpliterator = lastNodeSpliterator; |
|
} |
|
return true; |
|
} |
|
|
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public final S trySplit() { |
|
if (curNode == null || tryAdvanceSpliterator != null) |
|
return null; |
|
else if (lastNodeSpliterator != null) |
|
return (S) lastNodeSpliterator.trySplit(); |
|
else if (curChildIndex < curNode.getChildCount() - 1) |
|
return (S) curNode.getChild(curChildIndex++).spliterator(); |
|
else { |
|
curNode = (N) curNode.getChild(curChildIndex); |
|
if (curNode.getChildCount() == 0) { |
|
lastNodeSpliterator = (S) curNode.spliterator(); |
|
return (S) lastNodeSpliterator.trySplit(); |
|
} |
|
else { |
|
curChildIndex = 0; |
|
return (S) curNode.getChild(curChildIndex++).spliterator(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public final long estimateSize() { |
|
if (curNode == null) |
|
return 0; |
|
|
|
// Will not reflect the effects of partial traversal. |
|
|
|
if (lastNodeSpliterator != null) |
|
return lastNodeSpliterator.estimateSize(); |
|
else { |
|
long size = 0; |
|
for (int i = curChildIndex; i < curNode.getChildCount(); i++) |
|
size += curNode.getChild(i).count(); |
|
return size; |
|
} |
|
} |
|
|
|
@Override |
|
public final int characteristics() { |
|
return Spliterator.SIZED; |
|
} |
|
|
|
private static final class OfRef<T> |
|
extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>> { |
|
|
|
OfRef(Node<T> curNode) { |
|
super(curNode); |
|
} |
|
|
|
@Override |
|
public boolean tryAdvance(Consumer<? super T> consumer) { |
|
if (!initTryAdvance()) |
|
return false; |
|
|
|
boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer); |
|
if (!hasNext) { |
|
if (lastNodeSpliterator == null) { |
|
|
|
Node<T> leaf = findNextLeafNode(tryAdvanceStack); |
|
if (leaf != null) { |
|
tryAdvanceSpliterator = leaf.spliterator(); |
|
|
|
return tryAdvanceSpliterator.tryAdvance(consumer); |
|
} |
|
} |
|
|
|
curNode = null; |
|
} |
|
return hasNext; |
|
} |
|
|
|
@Override |
|
public void forEachRemaining(Consumer<? super T> consumer) { |
|
if (curNode == null) |
|
return; |
|
|
|
if (tryAdvanceSpliterator == null) { |
|
if (lastNodeSpliterator == null) { |
|
Deque<Node<T>> stack = initStack(); |
|
Node<T> leaf; |
|
while ((leaf = findNextLeafNode(stack)) != null) { |
|
leaf.forEach(consumer); |
|
} |
|
curNode = null; |
|
} |
|
else |
|
lastNodeSpliterator.forEachRemaining(consumer); |
|
} |
|
else |
|
while(tryAdvance(consumer)) { } |
|
} |
|
} |
|
|
|
private abstract static class OfPrimitive<T, T_CONS, T_ARR, |
|
T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>, |
|
N extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, N>> |
|
extends InternalNodeSpliterator<T, T_SPLITR, N> |
|
implements Spliterator.OfPrimitive<T, T_CONS, T_SPLITR> { |
|
|
|
OfPrimitive(N cur) { |
|
super(cur); |
|
} |
|
|
|
@Override |
|
public boolean tryAdvance(T_CONS consumer) { |
|
if (!initTryAdvance()) |
|
return false; |
|
|
|
boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer); |
|
if (!hasNext) { |
|
if (lastNodeSpliterator == null) { |
|
|
|
N leaf = findNextLeafNode(tryAdvanceStack); |
|
if (leaf != null) { |
|
tryAdvanceSpliterator = leaf.spliterator(); |
|
|
|
return tryAdvanceSpliterator.tryAdvance(consumer); |
|
} |
|
} |
|
|
|
curNode = null; |
|
} |
|
return hasNext; |
|
} |
|
|
|
@Override |
|
public void forEachRemaining(T_CONS consumer) { |
|
if (curNode == null) |
|
return; |
|
|
|
if (tryAdvanceSpliterator == null) { |
|
if (lastNodeSpliterator == null) { |
|
Deque<N> stack = initStack(); |
|
N leaf; |
|
while ((leaf = findNextLeafNode(stack)) != null) { |
|
leaf.forEach(consumer); |
|
} |
|
curNode = null; |
|
} |
|
else |
|
lastNodeSpliterator.forEachRemaining(consumer); |
|
} |
|
else |
|
while(tryAdvance(consumer)) { } |
|
} |
|
} |
|
|
|
private static final class OfInt |
|
extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> |
|
implements Spliterator.OfInt { |
|
|
|
OfInt(Node.OfInt cur) { |
|
super(cur); |
|
} |
|
} |
|
|
|
private static final class OfLong |
|
extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> |
|
implements Spliterator.OfLong { |
|
|
|
OfLong(Node.OfLong cur) { |
|
super(cur); |
|
} |
|
} |
|
|
|
private static final class OfDouble |
|
extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble> |
|
implements Spliterator.OfDouble { |
|
|
|
OfDouble(Node.OfDouble cur) { |
|
super(cur); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final class FixedNodeBuilder<T> |
|
extends ArrayNode<T> |
|
implements Node.Builder<T> { |
|
|
|
FixedNodeBuilder(long size, IntFunction<T[]> generator) { |
|
super(size, generator); |
|
assert size < MAX_ARRAY_SIZE; |
|
} |
|
|
|
@Override |
|
public Node<T> build() { |
|
if (curSize < array.length) |
|
throw new IllegalStateException(String.format("Current size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
return this; |
|
} |
|
|
|
@Override |
|
public void begin(long size) { |
|
if (size != array.length) |
|
throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d", |
|
size, array.length)); |
|
curSize = 0; |
|
} |
|
|
|
@Override |
|
public void accept(T t) { |
|
if (curSize < array.length) { |
|
array[curSize++] = t; |
|
} else { |
|
throw new IllegalStateException(String.format("Accept exceeded fixed size of %d", |
|
array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public void end() { |
|
if (curSize < array.length) |
|
throw new IllegalStateException(String.format("End size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("FixedNodeBuilder[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final class SpinedNodeBuilder<T> |
|
extends SpinedBuffer<T> |
|
implements Node<T>, Node.Builder<T> { |
|
private boolean building = false; |
|
|
|
SpinedNodeBuilder() {} |
|
|
|
@Override |
|
public Spliterator<T> spliterator() { |
|
assert !building : "during building"; |
|
return super.spliterator(); |
|
} |
|
|
|
@Override |
|
public void forEach(Consumer<? super T> consumer) { |
|
assert !building : "during building"; |
|
super.forEach(consumer); |
|
} |
|
|
|
|
|
@Override |
|
public void begin(long size) { |
|
assert !building : "was already building"; |
|
building = true; |
|
clear(); |
|
ensureCapacity(size); |
|
} |
|
|
|
@Override |
|
public void accept(T t) { |
|
assert building : "not building"; |
|
super.accept(t); |
|
} |
|
|
|
@Override |
|
public void end() { |
|
assert building : "was not building"; |
|
building = false; |
|
// @@@ check begin(size) and size |
|
} |
|
|
|
@Override |
|
public void copyInto(T[] array, int offset) { |
|
assert !building : "during building"; |
|
super.copyInto(array, offset); |
|
} |
|
|
|
@Override |
|
public T[] asArray(IntFunction<T[]> arrayFactory) { |
|
assert !building : "during building"; |
|
return super.asArray(arrayFactory); |
|
} |
|
|
|
@Override |
|
public Node<T> build() { |
|
assert !building : "during building"; |
|
return this; |
|
} |
|
} |
|
|
|
// |
|
|
|
private static final int[] EMPTY_INT_ARRAY = new int[0]; |
|
private static final long[] EMPTY_LONG_ARRAY = new long[0]; |
|
private static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; |
|
|
|
private static class IntArrayNode implements Node.OfInt { |
|
final int[] array; |
|
int curSize; |
|
|
|
IntArrayNode(long size) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
this.array = new int[(int) size]; |
|
this.curSize = 0; |
|
} |
|
|
|
IntArrayNode(int[] array) { |
|
this.array = array; |
|
this.curSize = array.length; |
|
} |
|
|
|
// Node |
|
|
|
@Override |
|
public Spliterator.OfInt spliterator() { |
|
return Arrays.spliterator(array, 0, curSize); |
|
} |
|
|
|
@Override |
|
public int[] asPrimitiveArray() { |
|
if (array.length == curSize) { |
|
return array; |
|
} else { |
|
return Arrays.copyOf(array, curSize); |
|
} |
|
} |
|
|
|
@Override |
|
public void copyInto(int[] dest, int destOffset) { |
|
System.arraycopy(array, 0, dest, destOffset, curSize); |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return curSize; |
|
} |
|
|
|
@Override |
|
public void forEach(IntConsumer consumer) { |
|
for (int i = 0; i < curSize; i++) { |
|
consumer.accept(array[i]); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("IntArrayNode[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static class LongArrayNode implements Node.OfLong { |
|
final long[] array; |
|
int curSize; |
|
|
|
LongArrayNode(long size) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
this.array = new long[(int) size]; |
|
this.curSize = 0; |
|
} |
|
|
|
LongArrayNode(long[] array) { |
|
this.array = array; |
|
this.curSize = array.length; |
|
} |
|
|
|
@Override |
|
public Spliterator.OfLong spliterator() { |
|
return Arrays.spliterator(array, 0, curSize); |
|
} |
|
|
|
@Override |
|
public long[] asPrimitiveArray() { |
|
if (array.length == curSize) { |
|
return array; |
|
} else { |
|
return Arrays.copyOf(array, curSize); |
|
} |
|
} |
|
|
|
@Override |
|
public void copyInto(long[] dest, int destOffset) { |
|
System.arraycopy(array, 0, dest, destOffset, curSize); |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return curSize; |
|
} |
|
|
|
@Override |
|
public void forEach(LongConsumer consumer) { |
|
for (int i = 0; i < curSize; i++) { |
|
consumer.accept(array[i]); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("LongArrayNode[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static class DoubleArrayNode implements Node.OfDouble { |
|
final double[] array; |
|
int curSize; |
|
|
|
DoubleArrayNode(long size) { |
|
if (size >= MAX_ARRAY_SIZE) |
|
throw new IllegalArgumentException(BAD_SIZE); |
|
this.array = new double[(int) size]; |
|
this.curSize = 0; |
|
} |
|
|
|
DoubleArrayNode(double[] array) { |
|
this.array = array; |
|
this.curSize = array.length; |
|
} |
|
|
|
@Override |
|
public Spliterator.OfDouble spliterator() { |
|
return Arrays.spliterator(array, 0, curSize); |
|
} |
|
|
|
@Override |
|
public double[] asPrimitiveArray() { |
|
if (array.length == curSize) { |
|
return array; |
|
} else { |
|
return Arrays.copyOf(array, curSize); |
|
} |
|
} |
|
|
|
@Override |
|
public void copyInto(double[] dest, int destOffset) { |
|
System.arraycopy(array, 0, dest, destOffset, curSize); |
|
} |
|
|
|
@Override |
|
public long count() { |
|
return curSize; |
|
} |
|
|
|
@Override |
|
public void forEach(DoubleConsumer consumer) { |
|
for (int i = 0; i < curSize; i++) { |
|
consumer.accept(array[i]); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("DoubleArrayNode[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static final class IntFixedNodeBuilder |
|
extends IntArrayNode |
|
implements Node.Builder.OfInt { |
|
|
|
IntFixedNodeBuilder(long size) { |
|
super(size); |
|
assert size < MAX_ARRAY_SIZE; |
|
} |
|
|
|
@Override |
|
public Node.OfInt build() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("Current size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
@Override |
|
public void begin(long size) { |
|
if (size != array.length) { |
|
throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d", |
|
size, array.length)); |
|
} |
|
|
|
curSize = 0; |
|
} |
|
|
|
@Override |
|
public void accept(int i) { |
|
if (curSize < array.length) { |
|
array[curSize++] = i; |
|
} else { |
|
throw new IllegalStateException(String.format("Accept exceeded fixed size of %d", |
|
array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public void end() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("End size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("IntFixedNodeBuilder[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static final class LongFixedNodeBuilder |
|
extends LongArrayNode |
|
implements Node.Builder.OfLong { |
|
|
|
LongFixedNodeBuilder(long size) { |
|
super(size); |
|
assert size < MAX_ARRAY_SIZE; |
|
} |
|
|
|
@Override |
|
public Node.OfLong build() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("Current size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
@Override |
|
public void begin(long size) { |
|
if (size != array.length) { |
|
throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d", |
|
size, array.length)); |
|
} |
|
|
|
curSize = 0; |
|
} |
|
|
|
@Override |
|
public void accept(long i) { |
|
if (curSize < array.length) { |
|
array[curSize++] = i; |
|
} else { |
|
throw new IllegalStateException(String.format("Accept exceeded fixed size of %d", |
|
array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public void end() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("End size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("LongFixedNodeBuilder[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static final class DoubleFixedNodeBuilder |
|
extends DoubleArrayNode |
|
implements Node.Builder.OfDouble { |
|
|
|
DoubleFixedNodeBuilder(long size) { |
|
super(size); |
|
assert size < MAX_ARRAY_SIZE; |
|
} |
|
|
|
@Override |
|
public Node.OfDouble build() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("Current size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
@Override |
|
public void begin(long size) { |
|
if (size != array.length) { |
|
throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d", |
|
size, array.length)); |
|
} |
|
|
|
curSize = 0; |
|
} |
|
|
|
@Override |
|
public void accept(double i) { |
|
if (curSize < array.length) { |
|
array[curSize++] = i; |
|
} else { |
|
throw new IllegalStateException(String.format("Accept exceeded fixed size of %d", |
|
array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public void end() { |
|
if (curSize < array.length) { |
|
throw new IllegalStateException(String.format("End size %d is less than fixed size %d", |
|
curSize, array.length)); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return String.format("DoubleFixedNodeBuilder[%d][%s]", |
|
array.length - curSize, Arrays.toString(array)); |
|
} |
|
} |
|
|
|
private static final class IntSpinedNodeBuilder |
|
extends SpinedBuffer.OfInt |
|
implements Node.OfInt, Node.Builder.OfInt { |
|
private boolean building = false; |
|
|
|
IntSpinedNodeBuilder() {} |
|
|
|
@Override |
|
public Spliterator.OfInt spliterator() { |
|
assert !building : "during building"; |
|
return super.spliterator(); |
|
} |
|
|
|
@Override |
|
public void forEach(IntConsumer consumer) { |
|
assert !building : "during building"; |
|
super.forEach(consumer); |
|
} |
|
|
|
|
|
@Override |
|
public void begin(long size) { |
|
assert !building : "was already building"; |
|
building = true; |
|
clear(); |
|
ensureCapacity(size); |
|
} |
|
|
|
@Override |
|
public void accept(int i) { |
|
assert building : "not building"; |
|
super.accept(i); |
|
} |
|
|
|
@Override |
|
public void end() { |
|
assert building : "was not building"; |
|
building = false; |
|
// @@@ check begin(size) and size |
|
} |
|
|
|
@Override |
|
public void copyInto(int[] array, int offset) throws IndexOutOfBoundsException { |
|
assert !building : "during building"; |
|
super.copyInto(array, offset); |
|
} |
|
|
|
@Override |
|
public int[] asPrimitiveArray() { |
|
assert !building : "during building"; |
|
return super.asPrimitiveArray(); |
|
} |
|
|
|
@Override |
|
public Node.OfInt build() { |
|
assert !building : "during building"; |
|
return this; |
|
} |
|
} |
|
|
|
private static final class LongSpinedNodeBuilder |
|
extends SpinedBuffer.OfLong |
|
implements Node.OfLong, Node.Builder.OfLong { |
|
private boolean building = false; |
|
|
|
LongSpinedNodeBuilder() {} |
|
|
|
@Override |
|
public Spliterator.OfLong spliterator() { |
|
assert !building : "during building"; |
|
return super.spliterator(); |
|
} |
|
|
|
@Override |
|
public void forEach(LongConsumer consumer) { |
|
assert !building : "during building"; |
|
super.forEach(consumer); |
|
} |
|
|
|
|
|
@Override |
|
public void begin(long size) { |
|
assert !building : "was already building"; |
|
building = true; |
|
clear(); |
|
ensureCapacity(size); |
|
} |
|
|
|
@Override |
|
public void accept(long i) { |
|
assert building : "not building"; |
|
super.accept(i); |
|
} |
|
|
|
@Override |
|
public void end() { |
|
assert building : "was not building"; |
|
building = false; |
|
// @@@ check begin(size) and size |
|
} |
|
|
|
@Override |
|
public void copyInto(long[] array, int offset) { |
|
assert !building : "during building"; |
|
super.copyInto(array, offset); |
|
} |
|
|
|
@Override |
|
public long[] asPrimitiveArray() { |
|
assert !building : "during building"; |
|
return super.asPrimitiveArray(); |
|
} |
|
|
|
@Override |
|
public Node.OfLong build() { |
|
assert !building : "during building"; |
|
return this; |
|
} |
|
} |
|
|
|
private static final class DoubleSpinedNodeBuilder |
|
extends SpinedBuffer.OfDouble |
|
implements Node.OfDouble, Node.Builder.OfDouble { |
|
private boolean building = false; |
|
|
|
DoubleSpinedNodeBuilder() {} |
|
|
|
@Override |
|
public Spliterator.OfDouble spliterator() { |
|
assert !building : "during building"; |
|
return super.spliterator(); |
|
} |
|
|
|
@Override |
|
public void forEach(DoubleConsumer consumer) { |
|
assert !building : "during building"; |
|
super.forEach(consumer); |
|
} |
|
|
|
|
|
@Override |
|
public void begin(long size) { |
|
assert !building : "was already building"; |
|
building = true; |
|
clear(); |
|
ensureCapacity(size); |
|
} |
|
|
|
@Override |
|
public void accept(double i) { |
|
assert building : "not building"; |
|
super.accept(i); |
|
} |
|
|
|
@Override |
|
public void end() { |
|
assert building : "was not building"; |
|
building = false; |
|
// @@@ check begin(size) and size |
|
} |
|
|
|
@Override |
|
public void copyInto(double[] array, int offset) { |
|
assert !building : "during building"; |
|
super.copyInto(array, offset); |
|
} |
|
|
|
@Override |
|
public double[] asPrimitiveArray() { |
|
assert !building : "during building"; |
|
return super.asPrimitiveArray(); |
|
} |
|
|
|
@Override |
|
public Node.OfDouble build() { |
|
assert !building : "during building"; |
|
return this; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("serial") |
|
private abstract static class SizedCollectorTask<P_IN, P_OUT, T_SINK extends Sink<P_OUT>, |
|
K extends SizedCollectorTask<P_IN, P_OUT, T_SINK, K>> |
|
extends CountedCompleter<Void> |
|
implements Sink<P_OUT> { |
|
protected final Spliterator<P_IN> spliterator; |
|
protected final PipelineHelper<P_OUT> helper; |
|
protected final long targetSize; |
|
protected long offset; |
|
protected long length; |
|
|
|
protected int index, fence; |
|
|
|
SizedCollectorTask(Spliterator<P_IN> spliterator, |
|
PipelineHelper<P_OUT> helper, |
|
int arrayLength) { |
|
assert spliterator.hasCharacteristics(Spliterator.SUBSIZED); |
|
this.spliterator = spliterator; |
|
this.helper = helper; |
|
this.targetSize = AbstractTask.suggestTargetSize(spliterator.estimateSize()); |
|
this.offset = 0; |
|
this.length = arrayLength; |
|
} |
|
|
|
SizedCollectorTask(K parent, Spliterator<P_IN> spliterator, |
|
long offset, long length, int arrayLength) { |
|
super(parent); |
|
assert spliterator.hasCharacteristics(Spliterator.SUBSIZED); |
|
this.spliterator = spliterator; |
|
this.helper = parent.helper; |
|
this.targetSize = parent.targetSize; |
|
this.offset = offset; |
|
this.length = length; |
|
|
|
if (offset < 0 || length < 0 || (offset + length - 1 >= arrayLength)) { |
|
throw new IllegalArgumentException( |
|
String.format("offset and length interval [%d, %d + %d) is not within array size interval [0, %d)", |
|
offset, offset, length, arrayLength)); |
|
} |
|
} |
|
|
|
@Override |
|
public void compute() { |
|
SizedCollectorTask<P_IN, P_OUT, T_SINK, K> task = this; |
|
Spliterator<P_IN> rightSplit = spliterator, leftSplit; |
|
while (rightSplit.estimateSize() > task.targetSize && |
|
(leftSplit = rightSplit.trySplit()) != null) { |
|
task.setPendingCount(1); |
|
long leftSplitSize = leftSplit.estimateSize(); |
|
task.makeChild(leftSplit, task.offset, leftSplitSize).fork(); |
|
task = task.makeChild(rightSplit, task.offset + leftSplitSize, |
|
task.length - leftSplitSize); |
|
} |
|
|
|
assert task.offset + task.length < MAX_ARRAY_SIZE; |
|
@SuppressWarnings("unchecked") |
|
T_SINK sink = (T_SINK) task; |
|
task.helper.wrapAndCopyInto(sink, rightSplit); |
|
task.propagateCompletion(); |
|
} |
|
|
|
abstract K makeChild(Spliterator<P_IN> spliterator, long offset, long size); |
|
|
|
@Override |
|
public void begin(long size) { |
|
if (size > length) |
|
throw new IllegalStateException("size passed to Sink.begin exceeds array length"); |
|
// Casts to int are safe since absolute size is verified to be within |
|
// bounds when the root concrete SizedCollectorTask is constructed |
|
|
|
index = (int) offset; |
|
fence = index + (int) length; |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
static final class OfRef<P_IN, P_OUT> |
|
extends SizedCollectorTask<P_IN, P_OUT, Sink<P_OUT>, OfRef<P_IN, P_OUT>> |
|
implements Sink<P_OUT> { |
|
private final P_OUT[] array; |
|
|
|
OfRef(Spliterator<P_IN> spliterator, PipelineHelper<P_OUT> helper, P_OUT[] array) { |
|
super(spliterator, helper, array.length); |
|
this.array = array; |
|
} |
|
|
|
OfRef(OfRef<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator, |
|
long offset, long length) { |
|
super(parent, spliterator, offset, length, parent.array.length); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
OfRef<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator, |
|
long offset, long size) { |
|
return new OfRef<>(this, spliterator, offset, size); |
|
} |
|
|
|
@Override |
|
public void accept(P_OUT value) { |
|
if (index >= fence) { |
|
throw new IndexOutOfBoundsException(Integer.toString(index)); |
|
} |
|
array[index++] = value; |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
static final class OfInt<P_IN> |
|
extends SizedCollectorTask<P_IN, Integer, Sink.OfInt, OfInt<P_IN>> |
|
implements Sink.OfInt { |
|
private final int[] array; |
|
|
|
OfInt(Spliterator<P_IN> spliterator, PipelineHelper<Integer> helper, int[] array) { |
|
super(spliterator, helper, array.length); |
|
this.array = array; |
|
} |
|
|
|
OfInt(SizedCollectorTask.OfInt<P_IN> parent, Spliterator<P_IN> spliterator, |
|
long offset, long length) { |
|
super(parent, spliterator, offset, length, parent.array.length); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
SizedCollectorTask.OfInt<P_IN> makeChild(Spliterator<P_IN> spliterator, |
|
long offset, long size) { |
|
return new SizedCollectorTask.OfInt<>(this, spliterator, offset, size); |
|
} |
|
|
|
@Override |
|
public void accept(int value) { |
|
if (index >= fence) { |
|
throw new IndexOutOfBoundsException(Integer.toString(index)); |
|
} |
|
array[index++] = value; |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
static final class OfLong<P_IN> |
|
extends SizedCollectorTask<P_IN, Long, Sink.OfLong, OfLong<P_IN>> |
|
implements Sink.OfLong { |
|
private final long[] array; |
|
|
|
OfLong(Spliterator<P_IN> spliterator, PipelineHelper<Long> helper, long[] array) { |
|
super(spliterator, helper, array.length); |
|
this.array = array; |
|
} |
|
|
|
OfLong(SizedCollectorTask.OfLong<P_IN> parent, Spliterator<P_IN> spliterator, |
|
long offset, long length) { |
|
super(parent, spliterator, offset, length, parent.array.length); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
SizedCollectorTask.OfLong<P_IN> makeChild(Spliterator<P_IN> spliterator, |
|
long offset, long size) { |
|
return new SizedCollectorTask.OfLong<>(this, spliterator, offset, size); |
|
} |
|
|
|
@Override |
|
public void accept(long value) { |
|
if (index >= fence) { |
|
throw new IndexOutOfBoundsException(Integer.toString(index)); |
|
} |
|
array[index++] = value; |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
static final class OfDouble<P_IN> |
|
extends SizedCollectorTask<P_IN, Double, Sink.OfDouble, OfDouble<P_IN>> |
|
implements Sink.OfDouble { |
|
private final double[] array; |
|
|
|
OfDouble(Spliterator<P_IN> spliterator, PipelineHelper<Double> helper, double[] array) { |
|
super(spliterator, helper, array.length); |
|
this.array = array; |
|
} |
|
|
|
OfDouble(SizedCollectorTask.OfDouble<P_IN> parent, Spliterator<P_IN> spliterator, |
|
long offset, long length) { |
|
super(parent, spliterator, offset, length, parent.array.length); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
SizedCollectorTask.OfDouble<P_IN> makeChild(Spliterator<P_IN> spliterator, |
|
long offset, long size) { |
|
return new SizedCollectorTask.OfDouble<>(this, spliterator, offset, size); |
|
} |
|
|
|
@Override |
|
public void accept(double value) { |
|
if (index >= fence) { |
|
throw new IndexOutOfBoundsException(Integer.toString(index)); |
|
} |
|
array[index++] = value; |
|
} |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private abstract static class ToArrayTask<T, T_NODE extends Node<T>, |
|
K extends ToArrayTask<T, T_NODE, K>> |
|
extends CountedCompleter<Void> { |
|
protected final T_NODE node; |
|
protected final int offset; |
|
|
|
ToArrayTask(T_NODE node, int offset) { |
|
this.node = node; |
|
this.offset = offset; |
|
} |
|
|
|
ToArrayTask(K parent, T_NODE node, int offset) { |
|
super(parent); |
|
this.node = node; |
|
this.offset = offset; |
|
} |
|
|
|
abstract void copyNodeToArray(); |
|
|
|
abstract K makeChild(int childIndex, int offset); |
|
|
|
@Override |
|
public void compute() { |
|
ToArrayTask<T, T_NODE, K> task = this; |
|
while (true) { |
|
if (task.node.getChildCount() == 0) { |
|
task.copyNodeToArray(); |
|
task.propagateCompletion(); |
|
return; |
|
} |
|
else { |
|
task.setPendingCount(task.node.getChildCount() - 1); |
|
|
|
int size = 0; |
|
int i = 0; |
|
for (;i < task.node.getChildCount() - 1; i++) { |
|
K leftTask = task.makeChild(i, task.offset + size); |
|
size += leftTask.node.count(); |
|
leftTask.fork(); |
|
} |
|
task = task.makeChild(i, task.offset + size); |
|
} |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfRef<T> |
|
extends ToArrayTask<T, Node<T>, OfRef<T>> { |
|
private final T[] array; |
|
|
|
private OfRef(Node<T> node, T[] array, int offset) { |
|
super(node, offset); |
|
this.array = array; |
|
} |
|
|
|
private OfRef(OfRef<T> parent, Node<T> node, int offset) { |
|
super(parent, node, offset); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
OfRef<T> makeChild(int childIndex, int offset) { |
|
return new OfRef<>(this, node.getChild(childIndex), offset); |
|
} |
|
|
|
@Override |
|
void copyNodeToArray() { |
|
node.copyInto(array, offset); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static class OfPrimitive<T, T_CONS, T_ARR, |
|
T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>, |
|
T_NODE extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>> |
|
extends ToArrayTask<T, T_NODE, OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>> { |
|
private final T_ARR array; |
|
|
|
private OfPrimitive(T_NODE node, T_ARR array, int offset) { |
|
super(node, offset); |
|
this.array = array; |
|
} |
|
|
|
private OfPrimitive(OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE> parent, T_NODE node, int offset) { |
|
super(parent, node, offset); |
|
this.array = parent.array; |
|
} |
|
|
|
@Override |
|
OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE> makeChild(int childIndex, int offset) { |
|
return new OfPrimitive<>(this, node.getChild(childIndex), offset); |
|
} |
|
|
|
@Override |
|
void copyNodeToArray() { |
|
node.copyInto(array, offset); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfInt |
|
extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> { |
|
private OfInt(Node.OfInt node, int[] array, int offset) { |
|
super(node, array, offset); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfLong |
|
extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> { |
|
private OfLong(Node.OfLong node, long[] array, int offset) { |
|
super(node, array, offset); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfDouble |
|
extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble> { |
|
private OfDouble(Node.OfDouble node, double[] array, int offset) { |
|
super(node, array, offset); |
|
} |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static class CollectorTask<P_IN, P_OUT, T_NODE extends Node<P_OUT>, T_BUILDER extends Node.Builder<P_OUT>> |
|
extends AbstractTask<P_IN, P_OUT, T_NODE, CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER>> { |
|
protected final PipelineHelper<P_OUT> helper; |
|
protected final LongFunction<T_BUILDER> builderFactory; |
|
protected final BinaryOperator<T_NODE> concFactory; |
|
|
|
CollectorTask(PipelineHelper<P_OUT> helper, |
|
Spliterator<P_IN> spliterator, |
|
LongFunction<T_BUILDER> builderFactory, |
|
BinaryOperator<T_NODE> concFactory) { |
|
super(helper, spliterator); |
|
this.helper = helper; |
|
this.builderFactory = builderFactory; |
|
this.concFactory = concFactory; |
|
} |
|
|
|
CollectorTask(CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> parent, |
|
Spliterator<P_IN> spliterator) { |
|
super(parent, spliterator); |
|
helper = parent.helper; |
|
builderFactory = parent.builderFactory; |
|
concFactory = parent.concFactory; |
|
} |
|
|
|
@Override |
|
protected CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> makeChild(Spliterator<P_IN> spliterator) { |
|
return new CollectorTask<>(this, spliterator); |
|
} |
|
|
|
@Override |
|
@SuppressWarnings("unchecked") |
|
protected T_NODE doLeaf() { |
|
T_BUILDER builder = builderFactory.apply(helper.exactOutputSizeIfKnown(spliterator)); |
|
return (T_NODE) helper.wrapAndCopyInto(builder, spliterator).build(); |
|
} |
|
|
|
@Override |
|
public void onCompletion(CountedCompleter<?> caller) { |
|
if (!isLeaf()) |
|
setLocalResult(concFactory.apply(leftChild.getLocalResult(), rightChild.getLocalResult())); |
|
super.onCompletion(caller); |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfRef<P_IN, P_OUT> |
|
extends CollectorTask<P_IN, P_OUT, Node<P_OUT>, Node.Builder<P_OUT>> { |
|
OfRef(PipelineHelper<P_OUT> helper, |
|
IntFunction<P_OUT[]> generator, |
|
Spliterator<P_IN> spliterator) { |
|
super(helper, spliterator, s -> builder(s, generator), ConcNode::new); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfInt<P_IN> |
|
extends CollectorTask<P_IN, Integer, Node.OfInt, Node.Builder.OfInt> { |
|
OfInt(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) { |
|
super(helper, spliterator, Nodes::intBuilder, ConcNode.OfInt::new); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfLong<P_IN> |
|
extends CollectorTask<P_IN, Long, Node.OfLong, Node.Builder.OfLong> { |
|
OfLong(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) { |
|
super(helper, spliterator, Nodes::longBuilder, ConcNode.OfLong::new); |
|
} |
|
} |
|
|
|
@SuppressWarnings("serial") |
|
private static final class OfDouble<P_IN> |
|
extends CollectorTask<P_IN, Double, Node.OfDouble, Node.Builder.OfDouble> { |
|
OfDouble(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) { |
|
super(helper, spliterator, Nodes::doubleBuilder, ConcNode.OfDouble::new); |
|
} |
|
} |
|
} |
|
} |