Home | History | Annotate | Download | only in stream
      1 /*
      2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 package java.util.stream;
     26 
     27 import java.util.ArrayDeque;
     28 import java.util.Arrays;
     29 import java.util.Collection;
     30 import java.util.Deque;
     31 import java.util.List;
     32 import java.util.Objects;
     33 import java.util.Spliterator;
     34 import java.util.Spliterators;
     35 import java.util.concurrent.CountedCompleter;
     36 import java.util.function.BinaryOperator;
     37 import java.util.function.Consumer;
     38 import java.util.function.DoubleConsumer;
     39 import java.util.function.IntConsumer;
     40 import java.util.function.IntFunction;
     41 import java.util.function.LongConsumer;
     42 import java.util.function.LongFunction;
     43 
     44 /**
     45  * Factory methods for constructing implementations of {@link Node} and
     46  * {@link Node.Builder} and their primitive specializations.  Fork/Join tasks
     47  * for collecting output from a {@link PipelineHelper} to a {@link Node} and
     48  * flattening {@link Node}s.
     49  *
     50  * @since 1.8
     51  */
     52 final class Nodes {
     53 
     54     private Nodes() {
     55         throw new Error("no instances");
     56     }
     57 
     58     /**
     59      * The maximum size of an array that can be allocated.
     60      */
     61     static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
     62 
     63     // IllegalArgumentException messages
     64     static final String BAD_SIZE = "Stream size exceeds max array size";
     65 
     66     @SuppressWarnings("rawtypes")
     67     private static final Node EMPTY_NODE = new EmptyNode.OfRef();
     68     private static final Node.OfInt EMPTY_INT_NODE = new EmptyNode.OfInt();
     69     private static final Node.OfLong EMPTY_LONG_NODE = new EmptyNode.OfLong();
     70     private static final Node.OfDouble EMPTY_DOUBLE_NODE = new EmptyNode.OfDouble();
     71 
     72     // General shape-based node creation methods
     73 
     74     /**
     75      * Produces an empty node whose count is zero, has no children and no content.
     76      *
     77      * @param <T> the type of elements of the created node
     78      * @param shape the shape of the node to be created
     79      * @return an empty node.
     80      */
     81     @SuppressWarnings("unchecked")
     82     static <T> Node<T> emptyNode(StreamShape shape) {
     83         switch (shape) {
     84             case REFERENCE:    return (Node<T>) EMPTY_NODE;
     85             case INT_VALUE:    return (Node<T>) EMPTY_INT_NODE;
     86             case LONG_VALUE:   return (Node<T>) EMPTY_LONG_NODE;
     87             case DOUBLE_VALUE: return (Node<T>) EMPTY_DOUBLE_NODE;
     88             default:
     89                 throw new IllegalStateException("Unknown shape " + shape);
     90         }
     91     }
     92 
     93     /**
     94      * Produces a concatenated {@link Node} that has two or more children.
     95      * <p>The count of the concatenated node is equal to the sum of the count
     96      * of each child. Traversal of the concatenated node traverses the content
     97      * of each child in encounter order of the list of children. Splitting a
     98      * spliterator obtained from the concatenated node preserves the encounter
     99      * order of the list of children.
    100      *
    101      * <p>The result may be a concatenated node, the input sole node if the size
    102      * of the list is 1, or an empty node.
    103      *
    104      * @param <T> the type of elements of the concatenated node
    105      * @param shape the shape of the concatenated node to be created
    106      * @param left the left input node
    107      * @param right the right input node
    108      * @return a {@code Node} covering the elements of the input nodes
    109      * @throws IllegalStateException if all {@link Node} elements of the list
    110      * are an not instance of type supported by this factory.
    111      */
    112     @SuppressWarnings("unchecked")
    113     static <T> Node<T> conc(StreamShape shape, Node<T> left, Node<T> right) {
    114         switch (shape) {
    115             case REFERENCE:
    116                 return new ConcNode<>(left, right);
    117             case INT_VALUE:
    118                 return (Node<T>) new ConcNode.OfInt((Node.OfInt) left, (Node.OfInt) right);
    119             case LONG_VALUE:
    120                 return (Node<T>) new ConcNode.OfLong((Node.OfLong) left, (Node.OfLong) right);
    121             case DOUBLE_VALUE:
    122                 return (Node<T>) new ConcNode.OfDouble((Node.OfDouble) left, (Node.OfDouble) right);
    123             default:
    124                 throw new IllegalStateException("Unknown shape " + shape);
    125         }
    126     }
    127 
    128     // Reference-based node methods
    129 
    130     /**
    131      * Produces a {@link Node} describing an array.
    132      *
    133      * <p>The node will hold a reference to the array and will not make a copy.
    134      *
    135      * @param <T> the type of elements held by the node
    136      * @param array the array
    137      * @return a node holding an array
    138      */
    139     static <T> Node<T> node(T[] array) {
    140         return new ArrayNode<>(array);
    141     }
    142 
    143     /**
    144      * Produces a {@link Node} describing a {@link Collection}.
    145      * <p>
    146      * The node will hold a reference to the collection and will not make a copy.
    147      *
    148      * @param <T> the type of elements held by the node
    149      * @param c the collection
    150      * @return a node holding a collection
    151      */
    152     static <T> Node<T> node(Collection<T> c) {
    153         return new CollectionNode<>(c);
    154     }
    155 
    156     /**
    157      * Produces a {@link Node.Builder}.
    158      *
    159      * @param exactSizeIfKnown -1 if a variable size builder is requested,
    160      * otherwise the exact capacity desired.  A fixed capacity builder will
    161      * fail if the wrong number of elements are added to the builder.
    162      * @param generator the array factory
    163      * @param <T> the type of elements of the node builder
    164      * @return a {@code Node.Builder}
    165      */
    166     static <T> Node.Builder<T> builder(long exactSizeIfKnown, IntFunction<T[]> generator) {
    167         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
    168                ? new FixedNodeBuilder<>(exactSizeIfKnown, generator)
    169                : builder();
    170     }
    171 
    172     /**
    173      * Produces a variable size @{link Node.Builder}.
    174      *
    175      * @param <T> the type of elements of the node builder
    176      * @return a {@code Node.Builder}
    177      */
    178     static <T> Node.Builder<T> builder() {
    179         return new SpinedNodeBuilder<>();
    180     }
    181 
    182     // Int nodes
    183 
    184     /**
    185      * Produces a {@link Node.OfInt} describing an int[] array.
    186      *
    187      * <p>The node will hold a reference to the array and will not make a copy.
    188      *
    189      * @param array the array
    190      * @return a node holding an array
    191      */
    192     static Node.OfInt node(int[] array) {
    193         return new IntArrayNode(array);
    194     }
    195 
    196     /**
    197      * Produces a {@link Node.Builder.OfInt}.
    198      *
    199      * @param exactSizeIfKnown -1 if a variable size builder is requested,
    200      * otherwise the exact capacity desired.  A fixed capacity builder will
    201      * fail if the wrong number of elements are added to the builder.
    202      * @return a {@code Node.Builder.OfInt}
    203      */
    204     static Node.Builder.OfInt intBuilder(long exactSizeIfKnown) {
    205         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
    206                ? new IntFixedNodeBuilder(exactSizeIfKnown)
    207                : intBuilder();
    208     }
    209 
    210     /**
    211      * Produces a variable size @{link Node.Builder.OfInt}.
    212      *
    213      * @return a {@code Node.Builder.OfInt}
    214      */
    215     static Node.Builder.OfInt intBuilder() {
    216         return new IntSpinedNodeBuilder();
    217     }
    218 
    219     // Long nodes
    220 
    221     /**
    222      * Produces a {@link Node.OfLong} describing a long[] array.
    223      * <p>
    224      * The node will hold a reference to the array and will not make a copy.
    225      *
    226      * @param array the array
    227      * @return a node holding an array
    228      */
    229     static Node.OfLong node(final long[] array) {
    230         return new LongArrayNode(array);
    231     }
    232 
    233     /**
    234      * Produces a {@link Node.Builder.OfLong}.
    235      *
    236      * @param exactSizeIfKnown -1 if a variable size builder is requested,
    237      * otherwise the exact capacity desired.  A fixed capacity builder will
    238      * fail if the wrong number of elements are added to the builder.
    239      * @return a {@code Node.Builder.OfLong}
    240      */
    241     static Node.Builder.OfLong longBuilder(long exactSizeIfKnown) {
    242         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
    243                ? new LongFixedNodeBuilder(exactSizeIfKnown)
    244                : longBuilder();
    245     }
    246 
    247     /**
    248      * Produces a variable size @{link Node.Builder.OfLong}.
    249      *
    250      * @return a {@code Node.Builder.OfLong}
    251      */
    252     static Node.Builder.OfLong longBuilder() {
    253         return new LongSpinedNodeBuilder();
    254     }
    255 
    256     // Double nodes
    257 
    258     /**
    259      * Produces a {@link Node.OfDouble} describing a double[] array.
    260      *
    261      * <p>The node will hold a reference to the array and will not make a copy.
    262      *
    263      * @param array the array
    264      * @return a node holding an array
    265      */
    266     static Node.OfDouble node(final double[] array) {
    267         return new DoubleArrayNode(array);
    268     }
    269 
    270     /**
    271      * Produces a {@link Node.Builder.OfDouble}.
    272      *
    273      * @param exactSizeIfKnown -1 if a variable size builder is requested,
    274      * otherwise the exact capacity desired.  A fixed capacity builder will
    275      * fail if the wrong number of elements are added to the builder.
    276      * @return a {@code Node.Builder.OfDouble}
    277      */
    278     static Node.Builder.OfDouble doubleBuilder(long exactSizeIfKnown) {
    279         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
    280                ? new DoubleFixedNodeBuilder(exactSizeIfKnown)
    281                : doubleBuilder();
    282     }
    283 
    284     /**
    285      * Produces a variable size @{link Node.Builder.OfDouble}.
    286      *
    287      * @return a {@code Node.Builder.OfDouble}
    288      */
    289     static Node.Builder.OfDouble doubleBuilder() {
    290         return new DoubleSpinedNodeBuilder();
    291     }
    292 
    293     // Parallel evaluation of pipelines to nodes
    294 
    295     /**
    296      * Collect, in parallel, elements output from a pipeline and describe those
    297      * elements with a {@link Node}.
    298      *
    299      * @implSpec
    300      * If the exact size of the output from the pipeline is known and the source
    301      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
    302      * then a flat {@link Node} will be returned whose content is an array,
    303      * since the size is known the array can be constructed in advance and
    304      * output elements can be placed into the array concurrently by leaf
    305      * tasks at the correct offsets.  If the exact size is not known, output
    306      * elements are collected into a conc-node whose shape mirrors that
    307      * of the computation. This conc-node can then be flattened in
    308      * parallel to produce a flat {@code Node} if desired.
    309      *
    310      * @param helper the pipeline helper describing the pipeline
    311      * @param flattenTree whether a conc node should be flattened into a node
    312      *                    describing an array before returning
    313      * @param generator the array generator
    314      * @return a {@link Node} describing the output elements
    315      */
    316     public static <P_IN, P_OUT> Node<P_OUT> collect(PipelineHelper<P_OUT> helper,
    317                                                     Spliterator<P_IN> spliterator,
    318                                                     boolean flattenTree,
    319                                                     IntFunction<P_OUT[]> generator) {
    320         long size = helper.exactOutputSizeIfKnown(spliterator);
    321         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
    322             if (size >= MAX_ARRAY_SIZE)
    323                 throw new IllegalArgumentException(BAD_SIZE);
    324             P_OUT[] array = generator.apply((int) size);
    325             new SizedCollectorTask.OfRef<>(spliterator, helper, array).invoke();
    326             return node(array);
    327         } else {
    328             Node<P_OUT> node = new CollectorTask.OfRef<>(helper, generator, spliterator).invoke();
    329             return flattenTree ? flatten(node, generator) : node;
    330         }
    331     }
    332 
    333     /**
    334      * Collect, in parallel, elements output from an int-valued pipeline and
    335      * describe those elements with a {@link Node.OfInt}.
    336      *
    337      * @implSpec
    338      * If the exact size of the output from the pipeline is known and the source
    339      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
    340      * then a flat {@link Node} will be returned whose content is an array,
    341      * since the size is known the array can be constructed in advance and
    342      * output elements can be placed into the array concurrently by leaf
    343      * tasks at the correct offsets.  If the exact size is not known, output
    344      * elements are collected into a conc-node whose shape mirrors that
    345      * of the computation. This conc-node can then be flattened in
    346      * parallel to produce a flat {@code Node.OfInt} if desired.
    347      *
    348      * @param  the type of elements from the source Spliterator
    349      * @param helper the pipeline helper describing the pipeline
    350      * @param flattenTree whether a conc node should be flattened into a node
    351      *                    describing an array before returning
    352      * @return a {@link Node.OfInt} describing the output elements
    353      */
    354     public static <P_IN> Node.OfInt collectInt(PipelineHelper<Integer> helper,
    355                                                Spliterator<P_IN> spliterator,
    356                                                boolean flattenTree) {
    357         long size = helper.exactOutputSizeIfKnown(spliterator);
    358         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
    359             if (size >= MAX_ARRAY_SIZE)
    360                 throw new IllegalArgumentException(BAD_SIZE);
    361             int[] array = new int[(int) size];
    362             new SizedCollectorTask.OfInt<>(spliterator, helper, array).invoke();
    363             return node(array);
    364         }
    365         else {
    366             Node.OfInt node = new CollectorTask.OfInt<>(helper, spliterator).invoke();
    367             return flattenTree ? flattenInt(node) : node;
    368         }
    369     }
    370 
    371     /**
    372      * Collect, in parallel, elements output from a long-valued pipeline and
    373      * describe those elements with a {@link Node.OfLong}.
    374      *
    375      * @implSpec
    376      * If the exact size of the output from the pipeline is known and the source
    377      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
    378      * then a flat {@link Node} will be returned whose content is an array,
    379      * since the size is known the array can be constructed in advance and
    380      * output elements can be placed into the array concurrently by leaf
    381      * tasks at the correct offsets.  If the exact size is not known, output
    382      * elements are collected into a conc-node whose shape mirrors that
    383      * of the computation. This conc-node can then be flattened in
    384      * parallel to produce a flat {@code Node.OfLong} if desired.
    385      *
    386      * @param  the type of elements from the source Spliterator
    387      * @param helper the pipeline helper describing the pipeline
    388      * @param flattenTree whether a conc node should be flattened into a node
    389      *                    describing an array before returning
    390      * @return a {@link Node.OfLong} describing the output elements
    391      */
    392     public static <P_IN> Node.OfLong collectLong(PipelineHelper<Long> helper,
    393                                                  Spliterator<P_IN> spliterator,
    394                                                  boolean flattenTree) {
    395         long size = helper.exactOutputSizeIfKnown(spliterator);
    396         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
    397             if (size >= MAX_ARRAY_SIZE)
    398                 throw new IllegalArgumentException(BAD_SIZE);
    399             long[] array = new long[(int) size];
    400             new SizedCollectorTask.OfLong<>(spliterator, helper, array).invoke();
    401             return node(array);
    402         }
    403         else {
    404             Node.OfLong node = new CollectorTask.OfLong<>(helper, spliterator).invoke();
    405             return flattenTree ? flattenLong(node) : node;
    406         }
    407     }
    408 
    409     /**
    410      * Collect, in parallel, elements output from n double-valued pipeline and
    411      * describe those elements with a {@link Node.OfDouble}.
    412      *
    413      * @implSpec
    414      * If the exact size of the output from the pipeline is known and the source
    415      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
    416      * then a flat {@link Node} will be returned whose content is an array,
    417      * since the size is known the array can be constructed in advance and
    418      * output elements can be placed into the array concurrently by leaf
    419      * tasks at the correct offsets.  If the exact size is not known, output
    420      * elements are collected into a conc-node whose shape mirrors that
    421      * of the computation. This conc-node can then be flattened in
    422      * parallel to produce a flat {@code Node.OfDouble} if desired.
    423      *
    424      * @param  the type of elements from the source Spliterator
    425      * @param helper the pipeline helper describing the pipeline
    426      * @param flattenTree whether a conc node should be flattened into a node
    427      *                    describing an array before returning
    428      * @return a {@link Node.OfDouble} describing the output elements
    429      */
    430     public static <P_IN> Node.OfDouble collectDouble(PipelineHelper<Double> helper,
    431                                                      Spliterator<P_IN> spliterator,
    432                                                      boolean flattenTree) {
    433         long size = helper.exactOutputSizeIfKnown(spliterator);
    434         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
    435             if (size >= MAX_ARRAY_SIZE)
    436                 throw new IllegalArgumentException(BAD_SIZE);
    437             double[] array = new double[(int) size];
    438             new SizedCollectorTask.OfDouble<>(spliterator, helper, array).invoke();
    439             return node(array);
    440         }
    441         else {
    442             Node.OfDouble node = new CollectorTask.OfDouble<>(helper, spliterator).invoke();
    443             return flattenTree ? flattenDouble(node) : node;
    444         }
    445     }
    446 
    447     // Parallel flattening of nodes
    448 
    449     /**
    450      * Flatten, in parallel, a {@link Node}.  A flattened node is one that has
    451      * no children.  If the node is already flat, it is simply returned.
    452      *
    453      * @implSpec
    454      * If a new node is to be created, the generator is used to create an array
    455      * whose length is {@link Node#count()}.  Then the node tree is traversed
    456      * and leaf node elements are placed in the array concurrently by leaf tasks
    457      * at the correct offsets.
    458      *
    459      * @param <T> type of elements contained by the node
    460      * @param node the node to flatten
    461      * @param generator the array factory used to create array instances
    462      * @return a flat {@code Node}
    463      */
    464     public static <T> Node<T> flatten(Node<T> node, IntFunction<T[]> generator) {
    465         if (node.getChildCount() > 0) {
    466             long size = node.count();
    467             if (size >= MAX_ARRAY_SIZE)
    468                 throw new IllegalArgumentException(BAD_SIZE);
    469             T[] array = generator.apply((int) size);
    470             new ToArrayTask.OfRef<>(node, array, 0).invoke();
    471             return node(array);
    472         } else {
    473             return node;
    474         }
    475     }
    476 
    477     /**
    478      * Flatten, in parallel, a {@link Node.OfInt}.  A flattened node is one that
    479      * has no children.  If the node is already flat, it is simply returned.
    480      *
    481      * @implSpec
    482      * If a new node is to be created, a new int[] array is created whose length
    483      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
    484      * elements are placed in the array concurrently by leaf tasks at the
    485      * correct offsets.
    486      *
    487      * @param node the node to flatten
    488      * @return a flat {@code Node.OfInt}
    489      */
    490     public static Node.OfInt flattenInt(Node.OfInt node) {
    491         if (node.getChildCount() > 0) {
    492             long size = node.count();
    493             if (size >= MAX_ARRAY_SIZE)
    494                 throw new IllegalArgumentException(BAD_SIZE);
    495             int[] array = new int[(int) size];
    496             new ToArrayTask.OfInt(node, array, 0).invoke();
    497             return node(array);
    498         } else {
    499             return node;
    500         }
    501     }
    502 
    503     /**
    504      * Flatten, in parallel, a {@link Node.OfLong}.  A flattened node is one that
    505      * has no children.  If the node is already flat, it is simply returned.
    506      *
    507      * @implSpec
    508      * If a new node is to be created, a new long[] array is created whose length
    509      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
    510      * elements are placed in the array concurrently by leaf tasks at the
    511      * correct offsets.
    512      *
    513      * @param node the node to flatten
    514      * @return a flat {@code Node.OfLong}
    515      */
    516     public static Node.OfLong flattenLong(Node.OfLong node) {
    517         if (node.getChildCount() > 0) {
    518             long size = node.count();
    519             if (size >= MAX_ARRAY_SIZE)
    520                 throw new IllegalArgumentException(BAD_SIZE);
    521             long[] array = new long[(int) size];
    522             new ToArrayTask.OfLong(node, array, 0).invoke();
    523             return node(array);
    524         } else {
    525             return node;
    526         }
    527     }
    528 
    529     /**
    530      * Flatten, in parallel, a {@link Node.OfDouble}.  A flattened node is one that
    531      * has no children.  If the node is already flat, it is simply returned.
    532      *
    533      * @implSpec
    534      * If a new node is to be created, a new double[] array is created whose length
    535      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
    536      * elements are placed in the array concurrently by leaf tasks at the
    537      * correct offsets.
    538      *
    539      * @param node the node to flatten
    540      * @return a flat {@code Node.OfDouble}
    541      */
    542     public static Node.OfDouble flattenDouble(Node.OfDouble node) {
    543         if (node.getChildCount() > 0) {
    544             long size = node.count();
    545             if (size >= MAX_ARRAY_SIZE)
    546                 throw new IllegalArgumentException(BAD_SIZE);
    547             double[] array = new double[(int) size];
    548             new ToArrayTask.OfDouble(node, array, 0).invoke();
    549             return node(array);
    550         } else {
    551             return node;
    552         }
    553     }
    554 
    555     // Implementations
    556 
    557     private static abstract class EmptyNode<T, T_ARR, T_CONS> implements Node<T> {
    558         EmptyNode() { }
    559 
    560         @Override
    561         public T[] asArray(IntFunction<T[]> generator) {
    562             return generator.apply(0);
    563         }
    564 
    565         public void copyInto(T_ARR array, int offset) { }
    566 
    567         @Override
    568         public long count() {
    569             return 0;
    570         }
    571 
    572         public void forEach(T_CONS consumer) { }
    573 
    574         private static class OfRef<T> extends EmptyNode<T, T[], Consumer<? super T>> {
    575             private OfRef() {
    576                 super();
    577             }
    578 
    579             @Override
    580             public Spliterator<T> spliterator() {
    581                 return Spliterators.emptySpliterator();
    582             }
    583         }
    584 
    585         private static final class OfInt
    586                 extends EmptyNode<Integer, int[], IntConsumer>
    587                 implements Node.OfInt {
    588 
    589             OfInt() { } // Avoid creation of special accessor
    590 
    591             @Override
    592             public Spliterator.OfInt spliterator() {
    593                 return Spliterators.emptyIntSpliterator();
    594             }
    595 
    596             @Override
    597             public int[] asPrimitiveArray() {
    598                 return EMPTY_INT_ARRAY;
    599             }
    600         }
    601 
    602         private static final class OfLong
    603                 extends EmptyNode<Long, long[], LongConsumer>
    604                 implements Node.OfLong {
    605 
    606             OfLong() { } // Avoid creation of special accessor
    607 
    608             @Override
    609             public Spliterator.OfLong spliterator() {
    610                 return Spliterators.emptyLongSpliterator();
    611             }
    612 
    613             @Override
    614             public long[] asPrimitiveArray() {
    615                 return EMPTY_LONG_ARRAY;
    616             }
    617         }
    618 
    619         private static final class OfDouble
    620                 extends EmptyNode<Double, double[], DoubleConsumer>
    621                 implements Node.OfDouble {
    622 
    623             OfDouble() { } // Avoid creation of special accessor
    624 
    625             @Override
    626             public Spliterator.OfDouble spliterator() {
    627                 return Spliterators.emptyDoubleSpliterator();
    628             }
    629 
    630             @Override
    631             public double[] asPrimitiveArray() {
    632                 return EMPTY_DOUBLE_ARRAY;
    633             }
    634         }
    635     }
    636 
    637     /** Node class for a reference array */
    638     private static class ArrayNode<T> implements Node<T> {
    639         final T[] array;
    640         int curSize;
    641 
    642         @SuppressWarnings("unchecked")
    643         ArrayNode(long size, IntFunction<T[]> generator) {
    644             if (size >= MAX_ARRAY_SIZE)
    645                 throw new IllegalArgumentException(BAD_SIZE);
    646             this.array = generator.apply((int) size);
    647             this.curSize = 0;
    648         }
    649 
    650         ArrayNode(T[] array) {
    651             this.array = array;
    652             this.curSize = array.length;
    653         }
    654 
    655         // Node
    656 
    657         @Override
    658         public Spliterator<T> spliterator() {
    659             return Arrays.spliterator(array, 0, curSize);
    660         }
    661 
    662         @Override
    663         public void copyInto(T[] dest, int destOffset) {
    664             System.arraycopy(array, 0, dest, destOffset, curSize);
    665         }
    666 
    667         @Override
    668         public T[] asArray(IntFunction<T[]> generator) {
    669             if (array.length == curSize) {
    670                 return array;
    671             } else {
    672                 throw new IllegalStateException();
    673             }
    674         }
    675 
    676         @Override
    677         public long count() {
    678             return curSize;
    679         }
    680 
    681         @Override
    682         public void forEach(Consumer<? super T> consumer) {
    683             for (int i = 0; i < curSize; i++) {
    684                 consumer.accept(array[i]);
    685             }
    686         }
    687 
    688         //
    689 
    690         @Override
    691         public String toString() {
    692             return String.format("ArrayNode[%d][%s]",
    693                                  array.length - curSize, Arrays.toString(array));
    694         }
    695     }
    696 
    697     /** Node class for a Collection */
    698     private static final class CollectionNode<T> implements Node<T> {
    699         private final Collection<T> c;
    700 
    701         CollectionNode(Collection<T> c) {
    702             this.c = c;
    703         }
    704 
    705         // Node
    706 
    707         @Override
    708         public Spliterator<T> spliterator() {
    709             return c.stream().spliterator();
    710         }
    711 
    712         @Override
    713         public void copyInto(T[] array, int offset) {
    714             for (T t : c)
    715                 array[offset++] = t;
    716         }
    717 
    718         @Override
    719         @SuppressWarnings("unchecked")
    720         public T[] asArray(IntFunction<T[]> generator) {
    721             return c.toArray(generator.apply(c.size()));
    722         }
    723 
    724         @Override
    725         public long count() {
    726             return c.size();
    727         }
    728 
    729         @Override
    730         public void forEach(Consumer<? super T> consumer) {
    731             c.forEach(consumer);
    732         }
    733 
    734         //
    735 
    736         @Override
    737         public String toString() {
    738             return String.format("CollectionNode[%d][%s]", c.size(), c);
    739         }
    740     }
    741 
    742     /**
    743      * Node class for an internal node with two or more children
    744      */
    745     private static abstract class AbstractConcNode<T, T_NODE extends Node<T>> implements Node<T> {
    746         protected final T_NODE left;
    747         protected final T_NODE right;
    748         private final long size;
    749 
    750         AbstractConcNode(T_NODE left, T_NODE right) {
    751             this.left = left;
    752             this.right = right;
    753             // The Node count will be required when the Node spliterator is
    754             // obtained and it is cheaper to aggressively calculate bottom up
    755             // as the tree is built rather than later on from the top down
    756             // traversing the tree
    757             this.size = left.count() + right.count();
    758         }
    759 
    760         @Override
    761         public int getChildCount() {
    762             return 2;
    763         }
    764 
    765         @Override
    766         public T_NODE getChild(int i) {
    767             if (i == 0) return left;
    768             if (i == 1) return right;
    769             throw new IndexOutOfBoundsException();
    770         }
    771 
    772         @Override
    773         public long count() {
    774             return size;
    775         }
    776     }
    777 
    778     static final class ConcNode<T>
    779             extends AbstractConcNode<T, Node<T>>
    780             implements Node<T> {
    781 
    782         ConcNode(Node<T> left, Node<T> right) {
    783             super(left, right);
    784         }
    785 
    786         @Override
    787         public Spliterator<T> spliterator() {
    788             return new Nodes.InternalNodeSpliterator.OfRef<>(this);
    789         }
    790 
    791         @Override
    792         public void copyInto(T[] array, int offset) {
    793             Objects.requireNonNull(array);
    794             left.copyInto(array, offset);
    795             // Cast to int is safe since it is the callers responsibility to
    796             // ensure that there is sufficient room in the array
    797             right.copyInto(array, offset + (int) left.count());
    798         }
    799 
    800         @Override
    801         public T[] asArray(IntFunction<T[]> generator) {
    802             long size = count();
    803             if (size >= MAX_ARRAY_SIZE)
    804                 throw new IllegalArgumentException(BAD_SIZE);
    805             T[] array = generator.apply((int) size);
    806             copyInto(array, 0);
    807             return array;
    808         }
    809 
    810         @Override
    811         public void forEach(Consumer<? super T> consumer) {
    812             left.forEach(consumer);
    813             right.forEach(consumer);
    814         }
    815 
    816         @Override
    817         public Node<T> truncate(long from, long to, IntFunction<T[]> generator) {
    818             if (from == 0 && to == count())
    819                 return this;
    820             long leftCount = left.count();
    821             if (from >= leftCount)
    822                 return right.truncate(from - leftCount, to - leftCount, generator);
    823             else if (to <= leftCount)
    824                 return left.truncate(from, to, generator);
    825             else {
    826                 return Nodes.conc(getShape(), left.truncate(from, leftCount, generator),
    827                                   right.truncate(0, to - leftCount, generator));
    828             }
    829         }
    830 
    831         @Override
    832         public String toString() {
    833             if (count() < 32) {
    834                 return String.format("ConcNode[%s.%s]", left, right);
    835             } else {
    836                 return String.format("ConcNode[size=%d]", count());
    837             }
    838         }
    839 
    840         private abstract static class OfPrimitive<E, T_CONS, T_ARR,
    841                                                   T_SPLITR extends Spliterator.OfPrimitive<E, T_CONS, T_SPLITR>,
    842                                                   T_NODE extends Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE>>
    843                 extends AbstractConcNode<E, T_NODE>
    844                 implements Node.OfPrimitive<E, T_CONS, T_ARR, T_SPLITR, T_NODE> {
    845 
    846             OfPrimitive(T_NODE left, T_NODE right) {
    847                 super(left, right);
    848             }
    849 
    850             @Override
    851             public void forEach(T_CONS consumer) {
    852                 left.forEach(consumer);
    853                 right.forEach(consumer);
    854             }
    855 
    856             @Override
    857             public void copyInto(T_ARR array, int offset) {
    858                 left.copyInto(array, offset);
    859                 // Cast to int is safe since it is the callers responsibility to
    860                 // ensure that there is sufficient room in the array
    861                 right.copyInto(array, offset + (int) left.count());
    862             }
    863 
    864             @Override
    865             public T_ARR asPrimitiveArray() {
    866                 long size = count();
    867                 if (size >= MAX_ARRAY_SIZE)
    868                     throw new IllegalArgumentException(BAD_SIZE);
    869                 T_ARR array = newArray((int) size);
    870                 copyInto(array, 0);
    871                 return array;
    872             }
    873 
    874             @Override
    875             public String toString() {
    876                 if (count() < 32)
    877                     return String.format("%s[%s.%s]", this.getClass().getName(), left, right);
    878                 else
    879                     return String.format("%s[size=%d]", this.getClass().getName(), count());
    880             }
    881         }
    882 
    883         static final class OfInt
    884                 extends ConcNode.OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt>
    885                 implements Node.OfInt {
    886 
    887             OfInt(Node.OfInt left, Node.OfInt right) {
    888                 super(left, right);
    889             }
    890 
    891             @Override
    892             public Spliterator.OfInt spliterator() {
    893                 return new InternalNodeSpliterator.OfInt(this);
    894             }
    895         }
    896 
    897         static final class OfLong
    898                 extends ConcNode.OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong>
    899                 implements Node.OfLong {
    900 
    901             OfLong(Node.OfLong left, Node.OfLong right) {
    902                 super(left, right);
    903             }
    904 
    905             @Override
    906             public Spliterator.OfLong spliterator() {
    907                 return new InternalNodeSpliterator.OfLong(this);
    908             }
    909         }
    910 
    911         static final class OfDouble
    912                 extends ConcNode.OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble>
    913                 implements Node.OfDouble {
    914 
    915             OfDouble(Node.OfDouble left, Node.OfDouble right) {
    916                 super(left, right);
    917             }
    918 
    919             @Override
    920             public Spliterator.OfDouble spliterator() {
    921                 return new InternalNodeSpliterator.OfDouble(this);
    922             }
    923         }
    924     }
    925 
    926     /** Abstract class for spliterator for all internal node classes */
    927     private static abstract class InternalNodeSpliterator<T,
    928                                                           S extends Spliterator<T>,
    929                                                           N extends Node<T>>
    930             implements Spliterator<T> {
    931         // Node we are pointing to
    932         // null if full traversal has occurred
    933         N curNode;
    934 
    935         // next child of curNode to consume
    936         int curChildIndex;
    937 
    938         // The spliterator of the curNode if that node is last and has no children.
    939         // This spliterator will be delegated to for splitting and traversing.
    940         // null if curNode has children
    941         S lastNodeSpliterator;
    942 
    943         // spliterator used while traversing with tryAdvance
    944         // null if no partial traversal has occurred
    945         S tryAdvanceSpliterator;
    946 
    947         // node stack used when traversing to search and find leaf nodes
    948         // null if no partial traversal has occurred
    949         Deque<N> tryAdvanceStack;
    950 
    951         InternalNodeSpliterator(N curNode) {
    952             this.curNode = curNode;
    953         }
    954 
    955         /**
    956          * Initiate a stack containing, in left-to-right order, the child nodes
    957          * covered by this spliterator
    958          */
    959         @SuppressWarnings("unchecked")
    960         protected final Deque<N> initStack() {
    961             // Bias size to the case where leaf nodes are close to this node
    962             // 8 is the minimum initial capacity for the ArrayDeque implementation
    963             Deque<N> stack = new ArrayDeque<>(8);
    964             for (int i = curNode.getChildCount() - 1; i >= curChildIndex; i--)
    965                 stack.addFirst((N) curNode.getChild(i));
    966             return stack;
    967         }
    968 
    969         /**
    970          * Depth first search, in left-to-right order, of the node tree, using
    971          * an explicit stack, to find the next non-empty leaf node.
    972          */
    973         @SuppressWarnings("unchecked")
    974         protected final N findNextLeafNode(Deque<N> stack) {
    975             N n = null;
    976             while ((n = stack.pollFirst()) != null) {
    977                 if (n.getChildCount() == 0) {
    978                     if (n.count() > 0)
    979                         return n;
    980                 } else {
    981                     for (int i = n.getChildCount() - 1; i >= 0; i--)
    982                         stack.addFirst((N) n.getChild(i));
    983                 }
    984             }
    985 
    986             return null;
    987         }
    988 
    989         @SuppressWarnings("unchecked")
    990         protected final boolean initTryAdvance() {
    991             if (curNode == null)
    992                 return false;
    993 
    994             if (tryAdvanceSpliterator == null) {
    995                 if (lastNodeSpliterator == null) {
    996                     // Initiate the node stack
    997                     tryAdvanceStack = initStack();
    998                     N leaf = findNextLeafNode(tryAdvanceStack);
    999                     if (leaf != null)
   1000                         tryAdvanceSpliterator = (S) leaf.spliterator();
   1001                     else {
   1002                         // A non-empty leaf node was not found
   1003                         // No elements to traverse
   1004                         curNode = null;
   1005                         return false;
   1006                     }
   1007                 }
   1008                 else
   1009                     tryAdvanceSpliterator = lastNodeSpliterator;
   1010             }
   1011             return true;
   1012         }
   1013 
   1014         @Override
   1015         @SuppressWarnings("unchecked")
   1016         public final S trySplit() {
   1017             if (curNode == null || tryAdvanceSpliterator != null)
   1018                 return null; // Cannot split if fully or partially traversed
   1019             else if (lastNodeSpliterator != null)
   1020                 return (S) lastNodeSpliterator.trySplit();
   1021             else if (curChildIndex < curNode.getChildCount() - 1)
   1022                 return (S) curNode.getChild(curChildIndex++).spliterator();
   1023             else {
   1024                 curNode = (N) curNode.getChild(curChildIndex);
   1025                 if (curNode.getChildCount() == 0) {
   1026                     lastNodeSpliterator = (S) curNode.spliterator();
   1027                     return (S) lastNodeSpliterator.trySplit();
   1028                 }
   1029                 else {
   1030                     curChildIndex = 0;
   1031                     return (S) curNode.getChild(curChildIndex++).spliterator();
   1032                 }
   1033             }
   1034         }
   1035 
   1036         @Override
   1037         public final long estimateSize() {
   1038             if (curNode == null)
   1039                 return 0;
   1040 
   1041             // Will not reflect the effects of partial traversal.
   1042             // This is compliant with the specification
   1043             if (lastNodeSpliterator != null)
   1044                 return lastNodeSpliterator.estimateSize();
   1045             else {
   1046                 long size = 0;
   1047                 for (int i = curChildIndex; i < curNode.getChildCount(); i++)
   1048                     size += curNode.getChild(i).count();
   1049                 return size;
   1050             }
   1051         }
   1052 
   1053         @Override
   1054         public final int characteristics() {
   1055             return Spliterator.SIZED;
   1056         }
   1057 
   1058         private static final class OfRef<T>
   1059                 extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>> {
   1060 
   1061             OfRef(Node<T> curNode) {
   1062                 super(curNode);
   1063             }
   1064 
   1065             @Override
   1066             public boolean tryAdvance(Consumer<? super T> consumer) {
   1067                 if (!initTryAdvance())
   1068                     return false;
   1069 
   1070                 boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer);
   1071                 if (!hasNext) {
   1072                     if (lastNodeSpliterator == null) {
   1073                         // Advance to the spliterator of the next non-empty leaf node
   1074                         Node<T> leaf = findNextLeafNode(tryAdvanceStack);
   1075                         if (leaf != null) {
   1076                             tryAdvanceSpliterator = leaf.spliterator();
   1077                             // Since the node is not-empty the spliterator can be advanced
   1078                             return tryAdvanceSpliterator.tryAdvance(consumer);
   1079                         }
   1080                     }
   1081                     // No more elements to traverse
   1082                     curNode = null;
   1083                 }
   1084                 return hasNext;
   1085             }
   1086 
   1087             @Override
   1088             public void forEachRemaining(Consumer<? super T> consumer) {
   1089                 if (curNode == null)
   1090                     return;
   1091 
   1092                 if (tryAdvanceSpliterator == null) {
   1093                     if (lastNodeSpliterator == null) {
   1094                         Deque<Node<T>> stack = initStack();
   1095                         Node<T> leaf;
   1096                         while ((leaf = findNextLeafNode(stack)) != null) {
   1097                             leaf.forEach(consumer);
   1098                         }
   1099                         curNode = null;
   1100                     }
   1101                     else
   1102                         lastNodeSpliterator.forEachRemaining(consumer);
   1103                 }
   1104                 else
   1105                     while(tryAdvance(consumer)) { }
   1106             }
   1107         }
   1108 
   1109         private static abstract class OfPrimitive<T, T_CONS, T_ARR,
   1110                                                   T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>,
   1111                                                   N extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, N>>
   1112                 extends InternalNodeSpliterator<T, T_SPLITR, N>
   1113                 implements Spliterator.OfPrimitive<T, T_CONS, T_SPLITR> {
   1114 
   1115             OfPrimitive(N cur) {
   1116                 super(cur);
   1117             }
   1118 
   1119             @Override
   1120             public boolean tryAdvance(T_CONS consumer) {
   1121                 if (!initTryAdvance())
   1122                     return false;
   1123 
   1124                 boolean hasNext = tryAdvanceSpliterator.tryAdvance(consumer);
   1125                 if (!hasNext) {
   1126                     if (lastNodeSpliterator == null) {
   1127                         // Advance to the spliterator of the next non-empty leaf node
   1128                         N leaf = findNextLeafNode(tryAdvanceStack);
   1129                         if (leaf != null) {
   1130                             tryAdvanceSpliterator = leaf.spliterator();
   1131                             // Since the node is not-empty the spliterator can be advanced
   1132                             return tryAdvanceSpliterator.tryAdvance(consumer);
   1133                         }
   1134                     }
   1135                     // No more elements to traverse
   1136                     curNode = null;
   1137                 }
   1138                 return hasNext;
   1139             }
   1140 
   1141             @Override
   1142             public void forEachRemaining(T_CONS consumer) {
   1143                 if (curNode == null)
   1144                     return;
   1145 
   1146                 if (tryAdvanceSpliterator == null) {
   1147                     if (lastNodeSpliterator == null) {
   1148                         Deque<N> stack = initStack();
   1149                         N leaf;
   1150                         while ((leaf = findNextLeafNode(stack)) != null) {
   1151                             leaf.forEach(consumer);
   1152                         }
   1153                         curNode = null;
   1154                     }
   1155                     else
   1156                         lastNodeSpliterator.forEachRemaining(consumer);
   1157                 }
   1158                 else
   1159                     while(tryAdvance(consumer)) { }
   1160             }
   1161         }
   1162 
   1163         private static final class OfInt
   1164                 extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt>
   1165                 implements Spliterator.OfInt {
   1166 
   1167             OfInt(Node.OfInt cur) {
   1168                 super(cur);
   1169             }
   1170         }
   1171 
   1172         private static final class OfLong
   1173                 extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong>
   1174                 implements Spliterator.OfLong {
   1175 
   1176             OfLong(Node.OfLong cur) {
   1177                 super(cur);
   1178             }
   1179         }
   1180 
   1181         private static final class OfDouble
   1182                 extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble>
   1183                 implements Spliterator.OfDouble {
   1184 
   1185             OfDouble(Node.OfDouble cur) {
   1186                 super(cur);
   1187             }
   1188         }
   1189     }
   1190 
   1191     /**
   1192      * Fixed-sized builder class for reference nodes
   1193      */
   1194     private static final class FixedNodeBuilder<T>
   1195             extends ArrayNode<T>
   1196             implements Node.Builder<T> {
   1197 
   1198         FixedNodeBuilder(long size, IntFunction<T[]> generator) {
   1199             super(size, generator);
   1200             assert size < MAX_ARRAY_SIZE;
   1201         }
   1202 
   1203         @Override
   1204         public Node<T> build() {
   1205             if (curSize < array.length)
   1206                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
   1207                                                               curSize, array.length));
   1208             return this;
   1209         }
   1210 
   1211         @Override
   1212         public void begin(long size) {
   1213             if (size != array.length)
   1214                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
   1215                                                               size, array.length));
   1216             curSize = 0;
   1217         }
   1218 
   1219         @Override
   1220         public void accept(T t) {
   1221             if (curSize < array.length) {
   1222                 array[curSize++] = t;
   1223             } else {
   1224                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
   1225                                                               array.length));
   1226             }
   1227         }
   1228 
   1229         @Override
   1230         public void end() {
   1231             if (curSize < array.length)
   1232                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
   1233                                                               curSize, array.length));
   1234         }
   1235 
   1236         @Override
   1237         public String toString() {
   1238             return String.format("FixedNodeBuilder[%d][%s]",
   1239                                  array.length - curSize, Arrays.toString(array));
   1240         }
   1241     }
   1242 
   1243     /**
   1244      * Variable-sized builder class for reference nodes
   1245      */
   1246     private static final class SpinedNodeBuilder<T>
   1247             extends SpinedBuffer<T>
   1248             implements Node<T>, Node.Builder<T> {
   1249         private boolean building = false;
   1250 
   1251         SpinedNodeBuilder() {} // Avoid creation of special accessor
   1252 
   1253         @Override
   1254         public Spliterator<T> spliterator() {
   1255             assert !building : "during building";
   1256             return super.spliterator();
   1257         }
   1258 
   1259         @Override
   1260         public void forEach(Consumer<? super T> consumer) {
   1261             assert !building : "during building";
   1262             super.forEach(consumer);
   1263         }
   1264 
   1265         //
   1266         @Override
   1267         public void begin(long size) {
   1268             assert !building : "was already building";
   1269             building = true;
   1270             clear();
   1271             ensureCapacity(size);
   1272         }
   1273 
   1274         @Override
   1275         public void accept(T t) {
   1276             assert building : "not building";
   1277             super.accept(t);
   1278         }
   1279 
   1280         @Override
   1281         public void end() {
   1282             assert building : "was not building";
   1283             building = false;
   1284             // @@@ check begin(size) and size
   1285         }
   1286 
   1287         @Override
   1288         public void copyInto(T[] array, int offset) {
   1289             assert !building : "during building";
   1290             super.copyInto(array, offset);
   1291         }
   1292 
   1293         @Override
   1294         public T[] asArray(IntFunction<T[]> arrayFactory) {
   1295             assert !building : "during building";
   1296             return super.asArray(arrayFactory);
   1297         }
   1298 
   1299         @Override
   1300         public Node<T> build() {
   1301             assert !building : "during building";
   1302             return this;
   1303         }
   1304     }
   1305 
   1306     //
   1307 
   1308     private static final int[] EMPTY_INT_ARRAY = new int[0];
   1309     private static final long[] EMPTY_LONG_ARRAY = new long[0];
   1310     private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
   1311 
   1312     private static class IntArrayNode implements Node.OfInt {
   1313         final int[] array;
   1314         int curSize;
   1315 
   1316         IntArrayNode(long size) {
   1317             if (size >= MAX_ARRAY_SIZE)
   1318                 throw new IllegalArgumentException(BAD_SIZE);
   1319             this.array = new int[(int) size];
   1320             this.curSize = 0;
   1321         }
   1322 
   1323         IntArrayNode(int[] array) {
   1324             this.array = array;
   1325             this.curSize = array.length;
   1326         }
   1327 
   1328         // Node
   1329 
   1330         @Override
   1331         public Spliterator.OfInt spliterator() {
   1332             return Arrays.spliterator(array, 0, curSize);
   1333         }
   1334 
   1335         @Override
   1336         public int[] asPrimitiveArray() {
   1337             if (array.length == curSize) {
   1338                 return array;
   1339             } else {
   1340                 return Arrays.copyOf(array, curSize);
   1341             }
   1342         }
   1343 
   1344         @Override
   1345         public void copyInto(int[] dest, int destOffset) {
   1346             System.arraycopy(array, 0, dest, destOffset, curSize);
   1347         }
   1348 
   1349         @Override
   1350         public long count() {
   1351             return curSize;
   1352         }
   1353 
   1354         @Override
   1355         public void forEach(IntConsumer consumer) {
   1356             for (int i = 0; i < curSize; i++) {
   1357                 consumer.accept(array[i]);
   1358             }
   1359         }
   1360 
   1361         @Override
   1362         public String toString() {
   1363             return String.format("IntArrayNode[%d][%s]",
   1364                                  array.length - curSize, Arrays.toString(array));
   1365         }
   1366     }
   1367 
   1368     private static class LongArrayNode implements Node.OfLong {
   1369         final long[] array;
   1370         int curSize;
   1371 
   1372         LongArrayNode(long size) {
   1373             if (size >= MAX_ARRAY_SIZE)
   1374                 throw new IllegalArgumentException(BAD_SIZE);
   1375             this.array = new long[(int) size];
   1376             this.curSize = 0;
   1377         }
   1378 
   1379         LongArrayNode(long[] array) {
   1380             this.array = array;
   1381             this.curSize = array.length;
   1382         }
   1383 
   1384         @Override
   1385         public Spliterator.OfLong spliterator() {
   1386             return Arrays.spliterator(array, 0, curSize);
   1387         }
   1388 
   1389         @Override
   1390         public long[] asPrimitiveArray() {
   1391             if (array.length == curSize) {
   1392                 return array;
   1393             } else {
   1394                 return Arrays.copyOf(array, curSize);
   1395             }
   1396         }
   1397 
   1398         @Override
   1399         public void copyInto(long[] dest, int destOffset) {
   1400             System.arraycopy(array, 0, dest, destOffset, curSize);
   1401         }
   1402 
   1403         @Override
   1404         public long count() {
   1405             return curSize;
   1406         }
   1407 
   1408         @Override
   1409         public void forEach(LongConsumer consumer) {
   1410             for (int i = 0; i < curSize; i++) {
   1411                 consumer.accept(array[i]);
   1412             }
   1413         }
   1414 
   1415         @Override
   1416         public String toString() {
   1417             return String.format("LongArrayNode[%d][%s]",
   1418                                  array.length - curSize, Arrays.toString(array));
   1419         }
   1420     }
   1421 
   1422     private static class DoubleArrayNode implements Node.OfDouble {
   1423         final double[] array;
   1424         int curSize;
   1425 
   1426         DoubleArrayNode(long size) {
   1427             if (size >= MAX_ARRAY_SIZE)
   1428                 throw new IllegalArgumentException(BAD_SIZE);
   1429             this.array = new double[(int) size];
   1430             this.curSize = 0;
   1431         }
   1432 
   1433         DoubleArrayNode(double[] array) {
   1434             this.array = array;
   1435             this.curSize = array.length;
   1436         }
   1437 
   1438         @Override
   1439         public Spliterator.OfDouble spliterator() {
   1440             return Arrays.spliterator(array, 0, curSize);
   1441         }
   1442 
   1443         @Override
   1444         public double[] asPrimitiveArray() {
   1445             if (array.length == curSize) {
   1446                 return array;
   1447             } else {
   1448                 return Arrays.copyOf(array, curSize);
   1449             }
   1450         }
   1451 
   1452         @Override
   1453         public void copyInto(double[] dest, int destOffset) {
   1454             System.arraycopy(array, 0, dest, destOffset, curSize);
   1455         }
   1456 
   1457         @Override
   1458         public long count() {
   1459             return curSize;
   1460         }
   1461 
   1462         @Override
   1463         public void forEach(DoubleConsumer consumer) {
   1464             for (int i = 0; i < curSize; i++) {
   1465                 consumer.accept(array[i]);
   1466             }
   1467         }
   1468 
   1469         @Override
   1470         public String toString() {
   1471             return String.format("DoubleArrayNode[%d][%s]",
   1472                                  array.length - curSize, Arrays.toString(array));
   1473         }
   1474     }
   1475 
   1476     private static final class IntFixedNodeBuilder
   1477             extends IntArrayNode
   1478             implements Node.Builder.OfInt {
   1479 
   1480         IntFixedNodeBuilder(long size) {
   1481             super(size);
   1482             assert size < MAX_ARRAY_SIZE;
   1483         }
   1484 
   1485         @Override
   1486         public Node.OfInt build() {
   1487             if (curSize < array.length) {
   1488                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
   1489                                                               curSize, array.length));
   1490             }
   1491 
   1492             return this;
   1493         }
   1494 
   1495         @Override
   1496         public void begin(long size) {
   1497             if (size != array.length) {
   1498                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
   1499                                                               size, array.length));
   1500             }
   1501 
   1502             curSize = 0;
   1503         }
   1504 
   1505         @Override
   1506         public void accept(int i) {
   1507             if (curSize < array.length) {
   1508                 array[curSize++] = i;
   1509             } else {
   1510                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
   1511                                                               array.length));
   1512             }
   1513         }
   1514 
   1515         @Override
   1516         public void end() {
   1517             if (curSize < array.length) {
   1518                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
   1519                                                               curSize, array.length));
   1520             }
   1521         }
   1522 
   1523         @Override
   1524         public String toString() {
   1525             return String.format("IntFixedNodeBuilder[%d][%s]",
   1526                                  array.length - curSize, Arrays.toString(array));
   1527         }
   1528     }
   1529 
   1530     private static final class LongFixedNodeBuilder
   1531             extends LongArrayNode
   1532             implements Node.Builder.OfLong {
   1533 
   1534         LongFixedNodeBuilder(long size) {
   1535             super(size);
   1536             assert size < MAX_ARRAY_SIZE;
   1537         }
   1538 
   1539         @Override
   1540         public Node.OfLong build() {
   1541             if (curSize < array.length) {
   1542                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
   1543                                                               curSize, array.length));
   1544             }
   1545 
   1546             return this;
   1547         }
   1548 
   1549         @Override
   1550         public void begin(long size) {
   1551             if (size != array.length) {
   1552                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
   1553                                                               size, array.length));
   1554             }
   1555 
   1556             curSize = 0;
   1557         }
   1558 
   1559         @Override
   1560         public void accept(long i) {
   1561             if (curSize < array.length) {
   1562                 array[curSize++] = i;
   1563             } else {
   1564                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
   1565                                                               array.length));
   1566             }
   1567         }
   1568 
   1569         @Override
   1570         public void end() {
   1571             if (curSize < array.length) {
   1572                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
   1573                                                               curSize, array.length));
   1574             }
   1575         }
   1576 
   1577         @Override
   1578         public String toString() {
   1579             return String.format("LongFixedNodeBuilder[%d][%s]",
   1580                                  array.length - curSize, Arrays.toString(array));
   1581         }
   1582     }
   1583 
   1584     private static final class DoubleFixedNodeBuilder
   1585             extends DoubleArrayNode
   1586             implements Node.Builder.OfDouble {
   1587 
   1588         DoubleFixedNodeBuilder(long size) {
   1589             super(size);
   1590             assert size < MAX_ARRAY_SIZE;
   1591         }
   1592 
   1593         @Override
   1594         public Node.OfDouble build() {
   1595             if (curSize < array.length) {
   1596                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
   1597                                                               curSize, array.length));
   1598             }
   1599 
   1600             return this;
   1601         }
   1602 
   1603         @Override
   1604         public void begin(long size) {
   1605             if (size != array.length) {
   1606                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
   1607                                                               size, array.length));
   1608             }
   1609 
   1610             curSize = 0;
   1611         }
   1612 
   1613         @Override
   1614         public void accept(double i) {
   1615             if (curSize < array.length) {
   1616                 array[curSize++] = i;
   1617             } else {
   1618                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
   1619                                                               array.length));
   1620             }
   1621         }
   1622 
   1623         @Override
   1624         public void end() {
   1625             if (curSize < array.length) {
   1626                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
   1627                                                               curSize, array.length));
   1628             }
   1629         }
   1630 
   1631         @Override
   1632         public String toString() {
   1633             return String.format("DoubleFixedNodeBuilder[%d][%s]",
   1634                                  array.length - curSize, Arrays.toString(array));
   1635         }
   1636     }
   1637 
   1638     private static final class IntSpinedNodeBuilder
   1639             extends SpinedBuffer.OfInt
   1640             implements Node.OfInt, Node.Builder.OfInt {
   1641         private boolean building = false;
   1642 
   1643         IntSpinedNodeBuilder() {} // Avoid creation of special accessor
   1644 
   1645         @Override
   1646         public Spliterator.OfInt spliterator() {
   1647             assert !building : "during building";
   1648             return super.spliterator();
   1649         }
   1650 
   1651         @Override
   1652         public void forEach(IntConsumer consumer) {
   1653             assert !building : "during building";
   1654             super.forEach(consumer);
   1655         }
   1656 
   1657         //
   1658         @Override
   1659         public void begin(long size) {
   1660             assert !building : "was already building";
   1661             building = true;
   1662             clear();
   1663             ensureCapacity(size);
   1664         }
   1665 
   1666         @Override
   1667         public void accept(int i) {
   1668             assert building : "not building";
   1669             super.accept(i);
   1670         }
   1671 
   1672         @Override
   1673         public void end() {
   1674             assert building : "was not building";
   1675             building = false;
   1676             // @@@ check begin(size) and size
   1677         }
   1678 
   1679         @Override
   1680         public void copyInto(int[] array, int offset) throws IndexOutOfBoundsException {
   1681             assert !building : "during building";
   1682             super.copyInto(array, offset);
   1683         }
   1684 
   1685         @Override
   1686         public int[] asPrimitiveArray() {
   1687             assert !building : "during building";
   1688             return super.asPrimitiveArray();
   1689         }
   1690 
   1691         @Override
   1692         public Node.OfInt build() {
   1693             assert !building : "during building";
   1694             return this;
   1695         }
   1696     }
   1697 
   1698     private static final class LongSpinedNodeBuilder
   1699             extends SpinedBuffer.OfLong
   1700             implements Node.OfLong, Node.Builder.OfLong {
   1701         private boolean building = false;
   1702 
   1703         LongSpinedNodeBuilder() {} // Avoid creation of special accessor
   1704 
   1705         @Override
   1706         public Spliterator.OfLong spliterator() {
   1707             assert !building : "during building";
   1708             return super.spliterator();
   1709         }
   1710 
   1711         @Override
   1712         public void forEach(LongConsumer consumer) {
   1713             assert !building : "during building";
   1714             super.forEach(consumer);
   1715         }
   1716 
   1717         //
   1718         @Override
   1719         public void begin(long size) {
   1720             assert !building : "was already building";
   1721             building = true;
   1722             clear();
   1723             ensureCapacity(size);
   1724         }
   1725 
   1726         @Override
   1727         public void accept(long i) {
   1728             assert building : "not building";
   1729             super.accept(i);
   1730         }
   1731 
   1732         @Override
   1733         public void end() {
   1734             assert building : "was not building";
   1735             building = false;
   1736             // @@@ check begin(size) and size
   1737         }
   1738 
   1739         @Override
   1740         public void copyInto(long[] array, int offset) {
   1741             assert !building : "during building";
   1742             super.copyInto(array, offset);
   1743         }
   1744 
   1745         @Override
   1746         public long[] asPrimitiveArray() {
   1747             assert !building : "during building";
   1748             return super.asPrimitiveArray();
   1749         }
   1750 
   1751         @Override
   1752         public Node.OfLong build() {
   1753             assert !building : "during building";
   1754             return this;
   1755         }
   1756     }
   1757 
   1758     private static final class DoubleSpinedNodeBuilder
   1759             extends SpinedBuffer.OfDouble
   1760             implements Node.OfDouble, Node.Builder.OfDouble {
   1761         private boolean building = false;
   1762 
   1763         DoubleSpinedNodeBuilder() {} // Avoid creation of special accessor
   1764 
   1765         @Override
   1766         public Spliterator.OfDouble spliterator() {
   1767             assert !building : "during building";
   1768             return super.spliterator();
   1769         }
   1770 
   1771         @Override
   1772         public void forEach(DoubleConsumer consumer) {
   1773             assert !building : "during building";
   1774             super.forEach(consumer);
   1775         }
   1776 
   1777         //
   1778         @Override
   1779         public void begin(long size) {
   1780             assert !building : "was already building";
   1781             building = true;
   1782             clear();
   1783             ensureCapacity(size);
   1784         }
   1785 
   1786         @Override
   1787         public void accept(double i) {
   1788             assert building : "not building";
   1789             super.accept(i);
   1790         }
   1791 
   1792         @Override
   1793         public void end() {
   1794             assert building : "was not building";
   1795             building = false;
   1796             // @@@ check begin(size) and size
   1797         }
   1798 
   1799         @Override
   1800         public void copyInto(double[] array, int offset) {
   1801             assert !building : "during building";
   1802             super.copyInto(array, offset);
   1803         }
   1804 
   1805         @Override
   1806         public double[] asPrimitiveArray() {
   1807             assert !building : "during building";
   1808             return super.asPrimitiveArray();
   1809         }
   1810 
   1811         @Override
   1812         public Node.OfDouble build() {
   1813             assert !building : "during building";
   1814             return this;
   1815         }
   1816     }
   1817 
   1818     /*
   1819      * This and subclasses are not intended to be serializable
   1820      */
   1821     @SuppressWarnings("serial")
   1822     private static abstract class SizedCollectorTask<P_IN, P_OUT, T_SINK extends Sink<P_OUT>,
   1823                                                      K extends SizedCollectorTask<P_IN, P_OUT, T_SINK, K>>
   1824             extends CountedCompleter<Void>
   1825             implements Sink<P_OUT> {
   1826         protected final Spliterator<P_IN> spliterator;
   1827         protected final PipelineHelper<P_OUT> helper;
   1828         protected final long targetSize;
   1829         protected long offset;
   1830         protected long length;
   1831         // For Sink implementation
   1832         protected int index, fence;
   1833 
   1834         SizedCollectorTask(Spliterator<P_IN> spliterator,
   1835                            PipelineHelper<P_OUT> helper,
   1836                            int arrayLength) {
   1837             assert spliterator.hasCharacteristics(Spliterator.SUBSIZED);
   1838             this.spliterator = spliterator;
   1839             this.helper = helper;
   1840             this.targetSize = AbstractTask.suggestTargetSize(spliterator.estimateSize());
   1841             this.offset = 0;
   1842             this.length = arrayLength;
   1843         }
   1844 
   1845         SizedCollectorTask(K parent, Spliterator<P_IN> spliterator,
   1846                            long offset, long length, int arrayLength) {
   1847             super(parent);
   1848             assert spliterator.hasCharacteristics(Spliterator.SUBSIZED);
   1849             this.spliterator = spliterator;
   1850             this.helper = parent.helper;
   1851             this.targetSize = parent.targetSize;
   1852             this.offset = offset;
   1853             this.length = length;
   1854 
   1855             if (offset < 0 || length < 0 || (offset + length - 1 >= arrayLength)) {
   1856                 throw new IllegalArgumentException(
   1857                         String.format("offset and length interval [%d, %d + %d) is not within array size interval [0, %d)",
   1858                                       offset, offset, length, arrayLength));
   1859             }
   1860         }
   1861 
   1862         @Override
   1863         public void compute() {
   1864             SizedCollectorTask<P_IN, P_OUT, T_SINK, K> task = this;
   1865             Spliterator<P_IN> rightSplit = spliterator, leftSplit;
   1866             while (rightSplit.estimateSize() > task.targetSize &&
   1867                    (leftSplit = rightSplit.trySplit()) != null) {
   1868                 task.setPendingCount(1);
   1869                 long leftSplitSize = leftSplit.estimateSize();
   1870                 task.makeChild(leftSplit, task.offset, leftSplitSize).fork();
   1871                 task = task.makeChild(rightSplit, task.offset + leftSplitSize,
   1872                                       task.length - leftSplitSize);
   1873             }
   1874 
   1875             assert task.offset + task.length < MAX_ARRAY_SIZE;
   1876             @SuppressWarnings("unchecked")
   1877             T_SINK sink = (T_SINK) task;
   1878             task.helper.wrapAndCopyInto(sink, rightSplit);
   1879             task.propagateCompletion();
   1880         }
   1881 
   1882         abstract K makeChild(Spliterator<P_IN> spliterator, long offset, long size);
   1883 
   1884         @Override
   1885         public void begin(long size) {
   1886             if (size > length)
   1887                 throw new IllegalStateException("size passed to Sink.begin exceeds array length");
   1888             // Casts to int are safe since absolute size is verified to be within
   1889             // bounds when the root concrete SizedCollectorTask is constructed
   1890             // with the shared array
   1891             index = (int) offset;
   1892             fence = index + (int) length;
   1893         }
   1894 
   1895         @SuppressWarnings("serial")
   1896         static final class OfRef<P_IN, P_OUT>
   1897                 extends SizedCollectorTask<P_IN, P_OUT, Sink<P_OUT>, OfRef<P_IN, P_OUT>>
   1898                 implements Sink<P_OUT> {
   1899             private final P_OUT[] array;
   1900 
   1901             OfRef(Spliterator<P_IN> spliterator, PipelineHelper<P_OUT> helper, P_OUT[] array) {
   1902                 super(spliterator, helper, array.length);
   1903                 this.array = array;
   1904             }
   1905 
   1906             OfRef(OfRef<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator,
   1907                   long offset, long length) {
   1908                 super(parent, spliterator, offset, length, parent.array.length);
   1909                 this.array = parent.array;
   1910             }
   1911 
   1912             @Override
   1913             OfRef<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator,
   1914                                          long offset, long size) {
   1915                 return new OfRef<>(this, spliterator, offset, size);
   1916             }
   1917 
   1918             @Override
   1919             public void accept(P_OUT value) {
   1920                 if (index >= fence) {
   1921                     throw new IndexOutOfBoundsException(Integer.toString(index));
   1922                 }
   1923                 array[index++] = value;
   1924             }
   1925         }
   1926 
   1927         @SuppressWarnings("serial")
   1928         static final class OfInt<P_IN>
   1929                 extends SizedCollectorTask<P_IN, Integer, Sink.OfInt, OfInt<P_IN>>
   1930                 implements Sink.OfInt {
   1931             private final int[] array;
   1932 
   1933             OfInt(Spliterator<P_IN> spliterator, PipelineHelper<Integer> helper, int[] array) {
   1934                 super(spliterator, helper, array.length);
   1935                 this.array = array;
   1936             }
   1937 
   1938             OfInt(SizedCollectorTask.OfInt<P_IN> parent, Spliterator<P_IN> spliterator,
   1939                   long offset, long length) {
   1940                 super(parent, spliterator, offset, length, parent.array.length);
   1941                 this.array = parent.array;
   1942             }
   1943 
   1944             @Override
   1945             SizedCollectorTask.OfInt<P_IN> makeChild(Spliterator<P_IN> spliterator,
   1946                                                      long offset, long size) {
   1947                 return new SizedCollectorTask.OfInt<>(this, spliterator, offset, size);
   1948             }
   1949 
   1950             @Override
   1951             public void accept(int value) {
   1952                 if (index >= fence) {
   1953                     throw new IndexOutOfBoundsException(Integer.toString(index));
   1954                 }
   1955                 array[index++] = value;
   1956             }
   1957         }
   1958 
   1959         @SuppressWarnings("serial")
   1960         static final class OfLong<P_IN>
   1961                 extends SizedCollectorTask<P_IN, Long, Sink.OfLong, OfLong<P_IN>>
   1962                 implements Sink.OfLong {
   1963             private final long[] array;
   1964 
   1965             OfLong(Spliterator<P_IN> spliterator, PipelineHelper<Long> helper, long[] array) {
   1966                 super(spliterator, helper, array.length);
   1967                 this.array = array;
   1968             }
   1969 
   1970             OfLong(SizedCollectorTask.OfLong<P_IN> parent, Spliterator<P_IN> spliterator,
   1971                    long offset, long length) {
   1972                 super(parent, spliterator, offset, length, parent.array.length);
   1973                 this.array = parent.array;
   1974             }
   1975 
   1976             @Override
   1977             SizedCollectorTask.OfLong<P_IN> makeChild(Spliterator<P_IN> spliterator,
   1978                                                       long offset, long size) {
   1979                 return new SizedCollectorTask.OfLong<>(this, spliterator, offset, size);
   1980             }
   1981 
   1982             @Override
   1983             public void accept(long value) {
   1984                 if (index >= fence) {
   1985                     throw new IndexOutOfBoundsException(Integer.toString(index));
   1986                 }
   1987                 array[index++] = value;
   1988             }
   1989         }
   1990 
   1991         @SuppressWarnings("serial")
   1992         static final class OfDouble<P_IN>
   1993                 extends SizedCollectorTask<P_IN, Double, Sink.OfDouble, OfDouble<P_IN>>
   1994                 implements Sink.OfDouble {
   1995             private final double[] array;
   1996 
   1997             OfDouble(Spliterator<P_IN> spliterator, PipelineHelper<Double> helper, double[] array) {
   1998                 super(spliterator, helper, array.length);
   1999                 this.array = array;
   2000             }
   2001 
   2002             OfDouble(SizedCollectorTask.OfDouble<P_IN> parent, Spliterator<P_IN> spliterator,
   2003                      long offset, long length) {
   2004                 super(parent, spliterator, offset, length, parent.array.length);
   2005                 this.array = parent.array;
   2006             }
   2007 
   2008             @Override
   2009             SizedCollectorTask.OfDouble<P_IN> makeChild(Spliterator<P_IN> spliterator,
   2010                                                         long offset, long size) {
   2011                 return new SizedCollectorTask.OfDouble<>(this, spliterator, offset, size);
   2012             }
   2013 
   2014             @Override
   2015             public void accept(double value) {
   2016                 if (index >= fence) {
   2017                     throw new IndexOutOfBoundsException(Integer.toString(index));
   2018                 }
   2019                 array[index++] = value;
   2020             }
   2021         }
   2022     }
   2023 
   2024     @SuppressWarnings("serial")
   2025     private static abstract class ToArrayTask<T, T_NODE extends Node<T>,
   2026                                               K extends ToArrayTask<T, T_NODE, K>>
   2027             extends CountedCompleter<Void> {
   2028         protected final T_NODE node;
   2029         protected final int offset;
   2030 
   2031         ToArrayTask(T_NODE node, int offset) {
   2032             this.node = node;
   2033             this.offset = offset;
   2034         }
   2035 
   2036         ToArrayTask(K parent, T_NODE node, int offset) {
   2037             super(parent);
   2038             this.node = node;
   2039             this.offset = offset;
   2040         }
   2041 
   2042         abstract void copyNodeToArray();
   2043 
   2044         abstract K makeChild(int childIndex, int offset);
   2045 
   2046         @Override
   2047         public void compute() {
   2048             ToArrayTask<T, T_NODE, K> task = this;
   2049             while (true) {
   2050                 if (task.node.getChildCount() == 0) {
   2051                     task.copyNodeToArray();
   2052                     task.propagateCompletion();
   2053                     return;
   2054                 }
   2055                 else {
   2056                     task.setPendingCount(task.node.getChildCount() - 1);
   2057 
   2058                     int size = 0;
   2059                     int i = 0;
   2060                     for (;i < task.node.getChildCount() - 1; i++) {
   2061                         K leftTask = task.makeChild(i, task.offset + size);
   2062                         size += leftTask.node.count();
   2063                         leftTask.fork();
   2064                     }
   2065                     task = task.makeChild(i, task.offset + size);
   2066                 }
   2067             }
   2068         }
   2069 
   2070         @SuppressWarnings("serial")
   2071         private static final class OfRef<T>
   2072                 extends ToArrayTask<T, Node<T>, OfRef<T>> {
   2073             private final T[] array;
   2074 
   2075             private OfRef(Node<T> node, T[] array, int offset) {
   2076                 super(node, offset);
   2077                 this.array = array;
   2078             }
   2079 
   2080             private OfRef(OfRef<T> parent, Node<T> node, int offset) {
   2081                 super(parent, node, offset);
   2082                 this.array = parent.array;
   2083             }
   2084 
   2085             @Override
   2086             OfRef<T> makeChild(int childIndex, int offset) {
   2087                 return new OfRef<>(this, node.getChild(childIndex), offset);
   2088             }
   2089 
   2090             @Override
   2091             void copyNodeToArray() {
   2092                 node.copyInto(array, offset);
   2093             }
   2094         }
   2095 
   2096         @SuppressWarnings("serial")
   2097         private static class OfPrimitive<T, T_CONS, T_ARR,
   2098                                          T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>,
   2099                                          T_NODE extends Node.OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>>
   2100                 extends ToArrayTask<T, T_NODE, OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE>> {
   2101             private final T_ARR array;
   2102 
   2103             private OfPrimitive(T_NODE node, T_ARR array, int offset) {
   2104                 super(node, offset);
   2105                 this.array = array;
   2106             }
   2107 
   2108             private OfPrimitive(OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE> parent, T_NODE node, int offset) {
   2109                 super(parent, node, offset);
   2110                 this.array = parent.array;
   2111             }
   2112 
   2113             @Override
   2114             OfPrimitive<T, T_CONS, T_ARR, T_SPLITR, T_NODE> makeChild(int childIndex, int offset) {
   2115                 return new OfPrimitive<>(this, node.getChild(childIndex), offset);
   2116             }
   2117 
   2118             @Override
   2119             void copyNodeToArray() {
   2120                 node.copyInto(array, offset);
   2121             }
   2122         }
   2123 
   2124         @SuppressWarnings("serial")
   2125         private static final class OfInt
   2126                 extends OfPrimitive<Integer, IntConsumer, int[], Spliterator.OfInt, Node.OfInt> {
   2127             private OfInt(Node.OfInt node, int[] array, int offset) {
   2128                 super(node, array, offset);
   2129             }
   2130         }
   2131 
   2132         @SuppressWarnings("serial")
   2133         private static final class OfLong
   2134                 extends OfPrimitive<Long, LongConsumer, long[], Spliterator.OfLong, Node.OfLong> {
   2135             private OfLong(Node.OfLong node, long[] array, int offset) {
   2136                 super(node, array, offset);
   2137             }
   2138         }
   2139 
   2140         @SuppressWarnings("serial")
   2141         private static final class OfDouble
   2142                 extends OfPrimitive<Double, DoubleConsumer, double[], Spliterator.OfDouble, Node.OfDouble> {
   2143             private OfDouble(Node.OfDouble node, double[] array, int offset) {
   2144                 super(node, array, offset);
   2145             }
   2146         }
   2147     }
   2148 
   2149     @SuppressWarnings("serial")
   2150     private static class CollectorTask<P_IN, P_OUT, T_NODE extends Node<P_OUT>, T_BUILDER extends Node.Builder<P_OUT>>
   2151             extends AbstractTask<P_IN, P_OUT, T_NODE, CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER>> {
   2152         protected final PipelineHelper<P_OUT> helper;
   2153         protected final LongFunction<T_BUILDER> builderFactory;
   2154         protected final BinaryOperator<T_NODE> concFactory;
   2155 
   2156         CollectorTask(PipelineHelper<P_OUT> helper,
   2157                       Spliterator<P_IN> spliterator,
   2158                       LongFunction<T_BUILDER> builderFactory,
   2159                       BinaryOperator<T_NODE> concFactory) {
   2160             super(helper, spliterator);
   2161             this.helper = helper;
   2162             this.builderFactory = builderFactory;
   2163             this.concFactory = concFactory;
   2164         }
   2165 
   2166         CollectorTask(CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> parent,
   2167                       Spliterator<P_IN> spliterator) {
   2168             super(parent, spliterator);
   2169             helper = parent.helper;
   2170             builderFactory = parent.builderFactory;
   2171             concFactory = parent.concFactory;
   2172         }
   2173 
   2174         @Override
   2175         protected CollectorTask<P_IN, P_OUT, T_NODE, T_BUILDER> makeChild(Spliterator<P_IN> spliterator) {
   2176             return new CollectorTask<>(this, spliterator);
   2177         }
   2178 
   2179         @Override
   2180         @SuppressWarnings("unchecked")
   2181         protected T_NODE doLeaf() {
   2182             T_BUILDER builder = builderFactory.apply(helper.exactOutputSizeIfKnown(spliterator));
   2183             return (T_NODE) helper.wrapAndCopyInto(builder, spliterator).build();
   2184         }
   2185 
   2186         @Override
   2187         public void onCompletion(CountedCompleter<?> caller) {
   2188             if (!isLeaf())
   2189                 setLocalResult(concFactory.apply(leftChild.getLocalResult(), rightChild.getLocalResult()));
   2190             super.onCompletion(caller);
   2191         }
   2192 
   2193         @SuppressWarnings("serial")
   2194         private static final class OfRef<P_IN, P_OUT>
   2195                 extends CollectorTask<P_IN, P_OUT, Node<P_OUT>, Node.Builder<P_OUT>> {
   2196             OfRef(PipelineHelper<P_OUT> helper,
   2197                   IntFunction<P_OUT[]> generator,
   2198                   Spliterator<P_IN> spliterator) {
   2199                 super(helper, spliterator, s -> builder(s, generator), ConcNode::new);
   2200             }
   2201         }
   2202 
   2203         @SuppressWarnings("serial")
   2204         private static final class OfInt<P_IN>
   2205                 extends CollectorTask<P_IN, Integer, Node.OfInt, Node.Builder.OfInt> {
   2206             OfInt(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) {
   2207                 super(helper, spliterator, Nodes::intBuilder, ConcNode.OfInt::new);
   2208             }
   2209         }
   2210 
   2211         @SuppressWarnings("serial")
   2212         private static final class OfLong<P_IN>
   2213                 extends CollectorTask<P_IN, Long, Node.OfLong, Node.Builder.OfLong> {
   2214             OfLong(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) {
   2215                 super(helper, spliterator, Nodes::longBuilder, ConcNode.OfLong::new);
   2216             }
   2217         }
   2218 
   2219         @SuppressWarnings("serial")
   2220         private static final class OfDouble<P_IN>
   2221                 extends CollectorTask<P_IN, Double, Node.OfDouble, Node.Builder.OfDouble> {
   2222             OfDouble(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) {
   2223                 super(helper, spliterator, Nodes::doubleBuilder, ConcNode.OfDouble::new);
   2224             }
   2225         }
   2226     }
   2227 }
   2228