github.com/searKing/golang/go@v1.2.74/container/stream/op/terminal/abstract_pipeline.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package terminal
     6  
     7  import (
     8  	"context"
     9  	"runtime"
    10  
    11  	"github.com/searKing/golang/go/error/exception"
    12  	"github.com/searKing/golang/go/util/class"
    13  	"github.com/searKing/golang/go/util/optional"
    14  	"github.com/searKing/golang/go/util/spliterator"
    15  )
    16  
    17  const (
    18  	MsgStreamLinked = "stream has already been operated upon or closed"
    19  	MsgConsumed     = "source already consumed or closed"
    20  )
    21  
    22  var (
    23  	ParallelTargetSize = runtime.GOMAXPROCS(-1)
    24  )
    25  
    26  /**
    27   * Abstract base class for "pipeline" classes, which are the core
    28   * implementations of the Stream interface and its primitive specializations.
    29   * Manages construction and evaluation of stream pipelines.
    30   *
    31   * <p>An {@code AbstractPipeline} represents an initial portion of a stream
    32   * pipeline, encapsulating a stream source and zero or more intermediate
    33   * operations.  The individual {@code AbstractPipeline} objects are often
    34   * referred to as <em>stages</em>, where each stage describes either the stream
    35   * source or an intermediate operation.
    36   *
    37   * <p>A concrete intermediate stage is generally built from an
    38   * {@code AbstractPipeline}, a shape-specific pipeline class which extends it
    39   * (e.g., {@code IntPipeline}) which is also abstract, and an operation-specific
    40   * concrete class which extends that.  {@code AbstractPipeline} contains most of
    41   * the mechanics of evaluating the pipeline, and implements methods that will be
    42   * used by the operation; the shape-specific classes add helper methods for
    43   * dealing with collection of results into the appropriate shape-specific
    44   * containers.
    45   *
    46   * <p>After chaining a new intermediate operation, or executing a terminal
    47   * operation, the stream is considered to be consumed, and no more intermediate
    48   * or terminal operations are permitted on this stream instance.
    49   *
    50   * @implNote
    51   * <p>For sequential streams, and parallel streams without
    52   * <a href="package-summary.html#StreamOps">stateful intermediate
    53   * operations</a>, parallel streams, pipeline evaluation is done in a single
    54   * pass that "jams" all the operations together.  For parallel streams with
    55   * stateful operations, execution is divided into segments, where each
    56   * stateful operations marks the end of a segment, and each segment is
    57   * evaluated separately and the result used as the input to the next
    58   * segment.  In all cases, the source data is not consumed until a terminal
    59   * operation begins.
    60   *
    61   * @param <E_IN>  type of input elements
    62   * @param <E_OUT> type of output elements
    63   * @param <S> type of the subclass implementing {@code BaseStream}
    64   * @since 1.8
    65   */
    66  type AbstractPipeline struct {
    67  	class.Class
    68  	/**
    69  	 * True if pipeline is parallel, otherwise the pipeline is sequential; only
    70  	 * valid for the source stage.
    71  	 */
    72  	parallel bool
    73  
    74  	/**
    75  	 * True if this pipeline has been linked or consumed
    76  	 */
    77  	linkedOrConsumed bool
    78  
    79  	/** Target split size, common to all tasks in a computation */
    80  	targetSize int // may be lazily initialized
    81  }
    82  
    83  func (p *AbstractPipeline) IsParallel() bool {
    84  	return p.parallel
    85  }
    86  
    87  func (p *AbstractPipeline) SetParallel(parallel bool) {
    88  	p.parallel = parallel
    89  }
    90  
    91  func (p *AbstractPipeline) IsLinkedOrConsumed() bool {
    92  	return p.linkedOrConsumed
    93  }
    94  
    95  func (p *AbstractPipeline) SetLinkedOrConsumed(linkedOrConsumed bool) {
    96  	p.linkedOrConsumed = linkedOrConsumed
    97  }
    98  
    99  /**
   100   * Returns the targetSize, initializing it via the supplied
   101   * size estimate if not already initialized.
   102   */
   103  func (p *AbstractPipeline) GetTargetSize(sizeEstimate int) int {
   104  	if p.targetSize == 0 {
   105  		p.targetSize = p.SuggestTargetSize(sizeEstimate)
   106  	}
   107  	return p.targetSize
   108  }
   109  
   110  func (p *AbstractPipeline) SetTargetSize(targetSize int) {
   111  	p.targetSize = targetSize
   112  }
   113  
   114  /**
   115   * Returns a suggested target leaf size based on the initial size estimate.
   116   *
   117   * @return suggested target leaf size
   118   */
   119  func (p *AbstractPipeline) SuggestTargetSize(sizeEstimate int) int {
   120  	est := sizeEstimate / ParallelTargetSize
   121  	if est > 0 {
   122  		return est
   123  	}
   124  	return 1
   125  }
   126  
   127  // Terminal evaluation methods
   128  
   129  /**
   130   * Evaluate the pipeline with a terminal operation to produce a result.
   131   *
   132   * @param <R> the type of result
   133   * @param terminalOp the terminal operation to be applied to the pipeline.
   134   * @return the result
   135   */
   136  func (p *AbstractPipeline) Evaluate(ctx context.Context, terminalOp Operation, split spliterator.Spliterator) optional.Optional {
   137  	if p.IsLinkedOrConsumed() {
   138  		panic(exception.NewIllegalStateException1(MsgStreamLinked))
   139  	}
   140  	p.SetLinkedOrConsumed(true)
   141  	defer p.SetLinkedOrConsumed(false)
   142  
   143  	if p.IsParallel() {
   144  		return terminalOp.EvaluateParallel(ctx, split)
   145  	}
   146  	return terminalOp.EvaluateSequential(ctx, split)
   147  }