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}