1use crate::{Duration, RwndAction, RwndDecision, RwndTrace};
57use dyn_clone::DynClone;
58
59#[cfg_attr(feature = "serde", typetag::serde)]
66pub trait RwndTraceConfig: DynClone + Send {
67 fn into_model(self: Box<Self>) -> Box<dyn RwndTrace>;
68}
69
70dyn_clone::clone_trait_object!(RwndTraceConfig);
71
72#[cfg(feature = "serde")]
73use serde::{Deserialize, Deserializer, Serialize, Serializer};
74
75#[derive(Debug, Clone)]
94pub struct StaticRwnd {
95 pub decision: RwndDecision,
96 pub duration: Option<Duration>,
97}
98
99#[derive(Debug, Clone, Default)]
110pub struct StaticRwndConfig {
111 pub duration: Option<Duration>,
112 pub set_rcv_buf: Option<u64>,
113 pub action: Option<RwndAction>,
114}
115
116#[cfg(feature = "serde")]
117impl<'de> Deserialize<'de> for StaticRwndConfig {
118 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
119 #[derive(Deserialize, Default)]
120 #[serde(default)]
121 struct Helper {
122 #[cfg_attr(feature = "human", serde(with = "humantime_serde"))]
123 #[serde(default)]
124 duration: Option<Duration>,
125 #[serde(default)]
126 set_rcv_buf: Option<u64>,
127 #[serde(default)]
128 app_read_bytes: Option<u64>,
129 #[serde(default)]
130 rwnd_remaining: Option<u64>,
131 }
132
133 let h = Helper::deserialize(deserializer)?;
134 let action = match (h.app_read_bytes, h.rwnd_remaining) {
135 (Some(bytes), None) => Some(RwndAction::AppRead { bytes }),
136 (None, Some(rwnd)) => Some(RwndAction::Remaining { rwnd }),
137 (Some(_), Some(_)) => {
138 return Err(serde::de::Error::custom(
139 "rwnd step cannot set both `app_read_bytes` and `rwnd_remaining`",
140 ));
141 }
142 (None, None) => None,
143 };
144 Ok(Self {
145 duration: h.duration,
146 set_rcv_buf: h.set_rcv_buf,
147 action,
148 })
149 }
150}
151
152#[cfg(feature = "serde")]
153impl Serialize for StaticRwndConfig {
154 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
155 #[derive(Serialize)]
156 struct Out {
157 #[serde(skip_serializing_if = "Option::is_none")]
158 #[cfg_attr(feature = "human", serde(with = "humantime_serde"))]
159 duration: Option<Duration>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 set_rcv_buf: Option<u64>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 app_read_bytes: Option<u64>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 rwnd_remaining: Option<u64>,
166 }
167
168 let (app_read_bytes, rwnd_remaining) = match &self.action {
169 Some(RwndAction::AppRead { bytes }) => (Some(*bytes), None),
170 Some(RwndAction::Remaining { rwnd }) => (None, Some(*rwnd)),
171 None => (None, None),
172 };
173 Out {
174 duration: self.duration,
175 set_rcv_buf: self.set_rcv_buf,
176 app_read_bytes,
177 rwnd_remaining,
178 }
179 .serialize(serializer)
180 }
181}
182
183pub struct RepeatedRwndPattern {
208 pub pattern: Vec<Box<dyn RwndTraceConfig>>,
209 pub count: usize,
210 current_model: Option<Box<dyn RwndTrace>>,
211 current_cycle: usize,
212 current_pattern: usize,
213}
214
215#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
219#[derive(Default, Clone)]
220pub struct RepeatedRwndPatternConfig {
221 pub pattern: Vec<Box<dyn RwndTraceConfig>>,
222 pub count: usize,
223}
224
225impl RwndTrace for StaticRwnd {
226 fn next_rwnd(&mut self) -> Option<(RwndDecision, Duration)> {
227 if let Some(duration) = self.duration.take() {
228 if duration.is_zero() {
229 None
230 } else {
231 Some((self.decision.clone(), duration))
232 }
233 } else {
234 None
235 }
236 }
237}
238
239impl RwndTrace for RepeatedRwndPattern {
240 fn next_rwnd(&mut self) -> Option<(RwndDecision, Duration)> {
241 let pattern_len = self.pattern.len();
242 let mut budget = pattern_len + 1;
248 loop {
249 if pattern_len == 0 || (self.count != 0 && self.current_cycle >= self.count) {
250 return None;
251 }
252 if budget == 0 {
253 return None;
254 }
255 if self.current_model.is_none() {
256 self.current_model = Some(self.pattern[self.current_pattern].clone().into_model());
257 }
258 match self.current_model.as_mut().unwrap().next_rwnd() {
259 Some(item) => return Some(item),
260 None => {
261 self.current_model = None;
262 budget -= 1;
263 self.current_pattern += 1;
264 if self.current_pattern >= pattern_len {
265 self.current_pattern = 0;
266 self.current_cycle += 1;
267 if self.count != 0 && self.current_cycle >= self.count {
268 return None;
269 }
270 }
271 }
272 }
273 }
274 }
275}
276
277impl StaticRwndConfig {
278 pub fn new() -> Self {
279 Self {
280 duration: None,
281 set_rcv_buf: None,
282 action: None,
283 }
284 }
285
286 pub fn duration(mut self, duration: Duration) -> Self {
287 self.duration = Some(duration);
288 self
289 }
290
291 pub fn set_rcv_buf(mut self, set_rcv_buf: u64) -> Self {
292 self.set_rcv_buf = Some(set_rcv_buf);
293 self
294 }
295
296 pub fn app_read(mut self, bytes: u64) -> Self {
297 self.action = Some(RwndAction::AppRead { bytes });
298 self
299 }
300
301 pub fn remaining(mut self, rwnd: u64) -> Self {
302 self.action = Some(RwndAction::Remaining { rwnd });
303 self
304 }
305
306 pub fn build(self) -> StaticRwnd {
307 StaticRwnd {
308 decision: RwndDecision {
309 set_rcv_buf: self.set_rcv_buf,
310 action: self.action,
311 },
312 duration: Some(self.duration.unwrap_or_else(|| Duration::from_secs(1))),
313 }
314 }
315}
316
317impl RepeatedRwndPatternConfig {
318 pub fn new() -> Self {
319 Self {
320 pattern: vec![],
321 count: 0,
322 }
323 }
324
325 pub fn pattern(mut self, pattern: Vec<Box<dyn RwndTraceConfig>>) -> Self {
326 self.pattern = pattern;
327 self
328 }
329
330 pub fn count(mut self, count: usize) -> Self {
331 self.count = count;
332 self
333 }
334
335 pub fn build(self) -> RepeatedRwndPattern {
336 RepeatedRwndPattern {
337 pattern: self.pattern,
338 count: self.count,
339 current_model: None,
340 current_cycle: 0,
341 current_pattern: 0,
342 }
343 }
344}
345
346macro_rules! impl_rwnd_trace_config {
347 ($name:ident) => {
348 #[cfg_attr(feature = "serde", typetag::serde)]
349 impl RwndTraceConfig for $name {
350 fn into_model(self: Box<$name>) -> Box<dyn RwndTrace> {
351 Box::new(self.build())
352 }
353 }
354 };
355}
356
357impl_rwnd_trace_config!(StaticRwndConfig);
358impl_rwnd_trace_config!(RepeatedRwndPatternConfig);
359
360#[cfg(test)]
361mod test {
362 use super::*;
363 use crate::model::StaticRwndConfig;
364 use crate::RwndTrace;
365
366 #[test]
367 fn test_static_rwnd_model_app_read() {
368 let mut static_rwnd = StaticRwndConfig::new()
369 .set_rcv_buf(65536)
370 .app_read(1024)
371 .duration(Duration::from_secs(1))
372 .build();
373 let (decision, duration) = static_rwnd.next_rwnd().unwrap();
374 assert_eq!(decision.set_rcv_buf, Some(65536));
375 assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
376 assert_eq!(duration, Duration::from_secs(1));
377 assert_eq!(static_rwnd.next_rwnd(), None);
378 }
379
380 #[test]
381 fn test_static_rwnd_model_remaining() {
382 let mut static_rwnd = StaticRwndConfig::new()
383 .remaining(32768)
384 .duration(Duration::from_secs(2))
385 .build();
386 let (decision, duration) = static_rwnd.next_rwnd().unwrap();
387 assert_eq!(decision.set_rcv_buf, None);
388 assert_eq!(decision.action, Some(RwndAction::Remaining { rwnd: 32768 }));
389 assert_eq!(duration, Duration::from_secs(2));
390 assert_eq!(static_rwnd.next_rwnd(), None);
391 }
392
393 #[test]
394 fn test_repeated_rwnd_pattern() {
395 let pat = vec![
396 Box::new(
397 StaticRwndConfig::new()
398 .app_read(1024)
399 .duration(Duration::from_secs(1)),
400 ) as Box<dyn RwndTraceConfig>,
401 Box::new(
402 StaticRwndConfig::new()
403 .remaining(32768)
404 .duration(Duration::from_secs(1)),
405 ) as Box<dyn RwndTraceConfig>,
406 ];
407 let mut model = RepeatedRwndPatternConfig::new()
408 .pattern(pat)
409 .count(2)
410 .build();
411 let next = model.next_rwnd().unwrap();
412 assert_eq!(next.0.action, Some(RwndAction::AppRead { bytes: 1024 }));
413 assert_eq!(next.1, Duration::from_secs(1));
414 let next = model.next_rwnd().unwrap();
415 assert_eq!(next.0.action, Some(RwndAction::Remaining { rwnd: 32768 }));
416 let next = model.next_rwnd().unwrap();
417 assert_eq!(next.0.action, Some(RwndAction::AppRead { bytes: 1024 }));
418 let next = model.next_rwnd().unwrap();
419 assert_eq!(next.0.action, Some(RwndAction::Remaining { rwnd: 32768 }));
420 assert_eq!(model.next_rwnd(), None);
421 }
422
423 #[test]
424 #[cfg(feature = "serde")]
425 fn test_serde_roundtrip_app_read() {
426 let cfg = Box::new(
427 StaticRwndConfig::new()
428 .set_rcv_buf(65536)
429 .app_read(1024)
430 .duration(Duration::from_secs(1)),
431 ) as Box<dyn RwndTraceConfig>;
432 let ser_str = serde_json::to_string(&cfg).unwrap();
433 #[cfg(feature = "human")]
434 let expected = "{\"StaticRwndConfig\":{\"duration\":\"1s\",\"set_rcv_buf\":65536,\"app_read_bytes\":1024}}";
435 #[cfg(not(feature = "human"))]
436 let expected = "{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"set_rcv_buf\":65536,\"app_read_bytes\":1024}}";
437 assert_eq!(ser_str, expected);
438
439 let des: Box<dyn RwndTraceConfig> = serde_json::from_str(&ser_str).unwrap();
440 let mut model = des.into_model();
441 let (decision, duration) = model.next_rwnd().unwrap();
442 assert_eq!(decision.set_rcv_buf, Some(65536));
443 assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
444 assert_eq!(duration, Duration::from_secs(1));
445 }
446
447 #[test]
448 #[cfg(feature = "serde")]
449 fn test_serde_roundtrip_remaining() {
450 let cfg = Box::new(
451 StaticRwndConfig::new()
452 .remaining(32768)
453 .duration(Duration::from_secs(1)),
454 ) as Box<dyn RwndTraceConfig>;
455 let ser_str = serde_json::to_string(&cfg).unwrap();
456 #[cfg(feature = "human")]
457 let expected = "{\"StaticRwndConfig\":{\"duration\":\"1s\",\"rwnd_remaining\":32768}}";
458 #[cfg(not(feature = "human"))]
459 let expected = "{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"rwnd_remaining\":32768}}";
460 assert_eq!(ser_str, expected);
461
462 let des: Box<dyn RwndTraceConfig> = serde_json::from_str(&ser_str).unwrap();
463 let mut model = des.into_model();
464 let (decision, _) = model.next_rwnd().unwrap();
465 assert_eq!(decision.action, Some(RwndAction::Remaining { rwnd: 32768 }));
466 }
467
468 #[test]
469 #[cfg(feature = "serde")]
470 fn test_serde_rejects_both() {
471 let json = "{\"StaticRwndConfig\":{\"app_read_bytes\":1024,\"rwnd_remaining\":32768}}";
474 let result: Result<Box<dyn RwndTraceConfig>, _> = serde_json::from_str(json);
475 let err = result
476 .err()
477 .expect("deserialization should have failed")
478 .to_string();
479 assert!(
480 err.contains("cannot set both"),
481 "expected 'cannot set both' in error, got: {err}"
482 );
483 }
484
485 #[test]
486 fn test_static_rwnd_set_rcv_buf_only() {
487 let mut model = StaticRwndConfig::new()
488 .set_rcv_buf(131072)
489 .duration(Duration::from_secs(1))
490 .build();
491 let (decision, duration) = model.next_rwnd().unwrap();
492 assert_eq!(decision.set_rcv_buf, Some(131072));
493 assert_eq!(decision.action, None);
494 assert_eq!(duration, Duration::from_secs(1));
495 assert_eq!(model.next_rwnd(), None);
496 }
497
498 #[test]
499 #[cfg(feature = "serde")]
500 fn test_serde_roundtrip_set_rcv_buf_only() {
501 let cfg = Box::new(
502 StaticRwndConfig::new()
503 .set_rcv_buf(131072)
504 .duration(Duration::from_secs(1)),
505 ) as Box<dyn RwndTraceConfig>;
506 let ser_str = serde_json::to_string(&cfg).unwrap();
507 #[cfg(feature = "human")]
508 let expected = "{\"StaticRwndConfig\":{\"duration\":\"1s\",\"set_rcv_buf\":131072}}";
509 #[cfg(not(feature = "human"))]
510 let expected =
511 "{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"set_rcv_buf\":131072}}";
512 assert_eq!(ser_str, expected);
513
514 let des: Box<dyn RwndTraceConfig> = serde_json::from_str(&ser_str).unwrap();
515 let mut model = des.into_model();
516 let (decision, duration) = model.next_rwnd().unwrap();
517 assert_eq!(decision.set_rcv_buf, Some(131072));
518 assert_eq!(decision.action, None);
519 assert_eq!(duration, Duration::from_secs(1));
520 assert_eq!(model.next_rwnd(), None);
521 }
522
523 #[test]
524 #[cfg(feature = "serde")]
525 fn test_serde_action_none_when_neither_set() {
526 let json = "{\"StaticRwndConfig\":{\"set_rcv_buf\":65536}}";
528 let des: Box<dyn RwndTraceConfig> = serde_json::from_str(json).unwrap();
529 let mut model = des.into_model();
530 let (decision, _) = model.next_rwnd().unwrap();
531 assert_eq!(decision.set_rcv_buf, Some(65536));
532 assert_eq!(decision.action, None);
533 }
534
535 #[test]
536 fn test_repeated_rwnd_pattern_all_zero_duration_terminates() {
537 let pat = vec![
541 Box::new(
542 StaticRwndConfig::new()
543 .app_read(1024)
544 .duration(Duration::ZERO),
545 ) as Box<dyn RwndTraceConfig>,
546 Box::new(
547 StaticRwndConfig::new()
548 .remaining(32768)
549 .duration(Duration::ZERO),
550 ) as Box<dyn RwndTraceConfig>,
551 ];
552 let mut model = RepeatedRwndPatternConfig::new()
553 .pattern(pat)
554 .count(0) .build();
556 assert_eq!(model.next_rwnd(), None);
557 }
558}