| /* | |
| * Copyright (c) 2012, 2025, 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 jdk.internal.util.ArraysSupport; | |
| import java.util.ArrayDeque; | |
| import java.util.Arrays; | |
| import java.util.Collection; | |
| import java.util.Deque; | |
| 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 {@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 = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; | |
| // 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(); | |
| /** | |
| * @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. | |
| * | |
| * @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) { | |
| return (Node<T>) switch (shape) { | |
| case REFERENCE -> EMPTY_NODE; | |
| case INT_VALUE -> EMPTY_INT_NODE; | |
| case LONG_VALUE -> EMPTY_LONG_NODE; | |
| case DOUBLE_VALUE -> EMPTY_DOUBLE_NODE; | |
| }; | |
| } | |
| /** | |
| * 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) { | |
| return (Node<T>) switch (shape) { | |
| case REFERENCE -> new ConcNode<>(left, right); | |
| case INT_VALUE -> new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right); | |
| case LONG_VALUE -> new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right); | |
| case DOUBLE_VALUE -> new ConcNode.OfDouble((Node.OfDouble) left, (Node.OfDouble) right); | |
| }; | |
| } | |
| // Reference-based node methods | |
| /** | |
| * 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(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 {@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 {@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 {@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 {@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 {@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 {@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 {@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(); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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; | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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; | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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 */ | |
| private static class ArrayNode<T> implements Node<T> { | |
| final T[] array; | |
| int curSize; | |
| 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 */ | |
| 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 | |
| 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 | |
| */ | |
| 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()); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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 */ | |
| 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 | |
| */ | |
| @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. | |
| */ | |
| @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)) { } | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| private static final class OfInt | |
| extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> | |
| implements Spliterator.OfInt { | |
| OfInt(Node.OfInt cur) { | |
| super(cur); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| private static final class OfLong | |
| extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> | |
| implements Spliterator.OfLong { | |
| OfLong(Node.OfLong cur) { | |
| super(cur); | |
| } | |
| } | |
| @SuppressWarnings("overloads") | |
| 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 | |
| */ | |
| 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 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); | |
| long size = 0; | |
| int i = 0; | |
| for (;i < task.node.getChildCount() - 1; i++) { | |
| K leftTask = task.makeChild(i, (int) (task.offset + size)); | |
| size += leftTask.node.count(); | |
| leftTask.fork(); | |
| } | |
| task = task.makeChild(i, (int) (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); | |
| } | |
| } | |
| } | |
| } |