grit_data_prison/lib.rs
1//====== MAIN DOCS ======
2/*!     
3This crate provides the struct [Prison<T>](crate::single_threaded::Prison), a generational arena data structure
4that allows simultaneous interior mutability to each and every element by providing `.visit()` methods
5that take closures that are passed mutable references to the values, or by using the `.guard()` methods to
6obtain a guarded mutable reference to the value.
7
8This documentation describes the usage of [Prison<T>](crate::single_threaded::Prison), how its methods differ from
9those found on a [Vec], how to access the data contained in it, and how it achieves memory safety.
10
11### Project Links
12grit-data-prison on [Crates.io](https://crates.io/crates/grit-data-prison)
13grit-data-prison on [Lib.rs](https://lib.rs/crates/grit-data-prison)
14grit-data-prison on [Github](https://github.com/gabe-lee/grit-data-prison)
15grit-data-prison on [Docs.rs](https://docs.rs/grit-data-prison/0.4.0/grit_data_prison/)
16
17### Quick Look
18- Uses an underlying [Vec<T>] to store items of the same type
19- Acts primarily as a Generational Arena, where each element is accessed using a [CellKey] that differentiates two values that may have been located at the same index but represent fundamentally separate data
20- Can *also* be indexed with a plain [usize] for simple use cases
21- Provides safe (***needs verification***) interior mutability by doing reference counting on each element to adhere to Rust's memory safety rules
22- Uses [usize] refernce counters on each element and a master [usize] counter to track the number/location of active references and prevent mutable reference aliasing and disallow scenarios that could invalidate existing references
23- [CellKey] uses a [usize] index and [usize] generation to match an index to the context in which it was created and prevent two unrelated values that both at some point lived at the same index from being mistaken as equal
24- All methods return an [AccessError] where the scenario would cause a panic if not caught
25
26### NOTE
27This package is still UNSTABLE and may go through several iterations before I consider it good enough to set in stone, see [changelog](#changelog)
28- Version 0.4.x is a (very minor) breaking api change for 0.3.x and older, but it shouldn't affect most users and is an easy update
29- ALSO: Version 0.2.x and older were discovered to have a soft memory leak when using `insert_at()` and `overwrite()`, see [changelog](#changelog) for details
30# Motivation
31
32I wanted a data structure that met these criteria:
33- Backed by a [Vec<T>] (or similar) for cache efficiency
34- Allowed interior mutability to each of its elements
35- Was fully memory safe (***needs verification***)
36- Always returned a relevant error instead of panicking
37- Was easier to reason about when and where it might error than reference counting
38
39# Usage
40
41This crate is [on crates.io](https://crates.io/crates/grit-data-prison)
42
43First, add this crate as a dependency to your project:
44```toml
45[dependencies]
46grit-data-prison = "0.3"
47```
48Then import [AccessError] and [CellKey] from the crate root, along with the relevant version you wish to use in
49the file where it is needed (right now only one flavor is available, [single_threaded]):
50```rust
51use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
52```
53Create a [Prison<T>](crate::single_threaded::Prison) and add your data to it using one of the `insert()` type methods
54
55Note the following quirks:
56- A [Prison](crate::single_threaded::Prison) does not need to be declared `mut` to mutate it
57- `insert()` and its variants return a [Result]<[CellKey], [AccessError]> that you need to handle
58- You can ignore the [CellKey] and simply look up the value by index if you wish (shown later)
59```rust
60# use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
61# fn main() -> Result<(), AccessError> {
62let prison: Prison<String> = Prison::new();
63let key_hello = prison.insert(String::from("Hello, "))?;
64prison.insert(String::from("World!"))?;
65# Ok(())
66# }
67```
68From here there are 2 main ways to access the values contained in the [Prison](crate::single_threaded::Prison)
69## Visiting the values in prison
70You can use one of the `.visit()` methods to access a mutable reference
71to your data from within a closure, either mutably or immutably
72```rust
73# use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
74# fn main() -> Result<(), AccessError> {
75# let prison: Prison<String> = Prison::new();
76# let key_hello = prison.insert(String::from("Hello, "))?;
77# prison.insert(String::from("World!"))?;
78prison.visit_mut_idx(1, |val_at_idx_1| {
79 *val_at_idx_1 = String::from("Rust!!");
80 Ok(())
81});
82# Ok(())
83# }
84```
85The rules for mutable or immutable references are the same as Rust's rules for normal variable referencing:
86- ONLY one mutable reference
87- OR any number of immutable references
88
89Visiting multiple values at the same time can be done by nesting `.visit()` calls,
90or by using one of the batch `.visit()` methods
91```rust
92# use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
93# fn main() -> Result<(), AccessError> {
94# let prison: Prison<String> = Prison::new();
95# let key_hello = prison.insert(String::from("Hello, "))?;
96# prison.insert(String::from("World!"))?;
97# prison.visit_mut_idx(1, |val_at_idx_1| {
98# *val_at_idx_1 = String::from("Rust!!");
99# Ok(())
100# });
101prison.visit_ref(key_hello, |val_0| {
102 prison.visit_ref_idx(1, |val_1| {
103 println!("{}{}", *val_0, *val_1); // Prints "Hello, Rust!!"
104 Ok(())
105 });
106 Ok(())
107});
108prison.visit_many_ref_idx(&[0, 1], |vals| {
109 println!("{}{}", vals[0], vals[1]); // Also prints "Hello, Rust!!"
110 Ok(())
111});
112# Ok(())
113# }
114```
115### Full Visit Example Code
116```rust
117use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
118
119fn main() -> Result<(), AccessError> {
120 let prison: Prison<String> = Prison::new();
121 let key_hello = prison.insert(String::from("Hello, "))?;
122 prison.insert(String::from("World!"))?;
123 prison.visit_mut_idx(1, |val_at_idx_1| {
124 *val_at_idx_1 = String::from("Rust!!");
125 Ok(())
126 });
127 prison.visit_ref(key_hello, |val_0| {
128 prison.visit_ref_idx(1, |val_1| {
129 println!("{}{}", *val_0, *val_1); // Prints "Hello, Rust!!"
130 Ok(())
131 });
132 Ok(())
133 });
134 prison.visit_many_ref_idx(&[0, 1], |vals| {
135 println!("{}{}", vals[0], vals[1]); // Also prints "Hello, Rust!!"
136 Ok(())
137 });
138 Ok(())
139}
140```
141## Guarding values with wrapper structs
142You can also use one of the `.guard()` methods to obtain a guarded wrapper around your data,
143keeping the value marked as referenced as long as the wrapper remains in scope.
144
145First you need to import one of [PrisonValueMut](crate::single_threaded::PrisonValueMut),
146[PrisonValueRef](crate::single_threaded::PrisonValueRef), [PrisonSliceMut](crate::single_threaded::PrisonSliceMut),
147or [PrisonSliceRef](crate::single_threaded::PrisonSliceRef) from the same module as
148[Prison](crate::single_threaded::Prison)
149```rust
150use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueRef}};
151```
152Then obtain a guarded wrapper by using the corresponding `.guard()` method
153```rust
154# use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueRef, PrisonValueMut}};
155# fn main() -> Result<(), AccessError> {
156let prison: Prison<String> = Prison::new();
157let key_hello = prison.insert(String::from("Hello, "))?;
158prison.insert(String::from("World!"))?;
159let grd_hello = prison.guard_ref(key_hello)?;
160# Ok(())
161# }
162```
163As long as the referencing rules aren't violated, you can guard (or visit) that value, even when other values from the same
164prison are being visited or guarded. The guarded wrappers (for example [PrisonValueRef](crate::single_threaded::PrisonValueRef))
165keep the element(s) marked with the appropriate form of referencing until they go out of scope.
166This can be done by wrapping the area it is used in a code block, or by manually passing it to the
167associated `::unguard()` function on the wrapper type to immediately drop it out of scope and update the
168reference count.
169
170The guarded wrapper types all implement [Deref], [AsRef], and [Borrow], while the mutable versions
171also implement [DerefMut], [AsMut], and [BorrowMut] to provide transparent access to their inner values
172```rust
173# use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueRef, PrisonValueMut, PrisonSliceRef}};
174# fn main() -> Result<(), AccessError> {
175# let prison: Prison<String> = Prison::new();
176# let key_hello = prison.insert(String::from("Hello, "))?;
177# prison.insert(String::from("World!"))?;
178{
179 let grd_hello = prison.guard_ref(key_hello)?;
180 let grd_world = prison.guard_ref_idx(1)?;
181 println!("{}{}", *grd_hello, *grd_world); // Prints "Hello, World!"
182}
183// block ends, both guards go out of scope and their reference counts return to what they were before
184let mut grd_world_to_rust = prison.guard_mut_idx(1)?;
185*grd_world_to_rust = String::from("Rust!!");
186PrisonValueMut::unguard(grd_world_to_rust); // index one is no longer marked mutably referenced
187let grd_both = prison.guard_many_ref_idx(&[0, 1])?;
188println!("{}{}", grd_both[0], grd_both[1]); // Prints "Hello, Rust!!"
189# Ok(())
190# }
191```
192### Full Guard Example Code
193```rust
194use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueRef, PrisonValueMut, PrisonSliceRef}};
195
196fn main() -> Result<(), AccessError> {
197 let prison: Prison<String> = Prison::new();
198 let key_hello = prison.insert(String::from("Hello, "))?;
199 prison.insert(String::from("World!"))?;
200 {
201 let grd_hello = prison.guard_ref(key_hello)?;
202 let grd_world = prison.guard_ref_idx(1)?;
203 println!("{}{}", *grd_hello, *grd_world); // Prints "Hello, World!"
204 }
205 // block ends, both guards go out of scope and their reference counts return to what they were before
206 let mut grd_world_to_rust = prison.guard_mut_idx(1)?;
207 *grd_world_to_rust = String::from("Rust!!");
208 PrisonValueMut::unguard(grd_world_to_rust); // index one is no longer marked mutably referenced
209 let grd_both = prison.guard_many_ref_idx(&[0, 1])?;
210 println!("{}{}", grd_both[0], grd_both[1]); // Prints "Hello, Rust!!"
211 Ok(())
212}
213```
214Operations that affect the underlying [Vec] can also be done
215from *within* `.visit()` closures or while values are `guard()`-ed as long as none of the following rules are violated:
216- The operation does not remove, read, or modify any element that is *currently* being referenced
217- The operation does not cause a re-allocation of the entire [Vec] (or otherwise cause the entire [Vec] to relocate to another memory address)
218```rust
219# use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueMut}};
220# fn main() -> Result<(), AccessError> {
221let prison: Prison<u64> = Prison::with_capacity(5);
222prison.insert(0)?;
223prison.insert(10)?;
224prison.insert(20)?;
225prison.insert(30)?;
226prison.insert(42)?;
227let mut accidental_val: u64 = 0;
228let mut grd_0 = prison.guard_mut_idx(0)?;
229prison.visit_ref_idx(3, |val| {
230 accidental_val = prison.remove_idx(4)?;
231 prison.insert(40)?;
232 Ok(())
233});
234*grd_0 = 80;
235PrisonValueMut::unguard(grd_0);
236// No values are actively referenced here so we can perform
237// an action that would cause re-allocation safely
238for i in 0..100u64 {
239 prison.insert(i + 100)?;
240}
241# Ok(())
242# }
243```
244Also provided is a quick shortcut to clone values out of the [Prison<T>](crate::single_threaded::Prison)
245when type T implements [Clone]. Because cloning values does not alter the original or presume any
246precondition regarding the content of the value, it is safe (in a single-threaded context) to
247clone values that are currently being guarded or visited.
248### Example
249```rust
250# use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison}};
251# fn main() -> Result<(), AccessError> {
252let prison: Prison<String> = Prison::new();
253let key_0 = prison.insert(String::from("Foo"))?;
254prison.insert(String::from("Bar"))?;
255let cloned_foo = prison.clone_val(key_0)?;
256let cloned_bar = prison.clone_val_idx(1)?;
257# Ok(())
258# }
259```
260For more examples, see the specific documentation for the relevant types/methods
261
262## JailCell
263Also included is the struct [JailCell<T>](crate::single_threaded::JailCell), which acts as a stand-alone
264version of a [Prison<T>](crate::single_threaded::Prison), but with no generation counter.
265
266[JailCell](crate::single_threaded::JailCell) includes the same basic interface as [Prison](crate::single_threaded::Prison)
267and also employs reference counting, but with a much simpler set of safety checks
268```rust
269use grit_data_prison::{AccessError, CellKey, single_threaded::{JailCell, JailValueRef}};
270
271fn main() -> Result<(), AccessError> {
272 let string_jail: JailCell<String> = JailCell::new(String::from("'Bad-Guy' Bert"));
273 string_jail.visit_mut(|criminal| {
274 let bigger_bad = String::from("Dr. Lego-Step");
275 println!("Breaking News: {} to be set free to make room for {}", *criminal, bigger_bad);
276 *criminal = bigger_bad;
277 Ok(())
278 })?;
279 let guarded_criminal = string_jail.guard_ref()?;
280 println!("{} will now be paraded around town for public shaming", *guarded_criminal);
281 assert_eq!(*guarded_criminal, String::from("Dr. Lego-Step"));
282 JailValueRef::unguard(guarded_criminal);
283 Ok(())
284}
285```
286See the documentation on [JailCell](crate::single_threaded::JailCell) for more info
287# Why this strange syntax?
288
289For the `visit()` methodology, closures provide a safe sandbox to access mutable references, as they cant be moved out of the closure,
290and because the `visit()` functions that take the closures handle all of the
291safety and housekeeping needed before and after.
292
293Since closures use generics the rust compiler can inline them in many/most/all? cases.
294
295The `guard()` methodology requires the values not be able to leak, alias, or never reset their reference counts,
296so they are wrapped in structs that provide limited access to the references and know how to
297automatically reset the reference counter for the value when they go out of scope
298
299# How is this safe?!
300
301The short answer is: it *should* be *mostly* safe.
302I welcome any feedback and analysis showing otherwise so I can fix it or revise my methodology.
303
304[Prison](crate::single_threaded::Prison) follows a few simple rules:
305- You can only get an immutable reference if the value has zero references or only immutable references
306- You can only get a mutable reference is the value has zero references of any type
307- Any method that would or *could* read, modify, or delete any element cannot be performed while that element is currently being referenced
308- Any method that would or *could* cause the underlying [Vec] to relocate to a different spot in memory cannot be performed while even ONE reference to ANY element in the [Vec] is still in scope
309
310In addition, it provides the functionality of a Generational Arena with these additional rules:
311- The [Prison](crate::single_threaded::Prison) has a master generation counter to track the largest generation of any element inside it
312- Every valid element has a generation attatched to it, and `insert()` operations return a [CellKey] that pairs the element index with the current largest generation value
313- Any operation that removes *or* overwrites a valid element *with a genreation counter that is equal to the largest generation* causes the master generation counter to increase by one
314
315It achieves all of the above with a few lightweight sentinel values:
316- A single [UnsafeCell](std::cell::UnsafeCell) to hold *all* of the [Prison](crate::single_threaded::Prison) internals and provide interior mutability
317- A master `access_count` [usize] on [Prison](crate::single_threaded::Prison) itself to track whether *any* reference is in active
318- Each element is (basically) a `Cell` or `Free` variant:
319 - `Free` elements act as nodes in a doubly linked list that tracks free indexes
320 - One [usize] that points to the previous free index before this one was made free
321 - One [usize] that points to the next free index after this one is filled
322 - `Cell`
323 - A `ref_count` [usize] that tracks both mutable and immutable references
324 - A `generation` [usize] to use when matching to the [CellKey] used to access the index
325 - A value of type `T`
326
327(see [performance](#performance) for more info on the *actual* specifics)
328
329Attempting to perform an action that would violate any of these rules will either be prevented from compiling
330or return an [AccessError] that describes why it was an error, and should never panic.
331### Example: compile-time safety
332```compile_fail
333# use grit_data_prison::{AccessError, CellKey, single_threaded::Prison};
334# fn main() -> Result<(), AccessError> {
335let prison: Prison<String> = Prison::new();
336prison.insert(String::from("cannot be stolen"));
337let mut steal_mut_ref: &mut String;
338let mut steal_prison: Prison<String>;
339prison.visit_mut_idx(0, |mut_ref| {
340 // will not compile: (error[E0521]: borrowed data escapes outside of closure)
341 steal_mut_ref = mut_ref;
342 // will not compile: (error[E0505]: cannot move out of `prison` because it is borrowed)
343 steal_prison = prison;
344 Ok(())
345});
346# Ok(())
347# }
348```
349### Example: run-time safety
350```rust
351# use grit_data_prison::{AccessError, CellKey, single_threaded::{Prison, PrisonValueMut}};
352struct MyStruct(u32);
353
354fn main() -> Result<(), AccessError> {
355 let prison: Prison<MyStruct> = Prison::with_capacity(2); // Note this prison can only hold 2 elements
356 let key_0 = prison.insert(MyStruct(1))?;
357 prison.insert(MyStruct(2))?;
358 let grd_0 = prison.guard_mut(key_0)?;
359 assert!(prison.guard_mut(key_0).is_err());
360 assert!(prison.guard_ref_idx(0).is_err());
361 PrisonValueMut::unguard(grd_0);
362 prison.visit_mut(key_0, |val_0| {
363 assert!(prison.visit_mut(key_0, |val_0_again| Ok(())).is_err());
364 assert!(prison.visit_ref(key_0, |val_0_again| Ok(())).is_err());
365 assert!(prison.visit_mut_idx(0, |val_0_again| Ok(())).is_err());
366 assert!(prison.visit_ref_idx(3, |val_3_out_of_bounds| Ok(())).is_err());
367 assert!(prison.guard_mut(key_0).is_err());
368 assert!(prison.guard_ref(key_0).is_err());
369 assert!(prison.guard_ref_idx(3).is_err());
370 prison.visit_ref_idx(1, |val_1| {
371 assert!(prison.remove_idx(1).is_err()); // would delete memory referenced by val_1
372 assert!(prison.remove(key_0).is_err()); // would delete memory referenced by val_0
373 assert!(prison.insert(MyStruct(3)).is_err()); // would cause reallocation and invalidate any current references
374 Ok(())
375 });
376 Ok(())
377 });
378 Ok(())
379}
380```
381# Crate Features
382`no_std`: This crate can be used with the `no_std` feature to use only imports from the `core` library instead of the `std` library
383
384Major Malfunctions:
385this crate can be passed one of three (optional) features that define how the library handles behavior that is DEFINITELY un-intended and should be considered a bug in the library itself. It defaults to `major_malf_is_err` if none are specified:
386- `major_malf_is_err`: major malfunctions will be returned as an [AccessError::MAJOR_MALFUNCTION(msg)], this is the default even if not specified
387- `major_malf_is_panic`: major malfunctions will result in a call to `panic(msg)` describing the unexpected behavior
388- `major_malf_is_undefined`: branches where a major malfunction would nomally be are replaced with [unreachable_unchecked()], possibly allowing them to be removed from compilation entirely
389# Performance
390
391### Speed
392(Benchmarks are Coming Soon™)
393
394### Size
395[Prison<T>](crate::single_threaded::Prison) has 4 [usize] house-keeping values in addition to a [Vec<PrisonCell<T>>]
396
397Although the abstract of each `PrisonCell<T>` is as described as found in [How is This Safe?!](#how-is-this-safe),
398the truth of the matter it that Rust was not optimising the memory footprint where it could have done so using Enums, so I had to roll my own
399type of enum:
400- Each element is a struct with a custom-enforced a `Cell` or `Free` variant, with the variant tracked in the top bit of one of its fields:
401 - field `refs_or_next` holds a [usize] that holds either the reference count in `Cell` variant or the next free in `Free` variant
402 - field `d_gen_or_prev` holds a [usize] that holds either the generation count in `Cell` variant or the prev free in `Free` variant
403 - In addition, the most significant bit of `d_gen_or_prev` is reserved for marking the variant of the `PrisonCell` (the `d` is for `discriminant`). This means the *ACTUAL* maximum generation count is [isize::MAX](std::isize::MAX), but the prev index is unafected because a [Vec] cannot have more than [isize::MAX](std::isize::MAX) elements anyway...
404 - field `val` is a [`MaybeUninit<T>`] that is always assumed uninitialized when the element is in `Free` state, and always assumed initialized when it is in `Cell` state.
405
406Therefore the total _additional_ size compared to a [Vec<T>] on a 64-bit system is 32 bytes flat + 16 bytes per element,
407and these values are validated in the test suite with an optional test that checks [mem::size_of](std::mem::size_of) for several
408types of `T`
409
410# How this crate may change in the future
411
412This crate is very much UNSTABLE, meaning that not every error condition may be tested,
413methods may return different errors/values as my understanding of how they should be properly implemented
414evolves, I may add/remove methods altogether, etc.
415
416Possible future additions may include:
417- [x] Single-thread safe [Prison<T>](crate::single_threaded::Prison)
418- [x] `Guard` api for a more Rust-idiomatic way to access values
419- [x] Switch to reference counting with same memory footprint
420- [ ] Const Generic bounds to customize the size of internal utility values
421- [ ] More public methods (as long as they make sense and don't bloat the API)
422- [ ] Multi-thread safe `AtomicPrison<T>`
423- [x] ? Single standalone value version, [JailCell<T>](crate::single_threaded::JailCell)
424- [ ] ? Multi-thread safe standalone value version, `AtomicJailCell<T>`
425- [ ] ?? Completely unchecked and unsafe version `UnPrison<T>`
426- [ ] ??? Multi-thread ~~safe~~ unsafe version `AtomicUnPrison<T>`
427
428# How to Help/Contribute
429
430This crate is [on crates.io](https://crates.io/crates/grit-data-prison)
431The repo is [on github](https://github.com/gabe-lee/grit-data-prison)
432
433Feel free to leave feedback, or fork/branch the project and submit fixes/optimisations!
434
435If you can give me concrete examples that *definitely* violate memory-safety, meaning
436that the provided references can be made to point to invalid/illegal memory or violate aliasing rules
437(without the use of additional unsafe :P), leak memory, or otherwise cause unsafe conditions (for
438example changing an expected enum variant to another where the compiler doesnt expect it
439to be possible), I'd love to fix, further restrict, or rethink the crate entirely.
440
441The best way to do this would be to follow these steps:
442- make a `bug/something` or `issue/something` branch off of the `dev` branch
443- create a new test that demonstrates the current failing of the library
444- then do one of the following:
445 - solve the problem in your branch and create a pull request into the `dev` branch with a message explaining everything
446 - create a pull request with only the test proving the failure point with a message describing why it is a failure and that *this pull request does not solve the problem*
447# Changelog
448 - Version 0.4.0: BREAKING change: change `peek_ref()` and `peek_ref_idx()` to return [Result<T, AccessError>] instead of [Option<T>], and add `peek_ref()` to [JailCell](crate::single_threaded::JailCell)
449 - I know it's a very small difference, but breaking is breaking, sorry! It should have been a `Result` from the beginning to match the existing API and allow easy error propogation inside functions that expect `AccessError`s without a bunch of boilerplate testing for `Some`/`None` just to return a `AccessError::ValueDeleted` anyway
450 - Version 0.3.1: Non-Breaking feature: `peek_ref()` and `peek_ref_idx()`, UNSAFE methods that allow the caller to get a reference to a value while bypassing reference counting and other safety checks
451 - Version 0.3.0: MAJOR BREAKING change to API:
452 - Switch to reference counting instead of [bool] locks: the memory footprint is the same (in most cases) and the safety logic is almost the same. Reference counting gives more flexibility and finer grained control with no real penalty compared to using a [bool]
453 - `escort()` methods renamed to `guard()` methods
454 - `visit()` and `guard()` methods split into `_ref()` and `_mut()` variants
455 - [AccessError] variants renamed and changed to be more clear
456 - Addition of 3 crate features: `major_malf_is_err`, `major_malf_is_panic`, `major_malf_is_undefined` that allow conditional compilation choices for behavior that is *certainly* a bug in the library
457 - **Version 0.2.x and older discovered to have a soft memory leak and should be avoided:** when using `insert_at()` and `overwrite()` on indexes that werent the 'top' free in the stack, all other free indexes above them in the stack would be forgotten and never re-used. However, they should be freed when the entire Prison is freed. Sorry!
458 - Version 0.2.3: Non-Breaking feature: `clone_val()` methods to shortcut cloning a value when T implements [Clone]
459 - Version 0.2.2: Non-Breaking update to `PrisonValue` and `PrisonSlice` to reduce their memory footprint
460 - Version 0.2.1: Non-breaking addition of `escort()` api function (why didnt I think of this earlier?)
461 - Version 0.2.x: has a different API than version 0.1.x and is a move from a plain Vec to a Generational Arena
462 - Version 0.1.x: first version, plain old [Vec] with [usize] indexing
463*/
464
465#![deny(rustdoc::broken_intra_doc_links)]
466#![deny(rustdoc::private_intra_doc_links)]
467#![allow(rustdoc::invalid_html_tags)]
468#![deny(missing_docs)]
469#![allow(clippy::needless_return)]
470#![allow(clippy::needless_lifetimes)]
471
472//====== Crate Imports ======
473#[cfg(not(feature = "no_std"))]
474pub(crate) use std::{
475 borrow::{Borrow, BorrowMut},
476 cell::UnsafeCell,
477 error::Error,
478 fmt::{Debug, Display},
479 hint::unreachable_unchecked,
480 mem::{replace as mem_replace, MaybeUninit},
481 ops::{Deref, DerefMut, RangeBounds},
482};
483
484#[cfg(feature = "no_std")]
485pub(crate) use core::{
486 borrow::{Borrow, BorrowMut},
487 cell::UnsafeCell,
488 fmt::{Debug, Display},
489 hint::unreachable_unchecked,
490 mem::{replace as mem_replace, MaybeUninit},
491 ops::{Deref, DerefMut, RangeBounds},
492};
493
494#[cfg(feature = "no_std")]
495pub(crate) trait Error: Debug + Display {
496 fn source(&self) -> Option<&(dyn Error + 'static)> {
497 None
498 }
499}
500
501/// Module defining the version(s) of [Prison<T>](crate::single_threaded::Prison) and [JailCell<T>](crate::single_threaded::JailCell) suitable for use only from within a single-thread
502pub mod single_threaded;
503
504//ENUM AccessError
505/// Error type that provides helpful information about why an operation on any
506/// [Prison](crate::single_threaded::Prison) or [JailCell](crate::single_threaded::JailCell) failed
507///
508/// Every error returned from functions or methods defined in this crate will be one of these variants,
509/// and all safe versions of [Prison](crate::single_threaded::Prison) and [JailCell](crate::single_threaded::JailCell) are designed to never panic and always return errors.
510///
511/// Additional variants may be added in the future, therefore it is recommended you add a catch-all branch
512/// to any match statements on this enum to future-proof your code:
513/// ```rust
514/// # use grit_data_prison::AccessError;
515/// # fn main() {
516/// # let acc_err = AccessError::IndexOutOfRange(100);
517/// match acc_err {
518/// AccessError::IndexOutOfRange(bad_idx) => {},
519/// AccessError::ValueAlreadyMutablyReferenced(duplicate_idx) => {},
520/// // other variants
521/// _ => {}
522/// }
523/// # }
524/// ```
525///
526/// [AccessError] has a custom implementation for both [std::fmt::Display] and
527/// [std::fmt::Debug] traits, with the `Display` version giving a short description of the problem,
528/// and the `Debug` version giving a more in-depth explaination of exactly why an error had to be
529/// returned
530#[derive(PartialEq, Eq)] //COV_IGNORE
531pub enum AccessError {
532 /// Indicates that an operation attempted to access an index beyond the range of the [Prison<T>](crate::single_threaded::Prison),
533 /// along with the offending index
534 IndexOutOfRange(usize),
535 /// Indicates that an operation attempted to reference a value (mutably or immutably) already being mutably referenced by another operation,
536 /// along with the index in question
537 ValueAlreadyMutablyReferenced(usize),
538 /// Indicates that an operation attempted to mutably reference a value already being immutably referenced by another operation,
539 /// along with the index in question
540 ValueStillImmutablyReferenced(usize),
541 /// Indicates that an overwriteing insert would invalidate currently active references to a value
542 OverwriteWhileValueReferenced(usize),
543 /// Indicates that an insert would require re-allocation of the internal [Vec<T>], thereby invalidating
544 /// any currently active references
545 InsertAtMaxCapacityWhileAValueIsReferenced,
546 /// Indicates that the last element in the [Prison<T>](crate::single_threaded::Prison) is being accessed, and `remove()`-ing the value
547 /// from the underlying [Vec<T>] would invalidate the reference
548 RemoveWhileValueReferenced(usize),
549 /// Indicates that the value requested was deleted and a new value with an updated generation took its place
550 ///
551 /// Contains the index and generation from the invalid [CellKey], in that order
552 ValueDeleted(usize, usize),
553 /// Indicates that a very large number of removes and inserts caused the generation counter to reach its max value
554 MaxValueForGenerationReached,
555 /// Indicates that an attempted insert to a specific index would overwrite and invalidate a value still in use
556 IndexIsNotFree(usize),
557 /// Indicates that the underlying [Vec] reached the maximum capacity set by Rust ([isize::MAX])
558 MaximumCapacityReached,
559 /// Indicates that you (somehow) reached the limit for reference counting immutable references
560 MaximumImmutableReferencesReached(usize),
561 /// Indicates that the operation created an invalid and unexpected state. This may have resulted in memory leaking, mutable aliasing, undefined behavior, etc.
562 ///
563 /// This error should be considered a BUG inside the library crate `grit-data-prison` and reported to the author of the crate
564 #[allow(non_camel_case_types)]
565 MAJOR_MALFUNCTION(String),
566}
567
568impl AccessError {
569 /// Returns a string that shows the [AccessError] variant and value, if any
570 pub fn kind(&self) -> String {
571 match &*self {
572 Self::IndexOutOfRange(idx) => format!("AccessError::IndexOutOfRange({})", idx),
573 Self::ValueAlreadyMutablyReferenced(idx) => {
574 format!("AccessError::ValueAlreadyMutablyReferenced({})", idx)
575 }
576 Self::ValueStillImmutablyReferenced(idx) => {
577 format!("AccessError::ValueStillImmutablyReferenced({})", idx)
578 }
579 Self::InsertAtMaxCapacityWhileAValueIsReferenced => {
580 format!("AccessError::InsertAtMaxCapacityWhileAValueIsReferenced")
581 }
582 Self::ValueDeleted(idx, gen) => format!("AccessError::ValueDeleted({}, {})", idx, gen),
583 Self::MaxValueForGenerationReached => {
584 format!("AccessError::MaxValueForGenerationReached")
585 }
586 Self::RemoveWhileValueReferenced(idx) => {
587 format!("AccessError::RemoveWhileValueReferenced({})", idx)
588 }
589 Self::IndexIsNotFree(idx) => format!("AccessError::IndexIsNotFree({})", idx),
590 Self::MaximumCapacityReached => format!("AccessError::MaximumCapacityReached"),
591 Self::MaximumImmutableReferencesReached(idx) => {
592 format!("AccessError::MaximumImmutableReferencesReached({})", idx)
593 }
594 Self::OverwriteWhileValueReferenced(idx) => {
595 format!("AccessError::OverwriteWhileValueReferenced({})", idx)
596 }
597 Self::MAJOR_MALFUNCTION(msg) => format!("AccessError::MAJOR_MALFUNCTION({})", msg),
598 }
599 }
600}
601
602impl Display for AccessError {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 match &*self {
605 Self::IndexOutOfRange(idx) => write!(f, "Index [{}] is out of range", idx),
606 Self::ValueAlreadyMutablyReferenced(idx) => write!(f, "Value at index [{}] is already being mutably referenced by another operation", idx),
607 Self::ValueStillImmutablyReferenced(idx) => write!(f, "Value at index [{}] is still being immutably referenced by another operation, cannot mutably reference", idx),
608 Self::InsertAtMaxCapacityWhileAValueIsReferenced => write!(f, "Prison is at max capacity, cannot insert new value while any values are still referenced"),
609 Self::ValueDeleted(idx, gen) => write!(f, "Value requested at index {} gen {} was already deleted", idx, gen),
610 Self::MaxValueForGenerationReached => write!(f, "Maximum value for generation counter reached"),
611 Self::RemoveWhileValueReferenced(idx) => write!(f, "Index [{}] is currently being referenced, cannot remove", idx),
612 Self::IndexIsNotFree(idx) => write!(f, "Index [{}] is not free and may be still in use, cannot overwrite with unrelated value", idx),
613 Self::MaximumCapacityReached => write!(f, "Prison has reached the maximum capacity allowed by Rust"),
614 Self::MaximumImmutableReferencesReached(idx) => write!(f, "Value at index [{}] has reached the maximum number of immutable references: {}", idx, usize::MAX - 2),
615 Self::OverwriteWhileValueReferenced(idx) => write!(f, "Value at index [{}] still has active references, cannot overwrite", idx),
616 Self::MAJOR_MALFUNCTION(msg) => write!(f, "{}\n-------\nIndicates that the operation created an invalid and unexpected state. This may have resulted in memory leaking, mutable aliasing, undefined behavior, etc.", msg),
617 }
618 }
619}
620
621impl Debug for AccessError {
622 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
623 match &*self {
624 Self::IndexOutOfRange(idx) => write!(f, "Index [{}] is out of range", idx),
625 Self::ValueAlreadyMutablyReferenced(idx) => write!(f, "Value at index [{}] is already being mutably referenced by another operation\n---------\nMutably referencing the same cell twice or immutably referencing a value being mutably referenced violates Rust's memory saftey rules", idx),
626 Self::ValueStillImmutablyReferenced(idx) => write!(f, "Value at index [{}] is still being immutably referenced by another operation, cannot mutably reference\n---------\nMutably referencing a cell while an immutable reference to it is still in scope violates Rust's memory saftey rules", idx),
627 Self::InsertAtMaxCapacityWhileAValueIsReferenced => write!(f, "Prison is at max capacity, cannot insert new value while any values are still referenced\n---------\nInserting a value in a Vec at max capacity while a value reference is still in scope may cause re-allocation that will invalidate it"),
628 Self::ValueDeleted(idx, gen) => write!(f, "Value requested at index {} gen {} was already deleted\n---------\nWhen deleting a value, it is recomended you take steps to invalidate any held keys refering to it", idx, gen),
629 Self::MaxValueForGenerationReached => write!(f, "Maximum value for generation counter reached\n---------\nA large number of removals and inserts has caused the generation counter to reach its max value. Manually perform a Prison::purge() and re-issue the keys to continue using this Prison"),
630 Self::RemoveWhileValueReferenced(idx) => write!(f, "Index [{}] is currently being referenced, cannot remove\n---------\nRemoving a value with an active reference in scope will could overwrite the memory at that location and cause undefined behavior", idx),
631 Self::IndexIsNotFree(idx) => write!(f, "Index [{}] is not free and may be still in use, cannot overwrite with unrelated value\n---------\nWriting a new value to this index will cause any keys referencing the old value to return errors. If this is truly the behavior you want, use Prison::overwrite() instead of Prison::insert()", idx),
632 Self::MaximumCapacityReached => write!(f, "Prison has reached the maximum capacity allowed by Rust\n---------\nRust does not allow a [Vec] to have a capacity longer than [isize::MAX] becuase most operating systems only allow half of the total memory space to be addressed by programs"),
633 Self::MaximumImmutableReferencesReached(idx) => write!(f, "Value at index [{}] has reached the maximum number of immutable references: {}\n---------\nThis highly unlikely scenario means you somehow created {} immutable references to the value already", idx, usize::MAX - 2, usize::MAX - 2),
634 Self::OverwriteWhileValueReferenced(idx)=> write!(f, "Value at index [{}] still has active references, cannot overwrite\n---------\nOverwriting a value with active references is the same as mutating a variable being immutably referenced, violating Rust's memory safety rules", idx),
635 Self::MAJOR_MALFUNCTION(msg) => write!(f, "{}\n-------\nIndicates that the operation created an invalid and unexpected state. This may have resulted in memory leaking, mutable aliasing, undefined behavior, etc.\n---------\nThis error should be considered a BUG inside the library crate `grit-data-prison` and reported to the author of the crate", msg),
636 }
637 }
638}
639
640impl Error for AccessError {}
641
642//STRUCT CellKey
643/// Struct that defines a packaged index into a [Prison](crate::single_threaded::Prison)
644///
645/// This struct is designed to be passed to some other struct or function that needs to be able to
646/// reference the data stored at the cell number.
647#[derive(Debug, Copy, Clone, Eq, PartialEq)] //COV_IGNORE
648pub struct CellKey {
649 idx: usize,
650 gen: usize,
651}
652
653impl CellKey {
654 /// Create a new index from an index and generation
655 ///
656 /// Not recomended in most cases, as there is no way to guarantee an item with that
657 /// exact index and generation exists in your [Prison](crate::single_threaded::Prison)
658 pub fn from_raw_parts(idx: usize, gen: usize) -> CellKey {
659 return CellKey { idx, gen };
660 }
661
662 /// Return the internal index and generation from the cell key, in that order
663 ///
664 /// Not recomended in most cases. If you need just the index by itself,
665 /// use [CellKey::idx()] instead
666 pub fn into_raw_parts(&self) -> (usize, usize) {
667 return (self.idx, self.gen);
668 }
669
670 /// Return only the index of the [CellKey]
671 ///
672 /// Useful if you want to only get the value at the specified index in the [Prison](crate::single_threaded::Prison)
673 /// without checking that the generations match
674 pub fn idx(&self) -> usize {
675 return self.idx;
676 }
677}
678
679//====== Crate Utilities ======
680//FN extract_true_start_end
681#[doc(hidden)]
682fn extract_true_start_end<B>(range: B, max_len: usize) -> (usize, usize)
683where
684 B: RangeBounds<usize>,
685{
686 let start = match range.start_bound() {
687 std::ops::Bound::Included(first) => *first,
688 std::ops::Bound::Excluded(one_before_first) => *one_before_first + 1,
689 std::ops::Bound::Unbounded => 0,
690 };
691 let end = match range.end_bound() {
692 std::ops::Bound::Included(last) => *last + 1,
693 std::ops::Bound::Excluded(one_after_last) => *one_after_last,
694 std::ops::Bound::Unbounded => max_len,
695 };
696 return (start, end);
697}
698
699//MACRO internal!
700macro_rules! internal {
701 ($p:tt) => {
702 unsafe { &mut *$p.internal.get() }
703 };
704}
705pub(crate) use internal;
706
707//MACRO major_malfunction!
708macro_rules! major_malfunction {
709 ($MSG:literal, $($VAR:expr),*) => {
710 if cfg!(feature = "major_malf_is_err") {
711 return Err(AccessError::MAJOR_MALFUNCTION(format!($MSG, $($VAR,)*)));
712 } else if cfg!(feature = "major_malf_is_panic") {
713 panic!($MSG, $($VAR,)*)
714 } else if cfg!(feature = "major_malf_is_undefined") {
715 unsafe { unreachable_unchecked() }
716 } else {
717 return Err(AccessError::MAJOR_MALFUNCTION(format!($MSG, $($VAR,)*)));
718 }
719 };
720}
721pub(crate) use major_malfunction;