1use core::{
2 cmp,
3 convert::TryFrom,
4 fmt,
5 hash::{Hash, Hasher},
6 marker::PhantomData,
7 mem,
8 ptr::NonNull,
9};
10
11use crate::{Null, TagNonNull, TagPtr};
12
13impl<T, const N: usize> Clone for TagNonNull<T, N> {
16 impl_clone!();
17}
18
19impl<T, const N: usize> Copy for TagNonNull<T, N> {}
22
23impl<T, const N: usize> TagNonNull<T, N> {
26 doc_comment! {
27 doc_tag_bits!(),
28 pub const TAG_BITS: usize = N;
29 }
30
31 doc_comment! {
32 doc_tag_mask!(),
33 pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS);
34 }
35
36 doc_comment! {
37 doc_ptr_mask!(),
38 pub const POINTER_MASK: usize = !Self::TAG_MASK;
39 }
40
41 const COMPOSE_ERR_MSG: &'static str =
42 "argument `ptr` is mis-aligned for `N` tag bits and could be parsed as marked `null` \
43 pointer.";
44
45 #[inline]
53 pub const unsafe fn new_unchecked(marked_ptr: TagPtr<T, N>) -> Self {
54 Self { inner: NonNull::new_unchecked(marked_ptr.inner), _marker: PhantomData }
55 }
56
57 doc_comment! {
58 doc_from_usize!(),
59 #[inline]
60 pub const unsafe fn from_usize(val: usize) -> Self {
61 Self { inner: NonNull::new_unchecked(val as *mut _), _marker: PhantomData }
62 }
63 }
64
65 doc_comment! {
66 doc_into_raw!(),
67 #[inline]
68 pub const fn into_raw(self) -> NonNull<T> {
69 self.inner
70 }
71 }
72
73 doc_comment! {
74 doc_cast!(),
75 pub const fn cast<U>(self) -> TagNonNull<U, N> {
76 TagNonNull { inner: self.inner.cast(), _marker: PhantomData }
77 }
78 }
79
80 doc_comment! {
81 doc_into_usize!(),
82 #[inline]
83 pub fn into_usize(self) -> usize {
84 self.inner.as_ptr() as _
85 }
86 }
87
88 #[inline]
90 pub const fn into_marked_ptr(self) -> TagPtr<T, N> {
91 TagPtr::new(self.inner.as_ptr())
92 }
93
94 #[inline]
101 pub fn new(marked_ptr: TagPtr<T, N>) -> Result<Self, Null> {
102 Self::try_from(marked_ptr)
103 }
104
105 #[inline]
107 pub const fn dangling() -> Self {
108 let alignment = mem::align_of::<T>();
109 let val = if alignment >= Self::TAG_MASK + 1 { alignment } else { Self::TAG_MASK + 1 };
110 unsafe { Self::from_usize(val) }
112 }
113
114 doc_comment! {
115 doc_compose!(),
116 #[inline]
122 pub fn compose(ptr: NonNull<T>, tag: usize) -> Self {
123 Self::try_compose(ptr, tag).expect(Self::COMPOSE_ERR_MSG)
124 }
125 }
126
127 #[inline]
138 pub fn try_compose(ptr: NonNull<T>, tag: usize) -> Result<Self, Null> {
139 Self::try_compose_inner(ptr.as_ptr(), tag)
140 }
141
142 #[inline]
150 pub unsafe fn compose_unchecked(ptr: NonNull<T>, tag: usize) -> Self {
151 Self::new_unchecked(TagPtr::compose(ptr.as_ptr(), tag))
152 }
153
154 doc_comment! {
155 doc_clear_tag!(),
156 #[inline]
157 pub fn clear_tag(self) -> Self {
158 Self { inner: self.decompose_non_null(), _marker: PhantomData }
159 }
160 }
161
162 doc_comment! {
163 doc_split_tag!(),
164 #[inline]
165 pub fn split_tag(self) -> (Self, usize) {
166 let (inner, tag) = self.decompose();
167 (Self { inner, _marker: PhantomData }, tag)
168 }
169 }
170
171 doc_comment! {
172 doc_set_tag!(),
173 #[inline]
174 pub fn set_tag(self, tag: usize) -> Self {
175 let ptr = self.decompose_non_null();
176 unsafe { Self::compose_unchecked(ptr, tag) }
178 }
179 }
180
181 doc_comment! {
182 doc_update_tag!(),
183 #[inline]
184 pub fn update_tag(self, func: impl FnOnce(usize) -> usize) -> Self {
185 let (ptr, tag) = self.decompose();
186 unsafe { Self::compose_unchecked(ptr, func(tag)) }
188 }
189 }
190
191 doc_comment! {
192 doc_add_tag!(),
193 #[inline]
198 pub unsafe fn add_tag(self, value: usize) -> Self {
199 Self::from_usize(self.into_usize().wrapping_add(value))
200 }
201 }
202
203 doc_comment! {
204 doc_sub_tag!(),
205 #[inline]
210 pub unsafe fn sub_tag(self, value: usize) -> Self {
211 Self::from_usize(self.into_usize().wrapping_sub(value))
212 }
213 }
214
215 doc_comment! {
216 doc_decompose!(),
217 #[inline]
218 pub fn decompose(self) -> (NonNull<T>, usize) {
219 (self.decompose_non_null(), self.decompose_tag())
220 }
221 }
222
223 doc_comment! {
224 doc_decompose_ptr!(),
225 #[inline]
226 pub fn decompose_ptr(self) -> *mut T {
227 crate::decompose_ptr(self.inner.as_ptr() as usize, Self::TAG_BITS)
228 }
229 }
230
231 doc_comment! {
232 doc_decompose_non_null!(),
233 #[inline]
234 pub fn decompose_non_null(self) -> NonNull<T> {
235 unsafe { NonNull::new_unchecked(self.decompose_ptr()) }
237 }
238 }
239
240 doc_comment! {
241 doc_decompose_tag!(),
242 #[inline]
243 pub fn decompose_tag(self) -> usize {
244 crate::decompose_tag::<T>(self.inner.as_ptr() as usize, Self::TAG_BITS)
245 }
246 }
247
248 doc_comment! {
249 doc_as_ref!("non-nullable"),
250 #[inline]
251 pub unsafe fn as_ref(&self) -> &T {
252 &*self.decompose_non_null().as_ptr()
253 }
254 }
255
256 doc_comment! {
257 doc_as_mut!("non-nullable", TagNonNull),
258 #[inline]
259 pub unsafe fn as_mut(&mut self) -> &mut T {
260 &mut *self.decompose_non_null().as_ptr()
261 }
262 }
263
264 #[inline]
271 pub unsafe fn decompose_ref(&self) -> (&T, usize) {
272 let (ptr, tag) = self.decompose();
273 (&*ptr.as_ptr(), tag)
274 }
275
276 #[inline]
283 pub unsafe fn decompose_mut(&mut self) -> (&mut T, usize) {
284 let (ptr, tag) = self.decompose();
285 (&mut *ptr.as_ptr(), tag)
286 }
287
288 #[inline]
289 fn try_compose_inner(ptr: *mut T, tag: usize) -> Result<Self, Null> {
290 match ptr as usize & Self::POINTER_MASK {
291 0 => Err(Null(ptr as usize)),
292 _ => Ok(unsafe { Self::new_unchecked(TagPtr::compose(ptr, tag)) }),
294 }
295 }
296}
297
298impl<T, const N: usize> fmt::Debug for TagNonNull<T, N> {
301 impl_debug!("TagNonNull");
302}
303
304impl<T, const N: usize> fmt::Pointer for TagNonNull<T, N> {
307 impl_pointer!();
308}
309
310impl<T, const N: usize> From<&T> for TagNonNull<T, N> {
313 #[inline]
314 fn from(reference: &T) -> Self {
315 Self { inner: NonNull::from(reference), _marker: PhantomData }
316 }
317}
318
319impl<T, const N: usize> From<&mut T> for TagNonNull<T, N> {
322 #[inline]
323 fn from(reference: &mut T) -> Self {
324 Self { inner: NonNull::from(reference), _marker: PhantomData }
325 }
326}
327
328impl<T, const N: usize> PartialEq for TagNonNull<T, N> {
331 impl_partial_eq!();
332}
333
334impl<T, const N: usize> PartialOrd for TagNonNull<T, N> {
337 impl_partial_ord!();
338}
339
340impl<T, const N: usize> Eq for TagNonNull<T, N> {}
343
344impl<T, const N: usize> Ord for TagNonNull<T, N> {
347 impl_ord!();
348}
349
350impl<T, const N: usize> Hash for TagNonNull<T, N> {
353 impl_hash!();
354}
355
356impl<T, const N: usize> TryFrom<*mut T> for TagNonNull<T, N> {
359 type Error = Null;
360
361 #[inline]
362 fn try_from(ptr: *mut T) -> Result<Self, Self::Error> {
363 Self::try_compose_inner(ptr, 0)
364 }
365}
366
367impl<T, const N: usize> TryFrom<*const T> for TagNonNull<T, N> {
370 type Error = Null;
371
372 #[inline]
373 fn try_from(ptr: *const T) -> Result<Self, Self::Error> {
374 Self::try_from(ptr as *mut _)
375 }
376}
377
378impl<T, const N: usize> TryFrom<TagPtr<T, N>> for TagNonNull<T, N> {
381 type Error = Null;
382
383 #[inline]
384 fn try_from(ptr: TagPtr<T, N>) -> Result<Self, Self::Error> {
385 Self::try_from(ptr.into_raw())
386 }
387}
388
389impl<T, const N: usize> TryFrom<NonNull<T>> for TagNonNull<T, N> {
392 type Error = Null;
393
394 #[inline]
395 fn try_from(ptr: NonNull<T>) -> Result<Self, Self::Error> {
396 Self::try_from(ptr.as_ptr())
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use core::ptr::NonNull;
403
404 use crate::Null;
405
406 type TagNonNull = crate::TagNonNull<i32, 2>;
407
408 #[test]
409 fn test_dangling() {
410 assert_eq!(TagNonNull::dangling().into_raw(), NonNull::dangling());
411
412 #[repr(align(64))]
413 struct Alignment64;
414 assert_eq!(crate::TagNonNull::<Alignment64, 0>::dangling().into_usize(), 64);
415 }
416
417 #[test]
418 fn test_try_compose() {
419 let reference = &1;
420 let ptr = NonNull::from(reference);
421 let res = TagNonNull::try_compose(ptr, 0b11).map(|ptr| ptr.decompose());
422 assert_eq!(res, Ok((ptr, 0b11)));
423
424 let dangling = NonNull::dangling();
425 let res = TagNonNull::try_compose(dangling, 0).map(|ptr| ptr.decompose());
426 assert_eq!(res, Ok((dangling, 0)));
427
428 let ptr = NonNull::new(0b11 as *mut i32).unwrap();
429 let res = TagNonNull::try_compose(ptr, 0b11);
430 assert_eq!(res, Err(Null(0b11)));
431 }
432}