flagged_pointer/
flag.rs

1//! # Flag Metadata
2//!
3//! This module defines the `FlagMeta` trait and implementations for encoding
4//! flag information within unused pointer bits.
5
6/// A trait for types that can be encoded as flags in unused pointer bits.
7///
8/// This trait is `unsafe` to implement because it requires careful attention to
9/// bit patterns and must not conflict with valid pointer bits.
10///
11/// # Safety
12///
13/// Implementors must ensure:
14/// 1. usize returned by `mask()` includes all bits that are guaranteed to be unused in pointers, more bits is ok but less bits will cause UB.
15/// 2. `to_usize` and `from_usize` are inverse operations
16/// 3. All possible values of `USED_FLAG_BITS_MASK` are valid for the type
17///
18/// # Examples
19///
20/// ```
21/// use flagged_pointer::flag::FlagMeta;
22///
23/// #[derive(Copy, Clone)]
24/// struct MyFlags(u8);
25///
26/// unsafe impl FlagMeta for MyFlags {
27///     const USED_FLAG_BITS_MASK: usize = 0b111; // Use bottom 3 bits
28///     
29///     fn to_usize(self) -> usize {
30///         self.0 as usize
31///     }
32///     
33///     unsafe fn from_usize(nz: usize) -> Self {
34///         MyFlags(nz as u8)
35///     }
36/// }
37/// ```
38pub unsafe trait FlagMeta: Copy {
39    /// Bitmask indicating which bits are used for flags, only for compile time check
40    /// If bitmask is not known at compile time, set it to 0 (means no compile time check)
41    const USED_FLAG_BITS_MASK: usize;
42
43    /// Returns the bitmask for flag bits.
44    /// Defaults to `USED_FLAG_BITS_MASK` but can be overridden for dynamic masks.
45    fn mask() -> usize {
46        Self::USED_FLAG_BITS_MASK
47    }
48
49    /// Converts the flag type to a `usize` for storage in pointer bits.
50    fn to_usize(self) -> usize;
51
52    /// Converts a `usize` back to the flag type.
53    ///
54    /// # Safety
55    /// The caller must ensure that `nz` contains only valid flag bits.
56    unsafe fn from_usize(nz: usize) -> Self;
57}
58
59/// Implement `FlagMeta` for `enumflags2::BitFlags`
60/// Because `const_ops` is not stable, we have to record the num type and manually cast it in compile time
61mod enumflag_impl {
62    use std::mem::transmute_copy;
63
64    use enumflags2::{BitFlag, BitFlags};
65
66    use crate::flag::FlagMeta;
67
68    enum NumType {
69        Usize,
70        U64,
71        U32,
72        U16,
73        U8,
74    }
75
76    trait ConstNumType: Copy {
77        const NUM_TYPE: NumType;
78    }
79
80    impl ConstNumType for u8 {
81        const NUM_TYPE: NumType = NumType::U8;
82    }
83    impl ConstNumType for u16 {
84        const NUM_TYPE: NumType = NumType::U16;
85    }
86    impl ConstNumType for u32 {
87        const NUM_TYPE: NumType = NumType::U32;
88    }
89    impl ConstNumType for u64 {
90        const NUM_TYPE: NumType = NumType::U64;
91    }
92    impl ConstNumType for usize {
93        const NUM_TYPE: NumType = NumType::Usize;
94    }
95
96    const fn cast_from_usize<N>(nz: usize) -> N
97    where
98        N: ConstNumType,
99    {
100        if size_of::<N>() > size_of::<usize>() {
101            panic!("cast_to_usize: num size is larger than usize");
102        }
103
104        match N::NUM_TYPE {
105            NumType::U8 => unsafe { transmute_copy(&(nz as u8)) },
106            NumType::U16 => unsafe { transmute_copy(&(nz as u16)) },
107            NumType::U32 => unsafe { transmute_copy(&(nz as u32)) },
108            NumType::U64 => unsafe { transmute_copy(&(nz as u64)) },
109            NumType::Usize => unsafe { transmute_copy(&nz) },
110        }
111    }
112
113    const fn cast_to_usize<N>(num: N) -> usize
114    where
115        N: ConstNumType,
116    {
117        if size_of::<N>() > size_of::<usize>() {
118            panic!("cast_to_usize: num size is larger than usize");
119        }
120
121        match N::NUM_TYPE {
122            NumType::U8 => unsafe {
123                let n_u8: u8 = transmute_copy(&num);
124                n_u8 as usize
125            },
126            NumType::U16 => unsafe {
127                let n_u16: u16 = transmute_copy(&num);
128                n_u16 as usize
129            },
130            NumType::U32 => unsafe {
131                let n_u32: u32 = transmute_copy(&num);
132                n_u32 as usize
133            },
134            NumType::U64 => unsafe {
135                let n_u64: u64 = transmute_copy(&num);
136                n_u64 as usize
137            },
138            NumType::Usize => unsafe {
139                let n_usize: usize = transmute_copy(&num);
140                n_usize
141            },
142        }
143    }
144
145    unsafe impl<F> FlagMeta for BitFlags<F>
146    where
147        F: BitFlag,
148        F::Numeric: ConstNumType,
149    {
150        const USED_FLAG_BITS_MASK: usize = cast_to_usize(F::ALL_BITS);
151
152        fn to_usize(self) -> usize {
153            cast_to_usize(self.bits())
154        }
155
156        unsafe fn from_usize(nz: usize) -> Self {
157            unsafe { Self::from_bits_unchecked(cast_from_usize(nz)) }
158        }
159    }
160}