@@ -74,7 +74,7 @@ pub trait DelayPerPacketTraceConfig: DynClone + Send + std::fmt::Debug {
7474dyn_clone:: clone_trait_object!( DelayPerPacketTraceConfig ) ;
7575
7676use rand:: { rngs:: StdRng , RngCore , SeedableRng } ;
77- use rand_distr:: { Distribution , LogNormal , Normal } ;
77+ use rand_distr:: { Distribution , LogNormal , Normal , Uniform } ;
7878#[ cfg( feature = "serde" ) ]
7979use serde:: { Deserialize , Serialize } ;
8080
@@ -210,6 +210,67 @@ pub struct RepeatedDelayPerPacketPatternConfig {
210210 pub count : usize ,
211211}
212212
213+ /// The model of a per-packet delay trace subjects to a uniform distribution.
214+ ///
215+ /// The delay will subject to a uniform distribution in [lower_bound, upper_bound]
216+ ///
217+ /// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
218+ ///
219+ /// ## Examples
220+ ///
221+ /// Default values for lower_bound and upper_bound are 0ms and 10ms, respectively.
222+ ///
223+ /// ```
224+ /// # use netem_trace::model::UniformDelayPerPacketConfig;
225+ /// # use netem_trace::{Delay, DelayPerPacketTrace};
226+ /// let mut uniform_delay = UniformDelayPerPacketConfig::new()
227+ /// .upper_bound(Delay::from_micros(12100))
228+ /// .lower_bound(Delay::from_micros(11900))
229+ /// .count(2)
230+ /// .seed(42)
231+ /// .build();
232+ /// assert_eq!(uniform_delay.next_delay(), Some(Delay::from_nanos(12005311)));
233+ /// assert_eq!(uniform_delay.next_delay(), Some(Delay::from_nanos(12008545)));
234+ /// assert_eq!(uniform_delay.next_delay(), None);
235+ /// ```
236+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
237+ pub struct UniformDelayPerPacket < Rng = StdRng >
238+ where
239+ Rng : RngCore ,
240+ {
241+ pub upper_bound : Delay ,
242+ pub lower_bound : Delay ,
243+ pub seed : u64 ,
244+ pub count : usize ,
245+ current_count : usize ,
246+ rng : Rng ,
247+ uniform : Uniform < f64 > ,
248+ }
249+
250+ /// The configuration struct for [`UniformDelayPerPacket`].
251+ ///
252+ /// See [`UniformDelayPerPacket`] for more details.
253+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) , serde( default ) ) ]
254+ #[ derive( Default , Debug , Clone , Copy , PartialEq ) ]
255+ pub struct UniformDelayPerPacketConfig {
256+ #[ cfg_attr( feature = "serde" , serde( skip_serializing_if = "Option::is_none" ) ) ]
257+ #[ cfg_attr(
258+ all( feature = "serde" , feature = "human" ) ,
259+ serde( with = "humantime_serde" )
260+ ) ]
261+ pub upper_bound : Option < Delay > ,
262+ #[ cfg_attr( feature = "serde" , serde( skip_serializing_if = "Option::is_none" ) ) ]
263+ #[ cfg_attr(
264+ all( feature = "serde" , feature = "human" ) ,
265+ serde( with = "humantime_serde" )
266+ ) ]
267+ pub lower_bound : Option < Delay > ,
268+ #[ cfg_attr( feature = "serde" , serde( default ) ) ]
269+ pub count : usize ,
270+ #[ cfg_attr( feature = "serde" , serde( skip_serializing_if = "Option::is_none" ) ) ]
271+ pub seed : Option < u64 > ,
272+ }
273+
213274/// The model of a per-packet delay trace subjects to a normal distribution.
214275///
215276/// The delay will subject to N(mean, std_dev²), but bounded within [lower_bound, upper_bound] (optional)
@@ -435,6 +496,18 @@ impl DelayPerPacketTrace for RepeatedDelayPerPacketPattern {
435496 }
436497}
437498
499+ impl < Rng : RngCore + Send > DelayPerPacketTrace for UniformDelayPerPacket < Rng > {
500+ fn next_delay ( & mut self ) -> Option < Delay > {
501+ if self . count != 0 && self . count == self . current_count {
502+ None
503+ } else {
504+ self . current_count += 1 ;
505+ let delay = self . uniform . sample ( & mut self . rng ) . max ( 0.0 ) ;
506+ Some ( Delay :: from_secs_f64 ( delay) )
507+ }
508+ }
509+ }
510+
438511impl < Rng : RngCore + Send > DelayPerPacketTrace for NormalizedDelayPerPacket < Rng > {
439512 fn next_delay ( & mut self ) -> Option < Delay > {
440513 if self . count != 0 && self . count == self . current_count {
@@ -525,6 +598,124 @@ impl RepeatedDelayPerPacketPatternConfig {
525598 }
526599}
527600
601+ impl UniformDelayPerPacketConfig {
602+ /// Creates an uninitialized config
603+ pub fn new ( ) -> Self {
604+ Self {
605+ upper_bound : None ,
606+ lower_bound : None ,
607+ count : 0 ,
608+ seed : None ,
609+ }
610+ }
611+
612+ /// Sets the upper bound
613+ ///
614+ /// If the upper bound is not set, the upper bound will be 10ms.
615+ pub fn upper_bound ( mut self , upper_bound : Delay ) -> Self {
616+ self . upper_bound = Some ( upper_bound) ;
617+ self
618+ }
619+
620+ /// Sets the lower bound
621+ ///
622+ /// If the lower bound is not set, 0ms will be used.
623+ pub fn lower_bound ( mut self , lower_bound : Delay ) -> Self {
624+ self . lower_bound = Some ( lower_bound) ;
625+ self
626+ }
627+
628+ /// Sets the number of packets to repeat
629+ ///
630+ /// If the count is not set, it will be set to 0 (ie, infinite repeat).
631+ pub fn count ( mut self , count : usize ) -> Self {
632+ self . count = count;
633+ self
634+ }
635+
636+ /// Set the seed for a random generator
637+ ///
638+ /// If the seed is not set, `42` will be used.
639+ pub fn seed ( mut self , seed : u64 ) -> Self {
640+ self . seed = Some ( seed) ;
641+ self
642+ }
643+
644+ /// Allows to use a randomly generated seed
645+ ///
646+ /// This is equivalent to: `self.seed(rand::random())`
647+ pub fn random_seed ( mut self ) -> Self {
648+ self . seed = Some ( rand:: random ( ) ) ;
649+ self
650+ }
651+
652+ /// Creates a new [`UniformDelayPerPacket`] corresponding to this config.
653+ ///
654+ /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
655+ /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
656+ /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
657+ /// to this end one can use [`build_with_rng`](Self::build_with_rng).
658+ pub fn build ( self ) -> UniformDelayPerPacket {
659+ self . build_with_rng ( )
660+ }
661+
662+ /// Creates a new [`UniformDelayPerPacket`] corresponding to this config.
663+ ///
664+ /// Unlike [`build`](Self::build), this method let you choose the random generator.
665+ ///
666+ /// # Example
667+ /// ```rust
668+ /// # use netem_trace::model::UniformDelayPerPacketConfig;
669+ /// # use netem_trace::{Delay, DelayPerPacketTrace};
670+ /// # use rand::rngs::StdRng;
671+ /// # use rand_chacha::ChaCha20Rng;
672+ ///
673+ /// let uniform_delay = UniformDelayPerPacketConfig::new()
674+ /// .upper_bound(Delay::from_micros(12100))
675+ /// .lower_bound(Delay::from_micros(11900))
676+ /// .count(3)
677+ /// .seed(42);
678+ ///
679+ /// let mut default_build = uniform_delay.clone().build();
680+ /// let mut std_build = uniform_delay.clone().build_with_rng::<StdRng>();
681+ /// // ChaCha is deterministic and portable, unlike StdRng
682+ /// let mut chacha_build = uniform_delay.clone().build_with_rng::<ChaCha20Rng>();
683+ ///
684+ /// for cha in [12002810, 11982040, 11919563] {
685+ /// let default = default_build.next_delay();
686+ /// let std = std_build.next_delay();
687+ /// let chacha = chacha_build.next_delay();
688+ ///
689+ /// assert!(default.is_some());
690+ /// assert_eq!(default, std);
691+ /// assert_ne!(default, chacha);
692+ /// assert_eq!(chacha, Some(Delay::from_nanos(cha)));
693+ /// }
694+ ///
695+ /// assert_eq!(default_build.next_delay(), None);
696+ /// assert_eq!(std_build.next_delay(), None);
697+ /// assert_eq!(chacha_build.next_delay(), None);
698+ /// ```
699+ pub fn build_with_rng < Rng : RngCore + SeedableRng > ( self ) -> UniformDelayPerPacket < Rng > {
700+ let upper_bound = self . upper_bound . unwrap_or ( Delay :: from_millis ( 10 ) ) ;
701+ let lower_bound = self . lower_bound . unwrap_or ( Delay :: ZERO ) ;
702+ let count = self . count ;
703+ let seed = self . seed . unwrap_or ( DEFAULT_RNG_SEED ) ;
704+ let rng = Rng :: seed_from_u64 ( seed) ;
705+ let uniform: Uniform < f64 > =
706+ Uniform :: new ( lower_bound. as_secs_f64 ( ) , upper_bound. as_secs_f64 ( ) ) . unwrap ( ) ;
707+ UniformDelayPerPacket {
708+ upper_bound,
709+ lower_bound,
710+ count,
711+ current_count : 0 ,
712+ seed,
713+ rng,
714+ uniform,
715+ }
716+ }
717+ }
718+
528719impl NormalizedDelayPerPacketConfig {
529720 /// Creates an uninitialized config
530721 pub fn new ( ) -> Self {
@@ -898,6 +1089,7 @@ macro_rules! impl_delay_per_packet_trace_config {
8981089}
8991090
9001091impl_delay_per_packet_trace_config ! ( StaticDelayPerPacketConfig ) ;
1092+ impl_delay_per_packet_trace_config ! ( UniformDelayPerPacketConfig ) ;
9011093impl_delay_per_packet_trace_config ! ( NormalizedDelayPerPacketConfig ) ;
9021094impl_delay_per_packet_trace_config ! ( LogNormalizedDelayPerPacketConfig ) ;
9031095impl_delay_per_packet_trace_config ! ( RepeatedDelayPerPacketPatternConfig ) ;
0 commit comments