1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! An object pool that guarantees pinning of its items and enables easy item access
//! via unsafe code by not maintaining any Rust references to its items.
//!
//! Features:
//!
//! * All items are guaranteed to be pinned for as long as they are in the pool.
//! * Fast lookup by opaque key returned from the pool.
//! * Out of band concurrent access to multiple items via pointers while
//! simultaneously adding/removing items from the pool.
//!
//! The pool is designed to offer efficient pinned storage for large collections. The exact
//! storage mechanism/layout is not part of the API contract and may change in future versions.
//!
//! # Basic usage
//!
//! ```
//! use pinned_pool::PinnedPool;
//!
//! let mut pool = PinnedPool::<String>::new();
//!
//! // Inserting an item gives you a key that you can later use to look up the item again.
//! let alice_key = pool.insert("Alice".to_string());
//! let bob_key = pool.insert("Bob".to_string());
//! let charlie_key = pool.insert("Charlie".to_string());
//!
//! println!(
//! "Object pool contains {} items, with an auto-adjusting capacity of {}",
//! pool.len(),
//! pool.capacity()
//! );
//!
//! // Retrieving items from a pool is fast, similar to `Vec[key]`.
//! let alice = pool.get(alice_key);
//! println!("Retrieved item: {alice}");
//!
//! pool.remove(bob_key);
//! pool.remove(charlie_key);
//!
//! // Retrieving an item borrows the pool for as long as you use the item, so we have to
//! // re-lookup `alice` here because otherwise the above `remove()` would be blocked.
//! let alice = pool.get(alice_key);
//! println!("Retrieved item after removal of other items: {alice}",);
//!
//! // You can also modify the items in-place.
//! let mut alice = pool.get_mut(alice_key);
//! alice.push_str(" Smith");
//! println!("Modified item: {alice}");
//! ```
//!
//! # Out of band concurrent access
//!
//! This requires unsafe code, which lowers the safety guardrails that prevent you from writing
//! invalid code. You are still not allowed to write invalid code but now you take on some of
//! the responsibility instead of leaving it all up to the compiler.
//!
//! ```
//! use std::ptr;
//!
//! use pinned_pool::{DropPolicy, PinnedPool};
//!
//! let mut pool = PinnedPool::<String>::builder()
//! // This is an extra safeguard, requiring you to remove all items from the pool before
//! // you drop it. It helps detect situations where you have forgotten to remove some items
//! // and are at risk of accessing items from unsafe code after the pool is destroyed. You
//! // are recommended to apply this policy for any pool whose items you access via pointers.
//! .drop_policy(DropPolicy::MustNotDropItems)
//! .build();
//!
//! // As we know that we will be accessing the items from unsafe code, we can immediately
//! // transform them to pointers at insertion time, without doing even a single lookup, for
//! // optimal efficiency. Every little bit adds up if you do it 100K times per second!
//! let inserter = pool.begin_insert();
//! let alice_key = inserter.key();
//! let alice_ptr = ptr::from_mut(inserter.insert_mut("Alice".to_string()).get_mut());
//!
//! let inserter = pool.begin_insert();
//! let bob_key = inserter.key();
//! let bob_ptr = ptr::from_mut(inserter.insert_mut("Bob".to_string()).get_mut());
//!
//! let inserter = pool.begin_insert();
//! let charlie_key = inserter.key();
//! let charlie_ptr = ptr::from_mut(inserter.insert_mut("Charlie".to_string()).get_mut());
//!
//! println!(
//! "Object pool contains {} items, with an auto-adjusting capacity of {}",
//! pool.len(),
//! pool.capacity()
//! );
//!
//! // We can do whatever we want to the items through the pointers, including writing
//! // to them (as long as we got the pointer from `insert_mut()`), provided that we
//! // do not access the item via the pool methods concurrently (e.g. `remove()` but
//! // also `get()` and `get_mut()` because they may create conflicting references).
//!
//! // SAFETY: See above comment.
//! unsafe {
//! (*alice_ptr).push_str(" Smith");
//! }
//! // SAFETY: See above comment.
//! unsafe {
//! (*bob_ptr).push_str(" Johnson");
//! }
//! // SAFETY: See above comment.
//! unsafe {
//! (*charlie_ptr).push_str(" Brown");
//! }
//!
//! // Once we remove an item, the pointer to it becomes invalid.
//! //
//! // This implies that we can only call this if we are 100% confident that nothing is concurrently
//! // accessing the item via the pointer (otherwise we would invalidate a pointer that is in use).
//! // We can know this is valid because we can read the code to determine there is no concurrent
//! // access (it is all in this one function) - it is not so easy in a real application. There is
//! // no compiler protection here - only human diligence can ensure this code is valid.
//! pool.remove(bob_key);
//!
//! // Same applies to get() - only valid if there is no conflicting access via pointers.
//! // By "conflicting" we mean something that would violate Rust's aliasing rules (either creating
//! // two `&mut` exclusive references or a `&mut` exclusive reference and a `&` shared reference
//! // at the same time to the same item in different parts of the app).
//! let charlie = pool.get(charlie_key);
//! println!("Retrieved item: {charlie}");
//!
//! // SAFETY: The reference `charlie` is not used after this point, so we can safely mutate
//! // the item via the pointer again. If we had still used `charlie` after this `println!`,
//! // this would be invalid code because this pointer dereference creates an exclusive reference.
//! unsafe {
//! (*charlie_ptr).push_str(" von Neumann");
//! }
//!
//! println!(
//! "Items accessed via pointers: Alice = {}, Charlie = {}",
//! // SAFETY: See above comments.
//! unsafe { &*alice_ptr },
//! // SAFETY: See above comments.
//! unsafe { &*charlie_ptr }
//! );
//!
//! // The policy we set at the beginning requires us to remove all items from the pool
//! // before we drop it. This is to ensure that we do not accidentally access items from
//! // unsafe code after the pool is dropped. It is an optional but recommended safeguard.
//! pool.remove(alice_key);
//! pool.remove(charlie_key);
//! ```
pub use *;
pub use *;
pub use *;
pub use *;