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}