rskit_stream/operators/
windowing.rs1use std::collections::VecDeque;
9use std::time::Duration;
10
11use futures::{Stream, StreamExt as _};
12
13use super::buffer::{take_window, window_capacity};
14
15pub fn tumbling_window<S, T>(
19 stream: S,
20 duration: Duration,
21 max_items: usize,
22) -> impl Stream<Item = Vec<T>> + Send + 'static
23where
24 S: Stream<Item = T> + Send + 'static,
25 T: Send + 'static,
26{
27 async_stream::stream! {
28 tokio::pin!(stream);
29 let (max_items, initial_capacity) = window_capacity(max_items);
30 let mut buf: Vec<T> = Vec::with_capacity(initial_capacity);
31 let mut deadline = tokio::time::Instant::now() + duration;
32 loop {
33 tokio::select! {
34 item = stream.next() => {
35 if let Some(v) = item {
36 buf.push(v);
37 if buf.len() >= max_items {
38 yield take_window(&mut buf, initial_capacity);
39 deadline = tokio::time::Instant::now() + duration;
40 }
41 } else {
42 if !buf.is_empty() {
43 yield take_window(&mut buf, initial_capacity);
44 }
45 break;
46 }
47 }
48 () = tokio::time::sleep_until(deadline) => {
49 if !buf.is_empty() {
50 yield take_window(&mut buf, initial_capacity);
51 }
52 deadline = tokio::time::Instant::now() + duration;
53 }
54 }
55 }
56 }
57}
58
59pub fn batch<S, T>(
64 stream: S,
65 size: usize,
66 timeout: Duration,
67) -> impl Stream<Item = Vec<T>> + Send + 'static
68where
69 S: Stream<Item = T> + Send + 'static,
70 T: Send + 'static,
71{
72 async_stream::stream! {
73 tokio::pin!(stream);
74 let size = size.max(1);
75 let mut buf: Vec<T> = Vec::with_capacity(size);
76 let mut deadline: Option<tokio::time::Instant> = None;
77
78 loop {
79 tokio::select! {
80 item = stream.next() => {
81 if let Some(v) = item {
82 if buf.is_empty() {
83 deadline = Some(tokio::time::Instant::now() + timeout);
84 }
85 buf.push(v);
86 if buf.len() >= size {
87 deadline = None;
88 yield std::mem::take(&mut buf);
89 }
90 } else {
91 if !buf.is_empty() {
92 yield std::mem::take(&mut buf);
93 }
94 break;
95 }
96 }
97 () = async {
98 match deadline {
99 Some(d) => tokio::time::sleep_until(d).await,
100 None => std::future::pending::<()>().await,
101 }
102 } => {
103 deadline = None;
104 if !buf.is_empty() {
105 yield std::mem::take(&mut buf);
106 }
107 }
108 }
109 }
110 }
111}
112
113pub fn sliding_window<S, T>(
115 stream: S,
116 size: usize,
117 step: usize,
118) -> impl Stream<Item = Vec<T>> + Send + 'static
119where
120 S: Stream<Item = T> + Send + 'static,
121 T: Clone + Send + 'static,
122{
123 async_stream::stream! {
124 tokio::pin!(stream);
125 let mut window = VecDeque::with_capacity(size.max(1));
126 let effective_step = step.max(1);
127
128 while let Some(item) = stream.next().await {
129 window.push_back(item);
130 while window.len() > size {
131 window.pop_front();
132 }
133 if size > 0 && window.len() == size {
134 yield window.iter().cloned().collect();
135 for _ in 0..effective_step.min(window.len()) {
136 window.pop_front();
137 }
138 }
139 }
140 }
141}