utally 1.0.1

Unique ids in static contexts
Documentation
#![warn(missing_docs)]
//! # UTally
//!
//! This tool was created to allow easily creating unique ids for types in both static and dynamic
//! contexts. The ids are sequential, this is basically a tally counter.
//!
//! # Usage
//!
//! Call [next] to acquire a new unique id.
//!
//! ```rust
//! let unique_id = utally::next();
//! ```
//! ## Using it in a static context
//!
//! You can use [LazyTally] in order to assign ids to types or functions by using them in static
//! contexts:
//!
//! ```rust
//! # use utally::*;
//! pub fn function_with_unique_id() -> usize {
//!     static SOME_TALLY: LazyTally = LazyTally::new();
//!     SOME_TALLY.get()
//! }
//! ```
//!
//! A call to `function_with_unique_id` would always return the same unique id.
use std::sync::{
    Once,
    atomic::{AtomicUsize, Ordering},
};

/// Global tally counter
#[unsafe(export_name = "__utally_global_counter")]
pub static GLOBAL_COUNTER: Tally = Tally::new(1);

#[unsafe(export_name = "utally_next")]
/// Acquires a new unique id. This adds 1 to the tally.
pub extern "C" fn next() -> usize {
    GLOBAL_COUNTER.next()
}

#[unsafe(export_name = "utally_peek")]
/// Peeks to see which value would be returned in the next call for [next]. Because of concurrency,
/// this does not mean you can call [next] after [peek] and get the same result in both. This
/// function is designed to be used only in case you want to know where the tally is at, without
/// increasing the values.
pub extern "C" fn peek() -> usize {
    GLOBAL_COUNTER.peek()
}

/// A tally counter
#[repr(C)]
pub struct Tally {
    tally: AtomicUsize,
}

impl Tally {
    /// Creates a new tally counter initializing at `initial`.
    pub const fn new(initial: usize) -> Tally {
        Tally {
            tally: AtomicUsize::new(initial),
        }
    }

    /// Adds 1 to the tally and returns the previous value.
    pub fn next(&self) -> usize {
        self.tally.fetch_add(1, Ordering::Relaxed)
    }

    /// Returns the current value of the tally counter.
    pub fn peek(&self) -> usize {
        self.tally.load(Ordering::Relaxed)
    }
}

/// A lazy tally value. Each object of this type has an unique tally value that is assigned
/// at the first call to [LazyTally::get].
#[repr(C)]
pub struct LazyTally {
    value: AtomicUsize,
    once: Once,
}

impl LazyTally {
    /// Creates a new [LazyTally] object. This object can store a unique id which is assigned to it
    /// at the first call to [LazyTally::get].
    pub const fn new() -> Self {
        LazyTally {
            value: AtomicUsize::new(0),
            once: Once::new(),
        }
    }

    /// Gets the tally for this object. Subsequent calls to this function will
    /// return the same value on this object. The guarantee is that different
    /// objects will return different values.
    pub fn get(&self) -> usize {
        let mut val = self.value.load(Ordering::Relaxed);
        self.once.call_once(|| {
            val = crate::next();
            self.value.store(val, Ordering::Relaxed);
        });
        val
    }
}
impl Default for LazyTally {
    fn default() -> Self {
        Self::new()
    }
}