Class StreamDemand


final class StreamDemand extends AbstractStreamStageGenerator
THE demand rule of a stream pipeline: head N bounds the PULL (decision 10, rule B) - use this, never re-derive which heads gate a loop or how the gate is folded into a loop condition.

Every head in a segment stops the pull once it has its N items. A segment runs from the nearest upstream stage that must see everything (sort, tail, join, and group/split/async, which are lowered as drains) - or the source - down to the next such stage. A must-drain stage always consumes its whole input; a head after it limits only what it emits, so it gates the drain's EMISSION loop, never the drain.

The gate is ordinary IR: each loop condition becomes c1 < l1 AND (c2 < l2 AND ... hasNext()) as nested short-circuit LOGICAL_AND blocks, the counters FIRST, so hasNext()/next() are never called once any head is satisfied. That ordering is backend Contract 12: no back end may hoist, prefetch or speculatively call hasNext() - on an unbounded or infinite source it blocks or never ends.

Which heads gate a loop are PullBoundingHeads (and AsyncCappingHeads for an async), where a segment ends DemandSegmentEnd - held instances, one question each; this class folds them into the loop's condition.

  • Constructor Details

  • Method Details

    • appendDemandCondition

      String appendDemandCondition(List<IRInstr> conditionEvaluation, List<StageState.Counter> bounds, List<IRInstr> rightInstructions, String rightResult, String scopeId, DebugInfo debugInfo)
      Append the demand-bounded condition c1 < l1 AND (... AND right) to conditionEvaluation and return its Boolean result. With no bounds, right is appended unchanged (byte-identical to an ungated loop). bounds are counter/limit pairs: the heads, or the async window's in-flight bound.
    • continueCondition

      FreshBooleanOperand continueCondition(List<StageState.Counter> heads, String scopeId, DebugInfo debugInfo)
      The demand condition of a loop whose own continuation is not a hasNext() - a for range, whose range test the back end evaluates itself. Returns the heads alone (c1 < l1 AND ...) in their own scope-free instruction list, or null when no head bounds the loop.
    • appendDemandOverPrimitive

      String appendDemandOverPrimitive(List<IRInstr> condition, List<StageState.Counter> heads, List<IRInstr> primitiveInstructions, String primitiveResult, String scopeId, DebugInfo debugInfo)
      Gate a loop whose continuation is a runtime helper's PRIMITIVE hasNext-style call (Ek9Group.hasNextGroup, Ek9Async.hasNext): with no heads the condition is unchanged; otherwise the primitive is lifted to a Boolean (Boolean._of) so it can be the right operand of the demand AND. Returns the {primitive} to branch on, appending everything to condition.
    • appendBoundedCondition

      String appendBoundedCondition(List<IRInstr> conditionEvaluation, List<IRInstr> leftInstructions, String leftResult, String leftPrimitive, List<IRInstr> rightInstructions, String rightResult, String scopeId, DebugInfo debugInfo)
      Append a short-circuit LEFT AND RIGHT Boolean condition to conditionEvaluation and return the combined Boolean result variable. RIGHT is evaluated ONLY when LEFT's primitive is true (LOGICAL_AND_BLOCK short-circuit). This is the generic two-operand AND-block; callers build LEFT (the short-circuit decision: head's counter < limit, the window's inFlight < W, etc.) and pass its result+primitive, plus the RIGHT operand's instructions+result (e.g. the managed hasNext()). Mirrors GuardedConditionEvaluator's guard-into-condition fold.