use crate::{ allocator::{biggest_region_allocator, count_allocator, AllocError, Allocator}, behavior::Behavior, Checked, IdAble, Wrapping, }; use core::{marker::PhantomData, ops::Range}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// generic fast Pool that manages a range of IDs, which can be access via /// `::next()` methods or transfered to a subpool /// /// the range of IDs this pool owns can be fragmented /// unused IDs will be returned to parent Pool if available (not yet coded) #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Pool { alloc: Allocator, #[allow(dead_code)] parent_channel: Option<()>, pub(crate) buffer: Range, //buffer is keept directly so that allocator isn't needed normally pub(crate) original_range: Range, // for wrapping pub(crate) one: B::Id, //convert only once _p: PhantomData, } impl Pool { pub fn new(range: Range) -> Self { Pool { alloc: Allocator::new(range.start..range.start), /* empty */ parent_channel: None, buffer: range.clone(), original_range: range, one: B::Id::from(1), _p: PhantomData::default(), } } #[cfg(feature = "num-traits")] pub fn new_full() -> Self where B::Id: num_traits::Bounded, { use crate::num_traits::Bounded; let range = B::Id::min_value()..B::Id::max_value(); Self::new(range) } /// Return a new ID, according to the Behavior of this Pool /// main function of this crate /// for details view the description in the respective Behavior /// `behavior.rs` pub fn next(&mut self) -> B::Return { B::next(self) } /// takes `size` IDs from own pool and transfers it to a subpool /// subpool can be moved to another thread and be safely used there within /// range creation of subpools takes a few hundred nanos pub fn subpool(&mut self, size: B::Id) -> Result> { self.unused_to_alloc(); match self.alloc.alloc(size) { Ok(alloc_range) => { let pool = Pool::new(alloc_range); self.re_buffer(); Ok(pool) }, Err(e) => { self.re_buffer(); Err(e) }, } } ///moves all unused ids back to our allocator, for further processing fn unused_to_alloc(&mut self) { self.alloc.free(self.buffer.clone()); //invalidate buffer self.buffer.end = self.buffer.start; } ///moves all unused ids back to our allocator, for further processing fn re_buffer(&mut self) { let size = biggest_region_allocator(&self.alloc); let r = self.alloc.alloc(size); debug_assert!(r.is_ok()); self.buffer = r.unwrap(); } } /// special subpool function that allows generation of a Wrapping Pool /// from a Checked Pool as a Wrapping pool does not violate the promises /// of Checked Pool /// Note: There is no such method to generate a UnChecked Pool from Checked /// pool, or any other pools than this #[allow(dead_code)] pub fn subpool_wrapping( pool: &mut Pool>, size: Id, ) -> Result>, AllocError> { if size < Id::from(1) { // negative size is not supported in Wrapping return Err(AllocError::SizeNotAvailable(size)); } pool.unused_to_alloc(); match pool.alloc.alloc(size) { Ok(alloc_range) => { let wpool = Pool::new(alloc_range); pool.re_buffer(); Ok(wpool) }, Err(e) => { pool.re_buffer(); Err(e) }, } } impl core::fmt::Debug for Pool where B::Id: core::fmt::Display, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let size = count_allocator(&self.alloc) + self.buffer.end - self.buffer.start; write!(f, "Pool({})", size) } } impl core::fmt::Display for Pool where B::Id: core::fmt::Display, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let size = count_allocator(&self.alloc) + self.buffer.end - self.buffer.start; write!(f, "Pool({})", size) } } /// Dropping Pool returns Id's to Parent pool, if it still exists (not yet /// implemented) impl Drop for Pool { fn drop(&mut self) { self.unused_to_alloc(); //TODO: notify parent } } #[cfg(test)] mod tests { use crate::{allocator::*, behavior::*, pool::*}; #[test] fn subpool() { let mut pool = Pool::>::new(0..10); assert!(pool.subpool(3).is_ok()); assert_eq!(pool.next(), Ok(3)); assert!(pool.subpool(3).is_ok()); assert_eq!(pool.next(), Ok(7)); assert_eq!(pool.next(), Ok(8)); assert_eq!(pool.next(), Ok(9)); } #[test] fn subpool_checked_to_wrapping() { let mut pool = Pool::>::new(0..8); assert!(pool.subpool(3).is_ok()); assert_eq!(pool.next(), Ok(3)); let wrapping = subpool_wrapping(&mut pool, 2); assert!(wrapping.is_ok()); let mut wrapping = wrapping.unwrap(); assert_eq!(pool.next(), Ok(6)); assert_eq!(pool.next(), Ok(7)); assert_eq!(pool.next(), Err(CheckedError::PoolEmpty)); assert_eq!(pool.next(), Err(CheckedError::PoolEmpty)); // Pool is depleted but wrapping still works assert_eq!(wrapping.next(), 4); assert_eq!(wrapping.next(), 5); assert_eq!(wrapping.next(), 4); assert_eq!(wrapping.next(), 5); assert_eq!(wrapping.next(), 4); assert_eq!(wrapping.next(), 5); } #[test] fn subpool_checked_to_wrapping_invalid() { let mut pool = Pool::>::new(0..8); assert!(pool.subpool(3).is_ok()); let wrapping = subpool_wrapping(&mut pool, 0); assert!(wrapping.is_err()); assert_eq!(wrapping.unwrap_err(), AllocError::SizeNotAvailable(0)); let wrapping = subpool_wrapping(&mut pool, 999); assert!(wrapping.is_err()); assert_eq!(wrapping.unwrap_err(), AllocError::SizeNotAvailable(999)); } #[test] fn subpool_error() { let mut pool = Pool::>::new(0..10); assert!(pool.subpool(3).is_ok()); assert_eq!(pool.next(), Ok(3)); assert!(pool.subpool(3).is_ok()); assert_eq!(pool.next(), Ok(7)); assert!(pool.subpool(3).is_err()); assert_eq!(pool.next(), Ok(8)); assert_eq!(pool.next(), Ok(9)); assert_eq!(pool.next(), Err(CheckedError::PoolEmpty)); } #[test] fn subpool_format() { let pool = Pool::>::new(0..10); assert_eq!(format!("{}", pool), "Pool(10)"); assert_eq!(format!("{:?}", pool), "Pool(10)"); } }