ptr_bool/lib.rs
1#![no_std]
2
3extern crate core;
4
5use core::{fmt, mem};
6
7/// A pointer and boolean bitpacked together.
8///
9/// [`PtrBool`]s are the same size as a raw pointer; four bytes on a 32-bit system and eight bytes
10/// on a 64-bit system. This is done by storing in the boolean value in the one-bit of the
11/// pointer.
12///
13/// # Caveats
14/// * The pointer must be aligned by two to ensure that the one-bit is unnecessary and can be used
15/// to stored the boolean value.
16///
17/// * Possibly a slight overhead when reading the values of the [`PtrBool`], as the boolean value
18/// must be omitted from the pointer *each time it's read*, and the pointer value must be omitted
19/// from the boolean value each time it is read. That is, unless there is some sort of unknown
20/// optimization built into Rust for the case of storing a boolean in one bit.
21///
22/// * The value stored inside of a [`PtrBool`] must be [`Sized`]. This is because the pointer is
23/// converted to and from a [`usize`]; which loses any metadata which would have been in the raw
24/// pointer.
25#[derive(Clone, Copy, PartialEq)]
26#[repr(transparent)]
27pub struct PtrBool<T> {
28 inner: *mut T,
29}
30
31impl<T> PtrBool<T> {
32 /// Returns the raw pointer value, omitting the boolean value.
33 #[inline]
34 fn get_ptr(&self) -> *mut T {
35 (self.inner as usize & !1) as *mut T
36 }
37
38 /// Returns the raw boolean value, omitting the pointer value.
39 #[inline]
40 fn get_bool(&self) -> bool {
41 self.inner as usize & 1 == 1
42 }
43}
44
45impl<T> PtrBool<T> {
46 /// Initializes a [`PtrBool`] from the provided pointer and boolean values if the pointer is
47 /// aligned by two.
48 ///
49 /// # Examples
50 /// ```
51 /// use ptr_bool::PtrBool;
52 ///
53 /// let mut my_value = 42;
54 /// let ptr = PtrBool::new(&mut my_value as *mut _, true).unwrap();
55 ///
56 /// assert_eq!(ptr.as_bool(), true);
57 /// unsafe {
58 /// assert_eq!(*ptr.as_ref().unwrap(), 42);
59 /// }
60 /// ```
61 pub fn new(mut ptr: *mut T, bool_: bool) -> Option<Self> {
62 // Check the alignment of the pointer.
63 if ptr as usize % 2 != 0 || mem::align_of::<T>() <= 1 {
64 return None;
65 }
66
67 if bool_ {
68 ptr = (ptr as usize | 1) as *mut T;
69 }
70
71 Some(Self { inner: ptr })
72 }
73
74 /// Initializes a [`PtrBool`] with a null pointer and the provided boolean value. Returns
75 /// `None` if the type of this [`PtrBool`] is not aligned by 2.
76 ///
77 /// # Examples
78 /// ```
79 /// use ptr_bool::PtrBool;
80 ///
81 /// let ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
82 ///
83 /// assert_eq!(ptr.as_bool(), true);
84 /// assert_eq!(ptr.is_null(), true);
85 /// ```
86 pub fn null(bool_: bool) -> Option<Self> {
87 PtrBool::new(core::ptr::null::<T>() as *mut T, bool_)
88 }
89
90 /// Converts this [`PtrBool`] into a boolean value.
91 ///
92 /// # Examples
93 /// ```
94 /// use ptr_bool::PtrBool;
95 ///
96 /// let ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
97 ///
98 /// assert_eq!(ptr.as_bool(), true);
99 /// ```
100 pub fn as_bool(&self) -> bool {
101 self.get_bool()
102 }
103
104 /// Sets the inner boolean value of this pointer.
105 ///
106 /// # Examples
107 /// ```
108 /// use ptr_bool::PtrBool;
109 ///
110 /// let mut ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
111 ///
112 /// assert_eq!(ptr.as_bool(), true);
113 /// ptr.set_bool(false);
114 /// assert_eq!(ptr.as_bool(), false);
115 /// ```
116 pub fn set_bool(&mut self, bool_: bool) {
117 self.inner = (self.get_ptr() as usize | if bool_ == true { 1 } else { 0 }) as *mut T;
118 }
119
120 /// Converts this [`PtrBool`] into a raw pointer.
121 ///
122 /// # Examples
123 /// ```
124 /// use ptr_bool::PtrBool;
125 /// use std::ptr;
126 ///
127 /// let mut ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
128 ///
129 /// assert_eq!(ptr.as_ptr(), ptr::null());
130 /// ```
131 pub fn as_ptr(&self) -> *const T {
132 self.get_ptr()
133 }
134
135 /// Converts this [`PtrBool`] into a raw mutable pointer.
136 ///
137 /// # Examples
138 /// ```
139 /// use ptr_bool::PtrBool;
140 /// use std::ptr;
141 ///
142 /// let mut ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
143 ///
144 /// assert_eq!(ptr.as_mut_ptr(), ptr::null_mut());
145 /// ```
146 pub fn as_mut_ptr(&self) -> *mut T {
147 self.get_ptr()
148 }
149
150 /// Returns `true` if the internal pointer is null.
151 ///
152 /// # Examples
153 /// ```
154 /// use ptr_bool::PtrBool;
155 /// use std::ptr;
156 ///
157 /// let mut ptr: PtrBool<usize> = PtrBool::null(true).unwrap();
158 ///
159 /// assert_eq!(ptr.is_null(), true);
160 /// ```
161 pub fn is_null(&self) -> bool {
162 self.get_ptr().is_null()
163 }
164
165 /// Converts this [`PtrBool`] into a reference.
166 ///
167 /// # Safety
168 /// By calling this method you promise that:
169 ///
170 /// * The wrapped pointer must be properly aligned.
171 ///
172 /// * This pointer must be dereferenceable in the sense defined in
173 /// [the `ptr` documentation](core::ptr).
174 ///
175 /// * The pointer must point to an initialized instance of `T`.
176 ///
177 /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is arbitrarily
178 /// chosen and does not necessarily reflect the actual lifetime of the data. In particular,
179 /// while this reference exists, the memory the pointer points to must not get mutated
180 /// (except inside `UnsafeCell`).
181 ///
182 /// If you are completely sure this pointer is not null; then the
183 /// [`as_ref_unchecked`](#method.as_ref_unchecked) method can be used instead.
184 ///
185 /// # Examples
186 /// ```
187 /// use ptr_bool::PtrBool;
188 ///
189 /// let raw_ptr = Box::into_raw(Box::new(42));
190 /// let ptr_bool = PtrBool::new(raw_ptr, true).unwrap();
191 ///
192 /// unsafe { assert_eq!(*ptr_bool.as_ref().unwrap(), 42) };
193 /// assert_eq!(ptr_bool.as_bool(), true);
194 ///
195 /// unsafe { Box::from_raw(ptr_bool.as_mut_ptr()) };
196 /// ```
197 pub unsafe fn as_ref<'a>(&self) -> Option<&'a T> {
198 self.get_ptr().as_ref()
199 }
200
201 /// Converts this [`PtrBool`] into a reference. Equivalent to `&*ptr_bool.as_mut_ptr()` in
202 /// terms of safety.
203 ///
204 /// # Safety
205 /// By calling this method you promise that:
206 ///
207 /// * The wrapped pointer must be properly aligned.
208 ///
209 /// * This pointer must be dereferenceable in the sense defined in
210 /// [the `ptr` documentation](core::ptr).
211 ///
212 /// * The pointer must point to an initialized instance of `T`.
213 ///
214 /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is arbitrarily
215 /// chosen and does not necessarily reflect the actual lifetime of the data. In particular,
216 /// while this reference exists, the memory the pointer points to must not get mutated
217 /// (except inside `UnsafeCell`).
218 ///
219 /// If this pointer may be null, use the [`as_ref`](#method.as_ref) method instead.
220 ///
221 /// # Examples
222 /// ```
223 /// use ptr_bool::PtrBool;
224 ///
225 /// let raw_ptr = Box::into_raw(Box::new(42));
226 /// let ptr_bool = PtrBool::new(raw_ptr, true).unwrap();
227 ///
228 /// unsafe { assert_eq!(*ptr_bool.as_ref_unchecked(), 42) };
229 /// assert_eq!(ptr_bool.as_bool(), true);
230 ///
231 /// unsafe { Box::from_raw(ptr_bool.as_mut_ptr()) };
232 /// ```
233 pub unsafe fn as_ref_unchecked<'a>(&self) -> &'a T {
234 &*self.get_ptr()
235 }
236
237 /// Converts this [`PtrBool`] into a reference.
238 ///
239 /// # Safety
240 /// By calling this method you promise that:
241 ///
242 /// * The wrapped pointer must be properly aligned.
243 ///
244 /// * This pointer must be dereferenceable in the sense defined in
245 /// [the `ptr` documentation](core::ptr).
246 ///
247 /// * The pointer must point to an initialized instance of `T`.
248 ///
249 /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is
250 /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data.
251 /// In particular, while this reference exists, the memory the pointer points to must
252 /// not get accessed (read or written) through any other pointer.
253 ///
254 /// If you are completely sure this pointer is not null; then the
255 /// [`as_mut_unchecked`](#method.as_mut_unchecked) method can be used instead.
256 ///
257 /// # Examples
258 /// ```
259 /// use ptr_bool::PtrBool;
260 ///
261 /// let raw_ptr = Box::into_raw(Box::new(42));
262 /// let ptr_bool = PtrBool::new(raw_ptr, true).unwrap();
263 ///
264 /// unsafe { assert_eq!(*ptr_bool.as_mut().unwrap(), 42) };
265 /// assert_eq!(ptr_bool.as_bool(), true);
266 ///
267 /// unsafe { Box::from_raw(ptr_bool.as_mut_ptr()) };
268 /// ```
269 pub unsafe fn as_mut<'a>(&self) -> Option<&'a mut T> {
270 self.get_ptr().as_mut()
271 }
272
273 /// Converts this [`PtrBool`] into a reference. Equivalent to `&mut *ptr_bool.as_mut_ptr()` in
274 /// terms of safety.
275 ///
276 /// # Safety
277 /// By calling this method you promise that:
278 ///
279 /// * The wrapped pointer must be properly aligned.
280 ///
281 /// * This pointer must be dereferenceable in the sense defined in
282 /// [the `ptr` documentation](core::ptr).
283 ///
284 /// * The pointer must point to an initialized instance of `T`.
285 ///
286 /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is
287 /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data.
288 /// In particular, while this reference exists, the memory the pointer points to must
289 /// not get accessed (read or written) through any other pointer.
290 ///
291 /// If this pointer may be null, use the [`as_mut`](#method.as_mut) method instead.
292 ///
293 /// # Examples
294 /// ```
295 /// use ptr_bool::PtrBool;
296 ///
297 /// let raw_ptr = Box::into_raw(Box::new(42));
298 /// let ptr_bool = PtrBool::new(raw_ptr, true).unwrap();
299 ///
300 /// unsafe { assert_eq!(*ptr_bool.as_mut_unchecked(), 42) };
301 /// assert_eq!(ptr_bool.as_bool(), true);
302 ///
303 /// unsafe { Box::from_raw(ptr_bool.as_mut_ptr()) };
304 /// ```
305 pub unsafe fn as_mut_unchecked<'a>(&self) -> &'a mut T {
306 &mut *self.get_ptr()
307 }
308}
309
310impl<T> fmt::Debug for PtrBool<T> {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.debug_tuple("PtrBool")
313 .field(&self.clone().get_ptr())
314 .field(&self.clone().get_bool())
315 .finish()
316 }
317}
318
319impl<T> fmt::Display for PtrBool<T> {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.debug_tuple("PtrBool")
322 .field(&self.clone().get_ptr())
323 .field(&self.clone().get_bool())
324 .finish()
325 }
326}
327
328impl<T> fmt::Pointer for PtrBool<T> {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 f.debug_tuple("PtrBool")
331 .field(&self.clone().get_ptr())
332 .field(&self.clone().get_bool())
333 .finish()
334 }
335}