tenshift_core/
transform.rs1use crate::error::Error;
17use crate::sample::Sample;
18
19#[non_exhaustive]
21pub enum TransformResult {
22 Sample(Sample),
24 Samples(Vec<Sample>),
26 Skip,
28 Error(Error),
30}
31
32pub trait Transform: Send + Sync {
59 fn apply(&self, sample: Sample) -> TransformResult;
61
62 fn name(&self) -> &str;
64}
65
66pub trait StatefulTransform: Send {
72 fn push(&mut self, sample: Sample) -> Vec<Sample>;
74
75 fn finish(&mut self) -> Vec<Sample>;
77
78 fn name(&self) -> &str;
80}
81
82pub struct MapTransform<F> {
84 func: F,
85}
86
87impl<F> MapTransform<F>
88where
89 F: Fn(Sample) -> crate::error::Result<Sample> + Send + Sync,
90{
91 pub fn new(func: F) -> Self {
93 Self { func }
94 }
95}
96
97impl<F> Transform for MapTransform<F>
98where
99 F: Fn(Sample) -> crate::error::Result<Sample> + Send + Sync,
100{
101 fn apply(&self, sample: Sample) -> TransformResult {
102 match (self.func)(sample) {
103 Ok(s) => TransformResult::Sample(s),
104 Err(e) => TransformResult::Error(e),
105 }
106 }
107
108 #[allow(clippy::needless_borrows_for_generic_args)]
109 fn name(&self) -> &str {
110 "map"
111 }
112}
113
114pub struct FlatMapTransform<F> {
116 func: F,
117}
118
119impl<F> FlatMapTransform<F>
120where
121 F: Fn(Sample) -> crate::error::Result<Vec<Sample>> + Send + Sync,
122{
123 pub fn new(func: F) -> Self {
125 Self { func }
126 }
127}
128
129impl<F> Transform for FlatMapTransform<F>
130where
131 F: Fn(Sample) -> crate::error::Result<Vec<Sample>> + Send + Sync,
132{
133 fn apply(&self, sample: Sample) -> TransformResult {
134 match (self.func)(sample) {
135 Ok(samples) => TransformResult::Samples(samples),
136 Err(e) => TransformResult::Error(e),
137 }
138 }
139
140 #[allow(clippy::needless_borrows_for_generic_args)]
141 fn name(&self) -> &str {
142 "flat_map"
143 }
144}
145
146pub struct FilterTransform<F> {
148 predicate: F,
149}
150
151impl<F> FilterTransform<F>
152where
153 F: Fn(&Sample) -> bool + Send + Sync,
154{
155 pub fn new(predicate: F) -> Self {
157 Self { predicate }
158 }
159}
160
161impl<F> Transform for FilterTransform<F>
162where
163 F: Fn(&Sample) -> bool + Send + Sync,
164{
165 fn apply(&self, sample: Sample) -> TransformResult {
166 if (self.predicate)(&sample) {
167 TransformResult::Sample(sample)
168 } else {
169 TransformResult::Skip
170 }
171 }
172
173 fn name(&self) -> &str {
174 "filter"
175 }
176}
177
178pub struct ShuffleBuffer {
184 buffer: Vec<Sample>,
185 capacity: usize,
186 rng_state: u64,
187}
188
189impl ShuffleBuffer {
190 pub fn new(capacity: usize, seed: Option<u64>) -> Self {
192 Self {
193 buffer: Vec::with_capacity(capacity),
194 capacity: capacity.max(1),
195 rng_state: seed.unwrap_or(crate::pipeline::DEFAULT_SHUFFLE_SEED),
196 }
197 }
198
199 fn next_rand(&mut self) -> u64 {
201 let mut x = self.rng_state;
202 x ^= x << 13;
203 x ^= x >> 7;
204 x ^= x << 17;
205 self.rng_state = x;
206 x
207 }
208}
209
210impl StatefulTransform for ShuffleBuffer {
211 fn push(&mut self, sample: Sample) -> Vec<Sample> {
212 if self.buffer.len() < self.capacity {
213 self.buffer.push(sample);
214 Vec::new()
215 } else {
216 #[allow(clippy::cast_possible_truncation)]
218 let idx = (self.next_rand() as usize) % self.buffer.len();
219 let evicted = std::mem::replace(&mut self.buffer[idx], sample);
220 vec![evicted]
221 }
222 }
223
224 fn finish(&mut self) -> Vec<Sample> {
225 let mut remaining = std::mem::take(&mut self.buffer);
227 for i in (1..remaining.len()).rev() {
229 #[allow(clippy::cast_possible_truncation)]
230 let j = (self.next_rand() as usize) % (i + 1);
231 remaining.swap(i, j);
232 }
233 remaining
234 }
235
236 fn name(&self) -> &str {
237 "shuffle"
238 }
239}
240
241pub struct BatchAccumulator {
243 batch_size: usize,
244 drop_last: bool,
245 buffer: Vec<Sample>,
246}
247
248impl BatchAccumulator {
249 pub fn new(batch_size: usize, drop_last: bool) -> Self {
251 Self {
252 batch_size: batch_size.max(1),
253 drop_last,
254 buffer: Vec::new(),
255 }
256 }
257}
258
259impl StatefulTransform for BatchAccumulator {
260 fn push(&mut self, sample: Sample) -> Vec<Sample> {
261 self.buffer.push(sample);
262 if self.buffer.len() >= self.batch_size {
263 std::mem::replace(&mut self.buffer, Vec::with_capacity(self.batch_size))
264 } else {
265 Vec::new()
266 }
267 }
268
269 fn finish(&mut self) -> Vec<Sample> {
270 if self.drop_last && self.buffer.len() < self.batch_size {
271 self.buffer.clear();
272 Vec::new()
273 } else {
274 std::mem::take(&mut self.buffer)
275 }
276 }
277
278 fn name(&self) -> &str {
279 "batch"
280 }
281}