idbag 0.2.3

A bag of integers.
Documentation
//! An id bag is conceptually a bag that initially contains (all) intger
//! values of the type of the bag.  An application can request to allocate an
//! id from the bag, which behaves like the identifier has been taken out of
//! the bag.  Once the id is dropped its integer will automatically be
//! returned to the bag and be available for allocation again.
//!
//! Allocated identifier objects are hashable and derefernces to its internal
//! integer value.
//!
//! # Introductionary example
//! ```
//! use idbag::IdBagU32;
//!
//! // Create a new id bag.
//! let idbag = IdBagU32::new();
//!
//! // Allocate an id; first id will have a value of 1.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 1);
//!
//! // The id's drop handler returns 1 to the idbag
//! drop(id);
//!
//! // Allocate another id; next id will have a value of 2.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 2);
//!
//! // The id's drop handler returns 2 to the idbag
//! drop(id);
//! ```
//!
//! # Assigned integer semantics
//! The assigned integer increases by one each time a new id has been allocated
//! and wraps around at its maximum value.

#![forbid(missing_docs)]
#![forbid(clippy::missing_docs_in_private_items)]

use std::{
  borrow::Borrow,
  fmt,
  hash::{Hash, Hasher},
  sync::{Arc, Weak}
};

use parking_lot::Mutex;

use hashbrown::HashSet;

use paste::paste;

/// Implement an id bag for an integer type.
macro_rules! impl_Bag {
  ($int_type:ident $int_name:ident) => {
    paste! {
      #[doc = "Internal representation of the  [`IdBag" $int_name "`]."]
      #[derive(Default)]
      pub struct [<InnerBag $int_name>] {
        set: HashSet<$int_type>,
        idgen: $int_type
      }
    }

    paste! {
      #[doc = "A collection of allocatable `" $int_type "` integers."]
      #[derive(Default)]
      #[repr(transparent)]
      pub struct [<IdBag $int_name>](Arc<Mutex<[<InnerBag $int_name>]>>);
    }

    paste! {
      impl [<IdBag $int_name>] {
        #[doc = "Create a new id bag containing `" $int_type "` integers."]
        #[must_use]
        pub fn new() -> Self {
          Self::default()
        }

        #[doc = r#"
        Create a new id generator using a pre-existing set.

        This is useful when multiple `IdBag` instances should share the
        same inner context.  The practical implication of this is that the
        `IdBag`'s that share the same inner context will not yield overlapping
        identifier values.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag1 = IdBag"# $int_name r#"::new();
        let idbag2 = IdBag"# $int_name r#"::with_inner(idbag1.clone_inner());

        let id1 = idbag1.alloc();
        assert_eq!(id1.val(), 1);

        let id2 = idbag2.alloc();
        assert_eq!(id2.val(), 2);
        ```
        "#]
        pub const fn with_inner(
          inner: Arc<Mutex<[<InnerBag $int_name>]>>
        ) -> Self {
          Self(inner)
        }

        /// Return a clone of the internal id collection.
        #[must_use]
        pub fn clone_inner(&self) -> Arc<Mutex<[<InnerBag $int_name>]>> {
          Arc::clone(&self.0)
        }


        #[doc = r#"
        Allocate a new identifier.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag = IdBag"# $int_name r#"::new();

        let id = idbag.alloc();
        assert_eq!(id.val(), 1);

        let id = idbag.alloc();
        assert_eq!(id.val(), 2);
        ```
        # Desgin flaws
        This function will hang if all the id's have been exhausted.
        "#]
        #[must_use]
        pub fn alloc(&self) -> [<Id $int_name>] {
          let mut g = self.0.lock();
          // ToDo: exhaust-deadlock
          let id = loop {
            g.idgen = g.idgen.wrapping_add(1);
            if !g.set.contains(&g.idgen) {
              break g.idgen;
            }
          };
          g.set.insert(id);
          drop(g);
          [<Id $int_name>] {
            id,
            idbag: Arc::downgrade(&self.0)
          }
        }


        #[doc = r#"
        Allocate a specific id, if available.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag = IdBag"# $int_name r#"::new();

        let Some(id) = idbag.alloc_id(42) else {
          panic!("Unexpectedly unable to allocate id 42");
        };
        assert_eq!(id.val(), 42);

        let id = idbag.alloc_id(42);
        assert_eq!(id, None);
        ```
        "#]
        #[must_use]
        pub fn alloc_id(&self, id: $int_type) -> Option<[<Id $int_name>]> {
          let mut g = self.0.lock();
          if g.set.contains(&id) {
            None
          } else {
            g.set.insert(id);
            drop(g);
            Some([<Id $int_name>] {
              id,
              idbag: Arc::downgrade(&self.0)
            })
          }
        }
      }
    }

    paste! {
      #[doc = r#"
      An allocated `"# $int_type r#"` identifier.
      "#]
      pub struct [<Id $int_name>] {
        id: $int_type,
        idbag: Weak<Mutex<[<InnerBag $int_name>]>>
      }

      impl fmt::Debug for [<Id $int_name>] {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(f, "Id:{}", self.id)
        }
      }

      impl Hash for [<Id $int_name>] {
        fn hash<H>(&self, state: &mut H)
        where
          H: Hasher
        {
          self.id.hash(state);
        }
      }

      impl PartialEq for [<Id $int_name>] {
        fn eq(&self, other: &Self) -> bool {
          self.id == other.id
        }
      }

      impl Eq for [<Id $int_name>] {}

      impl Borrow<$int_type> for [<Id $int_name>] {
        #[doc = r#"
        Allow `Id"# $int_name r#"` to be borrowed as a `&"# $int_type r#"`.

        This allows a `HashMap<Id"# $int_name r#", T>` to look up entries
        using a `&"# $int_type r#"`.
        "#]
        fn borrow(&self) -> &$int_type {
          &self.id
        }
      }

      impl [<Id $int_name>] {
        #[doc = r#"Return the internal `"# $int_type r#"` value."#]
        #[must_use]
        #[deprecated(since = "0.2.0", note = "Use `get()` method instead.")]
        pub const fn val(&self) -> $int_type {
          self.id
        }

        #[doc = r#"Return the internal `"# $int_type r#"` value."#]
        #[must_use]
        pub const fn get(&self) -> $int_type {
          self.id
        }

        #[doc = r#"
        Turn object into an [`ArcId"# $int_name r#"`].
        ```
        let idbag = idbag::IdBag"# $int_name r#"::new();

        let aid = idbag.alloc().into_arcid();
        ```
        "#]
        #[must_use]
        pub fn into_arcid(self) -> [<ArcId $int_name>] {
          [<ArcId $int_name>](Arc::new(self))
        }
      }

      impl Drop for [<Id $int_name>] {
        #[doc = r#"
        When an [`Id"# $int_name r#"`] is dropped, return the internal integer
        to the collection.
        "#]
        fn drop(&mut self) {
          if let Some(idbag) = self.idbag.upgrade() {
            let mut g = idbag.lock();
            g.set.remove(&self.id);
          }
        }
      }
    }


    paste! {
      #[doc = r#"
      An atomically referenced counted version of [`Id"# $int_name r#"`].

      This exists primarily because it should be possible to look up
      `Arc<Id"# $int_name r#">`'s by `"# $int_type r#"`'s in `HashMap`'s.
      While it is possible to look up `Id`'s in `HashMap`'s using
      `&"# $int_type r#"`'s, it doesn't work for `Arc<Id"# $int_name r#">`'s.
      To workaround this limitation this `ArcId"# $int_name r#"` newtype
      implements `Borrow<"# $int_type r#">`, which passes it through the
      `Arc` to it's inner `Id"# $int_name r#"`.
      "#]
      #[derive(Clone, Hash, PartialEq, Eq)]
      #[repr(transparent)]
      pub struct [<ArcId $int_name>](Arc<[<Id $int_name>]>);

      impl fmt::Debug for [<ArcId $int_name>] {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(f, "Id:{}", self.0.id)
        }
      }

      impl [<ArcId $int_name>] {
        #[doc = r#"
        Return the internal identifier `"# $int_type r#"` value.
        "#]
        #[must_use]
        pub fn val(&self) -> $int_type {
          self.0.get()
        }

        #[doc = r#"
        Returns the inner value, if the `ArcId` has exactly one strong
        reference.

        # Errors
        If there's more than one strong reference to the
        `ArcId"# $int_name r#"`, the `ArcId"# $int_name r#"` will be returned.
        "#]
        pub fn try_unwrap(this: Self) -> Result<[<Id $int_name>], Self> {
          Arc::<[<Id $int_name>]>::try_unwrap(this.0)
            .map_err([<ArcId $int_name>])
        }

        #[doc = r#"
        Returns the inner value, if the `ArcId"# $int_name r#"` has exactly
        one strong reference.
        "#]
        #[must_use]
        pub fn into_inner(this: Self) -> Option<[<Id $int_name>]> {
          Arc::<[<Id $int_name>]>::into_inner(this.0)
        }


        #[doc = r"
        Return the strong count of internal `Arc`.
        "]
        #[must_use]
        pub fn strong_count(&self) -> usize {
          Arc::strong_count(&self.0)
        }
      }

      impl Borrow<$int_type> for [<ArcId $int_name>] {
        fn borrow(&self) -> &$int_type {
          (*self.0).borrow()
        }
      }
    }
  };
}

impl_Bag! { u8 U8 }
impl_Bag! { u16 U16 }
impl_Bag! { u32 U32 }
impl_Bag! { u64 U64 }
impl_Bag! { usize Usize }

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :