Struct Counter

Source
pub struct Counter<const N: usize, const M: usize> { /* private fields */ }
Expand description

A counter useful for counting how many times a function invocation is called in last X seconds/minutes/hours. N is the number of buckets and M is the number of sub-buckets.

In the documentation and in the code, we use key to refer the temporal unit (e.g. seconds, minutes, hours) of the invocation. Because this library don’t want force you to use seconds, you can use any unit you want. You can consider to use std::time::Instant::elapsed().as_secs() as the key for instance.

Counter groups keys into buckets based on the group_shift_factor: key >> group_shift_factor % N will be the bucket index. The index for the sub-bucket is key % M.

§Internal structure

Internally, the Counter uses a ring buffer of N buckets. Each bucket has M sub-buckets. This allows the Counter to distribute the load across multiple sub-buckets when the keys have the same index.

For instance, Counter<3, 2>::new(4) will be like:

               ----------   ----------   ----------
               | (0, 0) |   | (0, 0) |   | (0, 0) |
               | (0, 0) |   | (0, 0) |   | (0, 0) |
               ----------   ----------   ----------
index              0            1            2
key range 1       0-16        17-31        32-47
key range 2      48-63        64-80         ...

Implementations§

Source§

impl<const N: usize, const M: usize> Counter<N, M>

Source

pub fn new(group_shift_factor: u32) -> Self

Create a new counter with the given group shift factor.

group_shift_factor is the number of bits to shift the key to get the group index.

Examples found in repository?
examples/readme.rs (line 9)
3fn main() {
4    const BUCKET_COUNT: usize = 16;
5    const SUB_BUCKET_COUNT: usize = 2;
6    const GROUP_SHIFT_FACTOR: u32 = 4;
7    // 4 is the group_shift_factor
8    // 16 is the number of buckets
9    let counter = Counter::<BUCKET_COUNT, SUB_BUCKET_COUNT>::new(GROUP_SHIFT_FACTOR);
10
11    // Typically you want to use something like `Instant::now().elapsed().as_secs()`
12    let mut now: u64 = 0;
13    counter.increment_by_one(now);
14
15    now += 1; // Simulate a second passing
16    counter.increment_by_one(now);
17
18    assert_eq!(counter.get_count_till(now), 2);
19
20    now += 2_u64.pow(GROUP_SHIFT_FACTOR) * BUCKET_COUNT as u64; // Move forward...
21    counter.increment_by_one(now);
22    // The counter forgot about the counts older than 2 ** 4 * 16 seconds
23    assert_eq!(counter.get_count_till(now), 1);
24}
Source

pub fn increment_by_one(&self, key: u64)

Register an invocation.

This will increment the value of the key by one. You can use std::time::Instant::elapsed().as_secs() as the key for instance.

Examples found in repository?
examples/readme.rs (line 13)
3fn main() {
4    const BUCKET_COUNT: usize = 16;
5    const SUB_BUCKET_COUNT: usize = 2;
6    const GROUP_SHIFT_FACTOR: u32 = 4;
7    // 4 is the group_shift_factor
8    // 16 is the number of buckets
9    let counter = Counter::<BUCKET_COUNT, SUB_BUCKET_COUNT>::new(GROUP_SHIFT_FACTOR);
10
11    // Typically you want to use something like `Instant::now().elapsed().as_secs()`
12    let mut now: u64 = 0;
13    counter.increment_by_one(now);
14
15    now += 1; // Simulate a second passing
16    counter.increment_by_one(now);
17
18    assert_eq!(counter.get_count_till(now), 2);
19
20    now += 2_u64.pow(GROUP_SHIFT_FACTOR) * BUCKET_COUNT as u64; // Move forward...
21    counter.increment_by_one(now);
22    // The counter forgot about the counts older than 2 ** 4 * 16 seconds
23    assert_eq!(counter.get_count_till(now), 1);
24}
Source

pub fn get_count_till(&self, key: u64) -> usize

Get the count of invocations till the given key.

This will return the total count of invocations till the given key. You can use std::time::Instant::elapsed().as_secs() as the key for instance.

Examples found in repository?
examples/readme.rs (line 18)
3fn main() {
4    const BUCKET_COUNT: usize = 16;
5    const SUB_BUCKET_COUNT: usize = 2;
6    const GROUP_SHIFT_FACTOR: u32 = 4;
7    // 4 is the group_shift_factor
8    // 16 is the number of buckets
9    let counter = Counter::<BUCKET_COUNT, SUB_BUCKET_COUNT>::new(GROUP_SHIFT_FACTOR);
10
11    // Typically you want to use something like `Instant::now().elapsed().as_secs()`
12    let mut now: u64 = 0;
13    counter.increment_by_one(now);
14
15    now += 1; // Simulate a second passing
16    counter.increment_by_one(now);
17
18    assert_eq!(counter.get_count_till(now), 2);
19
20    now += 2_u64.pow(GROUP_SHIFT_FACTOR) * BUCKET_COUNT as u64; // Move forward...
21    counter.increment_by_one(now);
22    // The counter forgot about the counts older than 2 ** 4 * 16 seconds
23    assert_eq!(counter.get_count_till(now), 1);
24}

Trait Implementations§

Source§

impl<const N: usize, const M: usize> Debug for Counter<N, M>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<const N: usize, const M: usize> !Freeze for Counter<N, M>

§

impl<const N: usize, const M: usize> RefUnwindSafe for Counter<N, M>

§

impl<const N: usize, const M: usize> Send for Counter<N, M>

§

impl<const N: usize, const M: usize> Sync for Counter<N, M>

§

impl<const N: usize, const M: usize> Unpin for Counter<N, M>

§

impl<const N: usize, const M: usize> UnwindSafe for Counter<N, M>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.