lifeguard-0.6.1/.cargo_vcs_info.json0000644000000001121375607723700130530ustar { "git": { "sha1": "92e5ab46cc089df84eddd40149f92ef13a4b5c58" } } lifeguard-0.6.1/.github/workflows/rust.yml0100644€±ƒN0000024000000004741375607573400172740ustar 00000000000000name: Rust on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose lifeguard-0.6.1/.gitignore0100644€±ƒN0000024000000000661375606354200141360ustar 00000000000000target Cargo.lock **callgrind.out** .idea/** **/*.iml lifeguard-0.6.1/Cargo.toml0000644000000015441375607723700110630ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "lifeguard" version = "0.6.1" authors = ["Zack Slayton "] description = "An object pool manager in Rust." readme = "README.md" keywords = ["memory", "allocate", "collect", "pool", "recycle"] license = "MIT" repository = "https://github.com/zslayton/lifeguard" [lib] name = "lifeguard" [dependencies] lifeguard-0.6.1/Cargo.toml.orig0100644€±ƒN0000024000000005311375607636100150350ustar 00000000000000[package] name = "lifeguard" version = "0.6.1" authors = ["Zack Slayton "] repository = "https://github.com/zslayton/lifeguard" readme = "README.md" description = "An object pool manager in Rust." keywords = [ "memory", "allocate", "collect", "pool", "recycle" ] license = "MIT" [lib] name = "lifeguard" [dependencies] lifeguard-0.6.1/LICENSE0100644€±ƒN0000024000000020671375606354200131560ustar 00000000000000The MIT License (MIT) Copyright (c) 2015 Zack Slayton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lifeguard-0.6.1/README.md0100644€±ƒN0000024000000117371375606354200134340ustar 00000000000000# lifeguard [![](https://api.travis-ci.org/zslayton/lifeguard.png?branch=master)](https://travis-ci.org/zslayton/lifeguard) [![](http://meritbadge.herokuapp.com/lifeguard)](https://crates.io/crates/lifeguard) ## Object Pool Manager [API Documentation](http://zslayton.github.io/lifeguard/lifeguard/) ### Examples `Pool` issues owned values wrapped in smartpointers. ```rust extern crate lifeguard; use lifeguard::*; fn main() { let pool : Pool = pool().with(StartingSize(10)).build(); { let string = pool.new_from("Hello, World!"); // Remove a value from the pool assert_eq!(9, pool.size()); } // Values that have gone out of scope are automatically moved back into the pool. assert_eq!(10, pool.size()); } ``` Values taken from the pool can be dereferenced to access/mutate their contents. ```rust extern crate lifeguard; use lifeguard::*; fn main() { let pool : Pool = pool().with(StartingSize(10)).build(); let mut string = pool.new_from("cat"); string.push_str("s love eating mice"); //string.as_mut() also works assert_eq!("cats love eating mice", *string); } ``` Values can be unwrapped, detaching them from the pool. ```rust extern crate lifeguard; use lifeguard::*; fn main() { let pool : Pool = pool().with(StartingSize(10)).build(); { let string : String = pool.new().detach(); } // The String goes out of scope and is dropped; it is not returned to the pool assert_eq!(9, pool.size()); } ``` Values can be manually entered into / returned to the pool. ```rust extern crate lifeguard; use lifeguard::*; fn main() { let pool : Pool = pool().with(StartingSize(10)).build(); { let string : String = pool.detached(); // An unwrapped String, detached from the Pool assert_eq!(9, pool.size()); let rstring : Recycled = pool.attach(string); // The String is attached to the pool again assert_eq!(9, pool.size()); // but it is still checked out from the pool } // rstring goes out of scope and is added back to the pool assert_eq!(10, pool.size()); } ``` `Pool`'s builder API can be used to customize the behavior of the pool. ```rust extern crate lifeguard; use lifeguard::*; fn main() { let pool : Pool = pool() // The pool will allocate 128 values for immediate use. More will be allocated on demand. .with(StartingSize(128)) // The pool will only grow up to 4096 values. Further values will be dropped. .with(MaxSize(4096)) // The pool will use this closure (or other object implementing Supply) to allocate .with(Supplier(|| String::with_capacity(1024))) .build(); // ... } ``` ### Highly Unscientific Benchmarks Benchmark source can be found [here](https://github.com/zslayton/lifeguard/blob/master/benches/lib.rs). Tests were run on an early 2015 MacBook Pro. Each benchmark comes in three flavors: 1. `tests::*_standard`: Uses the system allocator to create new values. 2. `tests::*_pooled_rc`: Uses a `Pool` to create new values which hold `Rc` references to the `Pool`. These values can be freely passed to other scopes. 3. `tests::*_pooled`: Uses a `Pool` to create new values which hold `&` locally-scoped references to the `Pool`. These values are the cheapest to create but are bound to the lifetime of the `Pool`. #### Uninitialized Allocation Compares the cost of allocating a new `String` (using `String::with_capacity`, as `String::new` does not allocate immediately) with the cost of retrieving a `String` from the pool. ```ignore tests::allocation_standard ... bench: 5,322,513 ns/iter (+/- 985,898) tests::allocation_pooled_rc ... bench: 784,885 ns/iter (+/- 95,245) tests::allocation_pooled ... bench: 565,864 ns/iter (+/- 66,036) ``` #### Initialized Allocation Compares the cost of allocating a new `String` and initializing it to a given value (via `&str::to_owned`) with the cost of retrieving a `String` from the pool and initializing it to the same value. ```ignore tests::initialized_allocation_standard ... bench: 5,329,948 ns/iter (+/- 547,725) tests::initialized_allocation_pooled_rc ... bench: 1,151,493 ns/iter (+/- 119,293) tests::initialized_allocation_pooled ... bench: 927,214 ns/iter (+/- 147,935) ``` #### Vec<Vec<String>> Allocation Creates a two-dimensional vector of initialized Strings. All `Vec`s and `String`s created are from a `Pool` where applicable. Adapted from [this benchmark](https://github.com/frankmcsherry/recycler/blob/master/benches/benches.rs#L10). ```ignore tests::vec_vec_str_standard ... bench: 1,353,906 ns/iter (+/- 142,094) tests::vec_vec_str_pooled_rc ... bench: 298,087 ns/iter (+/- 168,703) tests::vec_vec_str_pooled ... bench: 251,082 ns/iter (+/- 24,408) ``` Ideas and PRs welcome! Inspired by frankmcsherry's [recycler](https://github.com/frankmcsherry/recycler). lifeguard-0.6.1/benches/lib.rs0100644€±ƒN0000024000000132651375606354200146760ustar 00000000000000#![feature(test)] extern crate test; extern crate lifeguard; #[cfg(test)] mod tests { use test::Bencher; use test::black_box; use lifeguard::{Pool,Recycled,RcRecycled}; const ITERATIONS : u32 = 10_000; // Calling String::new() is very close to a no-op; no actual allocation // is performed until bytes are pushed onto the end of the String. As such, // we need to explicitly ask for some space to be available to trigger allocation. const EMPTY_STRING_CAPACITY : usize = 4; #[bench] fn allocation_standard(b: &mut Bencher) { b.iter(|| { for _ in 0..ITERATIONS { let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY)); let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY)); let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY)); let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY)); let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY)); } }); } #[bench] fn allocation_pooled(b: &mut Bencher) { let pool : Pool = Pool::with_size(5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new(); let _string = pool.new(); let _string = pool.new(); let _string = pool.new(); let _string = pool.new(); } }); } #[bench] fn allocation_pooled_rc(b: &mut Bencher) { let pool : Pool = Pool::with_size(5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_rc(); let _string = pool.new_rc(); let _string = pool.new_rc(); let _string = pool.new_rc(); let _string = pool.new_rc(); } }); } #[bench] fn initialized_allocation_standard(b: &mut Bencher) { b.iter(|| { for _ in 0..ITERATIONS { let _string = "man".to_owned(); let _string = "dog".to_owned(); let _string = "cat".to_owned(); let _string = "mouse".to_owned(); let _string = "cheese".to_owned(); } }); } #[bench] fn initialized_allocation_pooled(b: &mut Bencher) { let pool : Pool = Pool::with_size(5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_from("man"); let _string = pool.new_from("dog"); let _string = pool.new_from("cat"); let _string = pool.new_from("mouse"); let _string = pool.new_from("cheese"); } }); } #[bench] fn initialized_allocation_pooled_rc(b: &mut Bencher) { let pool : Pool = Pool::with_size(5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_rc_from("man"); let _string = pool.new_rc_from("dog"); let _string = pool.new_rc_from("cat"); let _string = pool.new_rc_from("mouse"); let _string = pool.new_rc_from("cheese"); } }); } #[bench] fn initialized_allocation_pooled_with_cap_empty(b: &mut Bencher) { let pool : Pool = Pool::with_size_and_max(0, 5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_from("man"); let _string = pool.new_from("dog"); let _string = pool.new_from("cat"); let _string = pool.new_from("mouse"); let _string = pool.new_from("cheese"); } }); } #[bench] fn initialized_allocation_pooled_with_cap_full(b: &mut Bencher) { let pool : Pool = Pool::with_size_and_max(5, 5); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_from("man"); let _string = pool.new_from("dog"); let _string = pool.new_from("cat"); let _string = pool.new_from("mouse"); let _string = pool.new_from("cheese"); } }); } #[bench] fn initialized_allocation_pooled_with_low_cap(b: &mut Bencher) { let pool : Pool = Pool::with_size_and_max(0, 2); b.iter(|| { for _ in 0..ITERATIONS { let _string = pool.new_from("man"); let _string = pool.new_from("dog"); let _string = pool.new_from("cat"); let _string = pool.new_from("mouse"); let _string = pool.new_from("cheese"); } }); } #[bench] fn vec_vec_str_standard(bencher: &mut Bencher) { bencher.iter(|| { let mut v1 = Vec::new(); for _ in 0..100 { let mut v2 = Vec::new(); for _ in 0..100 { v2.push(("test!").to_owned()); } v1.push(v2); } v1 }); } #[bench] fn vec_vec_str_pooled(bencher: &mut Bencher) { // Note that because we're using scoped values (not Rc'ed values) // and we're storing items from one pool in the other, // the order that our pools are declared matters. // Reversing them results in a compile error regarding lifetimes. let str_pool : Pool = Pool::with_size(10000); let vec_str_pool : Pool>> = Pool::with_size(100); bencher.iter(|| { let mut v1 = Vec::new(); for _ in 0..100 { let mut v2 = vec_str_pool.new(); for _ in 0..100 { v2.push(str_pool.new_from("test!")); } v1.push(v2); } v1 }); } #[bench] fn vec_vec_str_pooled_rc(bencher: &mut Bencher) { let vec_str_pool : Pool>> = Pool::with_size(100); let str_pool : Pool = Pool::with_size(10000); bencher.iter(|| { let mut v1 = Vec::new(); for _ in 0..100 { let mut v2 = vec_str_pool.new_rc(); for _ in 0..100 { v2.push(str_pool.new_rc_from("test!")); } v1.push(v2); } v1 }); } } lifeguard-0.6.1/examples/.cargo/config0100644€±ƒN0000024000000000201375606354200163130ustar 00000000000000paths = ["../"] lifeguard-0.6.1/src/lib.rs0100644€±ƒN0000024000000457001375607573400140630ustar 00000000000000#[allow(dead_code)] use std::rc::Rc; use std::cell::RefCell; use std::fmt; use std::ops::{Drop, Deref, DerefMut}; use std::convert::{AsRef, AsMut}; use std::cmp::{Ord, PartialOrd, PartialEq, Eq, Ordering}; use std::hash::{Hash, Hasher}; use std::borrow::Borrow; use std::collections::VecDeque; use std::mem::ManuallyDrop; use std::ptr; /// In order to be managed by a `Pool`, values must be of a type that /// implements the `Recycleable` trait. This allows the `Pool` to create /// new instances as well as reset existing instances to a like-new state. pub trait Recycleable { /// Allocates a new instance of the implementing type. fn new() -> Self; /// Sets the state of the modified instance to be that of a freshly /// allocated instance, thereby allowing it to be reused. fn reset(&mut self); } /// Informs how an already allocated value should be initialized /// when provided with a model value or other meaningful input. pub trait InitializeWith { fn initialize_with(&mut self, source: T); } impl Recycleable for String { #[inline] fn new() -> String { String::new() } #[inline] fn reset(&mut self) { self.clear(); } } impl Recycleable for Vec { #[inline] fn new() -> Vec { Vec::new() } #[inline] fn reset(&mut self) { self.clear(); } } impl Recycleable for VecDeque { #[inline] fn new() -> VecDeque { VecDeque::new() } #[inline] fn reset(&mut self) { self.clear(); } } impl InitializeWith for String where A : AsRef { #[inline] fn initialize_with(&mut self, source: A) { let s : &str = source.as_ref(); self.push_str(s); } } impl InitializeWith for Vec where I: Iterator{ #[inline] fn initialize_with(&mut self, source: I) { self.extend(source); } } /// A smartpointer which uses a shared reference (`&`) to know /// when to move its wrapped value back to the `Pool` that /// issued it. pub struct Recycled<'a, T: 'a> where T: Recycleable { value: RecycledInner<&'a RefCell>, T> } /// A smartpointer which uses reference counting (`Rc`) to know /// when to move its wrapped value back to the `Pool` that /// issued it. pub struct RcRecycled where T: Recycleable { value: RecycledInner>>, T> } macro_rules! impl_recycled { ($name: ident, $typ: ty, $pool: ty) => { impl <'a, T> AsRef for $typ where T : Recycleable { /// Gets a shared reference to the value wrapped by the smartpointer. fn as_ref(&self) -> &T { self.value.as_ref() } } impl <'a, T> AsMut for $typ where T : Recycleable { /// Gets a mutable reference to the value wrapped by the smartpointer. fn as_mut(&mut self) -> &mut T { self.value.as_mut() } } impl <'a, T> fmt::Debug for $typ where T : fmt::Debug + Recycleable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl <'a, T> fmt::Display for $typ where T : fmt::Display + Recycleable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } //-------- Passthrough trait implementations ----------- impl <'a, T> PartialEq for $typ where T : PartialEq + Recycleable { fn eq(&self, other: &Self) -> bool { self.value.eq(&other.value) } } impl <'a, T> Eq for $typ where T: Eq + Recycleable {} impl <'a, T> PartialOrd for $typ where T: PartialOrd + Recycleable { fn partial_cmp(&self, other: &Self) -> Option { self.value.partial_cmp(&other.value) } } impl <'a, T> Ord for $typ where T: Ord + Recycleable { fn cmp(&self, other: &Self) -> Ordering { self.value.cmp(&other.value) } } impl <'a, T> Hash for $typ where T: Hash + Recycleable { fn hash(&self, state: &mut H) { self.value.hash(state) } } //------------------------------------------------------ impl <'a, T> Deref for $typ where T : Recycleable { type Target = T; #[inline] fn deref(&self) -> &T { self.as_ref() } } impl <'a, T> DerefMut for $typ where T : Recycleable { #[inline] fn deref_mut(&mut self) -> &mut T { self.as_mut() } } impl <'a, T> $typ where T: Recycleable { fn new(pool: $pool, value: T) -> $typ { $name { value: RecycledInner::new(pool, value) } } #[inline] fn new_from(pool: $pool, value: T, source: A) -> $typ where T : InitializeWith { $name { value: RecycledInner::new_from(pool, value, source) } } #[inline] /// Disassociates the value from the `Pool` that issued it. This /// destroys the smartpointer and returns the previously wrapped value. pub fn detach(self) -> T { self.value.detach() } } } } impl_recycled!{ RcRecycled, RcRecycled, Rc>> } impl_recycled!{ Recycled, Recycled<'a, T>, &'a RefCell> } impl Clone for RcRecycled where T: Clone + Recycleable { fn clone(&self) -> Self { RcRecycled { value: self.value.clone() } } } impl <'a, T> Clone for Recycled<'a, T> where T: Clone + Recycleable { fn clone(&self) -> Self { Recycled { value: self.value.clone() } } } struct RecycledInner where P: Borrow>>, T : Recycleable { value: ManuallyDrop, pool: P } // ---------- Passthrough Trait Implementations ------------ impl PartialEq for RecycledInner where P: Borrow>>, T: PartialEq + Recycleable { fn eq(&self, other: &Self) -> bool { self.value.eq(&other.value) } } impl Eq for RecycledInner where P: Borrow>>, T: Eq + Recycleable { } impl PartialOrd for RecycledInner where P: Borrow>>, T: PartialOrd + Recycleable { fn partial_cmp(&self, other: &Self) -> Option { self.value.partial_cmp(&other.value) } } impl Ord for RecycledInner where P: Borrow>>, T: Ord + Recycleable { fn cmp(&self, other: &Self) -> Ordering { self.value.cmp(&other.value) } } impl Hash for RecycledInner where P: Borrow>>, T: Hash + Recycleable { fn hash(&self, state: &mut H) { self.value.hash(state) } } // Implementing Clone requires duplicating our shared reference to the capped collection, so we have // to provide separate implementations for RecycledInners used in Recycled and RcRecycled values. impl <'a, T> Clone for RecycledInner<&'a RefCell>, T> where T: Clone + Recycleable { fn clone(&self) -> Self { let pool_ref = &*self.pool; let mut cloned_value = pool_ref.borrow_mut().remove_or_create(); cloned_value.clone_from(&self.value); RecycledInner { value: ManuallyDrop::new(cloned_value), pool: pool_ref } } } impl Clone for RecycledInner>>, T> where T: Clone + Recycleable { fn clone(&self) -> Self { let pool_ref = self.pool.clone(); let mut cloned_value = pool_ref.borrow_mut().remove_or_create(); cloned_value.clone_from(&self.value); RecycledInner { value: ManuallyDrop::new(cloned_value), pool: pool_ref } } } // ------------------------------------------------------------- impl Drop for RecycledInner where P: Borrow>>, T : Recycleable { #[inline] fn drop(&mut self) { // We need to rescue `self.value` from `self` and then allow `self` to drop normally. let value = unsafe { // Make a byte-for-byte copy of `self.value` ptr::read(&self.value as *const ManuallyDrop) // Because its type is ManuallyDrop, the original value inside of `self` will not be // dropped when `self` drops. }; // Convert our newly-rescued `ManuallyDrop` into a `T` so it will eventually drop normally. let mut value = ManuallyDrop::into_inner(value); // If there's no room left in the pool, drop the value here. let pool_ref = self.pool.borrow(); if pool_ref.borrow().is_full() { drop(value); return; } // Otherwise, reset value and return it to the pool. value.reset(); pool_ref.borrow_mut().insert_prepared_value(value); } } impl AsRef for RecycledInner where P: Borrow>>, T : Recycleable { fn as_ref(&self) -> &T { &self.value } } impl AsMut for RecycledInner where P: Borrow>>, T : Recycleable { fn as_mut(&mut self) -> &mut T { &mut self.value } } impl fmt::Debug for RecycledInner where P: Borrow>>, T : fmt::Debug + Recycleable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl fmt::Display for RecycledInner where P: Borrow>>, T : fmt::Display + Recycleable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl Deref for RecycledInner where P: Borrow>>, T : Recycleable { type Target = T; #[inline] fn deref(& self) -> &T { self.as_ref() } } impl DerefMut for RecycledInner where P: Borrow>>, T : Recycleable { #[inline] fn deref_mut(&mut self) -> & mut T { self.as_mut() } } impl RecycledInner where P: Borrow>>, T : Recycleable { #[inline] fn new(pool: P, value: T) -> RecycledInner { RecycledInner { value: ManuallyDrop::new(value), pool } } #[inline] fn new_from(pool: P, mut value: T, source: A) -> RecycledInner where T : InitializeWith { value.initialize_with(source); RecycledInner { value: ManuallyDrop::new(value), pool } } #[inline] fn detach(self) -> T { // We need to split `self` into its component `value` and `pool` fields, drop the pool, // return the value, and forget `self` since its fields are now unwanted clones. let value = unsafe { // Make a byte-for-byte copy of `self.value` ptr::read(&self.value as *const ManuallyDrop) }; let pool = unsafe { // Make a byte-for-byte copy of `self.pool` ptr::read(&self.pool as *const P) }; // Forget `self` so it doesn't go through our custom `Drop` implementation mem::forget(self); // Allow `pool` to drop normally drop(pool); // Return the only surviving copy of `value` ManuallyDrop::into_inner(value) } } struct CappedCollection where T: Recycleable { values: Vec, cap: usize, supplier: Box> } impl CappedCollection where T: Recycleable { #[inline] pub fn new(mut supplier: Box>, starting_size: usize, max_size: usize) -> CappedCollection { use std::cmp; let starting_size = cmp::min(starting_size, max_size); let values: Vec = (0..starting_size) .map(|_| supplier.get() ) .collect(); CappedCollection { values: values, cap: max_size, supplier: supplier } } /// Note: This method does not perform a length check. /// The provided value must be reset() and there must be room in the pool before this is called. #[inline] pub fn insert_prepared_value(&mut self, value: T) { self.values.push(value) } #[inline] pub fn remove(&mut self) -> Option { self.values.pop() } #[inline] pub fn remove_or_create(&mut self) -> T { match self.remove() { Some(value) => value, None => self.supplier.get() } } #[inline] pub fn is_full(&self) -> bool { self.values.len() >= self.cap } #[inline] pub fn len(&self) -> usize { self.values.len() } #[inline] pub fn cap(&self) -> usize { self.cap } } /// Provides a method which will produce new instances of a type pub trait Supply { type Output: Recycleable; fn get(&mut self) -> Self::Output; } impl Supply for F where F: FnMut() -> T, T: Recycleable { type Output = T; fn get(&mut self) -> T { self() } } /// A collection of values that can be reused without requiring new allocations. /// /// `Pool` issues each value wrapped in a smartpointer. When the smartpointer goes out of /// scope, the wrapped value is automatically returned to the pool. pub struct Pool where T : Recycleable { values: Rc>>, } impl Pool where T: Recycleable { /// Creates a pool with `size` elements of type `T` allocated. #[inline] pub fn with_size(size: usize) -> Pool { Pool::with_size_and_max(size, usize::MAX) } /// Creates a pool with `size` elements of type `T` allocated /// and sets a maximum pool size of `max_size`. Values being /// added to the pool via `Pool::attach` or being returned to /// the pool upon dropping will instead be discarded if the pool /// is full. #[inline] pub fn with_size_and_max(starting_size: usize, max_size: usize) -> Pool { let supplier = Box::new(|| T::new()); let values: CappedCollection = CappedCollection::new(supplier, starting_size, max_size); Pool { values: Rc::new(RefCell::new(values)) } } /// Returns the number of values remaining in the pool. #[inline] pub fn size(&self) -> usize { (*self.values).borrow().len() } /// Returns the maximum number of values the pool can hold. #[inline] pub fn max_size(&self) -> usize { (*self.values).borrow().cap() } /// Removes a value from the pool and returns it wrapped in /// a `Recycled smartpointer. If the pool is empty when the /// method is called, a new value will be allocated. #[inline] pub fn new(&self) -> Recycled { let t = self.detached(); Recycled { value: RecycledInner::new(&*self.values, t) } } /// Removes a value from the pool, initializes it using the provided /// source value, and returns it wrapped in a `Recycled` smartpointer. /// If the pool is empty when the method is called, a new value will be /// allocated. #[inline(always)] pub fn new_from(&self, source: A) -> Recycled where T: InitializeWith { let t = self.detached(); Recycled { value: RecycledInner::new_from(&*self.values, t, source) } } /// Associates the provided value with the pool by wrapping it in a /// `Recycled` smartpointer. #[inline] pub fn attach(&self, value: T) -> Recycled { Recycled { value: RecycledInner::new(&*self.values, value) } } /// Removes a value from the pool and returns it without wrapping it in /// a smartpointer. When the value goes out of scope it will not be /// returned to the pool. #[inline] pub fn detached(&self) -> T { let mut collection = self.values.borrow_mut(); collection.remove_or_create() } /// Removes a value from the pool and returns it wrapped in /// an `RcRecycled` smartpointer. If the pool is empty when the /// method is called, a new value will be allocated. #[inline] pub fn new_rc(&self) -> RcRecycled { let t = self.detached(); let pool_reference = self.values.clone(); RcRecycled { value: RecycledInner::new(pool_reference, t) } } /// Removes a value from the pool, initializes it using the provided /// source value, and returns it wrapped in an `RcRecycled` smartpointer. /// If the pool is empty when the method is called, a new value will be /// allocated. #[inline(always)] pub fn new_rc_from(&self, source: A) -> RcRecycled where T: InitializeWith { let t = self.detached(); let pool_reference = self.values.clone(); RcRecycled { value: RecycledInner::new_from(pool_reference, t, source) } } /// Associates the provided value with the pool by wrapping it in an /// `RcRecycled` smartpointer. #[inline] pub fn attach_rc(&self, value: T) -> RcRecycled { let pool_reference = self.values.clone(); RcRecycled { value: RecycledInner::new(pool_reference, value) } } } /// Produces a `PoolBuilder` instance /// /// # Example /// /// ``` /// extern crate lifeguard; /// use lifeguard::*; /// /// fn main() { /// let mut pool: Pool = pool() /// .with(StartingSize(128)) /// .with(MaxSize(4096)) /// .with(Supplier(|| String::with_capacity(1024))) /// .build(); /// } /// ``` pub fn pool() -> PoolBuilder where T: Recycleable { PoolBuilder { starting_size: 16, max_size: usize::MAX, supplier: None } } /// Used to define settings for and ultimately create a `Pool`. pub struct PoolBuilder where T: Recycleable { pub starting_size: usize, pub max_size: usize, pub supplier: Option>>, } impl PoolBuilder where T: Recycleable { pub fn with(self, option_setter: U) -> PoolBuilder where U: OptionSetter> { option_setter.set_option(self) } pub fn build(self) -> Pool where T: Recycleable { let supplier = self.supplier.unwrap_or(Box::new(|| T::new())); let values: CappedCollection = CappedCollection::new(supplier, self.starting_size, self.max_size); Pool { values: Rc::new(RefCell::new(values)) } } } pub mod settings { use ::{PoolBuilder, Recycleable, Supply}; /// Implementing this trait allows a struct to act as a configuration /// parameter in the builder API. pub trait OptionSetter { fn set_option(self, T) -> T; } /// Specifies how many values should be requested from the Supplier at /// initialization time. These values will be available for immediate use. pub struct StartingSize(pub usize); /// Specifies the largest number of values the `Pool` will hold before it /// will begin to drop values being returned to it. pub struct MaxSize(pub usize); /// Specifies a value implementing `Supply` that will be used to allocate /// new values. If unspecified, `T::new()` will be invoked. pub struct Supplier(pub S) where S: Supply; impl OptionSetter> for StartingSize where T: Recycleable { fn set_option(self, mut builder: PoolBuilder) -> PoolBuilder { let StartingSize(size) = self; builder.starting_size = size; builder } } impl OptionSetter> for MaxSize where T: Recycleable { fn set_option(self, mut builder: PoolBuilder) -> PoolBuilder { let MaxSize(size) = self; builder.max_size = size; builder } } impl OptionSetter> for Supplier where S: Supply + 'static, T: Recycleable { fn set_option(self, mut builder: PoolBuilder) -> PoolBuilder { let Supplier(supplier) = self; builder.supplier = Some(Box::new(supplier) as Box>); builder } } } pub use settings::{OptionSetter, StartingSize, MaxSize, Supplier}; use std::mem; lifeguard-0.6.1/tests/lib.rs0100644€±ƒN0000024000000122141375606354200144220ustar 00000000000000extern crate lifeguard; #[cfg(test)] mod tests { use lifeguard::*; #[test] fn test_deref() { let str_pool : Pool = Pool::with_size(1); let rstring = str_pool.new_from("cat"); assert_eq!("cat", *rstring); } #[test] fn test_deref_rc() { let str_pool : Pool = Pool::with_size(1); let rstring = str_pool.new_rc_from("cat"); assert_eq!("cat", *rstring); } #[test] fn test_deref_mut() { let str_pool : Pool = Pool::with_size(1); let mut rstring = str_pool.new_from("cat"); rstring.push_str("s love eating mice"); assert_eq!("cats love eating mice", *rstring); } #[test] fn test_deref_mut_rc() { let str_pool : Pool = Pool::with_size(1); let mut rstring = str_pool.new_rc_from("cat"); rstring.push_str("s love eating mice"); assert_eq!("cats love eating mice", *rstring); } #[test] fn test_as_mut() { let str_pool : Pool = Pool::with_size(1); let mut rstring = str_pool.new_from("cat"); rstring.as_mut().push_str("s love eating mice"); assert_eq!("cats love eating mice", *rstring); } #[test] fn test_as_mut_rc() { let str_pool : Pool = Pool::with_size(1); let mut rstring = str_pool.new_rc_from("cat"); rstring.as_mut().push_str("s love eating mice"); assert_eq!("cats love eating mice", *rstring); } #[test] fn test_as_ref() { let str_pool : Pool = Pool::with_size(1); let rstring = str_pool.new_from("cat"); assert_eq!("cat", rstring.as_ref()); } #[test] fn test_as_ref_rc() { let str_pool : Pool = Pool::with_size(1); let rstring = str_pool.new_rc_from("cat"); assert_eq!("cat", rstring.as_ref()); } #[test] fn test_recycle() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let _rstring = str_pool.new_from("cat"); assert_eq!(0, str_pool.size()); } assert_eq!(1, str_pool.size()); } #[test] fn test_recycle_rc() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let _rstring = str_pool.new_rc_from("cat"); assert_eq!(0, str_pool.size()); } assert_eq!(1, str_pool.size()); } #[test] fn test_clone() { let str_pool : Pool = Pool::with_size(2); { assert_eq!(2, str_pool.size()); let text = str_pool.new_from("cat"); let text_clone = text.clone(); assert_eq!(text, text_clone); assert_eq!(0, str_pool.size()); } assert_eq!(2, str_pool.size()); } #[test] fn test_rc_clone() { let str_pool : Pool = Pool::with_size(2); { assert_eq!(2, str_pool.size()); let text = str_pool.new_rc_from("cat"); let text_clone = text.clone(); assert_eq!(text, text_clone); assert_eq!(0, str_pool.size()); } assert_eq!(2, str_pool.size()); } #[test] fn test_size_cap() { let str_pool : Pool = Pool::with_size_and_max(1, 1); { assert_eq!(1, str_pool.size()); let _rstring = str_pool.new_from("dog"); let _rstring2 = str_pool.new_from("cat"); assert_eq!(0, str_pool.size()); } assert_eq!(1, str_pool.size()); } #[test] fn test_detach() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let _string : String = str_pool.new().detach(); assert_eq!(0, str_pool.size()); } assert_eq!(0, str_pool.size()); } #[test] fn test_detach_rc() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let _string : String = str_pool.new_rc().detach(); assert_eq!(0, str_pool.size()); } assert_eq!(0, str_pool.size()); } #[test] fn test_attach() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let string: String = str_pool.new().detach(); assert_eq!(0, str_pool.size()); let _rstring: Recycled = str_pool.attach(string); } assert_eq!(1, str_pool.size()); } #[test] fn test_attach_rc() { let str_pool : Pool = Pool::with_size(1); { assert_eq!(1, str_pool.size()); let string: String = str_pool.new_rc().detach(); assert_eq!(0, str_pool.size()); let _rstring: RcRecycled = str_pool.attach_rc(string); } assert_eq!(1, str_pool.size()); } #[test] fn test_builder() { let pool = pool() .with(StartingSize(128)) .with(MaxSize(1_024)) .with(Supplier(|| String::with_capacity(16_000))) .build(); assert_eq!(pool.size(), 128); assert_eq!(pool.max_size(), 1_024); assert_eq!(pool.new().capacity(), 16_000); } #[test] fn test_new_from_iter(){ let vec_pool : Pool> = Pool::with_size(1); { let vec4 = vec_pool.new_from(0..4); assert_eq!(4, vec4.len()) } { let vec3 = vec_pool.new_from(0..3); assert_eq!(3, vec3.len()) } } }