mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8196106: Support nested infinite or recursive flat mapped streams
Reviewed-by: psandoz
This commit is contained in:
parent
58911ccc2c
commit
8a5b86c529
9 changed files with 536 additions and 183 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -36,10 +36,13 @@ import java.util.function.BiFunction;
|
|||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.DoublePredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.function.LongPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
|
@ -274,40 +277,41 @@ abstract class ReferencePipeline<P_IN, P_OUT>
|
|||
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
|
||||
@Override
|
||||
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
|
||||
return new Sink.ChainedReference<>(sink) {
|
||||
// true if cancellationRequested() has been called
|
||||
boolean cancellationRequestedCalled;
|
||||
boolean shorts = isShortCircuitingPipeline();
|
||||
final class FlatMap implements Sink<P_OUT>, Predicate<R> {
|
||||
boolean cancel;
|
||||
|
||||
@Override public void begin(long size) { sink.begin(-1); }
|
||||
@Override public void end() { sink.end(); }
|
||||
|
||||
@Override
|
||||
public void begin(long size) {
|
||||
downstream.begin(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(P_OUT u) {
|
||||
try (Stream<? extends R> result = mapper.apply(u)) {
|
||||
public void accept(P_OUT e) {
|
||||
try (Stream<? extends R> result = mapper.apply(e)) {
|
||||
if (result != null) {
|
||||
if (!cancellationRequestedCalled) {
|
||||
result.sequential().forEach(downstream);
|
||||
} else {
|
||||
var s = result.sequential().spliterator();
|
||||
do {
|
||||
} while (!downstream.cancellationRequested() && s.tryAdvance(downstream));
|
||||
}
|
||||
if (shorts)
|
||||
result.sequential().allMatch(this);
|
||||
else
|
||||
result.sequential().forEach(sink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancellationRequested() {
|
||||
// If this method is called then an operation within the stream
|
||||
// pipeline is short-circuiting (see AbstractPipeline.copyInto).
|
||||
// Note that we cannot differentiate between an upstream or
|
||||
// downstream operation
|
||||
cancellationRequestedCalled = true;
|
||||
return downstream.cancellationRequested();
|
||||
return cancel || (cancel |= sink.cancellationRequested());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean test(R output) {
|
||||
if (!cancel) {
|
||||
sink.accept(output);
|
||||
return !(cancel |= sink.cancellationRequested());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FlatMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -319,39 +323,46 @@ abstract class ReferencePipeline<P_IN, P_OUT>
|
|||
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
|
||||
@Override
|
||||
Sink<P_OUT> opWrapSink(int flags, Sink<Integer> sink) {
|
||||
return new Sink.ChainedReference<>(sink) {
|
||||
// true if cancellationRequested() has been called
|
||||
boolean cancellationRequestedCalled;
|
||||
IntConsumer fastPath =
|
||||
isShortCircuitingPipeline()
|
||||
? null
|
||||
: (sink instanceof IntConsumer ic)
|
||||
? ic
|
||||
: sink::accept;
|
||||
final class FlatMap implements Sink<P_OUT>, IntPredicate {
|
||||
boolean cancel;
|
||||
|
||||
// cache the consumer to avoid creation on every accepted element
|
||||
IntConsumer downstreamAsInt = downstream::accept;
|
||||
@Override public void begin(long size) { sink.begin(-1); }
|
||||
@Override public void end() { sink.end(); }
|
||||
|
||||
@Override
|
||||
public void begin(long size) {
|
||||
downstream.begin(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(P_OUT u) {
|
||||
try (IntStream result = mapper.apply(u)) {
|
||||
public void accept(P_OUT e) {
|
||||
try (IntStream result = mapper.apply(e)) {
|
||||
if (result != null) {
|
||||
if (!cancellationRequestedCalled) {
|
||||
result.sequential().forEach(downstreamAsInt);
|
||||
} else {
|
||||
var s = result.sequential().spliterator();
|
||||
do {
|
||||
} while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsInt));
|
||||
}
|
||||
if (fastPath == null)
|
||||
result.sequential().allMatch(this);
|
||||
else
|
||||
result.sequential().forEach(fastPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancellationRequested() {
|
||||
cancellationRequestedCalled = true;
|
||||
return downstream.cancellationRequested();
|
||||
return cancel || (cancel |= sink.cancellationRequested());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean test(int output) {
|
||||
if (!cancel) {
|
||||
sink.accept(output);
|
||||
return !(cancel |= sink.cancellationRequested());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FlatMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -363,39 +374,46 @@ abstract class ReferencePipeline<P_IN, P_OUT>
|
|||
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
|
||||
@Override
|
||||
Sink<P_OUT> opWrapSink(int flags, Sink<Double> sink) {
|
||||
return new Sink.ChainedReference<>(sink) {
|
||||
// true if cancellationRequested() has been called
|
||||
boolean cancellationRequestedCalled;
|
||||
DoubleConsumer fastPath =
|
||||
isShortCircuitingPipeline()
|
||||
? null
|
||||
: (sink instanceof DoubleConsumer dc)
|
||||
? dc
|
||||
: sink::accept;
|
||||
final class FlatMap implements Sink<P_OUT>, DoublePredicate {
|
||||
boolean cancel;
|
||||
|
||||
// cache the consumer to avoid creation on every accepted element
|
||||
DoubleConsumer downstreamAsDouble = downstream::accept;
|
||||
@Override public void begin(long size) { sink.begin(-1); }
|
||||
@Override public void end() { sink.end(); }
|
||||
|
||||
@Override
|
||||
public void begin(long size) {
|
||||
downstream.begin(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(P_OUT u) {
|
||||
try (DoubleStream result = mapper.apply(u)) {
|
||||
public void accept(P_OUT e) {
|
||||
try (DoubleStream result = mapper.apply(e)) {
|
||||
if (result != null) {
|
||||
if (!cancellationRequestedCalled) {
|
||||
result.sequential().forEach(downstreamAsDouble);
|
||||
} else {
|
||||
var s = result.sequential().spliterator();
|
||||
do {
|
||||
} while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsDouble));
|
||||
}
|
||||
if (fastPath == null)
|
||||
result.sequential().allMatch(this);
|
||||
else
|
||||
result.sequential().forEach(fastPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancellationRequested() {
|
||||
cancellationRequestedCalled = true;
|
||||
return downstream.cancellationRequested();
|
||||
return cancel || (cancel |= sink.cancellationRequested());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean test(double output) {
|
||||
if (!cancel) {
|
||||
sink.accept(output);
|
||||
return !(cancel |= sink.cancellationRequested());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FlatMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -408,39 +426,46 @@ abstract class ReferencePipeline<P_IN, P_OUT>
|
|||
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
|
||||
@Override
|
||||
Sink<P_OUT> opWrapSink(int flags, Sink<Long> sink) {
|
||||
return new Sink.ChainedReference<>(sink) {
|
||||
// true if cancellationRequested() has been called
|
||||
boolean cancellationRequestedCalled;
|
||||
LongConsumer fastPath =
|
||||
isShortCircuitingPipeline()
|
||||
? null
|
||||
: (sink instanceof LongConsumer lc)
|
||||
? lc
|
||||
: sink::accept;
|
||||
final class FlatMap implements Sink<P_OUT>, LongPredicate {
|
||||
boolean cancel;
|
||||
|
||||
// cache the consumer to avoid creation on every accepted element
|
||||
LongConsumer downstreamAsLong = downstream::accept;
|
||||
@Override public void begin(long size) { sink.begin(-1); }
|
||||
@Override public void end() { sink.end(); }
|
||||
|
||||
@Override
|
||||
public void begin(long size) {
|
||||
downstream.begin(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(P_OUT u) {
|
||||
try (LongStream result = mapper.apply(u)) {
|
||||
public void accept(P_OUT e) {
|
||||
try (LongStream result = mapper.apply(e)) {
|
||||
if (result != null) {
|
||||
if (!cancellationRequestedCalled) {
|
||||
result.sequential().forEach(downstreamAsLong);
|
||||
} else {
|
||||
var s = result.sequential().spliterator();
|
||||
do {
|
||||
} while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsLong));
|
||||
}
|
||||
if (fastPath == null)
|
||||
result.sequential().allMatch(this);
|
||||
else
|
||||
result.sequential().forEach(fastPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancellationRequested() {
|
||||
cancellationRequestedCalled = true;
|
||||
return downstream.cancellationRequested();
|
||||
return cancel || (cancel |= sink.cancellationRequested());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean test(long output) {
|
||||
if (!cancel) {
|
||||
sink.accept(output);
|
||||
return !(cancel |= sink.cancellationRequested());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FlatMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue