unempty/capacity.rs
1use std::{
2 cmp::{max, min},
3 fmt::Display,
4};
5
6/// Defines the capacity for a data structure, considering the non-empty nature of data structures in this crate.
7///
8/// Many data structures provide a `with_capacity` or similar constructor to enable pre-allocation.
9/// This has the potential to become confusing for users of a non-empty data structure:
10/// _is this capacity the full capacity, or the additional capacity?_
11///
12/// To prevent this confusion, this crate uses [`Capacity`] for these types of methods.
13///
14/// # `N` constant
15///
16/// The `N` constant is the capacity size of the statically sized portion of the data structure.
17/// For example, `unzero::Vec<T>` statically stores one `T`, so its value for `N` is 1.
18///
19/// # Kinds of capacity
20///
21/// - `total`: Total capacity means "this is the total size of the data structure,
22/// including the statically sized portion maintained by the non-empty data structure".
23/// - `dynamic`: Dynamic capacity means "this is the size of the dynamic portion of the data structure".
24/// Most non-empty data structures are backed by some other dynamically growable structure,
25/// this size represents the size of that structure directly.
26///
27/// For example, consider the following cases (`Vec` in the table below refers to [`unempty::Vec`]):
28///
29/// | Constructor | Total Capacity | Dynamic Capacity |
30/// | ----------------------------------- | -------------- | ---------------- |
31/// | `Vec::new(())` | 1 | 0 |
32/// | `Vec::with_capacity(10.into())` | 10 | 9 |
33/// | `let v = Vec::new(()); v.push(());` | 2 | 1 |
34///
35/// # `From` conversions
36///
37/// The `From` conversions provided for this data structure take the more conservative route and
38/// treat the original value being converted from as _total_ capacity.
39///
40/// [Kinds of capacity]: #kinds-of-capacity
41#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
42pub struct Capacity<const N: usize> {
43 total: usize,
44 dynamic: usize,
45}
46
47impl<const N: usize> Capacity<N> {
48 /// Create a [`Capacity`] with the provided _total_ capacity.
49 /// If the provided total capacity is less than `N`, it is increased to `N`.
50 ///
51 /// For definitions on kinds of capacity, see *[Kinds of capacity]*.
52 pub fn new_total(capacity: usize) -> Self {
53 let total = max(capacity, N);
54 let dynamic = capacity - N;
55 Self { total, dynamic }
56 }
57
58 /// Create a [`Capacity`] with the provided capacity for the dynamic portion of the data structure.
59 /// If the provided dynamic capacity would cause an integer overflow when accounting for `N`,
60 /// the dynamic capacity is reduced to `usize::MAX - N`.
61 ///
62 /// For definitions on kinds of capacity, see *[Kinds of capacity]*.
63 pub fn new_dynamic(capacity: usize) -> Self {
64 let dynamic = min(capacity, usize::MAX - N);
65 let total = dynamic + N;
66 Self { total, dynamic }
67 }
68
69 /// Reference the _total_ capacity specified.
70 ///
71 /// For definitions on kinds of capacity, see *[Kinds of capacity]*.
72 pub fn total(&self) -> usize {
73 self.total
74 }
75
76 /// Reference the _dynamic_ capacity specified.
77 ///
78 /// For definitions on kinds of capacity, see *[Kinds of capacity]*.
79 pub fn dynamic(&self) -> usize {
80 self.dynamic
81 }
82
83 /// Reference the size of `N`.
84 pub fn sizeof_n() -> usize {
85 N
86 }
87}
88
89impl<I, const N: usize> From<I> for Capacity<N>
90where
91 I: Into<usize>,
92{
93 fn from(total_capacity: I) -> Self {
94 Self::new_total(total_capacity.into())
95 }
96}
97
98impl<const N: usize> Default for Capacity<N> {
99 fn default() -> Self {
100 Self::new_total(N)
101 }
102}
103
104impl<const N: usize> Display for Capacity<N> {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(
107 f,
108 "capacity(static_size: {}, dynamic_size: {})",
109 self.total, self.dynamic
110 )
111 }
112}