/*
 * Copyright (c) 2012, 2015, 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.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;

Factory methods for constructing implementations of Node and Builder and their primitive specializations. Fork/Join tasks for collecting output from a PipelineHelper to a Node and flattening Nodes.
Since:1.8
/** * 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.
/** * The maximum size of an array that can be allocated. */
static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // IllegalArgumentException messages 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();
Returns:an array generator for an array whose elements are of type T.
/** * @return an array generator for an array whose elements are of type T. */
@SuppressWarnings("unchecked") static <T> IntFunction<T[]> castingArray() { return size -> (T[]) new Object[size]; } // General shape-based node creation methods
Produces an empty node whose count is zero, has no children and no content.
Params:
  • shape – the shape of the node to be created
Type parameters:
  • <T> – the type of elements of the created node
Returns:an empty node.
/** * 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 Node that has two or more children.

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.

The result may be a concatenated node, the input sole node if the size of the list is 1, or an empty node.

Params:
  • shape – the shape of the concatenated node to be created
  • left – the left input node
  • right – the right input node
Type parameters:
  • <T> – the type of elements of the concatenated node
Throws:
Returns:a Node covering the elements of the input nodes
/** * 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 left the left input node * @param right the right input node * @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, Node<T> left, Node<T> right) { switch (shape) { case REFERENCE: return new ConcNode<>(left, right); case INT_VALUE: return (Node<T>) new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right); case LONG_VALUE: return (Node<T>) new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right); case DOUBLE_VALUE: return (Node<T>) new ConcNode.OfDouble((Node.OfDouble) left, (Node.OfDouble) right); default: throw new IllegalStateException("Unknown shape " + shape); } } // Reference-based node methods
Produces a Node describing an array.

The node will hold a reference to the array and will not make a copy.

Params:
  • array – the array
Type parameters:
  • <T> – the type of elements held by the node
Returns:a node holding an array
/** * 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 Node describing a Collection.

The node will hold a reference to the collection and will not make a copy.

Params:
  • c – the collection
Type parameters:
  • <T> – the type of elements held by the node
Returns:a node holding a collection
/** * 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 Builder.
Params:
  • 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.
  • generator – the array factory
Type parameters:
  • <T> – the type of elements of the node builder
Returns:a Node.Builder
/** * 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}.
Type parameters:
  • <T> – the type of elements of the node builder
Returns:a Node.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 OfInt describing an int[] array.

The node will hold a reference to the array and will not make a copy.

Params:
  • array – the array
Returns:a node holding an array
/** * 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 OfInt.
Params:
  • 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.
Returns:a Node.Builder.OfInt
/** * 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}.
Returns:a Node.Builder.OfInt
/** * 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 OfLong describing a long[] array.

The node will hold a reference to the array and will not make a copy.

Params:
  • array – the array
Returns:a node holding an array
/** * 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 OfLong.
Params:
  • 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.
Returns:a Node.Builder.OfLong
/** * 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}.
Returns:a Node.Builder.OfLong
/** * 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 OfDouble describing a double[] array.

The node will hold a reference to the array and will not make a copy.

Params:
  • array – the array
Returns:a node holding an array
/** * 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 OfDouble.
Params:
  • 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.
Returns:a Node.Builder.OfDouble
/** * 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}.
Returns:a Node.Builder.OfDouble
/** * 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 Node.
Params:
  • helper – the pipeline helper describing the pipeline
  • flattenTree – whether a conc node should be flattened into a node describing an array before returning
  • generator – the array generator
Implementation Requirements: If the exact size of the output from the pipeline is known and the source Spliterator has the Spliterator.SUBSIZED characteristic, then a flat 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 Node if desired.
Returns:a Node describing the output elements
/** * 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(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; } }
Collect, in parallel, elements output from an int-valued pipeline and describe those elements with a OfInt.
Params:
  • helper – the pipeline helper describing the pipeline
  • flattenTree – whether a conc node should be flattened into a node describing an array before returning
Type parameters:
  • <P_IN> – the type of elements from the source Spliterator
Implementation Requirements: If the exact size of the output from the pipeline is known and the source Spliterator has the Spliterator.SUBSIZED characteristic, then a flat 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 Node.OfInt if desired.
Returns:a OfInt describing the output elements
/** * 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(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; } }
Collect, in parallel, elements output from a long-valued pipeline and describe those elements with a OfLong.
Params:
  • helper – the pipeline helper describing the pipeline
  • flattenTree – whether a conc node should be flattened into a node describing an array before returning
Type parameters:
  • <P_IN> – the type of elements from the source Spliterator
Implementation Requirements: If the exact size of the output from the pipeline is known and the source Spliterator has the Spliterator.SUBSIZED characteristic, then a flat 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 Node.OfLong if desired.
Returns:a OfLong describing the output elements
/** * 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(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; } }
Collect, in parallel, elements output from n double-valued pipeline and describe those elements with a OfDouble.
Params:
  • helper – the pipeline helper describing the pipeline
  • flattenTree – whether a conc node should be flattened into a node describing an array before returning
Type parameters:
  • <P_IN> – the type of elements from the source Spliterator
Implementation Requirements: If the exact size of the output from the pipeline is known and the source Spliterator has the Spliterator.SUBSIZED characteristic, then a flat 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 Node.OfDouble if desired.
Returns:a OfDouble describing the output elements
/** * 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(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
Flatten, in parallel, a Node. A flattened node is one that has no children. If the node is already flat, it is simply returned.
Params:
  • node – the node to flatten
  • generator – the array factory used to create array instances
Type parameters:
  • <T> – type of elements contained by the node
Implementation Requirements: If a new node is to be created, the generator is used to create an array whose length is 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.
Returns:a flat Node
/** * 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) { 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; } }
Flatten, in parallel, a OfInt. A flattened node is one that has no children. If the node is already flat, it is simply returned.
Params:
  • node – the node to flatten
Implementation Requirements: If a new node is to be created, a new int[] array is created whose length is 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.
Returns:a flat Node.OfInt
/** * 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) { 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; } }
Flatten, in parallel, a OfLong. A flattened node is one that has no children. If the node is already flat, it is simply returned.
Params:
  • node – the node to flatten
Implementation Requirements: If a new node is to be created, a new long[] array is created whose length is 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.
Returns:a flat Node.OfLong
/** * 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 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; } }
Flatten, in parallel, a OfDouble. A flattened node is one that has no children. If the node is already flat, it is simply returned.
Params:
  • node – the node to flatten
Implementation Requirements: If a new node is to be created, a new double[] array is created whose length is 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.
Returns:a flat Node.OfDouble
/** * 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) { 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() { } // Avoid creation of special accessor @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() { } // Avoid creation of special accessor @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() { } // Avoid creation of special accessor @Override public Spliterator.OfDouble spliterator() { return Spliterators.emptyDoubleSpliterator(); } @Override public double[] asPrimitiveArray() { return EMPTY_DOUBLE_ARRAY; } } }
Node class for a reference 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(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)); } }
Node class for a Collection
/** 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
/** * Node class for an internal node with two or more children */
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 // traversing the tree 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 // ensure that there is sufficient room in the array 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 // ensure that there is sufficient room in the array 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); } } }
Abstract class for spliterator for all internal node classes
/** Abstract class for spliterator for all internal node classes */
private abstract static class InternalNodeSpliterator<T, S extends Spliterator<T>, N extends Node<T>> 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
/** * Initiate a stack containing, in left-to-right order, the child nodes * covered by this spliterator */
@SuppressWarnings("unchecked") 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.
/** * Depth first search, in left-to-right order, of the node tree, using * an explicit stack, to find the next non-empty leaf node. */
@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) { // 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; } return true; } @Override @SuppressWarnings("unchecked") public final 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 final 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 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) { // Advance to the spliterator of the next non-empty leaf node Node<T> leaf = findNextLeafNode(tryAdvanceStack); if (leaf != null) { tryAdvanceSpliterator = leaf.spliterator(); // Since the node is not-empty the spliterator can be advanced return tryAdvanceSpliterator.tryAdvance(consumer); } } // No more elements to traverse 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) { // Advance to the spliterator of the next non-empty leaf node N leaf = findNextLeafNode(tryAdvanceStack); if (leaf != null) { tryAdvanceSpliterator = leaf.spliterator(); // Since the node is not-empty the spliterator can be advanced return tryAdvanceSpliterator.tryAdvance(consumer); } } // No more elements to traverse 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); } } }
Fixed-sized builder class for reference nodes
/** * 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
/** * 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 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() {} // 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 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() {} // 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 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() {} // 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 public double[] asPrimitiveArray() { assert !building : "during building"; return super.asPrimitiveArray(); } @Override public Node.OfDouble build() { assert !building : "during building"; return this; } } /* * This and subclasses are not intended to be serializable */ @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; // 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; 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 // with the shared array 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); } } } }