LCOV - code coverage report
Current view: top level - async-2.5.0/lib/src - stream_splitter.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 58 0.0 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
       2             : // for details. All rights reserved. Use of this source code is governed by a
       3             : // BSD-style license that can be found in the LICENSE file.
       4             : 
       5             : import 'dart:async';
       6             : 
       7             : import 'future_group.dart';
       8             : import 'result/result.dart';
       9             : 
      10             : /// A class that splits a single source stream into an arbitrary number of
      11             : /// (single-subscription) streams (called "branch") that emit the same events.
      12             : ///
      13             : /// Each branch will emit all the same values and errors as the source stream,
      14             : /// regardless of which values have been emitted on other branches. This means
      15             : /// that the splitter stores every event that has been emitted so far, which may
      16             : /// consume a lot of memory. The user can call [close] to indicate that no more
      17             : /// branches will be created, and this memory will be released.
      18             : ///
      19             : /// The source stream is only listened to once a branch is created *and listened
      20             : /// to*. It's paused when all branches are paused *or when all branches are
      21             : /// canceled*, and resumed once there's at least one branch that's listening and
      22             : /// unpaused. It's not canceled unless no branches are listening and [close] has
      23             : /// been called.
      24             : class StreamSplitter<T> {
      25             :   /// The wrapped stream.
      26             :   final Stream<T> _stream;
      27             : 
      28             :   /// The subscription to [_stream].
      29             :   ///
      30             :   /// This will be `null` until a branch has a listener.
      31             :   StreamSubscription<T>? _subscription;
      32             : 
      33             :   /// The buffer of events or errors that have already been emitted by
      34             :   /// [_stream].
      35             :   final _buffer = <Result<T>>[];
      36             : 
      37             :   /// The controllers for branches that are listening for future events from
      38             :   /// [_stream].
      39             :   ///
      40             :   /// Once a branch is canceled, it's removed from this list. When [_stream] is
      41             :   /// done, all branches are removed.
      42             :   final _controllers = <StreamController<T>>{};
      43             : 
      44             :   /// A group of futures returned by [close].
      45             :   ///
      46             :   /// This is used to ensure that [close] doesn't complete until all
      47             :   /// [StreamController.close] and [StreamSubscription.cancel] calls complete.
      48             :   final _closeGroup = FutureGroup();
      49             : 
      50             :   /// Whether [_stream] is done emitting events.
      51             :   var _isDone = false;
      52             : 
      53             :   /// Whether [close] has been called.
      54             :   var _isClosed = false;
      55             : 
      56             :   /// Splits [stream] into [count] identical streams.
      57             :   ///
      58             :   /// [count] defaults to 2. This is the same as creating [count] branches and
      59             :   /// then closing the [StreamSplitter].
      60           0 :   static List<Stream<T>> splitFrom<T>(Stream<T> stream, [int? count]) {
      61             :     count ??= 2;
      62           0 :     var splitter = StreamSplitter<T>(stream);
      63           0 :     var streams = List<Stream<T>>.generate(count, (_) => splitter.split());
      64           0 :     splitter.close();
      65             :     return streams;
      66             :   }
      67             : 
      68           0 :   StreamSplitter(this._stream);
      69             : 
      70             :   /// Returns a single-subscription stream that's a copy of the input stream.
      71             :   ///
      72             :   /// This will throw a [StateError] if [close] has been called.
      73           0 :   Stream<T> split() {
      74           0 :     if (_isClosed) {
      75           0 :       throw StateError("Can't call split() on a closed StreamSplitter.");
      76             :     }
      77             : 
      78           0 :     var controller = StreamController<T>(
      79           0 :         onListen: _onListen, onPause: _onPause, onResume: _onResume);
      80           0 :     controller.onCancel = () => _onCancel(controller);
      81             : 
      82           0 :     for (var result in _buffer) {
      83           0 :       result.addTo(controller);
      84             :     }
      85             : 
      86           0 :     if (_isDone) {
      87           0 :       _closeGroup.add(controller.close());
      88             :     } else {
      89           0 :       _controllers.add(controller);
      90             :     }
      91             : 
      92           0 :     return controller.stream;
      93             :   }
      94             : 
      95             :   /// Indicates that no more branches will be requested via [split].
      96             :   ///
      97             :   /// This clears the internal buffer of events. If there are no branches or all
      98             :   /// branches have been canceled, this cancels the subscription to the input
      99             :   /// stream.
     100             :   ///
     101             :   /// Returns a [Future] that completes once all events have been processed by
     102             :   /// all branches and (if applicable) the subscription to the input stream has
     103             :   /// been canceled.
     104           0 :   Future close() {
     105           0 :     if (_isClosed) return _closeGroup.future;
     106           0 :     _isClosed = true;
     107             : 
     108           0 :     _buffer.clear();
     109           0 :     if (_controllers.isEmpty) _cancelSubscription();
     110             : 
     111           0 :     return _closeGroup.future;
     112             :   }
     113             : 
     114             :   /// Cancel [_subscription] and close [_closeGroup].
     115             :   ///
     116             :   /// This should be called after all the branches' subscriptions have been
     117             :   /// canceled and the splitter has been closed. In that case, we won't use the
     118             :   /// events from [_subscription] any more, since there's nothing to pipe them
     119             :   /// to and no more branches will be created. If [_subscription] is done,
     120             :   /// canceling it will be a no-op.
     121             :   ///
     122             :   /// This may also be called before any branches have been created, in which
     123             :   /// case [_subscription] will be `null`.
     124           0 :   void _cancelSubscription() {
     125           0 :     assert(_controllers.isEmpty);
     126           0 :     assert(_isClosed);
     127             : 
     128             :     Future? future;
     129           0 :     if (_subscription != null) future = _subscription!.cancel();
     130           0 :     if (future != null) _closeGroup.add(future);
     131           0 :     _closeGroup.close();
     132             :   }
     133             : 
     134             :   // StreamController events
     135             : 
     136             :   /// Subscribe to [_stream] if we haven't yet done so, and resume the
     137             :   /// subscription if we have.
     138           0 :   void _onListen() {
     139           0 :     if (_isDone) return;
     140             : 
     141           0 :     if (_subscription != null) {
     142             :       // Resume the subscription in case it was paused, either because all the
     143             :       // controllers were paused or because the last one was canceled. If it
     144             :       // wasn't paused, this will be a no-op.
     145           0 :       _subscription!.resume();
     146             :     } else {
     147           0 :       _subscription =
     148           0 :           _stream.listen(_onData, onError: _onError, onDone: _onDone);
     149             :     }
     150             :   }
     151             : 
     152             :   /// Pauses [_subscription] if every controller is paused.
     153           0 :   void _onPause() {
     154           0 :     if (!_controllers.every((controller) => controller.isPaused)) return;
     155           0 :     _subscription!.pause();
     156             :   }
     157             : 
     158             :   /// Resumes [_subscription].
     159             :   ///
     160             :   /// If [_subscription] wasn't paused, this is a no-op.
     161           0 :   void _onResume() {
     162           0 :     _subscription!.resume();
     163             :   }
     164             : 
     165             :   /// Removes [controller] from [_controllers] and cancels or pauses
     166             :   /// [_subscription] as appropriate.
     167             :   ///
     168             :   /// Since the controller emitting a done event will cause it to register as
     169             :   /// canceled, this is the only way that a controller is ever removed from
     170             :   /// [_controllers].
     171           0 :   void _onCancel(StreamController controller) {
     172           0 :     _controllers.remove(controller);
     173           0 :     if (_controllers.isNotEmpty) return;
     174             : 
     175           0 :     if (_isClosed) {
     176           0 :       _cancelSubscription();
     177             :     } else {
     178           0 :       _subscription!.pause();
     179             :     }
     180             :   }
     181             : 
     182             :   // Stream events
     183             : 
     184             :   /// Buffers [data] and passes it to [_controllers].
     185           0 :   void _onData(T data) {
     186           0 :     if (!_isClosed) _buffer.add(Result.value(data));
     187           0 :     for (var controller in _controllers) {
     188           0 :       controller.add(data);
     189             :     }
     190             :   }
     191             : 
     192             :   /// Buffers [error] and passes it to [_controllers].
     193           0 :   void _onError(Object error, StackTrace stackTrace) {
     194           0 :     if (!_isClosed) _buffer.add(Result.error(error, stackTrace));
     195           0 :     for (var controller in _controllers) {
     196           0 :       controller.addError(error, stackTrace);
     197             :     }
     198             :   }
     199             : 
     200             :   /// Marks [_controllers] as done.
     201           0 :   void _onDone() {
     202           0 :     _isDone = true;
     203           0 :     for (var controller in _controllers) {
     204           0 :       _closeGroup.add(controller.close());
     205             :     }
     206             :   }
     207             : }

Generated by: LCOV version 1.14