tuwunel_core/utils/stream/
tools.rs1use std::{collections::HashMap, hash::Hash};
4
5use arrayvec::ArrayVec;
6use futures::{Stream, StreamExt};
7
8use super::ReadyExt;
9use crate::{expected, utils::rand::index};
10
11pub trait Tools<Item>
17where
18 Self: Stream<Item = Item> + Send + Sized,
19 <Self as Stream>::Item: Send,
20{
21 fn counts(self) -> impl Future<Output = HashMap<Item, usize>> + Send
31 where
32 <Self as Stream>::Item: Eq + Hash;
33
34 fn counts_by<K, F>(self, f: F) -> impl Future<Output = HashMap<K, usize>> + Send
43 where
44 F: Fn(Item) -> K + Send,
45 K: Eq + Hash + Send;
46
47 fn counts_by_with_cap<const CAP: usize, K, F>(
56 self,
57 f: F,
58 ) -> impl Future<Output = HashMap<K, usize>> + Send
59 where
60 F: Fn(Item) -> K + Send,
61 K: Eq + Hash + Send;
62
63 fn counts_with_cap<const CAP: usize>(
73 self,
74 ) -> impl Future<Output = HashMap<Item, usize>> + Send
75 where
76 <Self as Stream>::Item: Eq + Hash;
77
78 fn sample_by<const N: usize, K, F>(self, f: F) -> impl Future<Output = ArrayVec<K, N>> + Send
88 where
89 F: Fn(Item) -> K + Send,
90 K: Send;
91
92 fn fold_default<T, F, Fut>(self, f: F) -> impl Future<Output = T> + Send
97 where
98 F: Fn(T, Item) -> Fut + Send,
99 Fut: Future<Output = T> + Send,
100 T: Default + Send;
101}
102
103impl<Item, S> Tools<Item> for S
104where
105 S: Stream<Item = Item> + Send + Sized,
106 <Self as Stream>::Item: Send,
107{
108 #[inline]
109 fn counts(self) -> impl Future<Output = HashMap<Item, usize>> + Send
110 where
111 <Self as Stream>::Item: Eq + Hash,
112 {
113 self.counts_with_cap::<0>()
114 }
115
116 #[inline]
117 fn counts_by<K, F>(self, f: F) -> impl Future<Output = HashMap<K, usize>> + Send
118 where
119 F: Fn(Item) -> K + Send,
120 K: Eq + Hash + Send,
121 {
122 self.counts_by_with_cap::<0, K, F>(f)
123 }
124
125 #[inline]
126 fn counts_by_with_cap<const CAP: usize, K, F>(
127 self,
128 f: F,
129 ) -> impl Future<Output = HashMap<K, usize>> + Send
130 where
131 F: Fn(Item) -> K + Send,
132 K: Eq + Hash + Send,
133 {
134 self.map(f).counts_with_cap::<CAP>()
135 }
136
137 #[inline]
138 fn counts_with_cap<const CAP: usize>(
139 self,
140 ) -> impl Future<Output = HashMap<Item, usize>> + Send
141 where
142 <Self as Stream>::Item: Eq + Hash,
143 {
144 self.ready_fold(HashMap::with_capacity(CAP), |mut counts, item| {
145 let entry = counts.entry(item).or_default();
146 let value = *entry;
147 *entry = expected!(value + 1);
148 counts
149 })
150 }
151
152 #[inline]
153 fn sample_by<const N: usize, K, F>(self, f: F) -> impl Future<Output = ArrayVec<K, N>> + Send
154 where
155 F: Fn(Item) -> K + Send,
156 K: Send,
157 {
158 self.enumerate()
159 .ready_fold(ArrayVec::<K, N>::new(), move |mut reservoir, (i, item)| {
160 if reservoir.len() < N {
161 reservoir.push(f(item));
162 } else {
163 let slot = index(expected!(i + 1));
164 if slot < N {
165 reservoir[slot] = f(item);
166 }
167 }
168
169 reservoir
170 })
171 }
172
173 #[inline]
174 fn fold_default<T, F, Fut>(self, f: F) -> impl Future<Output = T> + Send
175 where
176 F: Fn(T, Item) -> Fut + Send,
177 Fut: Future<Output = T> + Send,
178 T: Default + Send,
179 {
180 self.fold(T::default(), f)
181 }
182}