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