2013-04-24 16:15:47 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
|
|
*
|
|
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
|
|
*
|
|
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
|
|
* accompanied this code).
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License version
|
|
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
|
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
|
|
* questions.
|
|
|
|
*/
|
|
|
|
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.Consumer;
|
|
|
|
import java.util.function.DoubleConsumer;
|
|
|
|
import java.util.function.IntConsumer;
|
|
|
|
import java.util.function.IntFunction;
|
|
|
|
import java.util.function.LongConsumer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Factory methods for constructing implementations of {@link Node} and
|
|
|
|
* {@link Node.Builder} and their primitive specializations. Fork/Join tasks
|
|
|
|
* for collecting output from a {@link PipelineHelper} to a {@link Node} and
|
|
|
|
* flattening {@link Node}s.
|
|
|
|
*
|
|
|
|
* @since 1.8
|
|
|
|
*/
|
|
|
|
final class Nodes {
|
|
|
|
|
|
|
|
private Nodes() {
|
|
|
|
throw new Error("no instances");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum size of an array that can be allocated.
|
|
|
|
*/
|
|
|
|
static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
// General shape-based node creation methods
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces an empty node whose count is zero, has no children and no content.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements of the created node
|
|
|
|
* @param shape the shape of the node to be created
|
|
|
|
* @return an empty node.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
static <T> Node<T> emptyNode(StreamShape shape) {
|
|
|
|
switch (shape) {
|
|
|
|
case REFERENCE: return (Node<T>) EMPTY_NODE;
|
|
|
|
case INT_VALUE: return (Node<T>) EMPTY_INT_NODE;
|
|
|
|
case LONG_VALUE: return (Node<T>) EMPTY_LONG_NODE;
|
|
|
|
case DOUBLE_VALUE: return (Node<T>) EMPTY_DOUBLE_NODE;
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException("Unknown shape " + shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a concatenated {@link Node} that has two or more children.
|
|
|
|
* <p>The count of the concatenated node is equal to the sum of the count
|
|
|
|
* of each child. Traversal of the concatenated node traverses the content
|
|
|
|
* of each child in encounter order of the list of children. Splitting a
|
|
|
|
* spliterator obtained from the concatenated node preserves the encounter
|
|
|
|
* order of the list of children.
|
|
|
|
*
|
|
|
|
* <p>The result may be a concatenated node, the input sole node if the size
|
|
|
|
* of the list is 1, or an empty node.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements of the concatenated node
|
|
|
|
* @param shape the shape of the concatenated node to be created
|
|
|
|
* @param nodes the input nodes
|
|
|
|
* @return a {@code Node} covering the elements of the input nodes
|
|
|
|
* @throws IllegalStateException if all {@link Node} elements of the list
|
|
|
|
* are an not instance of type supported by this factory.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
static <T> Node<T> conc(StreamShape shape, List<? extends Node<T>> nodes) {
|
|
|
|
int size = nodes.size();
|
|
|
|
if (size == 0)
|
|
|
|
return emptyNode(shape);
|
|
|
|
else if (size == 1)
|
|
|
|
return nodes.get(0);
|
|
|
|
else {
|
|
|
|
// Create a right-balanced tree when there are more that 2 nodes
|
|
|
|
switch (shape) {
|
|
|
|
case REFERENCE: {
|
|
|
|
List<Node<T>> refNodes = (List<Node<T>>) nodes;
|
|
|
|
ConcNode<T> c = new ConcNode<>(refNodes.get(size - 2), refNodes.get(size - 1));
|
|
|
|
for (int i = size - 3; i >= 0; i--) {
|
|
|
|
c = new ConcNode<>(refNodes.get(i), c);
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
case INT_VALUE: {
|
|
|
|
List<? extends Node.OfInt> intNodes = (List<? extends Node.OfInt>) nodes;
|
|
|
|
IntConcNode c = new IntConcNode(intNodes.get(size - 2), intNodes.get(size - 1));
|
|
|
|
for (int i = size - 3; i >= 0; i--) {
|
|
|
|
c = new IntConcNode(intNodes.get(i), c);
|
|
|
|
}
|
|
|
|
return (Node<T>) c;
|
|
|
|
}
|
|
|
|
case LONG_VALUE: {
|
|
|
|
List<? extends Node.OfLong> longNodes = (List<? extends Node.OfLong>) nodes;
|
|
|
|
LongConcNode c = new LongConcNode(longNodes.get(size - 2), longNodes.get(size - 1));
|
|
|
|
for (int i = size - 3; i >= 0; i--) {
|
|
|
|
c = new LongConcNode(longNodes.get(i), c);
|
|
|
|
}
|
|
|
|
return (Node<T>) c;
|
|
|
|
}
|
|
|
|
case DOUBLE_VALUE: {
|
|
|
|
List<? extends Node.OfDouble> doubleNodes = (List<? extends Node.OfDouble>) nodes;
|
|
|
|
DoubleConcNode c = new DoubleConcNode(doubleNodes.get(size - 2), doubleNodes.get(size - 1));
|
|
|
|
for (int i = size - 3; i >= 0; i--) {
|
|
|
|
c = new DoubleConcNode(doubleNodes.get(i), c);
|
|
|
|
}
|
|
|
|
return (Node<T>) c;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException("Unknown shape " + shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Truncate a {@link Node}, returning a node describing a subsequence of
|
|
|
|
* the contents of the input node.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements of the input node and truncated node
|
|
|
|
* @param input the input node
|
|
|
|
* @param from the starting offset to include in the truncated node (inclusive)
|
|
|
|
* @param to the ending offset ot include in the truncated node (exclusive)
|
|
|
|
* @param generator the array factory (only used for reference nodes)
|
|
|
|
* @return the truncated node
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
static <T> Node<T> truncateNode(Node<T> input, long from, long to, IntFunction<T[]> generator) {
|
|
|
|
StreamShape shape = input.getShape();
|
|
|
|
long size = truncatedSize(input.count(), from, to);
|
|
|
|
if (size == 0)
|
|
|
|
return emptyNode(shape);
|
|
|
|
else if (from == 0 && to >= input.count())
|
|
|
|
return input;
|
|
|
|
|
|
|
|
switch (shape) {
|
|
|
|
case REFERENCE: {
|
|
|
|
Spliterator<T> spliterator = input.spliterator();
|
|
|
|
Node.Builder<T> nodeBuilder = Nodes.builder(size, generator);
|
|
|
|
nodeBuilder.begin(size);
|
|
|
|
for (int i = 0; i < from && spliterator.tryAdvance(e -> { }); i++) { }
|
|
|
|
for (int i = 0; (i < size) && spliterator.tryAdvance(nodeBuilder); i++) { }
|
|
|
|
nodeBuilder.end();
|
|
|
|
return nodeBuilder.build();
|
|
|
|
}
|
|
|
|
case INT_VALUE: {
|
|
|
|
Spliterator.OfInt spliterator = ((Node.OfInt) input).spliterator();
|
|
|
|
Node.Builder.OfInt nodeBuilder = Nodes.intBuilder(size);
|
|
|
|
nodeBuilder.begin(size);
|
|
|
|
for (int i = 0; i < from && spliterator.tryAdvance((IntConsumer) e -> { }); i++) { }
|
|
|
|
for (int i = 0; (i < size) && spliterator.tryAdvance((IntConsumer) nodeBuilder); i++) { }
|
|
|
|
nodeBuilder.end();
|
|
|
|
return (Node<T>) nodeBuilder.build();
|
|
|
|
}
|
|
|
|
case LONG_VALUE: {
|
|
|
|
Spliterator.OfLong spliterator = ((Node.OfLong) input).spliterator();
|
|
|
|
Node.Builder.OfLong nodeBuilder = Nodes.longBuilder(size);
|
|
|
|
nodeBuilder.begin(size);
|
|
|
|
for (int i = 0; i < from && spliterator.tryAdvance((LongConsumer) e -> { }); i++) { }
|
|
|
|
for (int i = 0; (i < size) && spliterator.tryAdvance((LongConsumer) nodeBuilder); i++) { }
|
|
|
|
nodeBuilder.end();
|
|
|
|
return (Node<T>) nodeBuilder.build();
|
|
|
|
}
|
|
|
|
case DOUBLE_VALUE: {
|
|
|
|
Spliterator.OfDouble spliterator = ((Node.OfDouble) input).spliterator();
|
|
|
|
Node.Builder.OfDouble nodeBuilder = Nodes.doubleBuilder(size);
|
|
|
|
nodeBuilder.begin(size);
|
|
|
|
for (int i = 0; i < from && spliterator.tryAdvance((DoubleConsumer) e -> { }); i++) { }
|
|
|
|
for (int i = 0; (i < size) && spliterator.tryAdvance((DoubleConsumer) nodeBuilder); i++) { }
|
|
|
|
nodeBuilder.end();
|
|
|
|
return (Node<T>) nodeBuilder.build();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException("Unknown shape " + shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static long truncatedSize(long size, long from, long to) {
|
|
|
|
if (from >= 0)
|
|
|
|
size = Math.max(0, size - from);
|
|
|
|
long limit = to - from;
|
|
|
|
if (limit >= 0)
|
|
|
|
size = Math.min(size, limit);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reference-based node methods
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node} describing an array.
|
|
|
|
*
|
|
|
|
* <p>The node will hold a reference to the array and will not make a copy.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements held by the node
|
|
|
|
* @param array the array
|
|
|
|
* @return a node holding an array
|
|
|
|
*/
|
|
|
|
static <T> Node<T> node(T[] array) {
|
|
|
|
return new ArrayNode<>(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node} describing a {@link Collection}.
|
|
|
|
* <p>
|
|
|
|
* The node will hold a reference to the collection and will not make a copy.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements held by the node
|
|
|
|
* @param c the collection
|
|
|
|
* @return a node holding a collection
|
|
|
|
*/
|
|
|
|
static <T> Node<T> node(Collection<T> c) {
|
|
|
|
return new CollectionNode<>(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.Builder}.
|
|
|
|
*
|
|
|
|
* @param exactSizeIfKnown -1 if a variable size builder is requested,
|
|
|
|
* otherwise the exact capacity desired. A fixed capacity builder will
|
|
|
|
* fail if the wrong number of elements are added to the builder.
|
|
|
|
* @param generator the array factory
|
|
|
|
* @param <T> the type of elements of the node builder
|
|
|
|
* @return a {@code Node.Builder}
|
|
|
|
*/
|
|
|
|
static <T> Node.Builder<T> builder(long exactSizeIfKnown, IntFunction<T[]> generator) {
|
|
|
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
|
|
|
|
? new FixedNodeBuilder<>(exactSizeIfKnown, generator)
|
|
|
|
: builder();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a variable size @{link Node.Builder}.
|
|
|
|
*
|
|
|
|
* @param <T> the type of elements of the node builder
|
|
|
|
* @return a {@code Node.Builder}
|
|
|
|
*/
|
|
|
|
static <T> Node.Builder<T> builder() {
|
|
|
|
return new SpinedNodeBuilder<>();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Int nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.OfInt} describing an int[] array.
|
|
|
|
*
|
|
|
|
* <p>The node will hold a reference to the array and will not make a copy.
|
|
|
|
*
|
|
|
|
* @param array the array
|
|
|
|
* @return a node holding an array
|
|
|
|
*/
|
|
|
|
static Node.OfInt node(int[] array) {
|
|
|
|
return new IntArrayNode(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.Builder.OfInt}.
|
|
|
|
*
|
|
|
|
* @param exactSizeIfKnown -1 if a variable size builder is requested,
|
|
|
|
* otherwise the exact capacity desired. A fixed capacity builder will
|
|
|
|
* fail if the wrong number of elements are added to the builder.
|
|
|
|
* @return a {@code Node.Builder.OfInt}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfInt intBuilder(long exactSizeIfKnown) {
|
|
|
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
|
|
|
|
? new IntFixedNodeBuilder(exactSizeIfKnown)
|
|
|
|
: intBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a variable size @{link Node.Builder.OfInt}.
|
|
|
|
*
|
|
|
|
* @return a {@code Node.Builder.OfInt}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfInt intBuilder() {
|
|
|
|
return new IntSpinedNodeBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Long nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.OfLong} describing a long[] array.
|
|
|
|
* <p>
|
|
|
|
* The node will hold a reference to the array and will not make a copy.
|
|
|
|
*
|
|
|
|
* @param array the array
|
|
|
|
* @return a node holding an array
|
|
|
|
*/
|
|
|
|
static Node.OfLong node(final long[] array) {
|
|
|
|
return new LongArrayNode(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.Builder.OfLong}.
|
|
|
|
*
|
|
|
|
* @param exactSizeIfKnown -1 if a variable size builder is requested,
|
|
|
|
* otherwise the exact capacity desired. A fixed capacity builder will
|
|
|
|
* fail if the wrong number of elements are added to the builder.
|
|
|
|
* @return a {@code Node.Builder.OfLong}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfLong longBuilder(long exactSizeIfKnown) {
|
|
|
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
|
|
|
|
? new LongFixedNodeBuilder(exactSizeIfKnown)
|
|
|
|
: longBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a variable size @{link Node.Builder.OfLong}.
|
|
|
|
*
|
|
|
|
* @return a {@code Node.Builder.OfLong}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfLong longBuilder() {
|
|
|
|
return new LongSpinedNodeBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Double nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.OfDouble} describing a double[] array.
|
|
|
|
*
|
|
|
|
* <p>The node will hold a reference to the array and will not make a copy.
|
|
|
|
*
|
|
|
|
* @param array the array
|
|
|
|
* @return a node holding an array
|
|
|
|
*/
|
|
|
|
static Node.OfDouble node(final double[] array) {
|
|
|
|
return new DoubleArrayNode(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a {@link Node.Builder.OfDouble}.
|
|
|
|
*
|
|
|
|
* @param exactSizeIfKnown -1 if a variable size builder is requested,
|
|
|
|
* otherwise the exact capacity desired. A fixed capacity builder will
|
|
|
|
* fail if the wrong number of elements are added to the builder.
|
|
|
|
* @return a {@code Node.Builder.OfDouble}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfDouble doubleBuilder(long exactSizeIfKnown) {
|
|
|
|
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
|
|
|
|
? new DoubleFixedNodeBuilder(exactSizeIfKnown)
|
|
|
|
: doubleBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produces a variable size @{link Node.Builder.OfDouble}.
|
|
|
|
*
|
|
|
|
* @return a {@code Node.Builder.OfDouble}
|
|
|
|
*/
|
|
|
|
static Node.Builder.OfDouble doubleBuilder() {
|
|
|
|
return new DoubleSpinedNodeBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parallel evaluation of pipelines to nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collect, in parallel, elements output from a pipeline and describe those
|
|
|
|
* elements with a {@link Node}.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If the exact size of the output from the pipeline is known and the source
|
|
|
|
* {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
|
|
|
|
* then a flat {@link Node} will be returned whose content is an array,
|
|
|
|
* since the size is known the array can be constructed in advance and
|
|
|
|
* output elements can be placed into the array concurrently by leaf
|
|
|
|
* tasks at the correct offsets. If the exact size is not known, output
|
|
|
|
* elements are collected into a conc-node whose shape mirrors that
|
|
|
|
* of the computation. This conc-node can then be flattened in
|
|
|
|
* parallel to produce a flat {@code Node} if desired.
|
|
|
|
*
|
|
|
|
* @param helper the pipeline helper describing the pipeline
|
|
|
|
* @param flattenTree whether a conc node should be flattened into a node
|
|
|
|
* describing an array before returning
|
|
|
|
* @param generator the array generator
|
|
|
|
* @return a {@link Node} describing the output elements
|
|
|
|
*/
|
|
|
|
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("Stream size exceeds max array 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<>(helper, generator, spliterator).invoke();
|
|
|
|
return flattenTree ? flatten(node, generator) : node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collect, in parallel, elements output from an int-valued pipeline and
|
|
|
|
* describe those elements with a {@link Node.OfInt}.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If the exact size of the output from the pipeline is known and the source
|
|
|
|
* {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
|
|
|
|
* then a flat {@link Node} will be returned whose content is an array,
|
|
|
|
* since the size is known the array can be constructed in advance and
|
|
|
|
* output elements can be placed into the array concurrently by leaf
|
|
|
|
* tasks at the correct offsets. If the exact size is not known, output
|
|
|
|
* elements are collected into a conc-node whose shape mirrors that
|
|
|
|
* of the computation. This conc-node can then be flattened in
|
|
|
|
* parallel to produce a flat {@code Node.OfInt} if desired.
|
|
|
|
*
|
|
|
|
* @param <P_IN> the type of elements from the source Spliterator
|
|
|
|
* @param helper the pipeline helper describing the pipeline
|
|
|
|
* @param flattenTree whether a conc node should be flattened into a node
|
|
|
|
* describing an array before returning
|
|
|
|
* @return a {@link Node.OfInt} describing the output elements
|
|
|
|
*/
|
|
|
|
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("Stream size exceeds max array size");
|
|
|
|
int[] array = new int[(int) size];
|
|
|
|
new SizedCollectorTask.OfInt<>(spliterator, helper, array).invoke();
|
|
|
|
return node(array);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Node.OfInt node = new IntCollectorTask<>(helper, spliterator).invoke();
|
|
|
|
return flattenTree ? flattenInt(node) : node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collect, in parallel, elements output from a long-valued pipeline and
|
|
|
|
* describe those elements with a {@link Node.OfLong}.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If the exact size of the output from the pipeline is known and the source
|
|
|
|
* {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
|
|
|
|
* then a flat {@link Node} will be returned whose content is an array,
|
|
|
|
* since the size is known the array can be constructed in advance and
|
|
|
|
* output elements can be placed into the array concurrently by leaf
|
|
|
|
* tasks at the correct offsets. If the exact size is not known, output
|
|
|
|
* elements are collected into a conc-node whose shape mirrors that
|
|
|
|
* of the computation. This conc-node can then be flattened in
|
|
|
|
* parallel to produce a flat {@code Node.OfLong} if desired.
|
|
|
|
*
|
|
|
|
* @param <P_IN> the type of elements from the source Spliterator
|
|
|
|
* @param helper the pipeline helper describing the pipeline
|
|
|
|
* @param flattenTree whether a conc node should be flattened into a node
|
|
|
|
* describing an array before returning
|
|
|
|
* @return a {@link Node.OfLong} describing the output elements
|
|
|
|
*/
|
|
|
|
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("Stream size exceeds max array size");
|
|
|
|
long[] array = new long[(int) size];
|
|
|
|
new SizedCollectorTask.OfLong<>(spliterator, helper, array).invoke();
|
|
|
|
return node(array);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Node.OfLong node = new LongCollectorTask<>(helper, spliterator).invoke();
|
|
|
|
return flattenTree ? flattenLong(node) : node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collect, in parallel, elements output from n double-valued pipeline and
|
|
|
|
* describe those elements with a {@link Node.OfDouble}.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If the exact size of the output from the pipeline is known and the source
|
|
|
|
* {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
|
|
|
|
* then a flat {@link Node} will be returned whose content is an array,
|
|
|
|
* since the size is known the array can be constructed in advance and
|
|
|
|
* output elements can be placed into the array concurrently by leaf
|
|
|
|
* tasks at the correct offsets. If the exact size is not known, output
|
|
|
|
* elements are collected into a conc-node whose shape mirrors that
|
|
|
|
* of the computation. This conc-node can then be flattened in
|
|
|
|
* parallel to produce a flat {@code Node.OfDouble} if desired.
|
|
|
|
*
|
|
|
|
* @param <P_IN> the type of elements from the source Spliterator
|
|
|
|
* @param helper the pipeline helper describing the pipeline
|
|
|
|
* @param flattenTree whether a conc node should be flattened into a node
|
|
|
|
* describing an array before returning
|
|
|
|
* @return a {@link Node.OfDouble} describing the output elements
|
|
|
|
*/
|
|
|
|
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("Stream size exceeds max array size");
|
|
|
|
double[] array = new double[(int) size];
|
|
|
|
new SizedCollectorTask.OfDouble<>(spliterator, helper, array).invoke();
|
|
|
|
return node(array);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Node.OfDouble node = new DoubleCollectorTask<>(helper, spliterator).invoke();
|
|
|
|
return flattenTree ? flattenDouble(node) : node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parallel flattening of nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flatten, in parallel, a {@link Node}. A flattened node is one that has
|
|
|
|
* no children. If the node is already flat, it is simply returned.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If a new node is to be created, the generator is used to create an array
|
|
|
|
* whose length is {@link Node#count()}. Then the node tree is traversed
|
|
|
|
* and leaf node elements are placed in the array concurrently by leaf tasks
|
|
|
|
* at the correct offsets.
|
|
|
|
*
|
|
|
|
* @param <T> type of elements contained by the node
|
|
|
|
* @param node the node to flatten
|
|
|
|
* @param generator the array factory used to create array instances
|
|
|
|
* @return a flat {@code Node}
|
|
|
|
*/
|
|
|
|
public static <T> Node<T> flatten(Node<T> node, IntFunction<T[]> generator) {
|
|
|
|
if (node.getChildCount() > 0) {
|
|
|
|
T[] array = generator.apply((int) node.count());
|
|
|
|
new ToArrayTask.OfRef<>(node, array, 0).invoke();
|
|
|
|
return node(array);
|
|
|
|
} else {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flatten, in parallel, a {@link Node.OfInt}. A flattened node is one that
|
|
|
|
* has no children. If the node is already flat, it is simply returned.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If a new node is to be created, a new int[] array is created whose length
|
|
|
|
* is {@link Node#count()}. Then the node tree is traversed and leaf node
|
|
|
|
* elements are placed in the array concurrently by leaf tasks at the
|
|
|
|
* correct offsets.
|
|
|
|
*
|
|
|
|
* @param node the node to flatten
|
|
|
|
* @return a flat {@code Node.OfInt}
|
|
|
|
*/
|
|
|
|
public static Node.OfInt flattenInt(Node.OfInt node) {
|
|
|
|
if (node.getChildCount() > 0) {
|
|
|
|
int[] array = new int[(int) node.count()];
|
|
|
|
new ToArrayTask.OfInt(node, array, 0).invoke();
|
|
|
|
return node(array);
|
|
|
|
} else {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flatten, in parallel, a {@link Node.OfLong}. A flattened node is one that
|
|
|
|
* has no children. If the node is already flat, it is simply returned.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If a new node is to be created, a new long[] array is created whose length
|
|
|
|
* is {@link Node#count()}. Then the node tree is traversed and leaf node
|
|
|
|
* elements are placed in the array concurrently by leaf tasks at the
|
|
|
|
* correct offsets.
|
|
|
|
*
|
|
|
|
* @param node the node to flatten
|
|
|
|
* @return a flat {@code Node.OfLong}
|
|
|
|
*/
|
|
|
|
public static Node.OfLong flattenLong(Node.OfLong node) {
|
|
|
|
if (node.getChildCount() > 0) {
|
|
|
|
long[] array = new long[(int) node.count()];
|
|
|
|
new ToArrayTask.OfLong(node, array, 0).invoke();
|
|
|
|
return node(array);
|
|
|
|
} else {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flatten, in parallel, a {@link Node.OfDouble}. A flattened node is one that
|
|
|
|
* has no children. If the node is already flat, it is simply returned.
|
|
|
|
*
|
|
|
|
* @implSpec
|
|
|
|
* If a new node is to be created, a new double[] array is created whose length
|
|
|
|
* is {@link Node#count()}. Then the node tree is traversed and leaf node
|
|
|
|
* elements are placed in the array concurrently by leaf tasks at the
|
|
|
|
* correct offsets.
|
|
|
|
*
|
|
|
|
* @param node the node to flatten
|
|
|
|
* @return a flat {@code Node.OfDouble}
|
|
|
|
*/
|
|
|
|
public static Node.OfDouble flattenDouble(Node.OfDouble node) {
|
|
|
|
if (node.getChildCount() > 0) {
|
|
|
|
double[] array = new double[(int) node.count()];
|
|
|
|
new ToArrayTask.OfDouble(node, array, 0).invoke();
|
|
|
|
return node(array);
|
|
|
|
} else {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementations
|
|
|
|
|
|
|
|
private static abstract 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() { } // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfInt spliterator() {
|
|
|
|
return Spliterators.emptyIntSpliterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public int[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
return EMPTY_INT_ARRAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfLong
|
|
|
|
extends EmptyNode<Long, long[], LongConsumer>
|
|
|
|
implements Node.OfLong {
|
|
|
|
|
|
|
|
OfLong() { } // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfLong spliterator() {
|
|
|
|
return Spliterators.emptyLongSpliterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public long[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
return EMPTY_LONG_ARRAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfDouble
|
|
|
|
extends EmptyNode<Double, double[], DoubleConsumer>
|
|
|
|
implements Node.OfDouble {
|
|
|
|
|
|
|
|
OfDouble() { } // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfDouble spliterator() {
|
|
|
|
return Spliterators.emptyDoubleSpliterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public double[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
return EMPTY_DOUBLE_ARRAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Node class for a reference 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("Stream size exceeds max array 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Traversable
|
|
|
|
|
|
|
|
@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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Node class for a Collection */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Node class for an internal node with two or more children
|
|
|
|
*/
|
|
|
|
static final class ConcNode<T> implements Node<T> {
|
|
|
|
private final Node<T> left;
|
|
|
|
private final Node<T> right;
|
|
|
|
|
|
|
|
private final long size;
|
|
|
|
|
|
|
|
ConcNode(Node<T> left, Node<T> 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
|
|
|
|
// traversing the tree
|
|
|
|
this.size = left.count() + right.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Node
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator<T> spliterator() {
|
|
|
|
return new Nodes.InternalNodeSpliterator.OfRef<>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getChildCount() {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Node<T> getChild(int i) {
|
|
|
|
if (i == 0) return left;
|
|
|
|
if (i == 1) return right;
|
|
|
|
throw new IndexOutOfBoundsException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyInto(T[] array, int offset) {
|
|
|
|
Objects.requireNonNull(array);
|
|
|
|
left.copyInto(array, offset);
|
|
|
|
right.copyInto(array, offset + (int) left.count());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public T[] asArray(IntFunction<T[]> generator) {
|
|
|
|
T[] array = generator.apply((int) count());
|
|
|
|
copyInto(array, 0);
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long count() {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEach(Consumer<? super T> consumer) {
|
|
|
|
left.forEach(consumer);
|
|
|
|
right.forEach(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
if (count() < 32) {
|
|
|
|
return String.format("ConcNode[%s.%s]", left, right);
|
|
|
|
} else {
|
|
|
|
return String.format("ConcNode[size=%d]", count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Abstract class for spliterator for all internal node classes */
|
|
|
|
private static abstract class InternalNodeSpliterator<T,
|
|
|
|
S extends Spliterator<T>,
|
|
|
|
N extends Node<T>, C>
|
|
|
|
implements Spliterator<T> {
|
|
|
|
// Node we are pointing to
|
|
|
|
// null if full traversal has occurred
|
|
|
|
N curNode;
|
|
|
|
|
|
|
|
// next child of curNode to consume
|
|
|
|
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.
|
|
|
|
// null if curNode has children
|
|
|
|
S lastNodeSpliterator;
|
|
|
|
|
|
|
|
// spliterator used while traversing with tryAdvance
|
|
|
|
// null if no partial traversal has occurred
|
|
|
|
S tryAdvanceSpliterator;
|
|
|
|
|
|
|
|
// node stack used when traversing to search and find leaf nodes
|
|
|
|
// null if no partial traversal has occurred
|
|
|
|
Deque<N> tryAdvanceStack;
|
|
|
|
|
|
|
|
InternalNodeSpliterator(N curNode) {
|
|
|
|
this.curNode = curNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiate a stack containing, in left-to-right order, the child nodes
|
|
|
|
* covered by this spliterator
|
|
|
|
*/
|
|
|
|
protected final Deque<N> initStack() {
|
|
|
|
// Bias size to the case where leaf nodes are close to this node
|
|
|
|
// 8 is the minimum initial capacity for the ArrayDeque implementation
|
|
|
|
Deque<N> stack = new ArrayDeque<>(8);
|
|
|
|
for (int i = curNode.getChildCount() - 1; i >= curChildIndex; i--)
|
|
|
|
stack.addFirst((N) curNode.getChild(i));
|
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Depth first search, in left-to-right order, of the node tree, using
|
|
|
|
* an explicit stack, to find the next non-empty leaf node.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected final boolean internalTryAdvance(C consumer) {
|
|
|
|
if (curNode == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (tryAdvanceSpliterator == null) {
|
|
|
|
if (lastNodeSpliterator == null) {
|
|
|
|
// Initiate the node stack
|
|
|
|
tryAdvanceStack = initStack();
|
|
|
|
N leaf = findNextLeafNode(tryAdvanceStack);
|
|
|
|
if (leaf != null)
|
|
|
|
tryAdvanceSpliterator = (S) leaf.spliterator();
|
|
|
|
else {
|
|
|
|
// A non-empty leaf node was not found
|
|
|
|
// No elements to traverse
|
|
|
|
curNode = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tryAdvanceSpliterator = lastNodeSpliterator;
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean hasNext = tryAdvance(tryAdvanceSpliterator, consumer);
|
|
|
|
if (!hasNext) {
|
|
|
|
if (lastNodeSpliterator == null) {
|
|
|
|
// Advance to the spliterator of the next non-empty leaf node
|
|
|
|
Node<T> leaf = findNextLeafNode(tryAdvanceStack);
|
|
|
|
if (leaf != null) {
|
|
|
|
tryAdvanceSpliterator = (S) leaf.spliterator();
|
|
|
|
// Since the node is not-empty the spliterator can be advanced
|
|
|
|
return tryAdvance(tryAdvanceSpliterator, consumer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No more elements to traverse
|
|
|
|
curNode = null;
|
|
|
|
}
|
|
|
|
return hasNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract boolean tryAdvance(S spliterator, C consumer);
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public S trySplit() {
|
|
|
|
if (curNode == null || tryAdvanceSpliterator != null)
|
|
|
|
return null; // Cannot split if fully or partially traversed
|
|
|
|
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 long estimateSize() {
|
|
|
|
if (curNode == null)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Will not reflect the effects of partial traversal.
|
|
|
|
// This is compliant with the specification
|
|
|
|
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 int characteristics() {
|
|
|
|
return Spliterator.SIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfRef<T>
|
|
|
|
extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>, Consumer<? super T>> {
|
|
|
|
|
|
|
|
OfRef(Node<T> curNode) {
|
|
|
|
super(curNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean tryAdvance(Consumer<? super T> consumer) {
|
|
|
|
return internalTryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean tryAdvance(Spliterator<T> spliterator,
|
|
|
|
Consumer<? super T> consumer) {
|
|
|
|
return spliterator.tryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@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 static final class OfInt
|
|
|
|
extends InternalNodeSpliterator<Integer, Spliterator.OfInt, Node.OfInt, IntConsumer>
|
|
|
|
implements Spliterator.OfInt {
|
|
|
|
|
|
|
|
OfInt(Node.OfInt cur) {
|
|
|
|
super(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean tryAdvance(IntConsumer consumer) {
|
|
|
|
return internalTryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean tryAdvance(Spliterator.OfInt spliterator,
|
|
|
|
IntConsumer consumer) {
|
|
|
|
return spliterator.tryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEachRemaining(IntConsumer consumer) {
|
|
|
|
if (curNode == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tryAdvanceSpliterator == null) {
|
|
|
|
if (lastNodeSpliterator == null) {
|
|
|
|
Deque<Node.OfInt> stack = initStack();
|
|
|
|
Node.OfInt leaf;
|
|
|
|
while ((leaf = findNextLeafNode(stack)) != null) {
|
|
|
|
leaf.forEach(consumer);
|
|
|
|
}
|
|
|
|
curNode = null;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lastNodeSpliterator.forEachRemaining(consumer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while(tryAdvance(consumer)) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfLong
|
|
|
|
extends InternalNodeSpliterator<Long, Spliterator.OfLong, Node.OfLong, LongConsumer>
|
|
|
|
implements Spliterator.OfLong {
|
|
|
|
|
|
|
|
OfLong(Node.OfLong cur) {
|
|
|
|
super(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean tryAdvance(LongConsumer consumer) {
|
|
|
|
return internalTryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean tryAdvance(Spliterator.OfLong spliterator,
|
|
|
|
LongConsumer consumer) {
|
|
|
|
return spliterator.tryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEachRemaining(LongConsumer consumer) {
|
|
|
|
if (curNode == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tryAdvanceSpliterator == null) {
|
|
|
|
if (lastNodeSpliterator == null) {
|
|
|
|
Deque<Node.OfLong> stack = initStack();
|
|
|
|
Node.OfLong leaf;
|
|
|
|
while ((leaf = findNextLeafNode(stack)) != null) {
|
|
|
|
leaf.forEach(consumer);
|
|
|
|
}
|
|
|
|
curNode = null;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lastNodeSpliterator.forEachRemaining(consumer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while(tryAdvance(consumer)) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfDouble
|
|
|
|
extends InternalNodeSpliterator<Double, Spliterator.OfDouble, Node.OfDouble, DoubleConsumer>
|
|
|
|
implements Spliterator.OfDouble {
|
|
|
|
|
|
|
|
OfDouble(Node.OfDouble cur) {
|
|
|
|
super(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean tryAdvance(DoubleConsumer consumer) {
|
|
|
|
return internalTryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean tryAdvance(Spliterator.OfDouble spliterator,
|
|
|
|
DoubleConsumer consumer) {
|
|
|
|
return spliterator.tryAdvance(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEachRemaining(DoubleConsumer consumer) {
|
|
|
|
if (curNode == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tryAdvanceSpliterator == null) {
|
|
|
|
if (lastNodeSpliterator == null) {
|
|
|
|
Deque<Node.OfDouble> stack = initStack();
|
|
|
|
Node.OfDouble leaf;
|
|
|
|
while ((leaf = findNextLeafNode(stack)) != null) {
|
|
|
|
leaf.forEach(consumer);
|
|
|
|
}
|
|
|
|
curNode = null;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lastNodeSpliterator.forEachRemaining(consumer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while(tryAdvance(consumer)) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fixed-sized builder class for reference nodes
|
|
|
|
*/
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Variable-sized builder class for reference nodes
|
|
|
|
*/
|
|
|
|
private static final class SpinedNodeBuilder<T>
|
|
|
|
extends SpinedBuffer<T>
|
|
|
|
implements Node<T>, Node.Builder<T> {
|
|
|
|
private boolean building = false;
|
|
|
|
|
|
|
|
SpinedNodeBuilder() {} // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@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 abstract static class AbstractPrimitiveConcNode<E, N extends Node<E>>
|
|
|
|
implements Node<E> {
|
|
|
|
final N left;
|
|
|
|
final N right;
|
|
|
|
final long size;
|
|
|
|
|
|
|
|
AbstractPrimitiveConcNode(N left, N 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 by traversing the tree
|
|
|
|
this.size = left.count() + right.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getChildCount() {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public N getChild(int i) {
|
|
|
|
if (i == 0) return left;
|
|
|
|
if (i == 1) return right;
|
|
|
|
throw new IndexOutOfBoundsException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long count() {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
@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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static class IntArrayNode implements Node.OfInt {
|
|
|
|
final int[] array;
|
|
|
|
int curSize;
|
|
|
|
|
|
|
|
IntArrayNode(long size) {
|
|
|
|
if (size >= MAX_ARRAY_SIZE)
|
|
|
|
throw new IllegalArgumentException("Stream size exceeds max array 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
|
2013-06-13 11:13:30 +02:00
|
|
|
public int[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
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("Stream size exceeds max array 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
|
2013-06-13 11:13:30 +02:00
|
|
|
public long[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
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("Stream size exceeds max array 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
|
2013-06-13 11:13:30 +02:00
|
|
|
public double[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final class IntConcNode
|
|
|
|
extends AbstractPrimitiveConcNode<Integer, Node.OfInt>
|
|
|
|
implements Node.OfInt {
|
|
|
|
|
|
|
|
IntConcNode(Node.OfInt left, Node.OfInt right) {
|
|
|
|
super(left, right);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEach(IntConsumer consumer) {
|
|
|
|
left.forEach(consumer);
|
|
|
|
right.forEach(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfInt spliterator() {
|
|
|
|
return new InternalNodeSpliterator.OfInt(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyInto(int[] array, int offset) {
|
|
|
|
left.copyInto(array, offset);
|
|
|
|
right.copyInto(array, offset + (int) left.count());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public int[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
int[] array = new int[(int) count()];
|
|
|
|
copyInto(array, 0);
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final class LongConcNode
|
|
|
|
extends AbstractPrimitiveConcNode<Long, Node.OfLong>
|
|
|
|
implements Node.OfLong {
|
|
|
|
|
|
|
|
LongConcNode(Node.OfLong left, Node.OfLong right) {
|
|
|
|
super(left, right);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEach(LongConsumer consumer) {
|
|
|
|
left.forEach(consumer);
|
|
|
|
right.forEach(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfLong spliterator() {
|
|
|
|
return new InternalNodeSpliterator.OfLong(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyInto(long[] array, int offset) {
|
|
|
|
left.copyInto(array, offset);
|
|
|
|
right.copyInto(array, offset + (int) left.count());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public long[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
long[] array = new long[(int) count()];
|
|
|
|
copyInto(array, 0);
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final class DoubleConcNode
|
|
|
|
extends AbstractPrimitiveConcNode<Double, Node.OfDouble>
|
|
|
|
implements Node.OfDouble {
|
|
|
|
|
|
|
|
DoubleConcNode(Node.OfDouble left, Node.OfDouble right) {
|
|
|
|
super(left, right);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void forEach(DoubleConsumer consumer) {
|
|
|
|
left.forEach(consumer);
|
|
|
|
right.forEach(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Spliterator.OfDouble spliterator() {
|
|
|
|
return new InternalNodeSpliterator.OfDouble(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyInto(double[] array, int offset) {
|
|
|
|
left.copyInto(array, offset);
|
|
|
|
right.copyInto(array, offset + (int) left.count());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-06-13 11:13:30 +02:00
|
|
|
public double[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
double[] array = new double[(int) count()];
|
|
|
|
copyInto(array, 0);
|
|
|
|
return 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() {} // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@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
|
2013-06-13 11:13:30 +02:00
|
|
|
public int[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
assert !building : "during building";
|
2013-06-13 11:13:30 +02:00
|
|
|
return super.asPrimitiveArray();
|
2013-04-24 16:15:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@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() {} // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@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
|
2013-06-13 11:13:30 +02:00
|
|
|
public long[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
assert !building : "during building";
|
2013-06-13 11:13:30 +02:00
|
|
|
return super.asPrimitiveArray();
|
2013-04-24 16:15:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@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() {} // Avoid creation of special accessor
|
|
|
|
|
|
|
|
@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
|
2013-06-13 11:13:30 +02:00
|
|
|
public double[] asPrimitiveArray() {
|
2013-04-24 16:15:47 -07:00
|
|
|
assert !building : "during building";
|
2013-06-13 11:13:30 +02:00
|
|
|
return super.asPrimitiveArray();
|
2013-04-24 16:15:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Node.OfDouble build() {
|
|
|
|
assert !building : "during building";
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static abstract 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;
|
|
|
|
// For Sink implementation
|
|
|
|
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;
|
|
|
|
while (true) {
|
|
|
|
Spliterator<P_IN> leftSplit;
|
|
|
|
if (!AbstractTask.suggestSplit(task.spliterator, task.targetSize)
|
|
|
|
|| ((leftSplit = task.spliterator.trySplit()) == null)) {
|
|
|
|
if (task.offset + task.length >= MAX_ARRAY_SIZE)
|
|
|
|
throw new IllegalArgumentException("Stream size exceeds max array size");
|
|
|
|
T_SINK sink = (T_SINK) task;
|
|
|
|
task.helper.wrapAndCopyInto(sink, task.spliterator);
|
|
|
|
task.propagateCompletion();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
task.setPendingCount(1);
|
|
|
|
long leftSplitSize = leftSplit.estimateSize();
|
|
|
|
task.makeChild(leftSplit, task.offset, leftSplitSize).fork();
|
|
|
|
task = task.makeChild(task.spliterator, task.offset + leftSplitSize,
|
|
|
|
task.length - leftSplitSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
index = (int) offset;
|
|
|
|
fence = (int) offset + (int) length;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static abstract 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfInt
|
|
|
|
extends ToArrayTask<Integer, Node.OfInt, OfInt> {
|
|
|
|
private final int[] array;
|
|
|
|
|
|
|
|
private OfInt(Node.OfInt node, int[] array, int offset) {
|
|
|
|
super(node, offset);
|
|
|
|
this.array = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
private OfInt(OfInt parent, Node.OfInt node, int offset) {
|
|
|
|
super(parent, node, offset);
|
|
|
|
this.array = parent.array;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
OfInt makeChild(int childIndex, int offset) {
|
|
|
|
return new OfInt(this, node.getChild(childIndex), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
void copyNodeToArray() {
|
|
|
|
node.copyInto(array, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfLong
|
|
|
|
extends ToArrayTask<Long, Node.OfLong, OfLong> {
|
|
|
|
private final long[] array;
|
|
|
|
|
|
|
|
private OfLong(Node.OfLong node, long[] array, int offset) {
|
|
|
|
super(node, offset);
|
|
|
|
this.array = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
private OfLong(OfLong parent, Node.OfLong node, int offset) {
|
|
|
|
super(parent, node, offset);
|
|
|
|
this.array = parent.array;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
OfLong makeChild(int childIndex, int offset) {
|
|
|
|
return new OfLong(this, node.getChild(childIndex), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
void copyNodeToArray() {
|
|
|
|
node.copyInto(array, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class OfDouble
|
|
|
|
extends ToArrayTask<Double, Node.OfDouble, OfDouble> {
|
|
|
|
private final double[] array;
|
|
|
|
|
|
|
|
private OfDouble(Node.OfDouble node, double[] array, int offset) {
|
|
|
|
super(node, offset);
|
|
|
|
this.array = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
private OfDouble(OfDouble parent, Node.OfDouble node, int offset) {
|
|
|
|
super(parent, node, offset);
|
|
|
|
this.array = parent.array;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
OfDouble makeChild(int childIndex, int offset) {
|
|
|
|
return new OfDouble(this, node.getChild(childIndex), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
void copyNodeToArray() {
|
|
|
|
node.copyInto(array, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class CollectorTask<P_IN, P_OUT>
|
|
|
|
extends AbstractTask<P_IN, P_OUT, Node<P_OUT>, CollectorTask<P_IN, P_OUT>> {
|
|
|
|
private final PipelineHelper<P_OUT> helper;
|
|
|
|
private final IntFunction<P_OUT[]> generator;
|
|
|
|
|
|
|
|
CollectorTask(PipelineHelper<P_OUT> helper,
|
|
|
|
IntFunction<P_OUT[]> generator,
|
|
|
|
Spliterator<P_IN> spliterator) {
|
|
|
|
super(helper, spliterator);
|
|
|
|
this.helper = helper;
|
|
|
|
this.generator = generator;
|
|
|
|
}
|
|
|
|
|
|
|
|
CollectorTask(CollectorTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator) {
|
|
|
|
super(parent, spliterator);
|
|
|
|
helper = parent.helper;
|
|
|
|
generator = parent.generator;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected CollectorTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator) {
|
|
|
|
return new CollectorTask<>(this, spliterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Node<P_OUT> doLeaf() {
|
|
|
|
Node.Builder<P_OUT> builder
|
|
|
|
= builder(helper.exactOutputSizeIfKnown(spliterator),
|
|
|
|
generator);
|
|
|
|
return helper.wrapAndCopyInto(builder, spliterator).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCompletion(CountedCompleter caller) {
|
|
|
|
if (!isLeaf()) {
|
|
|
|
setLocalResult(new ConcNode<>(leftChild.getLocalResult(), rightChild.getLocalResult()));
|
|
|
|
}
|
|
|
|
super.onCompletion(caller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class IntCollectorTask<P_IN>
|
|
|
|
extends AbstractTask<P_IN, Integer, Node.OfInt, IntCollectorTask<P_IN>> {
|
|
|
|
private final PipelineHelper<Integer> helper;
|
|
|
|
|
|
|
|
IntCollectorTask(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) {
|
|
|
|
super(helper, spliterator);
|
|
|
|
this.helper = helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
IntCollectorTask(IntCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
|
|
|
|
super(parent, spliterator);
|
|
|
|
helper = parent.helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected IntCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
|
|
|
|
return new IntCollectorTask<>(this, spliterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Node.OfInt doLeaf() {
|
|
|
|
Node.Builder.OfInt builder = intBuilder(helper.exactOutputSizeIfKnown(spliterator));
|
|
|
|
return helper.wrapAndCopyInto(builder, spliterator).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCompletion(CountedCompleter caller) {
|
|
|
|
if (!isLeaf()) {
|
|
|
|
setLocalResult(new IntConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
|
|
|
|
}
|
|
|
|
super.onCompletion(caller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class LongCollectorTask<P_IN>
|
|
|
|
extends AbstractTask<P_IN, Long, Node.OfLong, LongCollectorTask<P_IN>> {
|
|
|
|
private final PipelineHelper<Long> helper;
|
|
|
|
|
|
|
|
LongCollectorTask(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) {
|
|
|
|
super(helper, spliterator);
|
|
|
|
this.helper = helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
LongCollectorTask(LongCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
|
|
|
|
super(parent, spliterator);
|
|
|
|
helper = parent.helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected LongCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
|
|
|
|
return new LongCollectorTask<>(this, spliterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Node.OfLong doLeaf() {
|
|
|
|
Node.Builder.OfLong builder = longBuilder(helper.exactOutputSizeIfKnown(spliterator));
|
|
|
|
return helper.wrapAndCopyInto(builder, spliterator).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCompletion(CountedCompleter caller) {
|
|
|
|
if (!isLeaf()) {
|
|
|
|
setLocalResult(new LongConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
|
|
|
|
}
|
|
|
|
super.onCompletion(caller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class DoubleCollectorTask<P_IN>
|
|
|
|
extends AbstractTask<P_IN, Double, Node.OfDouble, DoubleCollectorTask<P_IN>> {
|
|
|
|
private final PipelineHelper<Double> helper;
|
|
|
|
|
|
|
|
DoubleCollectorTask(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) {
|
|
|
|
super(helper, spliterator);
|
|
|
|
this.helper = helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
DoubleCollectorTask(DoubleCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
|
|
|
|
super(parent, spliterator);
|
|
|
|
helper = parent.helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected DoubleCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
|
|
|
|
return new DoubleCollectorTask<>(this, spliterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Node.OfDouble doLeaf() {
|
|
|
|
Node.Builder.OfDouble builder
|
|
|
|
= doubleBuilder(helper.exactOutputSizeIfKnown(spliterator));
|
|
|
|
return helper.wrapAndCopyInto(builder, spliterator).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCompletion(CountedCompleter caller) {
|
|
|
|
if (!isLeaf()) {
|
|
|
|
setLocalResult(new DoubleConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
|
|
|
|
}
|
|
|
|
super.onCompletion(caller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|