diff --git a/Cargo.toml b/Cargo.toml index fe55dad..be138df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ name = "wheel_timer" version = "0.3.1" description = "A simple hashed wheel timer." authors = [ "jim@barkingmousestudio.com" ] +edition = "2018" license = "MIT" homepage = "https://crates.io/crates/wheel_timer" documentation = "http://barkingmousestudio.com/wheel-timer-rs/wheel_timer/" diff --git a/benches/bench_wheel_timer.rs b/benches/bench_wheel_timer.rs index 6797be7..a912bc1 100644 --- a/benches/bench_wheel_timer.rs +++ b/benches/bench_wheel_timer.rs @@ -1,7 +1,6 @@ #![feature(test)] extern crate test; -extern crate wheel_timer; use test::Bencher; @@ -9,43 +8,43 @@ use wheel_timer::WheelTimer; #[bench] fn bench_wheel_timer_drain(b: &mut Bencher) { - let max_interval = 20; - let mut timer = WheelTimer::new(max_interval); - - b.iter(|| { - // Fill - for j in 0..100 { - timer.schedule(j%max_interval, j%max_interval); - } - - // Drain - for _ in 0..100 { - timer.tick(); - } - }); + let max_interval = 20; + let mut timer = WheelTimer::new(max_interval); + + b.iter(|| { + // Fill + for j in 0..100 { + timer.schedule(j % max_interval, j % max_interval); + } + + // Drain + for _ in 0..100 { + timer.tick(); + } + }); } #[bench] fn bench_wheel_timer_fill(b: &mut Bencher) { - let max_interval = 20; - let mut timer = WheelTimer::new(max_interval); - let mut i = 0; - - b.iter(|| { - timer.schedule(i%max_interval, i%max_interval); - i = i + 1; - }); + let max_interval = 20; + let mut timer = WheelTimer::new(max_interval); + let mut i = 0; + + b.iter(|| { + timer.schedule(i % max_interval, i % max_interval); + i += 1; + }); } #[bench] fn bench_wheel_timer_fast(b: &mut Bencher) { - let max_interval = 2; - let mut timer = WheelTimer::new(max_interval); - let mut i = 0; - - b.iter(|| { - timer.schedule(i%max_interval, i%max_interval); - timer.tick(); - i = i + 1; - }); + let max_interval = 2; + let mut timer = WheelTimer::new(max_interval); + let mut i = 0; + + b.iter(|| { + timer.schedule(i % max_interval, i % max_interval); + timer.tick(); + i += 1; + }); } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..792ff5f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,133 @@ +//! Simple hashed wheel timer with bounded interval. +//! +//! Relevant: +//! http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf + +use std::mem; +use std::ops::IndexMut; + +/// A simple wheel timer implementation with a fixed ring size. +pub struct WheelTimer { + max_interval: usize, + current_tick: usize, + size: usize, + + ring: Vec>, +} + +/// Iterator implementation allows for using the wheel timer in a for loop. +/// +/// # Example +/// +/// ``` +/// use wheel_timer::WheelTimer; +/// +/// let mut timer: WheelTimer = WheelTimer::new(20); +/// for result in timer { +/// // result is a vector of the values at that step +/// } +/// ``` +impl Iterator for WheelTimer { + type Item = Vec; + + fn next(&mut self) -> Option> { + if self.size() > 0 { + Some(self.tick()) + } else { + None + } + } +} + +impl WheelTimer { + /// Creates a new timer with the specified max interval. + /// + /// # Example + /// + /// ``` + /// use wheel_timer::WheelTimer; + /// + /// let mut timer: WheelTimer = WheelTimer::new(20); + /// ``` + pub fn new(max_interval: usize) -> WheelTimer { + // Initialize the ring with Nil values + let mut ring = Vec::with_capacity(max_interval); + for _ in 0..max_interval { + ring.push(Vec::new()) + } + + WheelTimer { + max_interval, + current_tick: 0, + ring, + size: 0, + } + } + + /// Returns the number of items currently scheduled. + /// + /// # Example + /// + /// ``` + /// use wheel_timer::WheelTimer; + /// + /// let mut timer: WheelTimer = WheelTimer::new(20); + /// timer.schedule(4, 1); + /// timer.schedule(7, 1); + /// timer.schedule(1, 1); + /// assert_eq!(timer.size(), 3); + /// ``` + pub fn size(&self) -> usize { + self.size + } + + /// Schedules a new value, available after `ticks` have passed. + /// + /// # Example + /// + /// ``` + /// use wheel_timer::WheelTimer; + /// + /// let mut timer: WheelTimer = WheelTimer::new(20); + /// timer.schedule(4, 7); // schedule value 7 for 4 ticks + /// ``` + pub fn schedule(&mut self, ticks: usize, value: T) { + // Compute the scheduled position in the wheel + let index = (self.current_tick + ticks) % self.max_interval; + + // Get the current node at `index` in the wheel and append the new node + self.ring.index_mut(index).push(value); + + // Increment the size counter + self.size += 1; + } + + /// Tick the timer, returning the node at the current tick. + /// + /// # Example + /// + /// ``` + /// use wheel_timer::WheelTimer; + /// + /// let mut timer: WheelTimer = WheelTimer::new(20); + /// timer.schedule(3, 4); // schedule value 4 for 3 ticks + /// timer.tick(); + /// timer.tick(); + /// timer.tick(); + /// let result = timer.tick(); // vec![4] + /// assert_eq!(result.len(), 1); + /// ``` + pub fn tick(&mut self) -> Vec { + // Get the node at the current tick in the wheel + let node = mem::replace(self.ring.index_mut(self.current_tick), Vec::new()); + + // Increment the timer + self.current_tick = (self.current_tick + 1) % self.max_interval; + + // Reduce the size by the length of the removed node + self.size -= node.len(); + + // Return the node that was in that spot + node + } +} diff --git a/src/wheel_timer.rs b/src/wheel_timer.rs deleted file mode 100644 index b200b58..0000000 --- a/src/wheel_timer.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! Simple hashed wheel timer with bounded interval. -//! -//! Relevant: -//! http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf - -use std::mem; -use std::ops::IndexMut; - -/// A simple wheel timer implementation with a fixed ring size. -pub struct WheelTimer { - max_interval: usize, - current_tick: usize, - size: usize, - - ring: Vec> -} - -/// Iterator implementation allows for using the wheel timer in a for loop. -/// -/// # Example -/// -/// ``` -/// use wheel_timer::WheelTimer; -/// -/// let mut timer: WheelTimer = WheelTimer::new(20); -/// for result in timer { -/// // result is a vector of the values at that step -/// } -/// ``` -impl Iterator for WheelTimer { - type Item = Vec; - - fn next(&mut self) -> Option> { - let size = self.size(); - return if size > 0 { - Some(self.tick()) - } else { - None - }; - } -} - -impl WheelTimer { - - /// Creates a new timer with the specified max interval. - /// - /// # Example - /// - /// ``` - /// use wheel_timer::WheelTimer; - /// - /// let mut timer: WheelTimer = WheelTimer::new(20); - /// ``` - pub fn new(max_interval: usize) -> WheelTimer { - // Initialize the ring with Nil values - let mut ring = Vec::with_capacity(max_interval); - for _ in 0..max_interval { - ring.push(Vec::new()) - } - - return WheelTimer{ - max_interval: max_interval, - current_tick: 0, - ring: ring, - size: 0, - } - } - - /// Returns the number of items currently scheduled. - /// - /// # Example - /// - /// ``` - /// use wheel_timer::WheelTimer; - /// - /// let mut timer: WheelTimer = WheelTimer::new(20); - /// timer.schedule(4, 1); - /// timer.schedule(7, 1); - /// timer.schedule(1, 1); - /// assert_eq!(timer.size(), 3); - /// ``` - pub fn size(&self) -> usize { - self.size - } - - /// Schedules a new value, available after `ticks` have passed. - /// - /// # Example - /// - /// ``` - /// use wheel_timer::WheelTimer; - /// - /// let mut timer: WheelTimer = WheelTimer::new(20); - /// timer.schedule(4, 7); // schedule value 7 for 4 ticks - /// ``` - pub fn schedule(&mut self, ticks: usize, value: T) { - // Compute the scheduled position in the wheel - let index = (self.current_tick + ticks) % self.max_interval; - - // Get the current node at `index` in the wheel and append the new node - self.ring.index_mut(index).push(value); - - // Increment the size counter - self.size = self.size + 1; - } - - /// Tick the timer, returning the node at the current tick. - /// - /// # Example - /// - /// ``` - /// use wheel_timer::WheelTimer; - /// - /// let mut timer: WheelTimer = WheelTimer::new(20); - /// timer.schedule(3, 4); // schedule value 4 for 3 ticks - /// timer.tick(); - /// timer.tick(); - /// timer.tick(); - /// let result = timer.tick(); // vec![4] - /// assert_eq!(result.len(), 1); - /// ``` - pub fn tick(&mut self) -> Vec { - // Get the node at the current tick in the wheel - let node = mem::replace(self.ring.index_mut(self.current_tick), Vec::new()); - - // Increment the timer - self.current_tick = (self.current_tick + 1) % self.max_interval; - - // Reduce the size by the length of the removed node - self.size = self.size - node.len(); - - // Return the node that was in that spot - return node - } -} diff --git a/tests/test_wheel_timer.rs b/tests/test_wheel_timer.rs index 719d969..a4e7596 100644 --- a/tests/test_wheel_timer.rs +++ b/tests/test_wheel_timer.rs @@ -1,53 +1,51 @@ -extern crate wheel_timer; - use wheel_timer::WheelTimer; #[test] fn wheel_timer_schedule_test() { - let mut timer = WheelTimer::new(10); - timer.schedule(3, "tick"); + let mut timer = WheelTimer::new(10); + timer.schedule(3, "tick"); - timer.tick(); - timer.tick(); - timer.tick(); + timer.tick(); + timer.tick(); + timer.tick(); - let list = timer.tick(); - assert_eq!(list.len(), 1); + let list = timer.tick(); + assert_eq!(list.len(), 1); - let val = list[0]; - assert_eq!(val, "tick"); + let val = list[0]; + assert_eq!(val, "tick"); } #[test] fn wheel_timer_tick_test() { - let mut timer = WheelTimer::new(10); + let mut timer = WheelTimer::new(10); - for i in 0..10 { - timer.schedule(i, i) - } + for i in 0..10 { + timer.schedule(i, i) + } - for i in 0..10 { - let list = timer.tick(); - assert_eq!(list.len(), 1); + for i in 0..10 { + let list = timer.tick(); + assert_eq!(list.len(), 1); - let val = list[0]; - assert_eq!(val, i); - } + let val = list[0]; + assert_eq!(val, i); + } } #[test] fn wheel_timer_size_test() { - let mut timer = Box::new(WheelTimer::new(10)); + let mut timer = Box::new(WheelTimer::new(10)); - for i in 0..10 { - timer.schedule(i, i) - } + for i in 0..10 { + timer.schedule(i, i) + } - assert_eq!(timer.size(), 10); + assert_eq!(timer.size(), 10); - for _ in 0..10 { - timer.tick(); - } + for _ in 0..10 { + timer.tick(); + } - assert_eq!(timer.size(), 0); + assert_eq!(timer.size(), 0); }