linearize/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(clippy::missing_safety_doc)]
3
4//! A crate for enumerable types.
5//!
6//! This crate provides the [Linearize] trait which defines a bijection between a type
7//! and an interval of the natural numbers.
8//!
9//! Given such a bijection, many useful things become possible. For example, this crate
10//! defines the types [StaticMap] and [StaticCopyMap] which provide high-performance,
11//! non-allocating mappings from linearizable types to arbitrary values.
12//!
13//! ```rust
14//! # use linearize::StaticMap;
15//! # use linearize_derive::Linearize;
16//! #
17//! #[derive(Linearize)]
18//! enum ColorFormat {
19//!     R,
20//!     Rgb {
21//!         alpha: bool,
22//!     },
23//! }
24//!
25//! let mut channels = StaticMap::default();
26//! channels[ColorFormat::R] = 1;
27//! channels[ColorFormat::Rgb { alpha: false }] = 3;
28//! channels[ColorFormat::Rgb { alpha: true }] = 4;
29//!
30//! assert_eq!(channels[ColorFormat::Rgb { alpha: false }], 3);
31//! ```
32//!
33//! These maps can be constructed conveniently with the [static_map] macro:
34//!
35//! ```rust
36//! # use linearize::{static_map, StaticMap};
37//! # use linearize_derive::Linearize;
38//! #
39//! # #[derive(Linearize)]
40//! # enum ColorFormat {
41//! #     R,
42//! #     Rgb {
43//! #         alpha: bool,
44//! #     },
45//! # }
46//! #
47//! let channels = static_map! {
48//!     ColorFormat::R => 1,
49//!     ColorFormat::Rgb { alpha } => 3 + alpha as u32,
50//! };
51//!
52//! assert_eq!(channels[ColorFormat::Rgb { alpha: false }], 3);
53//! ```
54//!
55//! # Features
56//!
57//! The following features are enabled by default:
58//!
59//! - `std`
60//!
61//! This crate provides the following features:
62//!
63//! - `alloc`: Adds a dependency on the `alloc` crate. This implements additional traits
64//!   for the map types.
65//! - `std`: Adds a dependency on the `std` crate.
66//! - `derive`: Provides the [Linearize](linearize_derive::Linearize) derive macro.
67//! - `serde-1`: Implements `Serialize` and `Deserialize` from serde 1.x for the map types.
68//! - `arbitrary-1`: Implements `Arbitrary` from arbitrary 1.x for the map types.
69//! - `bytemuck-1`: Implements `NoUninit`, `Zeroable`, and `AnyBitPattern` from bytemuck 1.x for the map types.
70//! - `rand-0_8`: Implements various distributions from rand 0.8.x for the map types.
71//! - `rand-0_9`: Implements various distributions from rand 0.9.x for the map types.
72
73#[cfg(feature = "alloc")]
74extern crate alloc;
75
76mod copy_map;
77mod foreign;
78mod impls;
79mod linearized;
80mod r#macro;
81mod map;
82mod storage;
83mod variants;
84
85use crate::{
86    sealed::Sealed,
87    storage::{CopyStorage, Storage},
88    variants::Variants,
89};
90#[cfg(feature = "serde-1")]
91pub use foreign::serde_1;
92#[cfg(feature = "derive")]
93pub use linearize_derive::Linearize;
94#[doc(hidden)]
95pub use r#macro::Builder;
96pub use {copy_map::StaticCopyMap, linearized::Linearized, map::StaticMap};
97
98/// Types whose values can be enumerated.
99///
100/// Types that implement this trait define a bijection between themselves and an interval
101/// of the natural numbers.
102///
103/// # Safety
104///
105/// - [`Self::Storage<T>`] must be `[T; Self::LENGTH]`.
106/// - [`Self::CopyStorage<T>`] must be `[T; Self::LENGTH]`.
107/// - [`Self::linearize`] must be a bijection to `[0, Self::LENGTH)`.
108/// - [`Self::from_linear_unchecked`] must be its inverse.
109///
110/// Note that the bijection implies that a roundtrip through
111/// `linearize | from_linear_unchecked` must return a value that is, for all intents and
112/// purposes, indistinguishable from the original value. The details of this depend on
113/// `Self`.
114pub unsafe trait Linearize {
115    /// `[T; Self::LENGTH]`
116    ///
117    /// This type exists due to a limitation of the rust type system. In a future version
118    /// of this crate, all uses of it will be replaced by `[T; Self::LENGTH]`.
119    type Storage<T>: Storage<Self, T>;
120
121    /// `[T; Self::LENGTH]`
122    ///
123    /// This type exists due to a limitation of the rust type system. In a future version
124    /// of this crate, all uses of it will be replaced by `[T; Self::LENGTH]`.
125    type CopyStorage<T>: CopyStorage<Self, T>
126    where
127        T: Copy;
128
129    /// The cardinality of this type.
130    const LENGTH: usize;
131
132    /// Maps this value to the natural numbers.
133    ///
134    /// This function is a bijection to the interval `[0, Self::LENGTH)`.
135    fn linearize(&self) -> usize;
136
137    /// The inverse of the `linearize` function.
138    ///
139    /// # Safety
140    ///
141    /// `linear` must be less than [`Self::LENGTH`].
142    unsafe fn from_linear_unchecked(linear: usize) -> Self
143    where
144        Self: Sized;
145}
146
147/// Extension trait for types implementing [Linearize].
148pub trait LinearizeExt: Linearize + Sealed {
149    /// A safe version of [Linearize::from_linear_unchecked].
150    ///
151    /// This function returns `None` if `linear >= Self::LENGTH`.
152    fn from_linear(linear: usize) -> Option<Self>
153    where
154        Self: Sized;
155
156    /// Returns an iterator over all values of this type.
157    fn variants() -> Variants<Self>
158    where
159        Self: Sized;
160
161    /// Linearizes this value and stores the value in a [Linearized] object.
162    ///
163    /// See the documentation of [Linearized] for why this might be useful.
164    fn linearized(&self) -> Linearized<Self>;
165}
166
167impl<T> LinearizeExt for T
168where
169    T: Linearize + ?Sized,
170{
171    fn from_linear(linear: usize) -> Option<Self>
172    where
173        Self: Sized,
174    {
175        (linear < Self::LENGTH).then(|| unsafe {
176            // SAFETY: This closure is only called if linear < Self::LENGTH.
177            Self::from_linear_unchecked(linear)
178        })
179    }
180
181    fn variants() -> Variants<Self>
182    where
183        Self: Sized,
184    {
185        Variants::new()
186    }
187
188    fn linearized(&self) -> Linearized<Self> {
189        Linearized::new(self)
190    }
191}
192
193impl<T> Sealed for T where T: Linearize + ?Sized {}
194
195mod sealed {
196    pub trait Sealed {}
197}
198
199pub mod iter {
200    //! All iterators exposed by this crate.
201    //!
202    //! This module exists only to keep the top-level namespace clean.
203    pub use crate::{
204        map::iters::{IntoIter, Iter, IterMut},
205        variants::Variants,
206    };
207}