linearize/
linearized.rs

1use {
2    crate::Linearize,
3    core::{
4        cmp::Ordering,
5        fmt::{Debug, Formatter},
6        hash::{Hash, Hasher},
7        marker::PhantomData,
8    },
9};
10
11/// Pre-computed output of [Linearize::linearize].
12///
13/// [StaticMap] and [StaticCopyMap] can be index directly with a [Linearize] type.
14/// That operation computes the output of [linearize] ad-hoc. While the linearize function
15/// is zero cost for many types, you might be using types for which the cost is non-zero
16/// or unknown.
17///
18/// In such situations, the `Linearized` type allows you to cache the output of
19/// [linearize].
20///
21/// It is guaranteed that the value cached by this type is the output of the linearize
22/// function. In particular, the value is less than [LENGTH].
23///
24/// # Example
25///
26/// ```rust
27/// # use linearize::{Linearize, LinearizeExt, StaticMap};
28/// fn add_one<L: Linearize>(map: &mut StaticMap<L, u8>, key: L) {
29///     let key = key.linearized();
30///     let v = map[key];
31///     map[key] = v + 1;
32/// }
33/// ```
34///
35/// # Trait Implementations
36///
37/// This type implements traits such as [Debug], [Hash], etc. These implementations
38/// operate on the pre-computed `usize`. In particular, the [Debug] implementation does
39/// not print the name of the original value used to create this object.
40///
41/// [StaticMap]: crate::StaticMap
42/// [StaticCopyMap]: crate::StaticCopyMap
43/// [linearize]: Linearize::linearize
44/// [LENGTH]: Linearize::LENGTH
45#[repr(transparent)]
46pub struct Linearized<L>
47where
48    L: ?Sized,
49{
50    index: usize,
51    _phantom: PhantomData<L>,
52}
53
54impl<L> Linearized<L>
55where
56    L: ?Sized,
57{
58    /// Pre-computes the linearized value.
59    ///
60    /// This function pre-computes the output of [linearize](Linearize::linearize).
61    ///
62    /// The [LinearizeExt](crate::LinearizeExt) extension trait provides the
63    /// [linearized](crate::LinearizeExt::linearized) function which does the same.
64    ///
65    /// # Example
66    ///
67    /// ```rust
68    /// # use linearize::{Linearize, LinearizeExt, Linearized, StaticMap};
69    /// fn get_value<L: Linearize>(map: &StaticMap<L, u8>, key: L) -> u8 {
70    ///     map[Linearized::new(&key)]
71    ///     // Or: map[key.linearized()]
72    /// }
73    /// ```
74    pub fn new(l: &L) -> Self
75    where
76        L: Linearize,
77    {
78        unsafe {
79            // SAFETY: l.linearize() guarantees that it is less than L::LENGTH.
80            Self::new_unchecked(l.linearize())
81        }
82    }
83
84    /// Wraps an already computed values.
85    ///
86    /// # Safety
87    ///
88    /// The index must be less than `L::LENGTH`.
89    pub const unsafe fn new_unchecked(index: usize) -> Self {
90        Self {
91            index,
92            _phantom: PhantomData,
93        }
94    }
95
96    /// Returns the linearized value.
97    ///
98    /// This function returns the output of [linearize](Linearize::linearize) that was
99    /// computed when this object was created.
100    ///
101    /// # Example
102    ///
103    /// ```rust
104    /// # use linearize::{Linearize, LinearizeExt};
105    /// fn get<L: Linearize>(key: L) {
106    ///     assert_eq!(key.linearized().get(), key.linearize());
107    /// }
108    /// ```
109    pub fn get(self) -> usize {
110        self.index
111    }
112
113    /// Returns the value that was used to create this object.
114    ///
115    /// This function returns the output of
116    /// [from_linear_unchecked](Linearize::from_linear_unchecked). This value is required
117    /// to be the same that was used to create this object.
118    ///
119    /// # Example
120    ///
121    /// ```rust
122    /// # use std::fmt::Debug;
123    /// # use linearize::{Linearize, LinearizeExt};
124    /// fn get<L: Linearize + Eq + Debug>(key: L) {
125    ///     assert_eq!(key.linearized().delinearize(), key);
126    /// }
127    /// ```
128    pub fn delinearize(self) -> L
129    where
130        L: Linearize + Sized,
131    {
132        unsafe {
133            // SAFETY: self.index is only written by Self::new_unchecked which requires
134            // that it is less than L::LENGTH.
135            L::from_linear_unchecked(self.index)
136        }
137    }
138}
139
140impl<L> Copy for Linearized<L> where L: ?Sized {}
141
142impl<L> Clone for Linearized<L>
143where
144    L: ?Sized,
145{
146    fn clone(&self) -> Self {
147        *self
148    }
149}
150
151impl<L> Debug for Linearized<L>
152where
153    L: ?Sized,
154{
155    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
156        self.index.fmt(f)
157    }
158}
159
160impl<L> Hash for Linearized<L>
161where
162    L: ?Sized,
163{
164    fn hash<H: Hasher>(&self, state: &mut H) {
165        self.index.hash(state)
166    }
167}
168
169impl<L> PartialEq for Linearized<L>
170where
171    L: ?Sized,
172{
173    fn eq(&self, other: &Self) -> bool {
174        self.index == other.index
175    }
176}
177
178impl<L> PartialEq<usize> for Linearized<L>
179where
180    L: ?Sized,
181{
182    fn eq(&self, other: &usize) -> bool {
183        self.index == *other
184    }
185}
186
187impl<L> Eq for Linearized<L> where L: ?Sized {}
188
189impl<L> PartialOrd for Linearized<L>
190where
191    L: ?Sized,
192{
193    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
194        Some(self.cmp(other))
195    }
196}
197
198impl<L> PartialOrd<usize> for Linearized<L>
199where
200    L: ?Sized,
201{
202    fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
203        self.index.partial_cmp(other)
204    }
205}
206
207impl<L> Ord for Linearized<L>
208where
209    L: ?Sized,
210{
211    fn cmp(&self, other: &Self) -> Ordering {
212        self.index.cmp(&other.index)
213    }
214}