Skip to main content

netem_trace/model/
rwnd.rs

1//! This module contains some predefined rwnd trace models.
2//!
3//! Enabled with feature `rwnd-model` or `model`.
4//!
5//! ## Predefined models
6//!
7//! - [`StaticRwnd`]: A trace model with a single rwnd decision.
8//! - [`RepeatedRwndPattern`]: A trace model with a repeated rwnd pattern.
9//!
10//! ## Examples
11//!
12//! An example to build model from configuration:
13//!
14//! ```
15//! # use netem_trace::model::StaticRwndConfig;
16//! # use netem_trace::{Duration, RwndTrace, RwndAction};
17//! let mut static_rwnd = StaticRwndConfig::new()
18//!     .set_rcv_buf(65536)
19//!     .app_read(1024)
20//!     .duration(Duration::from_secs(1))
21//!     .build();
22//! let (decision, duration) = static_rwnd.next_rwnd().unwrap();
23//! assert_eq!(decision.set_rcv_buf, Some(65536));
24//! assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
25//! assert_eq!(duration, Duration::from_secs(1));
26//! assert_eq!(static_rwnd.next_rwnd(), None);
27//! ```
28//!
29//! A more common use case is to build model from a configuration file (e.g. json file):
30//!
31//! ```
32//! # use netem_trace::model::{StaticRwndConfig, RwndTraceConfig};
33//! # use netem_trace::{Duration, RwndTrace, RwndAction};
34//! # #[cfg(feature = "human")]
35//! # let config_file_content = "{\"RepeatedRwndPatternConfig\":{\"pattern\":[{\"StaticRwndConfig\":{\"duration\":\"1s\",\"set_rcv_buf\":65536,\"app_read_bytes\":1024}},{\"StaticRwndConfig\":{\"duration\":\"1s\",\"rwnd_remaining\":32768}}],\"count\":2}}";
36//! // The content would be "{\"RepeatedRwndPatternConfig\":{\"pattern\":[{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"set_rcv_buf\":65536,\"app_read_bytes\":1024}},{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"rwnd_remaining\":32768}}],\"count\":2}}"
37//! // if the `human` feature is not enabled.
38//! # #[cfg(not(feature = "human"))]
39//! let config_file_content = "{\"RepeatedRwndPatternConfig\":{\"pattern\":[{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"set_rcv_buf\":65536,\"app_read_bytes\":1024}},{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"rwnd_remaining\":32768}}],\"count\":2}}";
40//! let des: Box<dyn RwndTraceConfig> = serde_json::from_str(config_file_content).unwrap();
41//! let mut model = des.into_model();
42//! let (decision, _) = model.next_rwnd().unwrap();
43//! assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
44//! let (decision, _) = model.next_rwnd().unwrap();
45//! assert_eq!(decision.action, Some(RwndAction::Remaining { rwnd: 32768 }));
46//! let (decision, _) = model.next_rwnd().unwrap();
47//! assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
48//! let (decision, _) = model.next_rwnd().unwrap();
49//! assert_eq!(decision.action, Some(RwndAction::Remaining { rwnd: 32768 }));
50//! assert_eq!(model.next_rwnd(), None);
51//! ```
52//!
53//! At most one of `app_read_bytes` or `rwnd_remaining` may be set per step —
54//! never both. A step with neither produces [`RwndDecision::action`] as `None`,
55//! which is valid for steps that only reconfigure the receive buffer.
56use crate::{Duration, RwndAction, RwndDecision, RwndTrace};
57use dyn_clone::DynClone;
58
59/// This trait is used to convert a rwnd trace configuration into a rwnd trace model.
60///
61/// Since trace model is often configured with files and often has inner states which
62/// is not suitable to be serialized/deserialized, this trait makes it possible to
63/// separate the configuration part into a simple struct for serialization/deserialization, and
64/// construct the model from the configuration.
65#[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/// The model of a static rwnd trace: a single decision valid for one duration.
76///
77/// ## Examples
78///
79/// ```
80/// # use netem_trace::model::StaticRwndConfig;
81/// # use netem_trace::{Duration, RwndTrace, RwndAction};
82/// let mut static_rwnd = StaticRwndConfig::new()
83///     .set_rcv_buf(65536)
84///     .app_read(1024)
85///     .duration(Duration::from_secs(1))
86///     .build();
87/// let (decision, duration) = static_rwnd.next_rwnd().unwrap();
88/// assert_eq!(decision.set_rcv_buf, Some(65536));
89/// assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
90/// assert_eq!(duration, Duration::from_secs(1));
91/// assert_eq!(static_rwnd.next_rwnd(), None);
92/// ```
93#[derive(Debug, Clone)]
94pub struct StaticRwnd {
95    pub decision: RwndDecision,
96    pub duration: Option<Duration>,
97}
98
99/// The configuration struct for [`StaticRwnd`].
100///
101/// The serialized JSON form is **flat**: a step looks like
102/// `{"duration":"1s","set_rcv_buf":65536,"app_read_bytes":1024}` (or
103/// `{"duration":"1s","rwnd_remaining":32768}`), never with an `action` wrapper.
104///
105/// At most one of `app_read_bytes` / `rwnd_remaining` may be set; the deserializer
106/// rejects inputs where both are present. A step with neither is valid and produces
107/// [`RwndDecision::action`] as `None` (useful for steps that only reconfigure the
108/// receive buffer).
109#[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
183/// The model contains an array of rwnd trace models.
184///
185/// Combine multiple rwnd trace models into one rwnd pattern,
186/// and repeat the pattern for `count` times.
187///
188/// If `count` is 0, the pattern will be repeated forever.
189///
190/// ## Examples
191///
192/// The most common use case is to read from a configuration file and
193/// deserialize it into a [`RepeatedRwndPatternConfig`]:
194///
195/// ```
196/// # use netem_trace::model::{StaticRwndConfig, RwndTraceConfig};
197/// # use netem_trace::{Duration, RwndTrace, RwndAction};
198/// # #[cfg(feature = "human")]
199/// # let config_file_content = "{\"RepeatedRwndPatternConfig\":{\"pattern\":[{\"StaticRwndConfig\":{\"duration\":\"1s\",\"set_rcv_buf\":65536,\"app_read_bytes\":1024}},{\"StaticRwndConfig\":{\"duration\":\"1s\",\"rwnd_remaining\":32768}}],\"count\":2}}";
200/// # #[cfg(not(feature = "human"))]
201/// let config_file_content = "{\"RepeatedRwndPatternConfig\":{\"pattern\":[{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"set_rcv_buf\":65536,\"app_read_bytes\":1024}},{\"StaticRwndConfig\":{\"duration\":{\"secs\":1,\"nanos\":0},\"rwnd_remaining\":32768}}],\"count\":2}}";
202/// let des: Box<dyn RwndTraceConfig> = serde_json::from_str(config_file_content).unwrap();
203/// let mut model = des.into_model();
204/// let (decision, _) = model.next_rwnd().unwrap();
205/// assert_eq!(decision.action, Some(RwndAction::AppRead { bytes: 1024 }));
206/// ```
207pub 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/// The configuration struct for [`RepeatedRwndPattern`].
216///
217/// See [`RepeatedRwndPattern`] for more details.
218#[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        // Allow at most pattern_len + 1 consecutive inner-None results before
243        // giving up. The +1 covers a possibly-exhausted current_model at entry;
244        // after that, each remaining slot is a fresh clone whose behaviour is
245        // deterministic. If all pattern_len fresh clones return None, the
246        // pattern will never produce a value regardless of count.
247        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        // Omit duration to avoid the human/non-human format ambiguity; we're testing
472        // the action constraint, not duration parsing.
473        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        // A step with only set_rcv_buf and no action fields should deserialize to action: None.
527        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        // All inner models have duration == 0 and return None immediately.
538        // With count == 0 (infinite repeat) the old recursive implementation
539        // would spin forever; the loop-based one must return None promptly.
540        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) // infinite
555            .build();
556        assert_eq!(model.next_rwnd(), None);
557    }
558}