about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-05-21 08:05:21 +0200
committerGitHub <noreply@github.com>2025-05-21 08:05:21 +0200
commitbb7291e71ba1461d273379a762bb1e5bab9ca8c1 (patch)
tree686eb07b543ceb8449660929647f8d1438bc286b
parentc5920278d398480c49138d71bdcfdaa8bcbfc182 (diff)
parent9d1cf125f8ba4e509d5eacc2193b6b2c0999f9e1 (diff)
downloadrust-bb7291e71ba1461d273379a762bb1e5bab9ca8c1.tar.gz
rust-bb7291e71ba1461d273379a762bb1e5bab9ca8c1.zip
Rollup merge of #141222 - mathisbot:ptr_trycastaligned, r=tgross35
Implement `ptr::try_cast_aligned` and `NonNull::try_cast_aligned`.

Implement three common methods on raw pointers and `NonNull`s: `try_cast_aligned`.

## Related links

- Tracking Issue: https://github.com/rust-lang/rust/issues/141221

## About `#[inline]`

Since the result of a call to `align_of` is a power of two known at compile time, the compiler is able to reduce a call to `try_cast_aligned` to only test and sete (or test and jne if followed by `unwrap`), at least on every tier 1 target's arch. This seemed like a good reason to `#[inline]` the function.

- https://godbolt.org/z/ocehvPWMx (raw inlining)
- https://godbolt.org/z/3qa4j4Yrn (comparison with no inlining)
-rw-r--r--library/core/src/ptr/const_ptr.rs28
-rw-r--r--library/core/src/ptr/mut_ptr.rs28
-rw-r--r--library/core/src/ptr/non_null.rs29
3 files changed, 85 insertions, 0 deletions
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 35089b4853d..f6109cafe86 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -66,6 +66,34 @@ impl<T: ?Sized> *const T {
         self as _
     }
 
+    /// Try to cast to a pointer of another type by checking aligment.
+    ///
+    /// If the pointer is properly aligned to the target type, it will be
+    /// cast to the target type. Otherwise, `None` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(pointer_try_cast_aligned)]
+    ///
+    /// let aligned: *const u8 = 0x1000 as _;
+    ///
+    /// // i32 has at most 4-byte alignment, so this will succeed
+    /// assert!(aligned.try_cast_aligned::<i32>().is_some());
+    ///
+    /// let unaligned: *const u8 = 0x1001 as _;
+    ///
+    /// // i32 has at least 2-byte alignment, so this will fail
+    /// assert!(unaligned.try_cast_aligned::<i32>().is_none());
+    /// ```
+    #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")]
+    #[must_use = "this returns the result of the operation, \
+                  without modifying the original"]
+    #[inline]
+    pub fn try_cast_aligned<U>(self) -> Option<*const U> {
+        if self.is_aligned_to(align_of::<U>()) { Some(self.cast()) } else { None }
+    }
+
     /// Uses the address value in a new pointer of another type.
     ///
     /// This operation will ignore the address part of its `meta` operand and discard existing
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 9cf251742d4..2662a4fdc31 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -48,6 +48,34 @@ impl<T: ?Sized> *mut T {
         self as _
     }
 
+    /// Try to cast to a pointer of another type by checking aligment.
+    ///
+    /// If the pointer is properly aligned to the target type, it will be
+    /// cast to the target type. Otherwise, `None` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(pointer_try_cast_aligned)]
+    ///
+    /// let aligned: *mut u8 = 0x1000 as _;
+    ///
+    /// // i32 has at most 4-byte alignment, so this will succeed
+    /// assert!(aligned.try_cast_aligned::<i32>().is_some());
+    ///
+    /// let unaligned: *mut u8 = 0x1001 as _;
+    ///
+    /// // i32 has at least 2-byte alignment, so this will fail
+    /// assert!(unaligned.try_cast_aligned::<i32>().is_none());
+    /// ```
+    #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")]
+    #[must_use = "this returns the result of the operation, \
+                  without modifying the original"]
+    #[inline]
+    pub fn try_cast_aligned<U>(self) -> Option<*mut U> {
+        if self.is_aligned_to(align_of::<U>()) { Some(self.cast()) } else { None }
+    }
+
     /// Uses the address value in a new pointer of another type.
     ///
     /// This operation will ignore the address part of its `meta` operand and discard existing
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 8b31328de04..bb344c6a0d3 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -490,6 +490,35 @@ impl<T: ?Sized> NonNull<T> {
         unsafe { NonNull { pointer: self.as_ptr() as *mut U } }
     }
 
+    /// Try to cast to a pointer of another type by checking aligment.
+    ///
+    /// If the pointer is properly aligned to the target type, it will be
+    /// cast to the target type. Otherwise, `None` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(pointer_try_cast_aligned)]
+    /// use std::ptr::NonNull;
+    ///
+    /// let aligned: NonNull<u8> = NonNull::new(0x1000 as _).unwrap();
+    ///
+    /// // i32 has at most 4-byte alignment, so this will succeed
+    /// assert!(aligned.try_cast_aligned::<i32>().is_some());
+    ///
+    /// let unaligned: NonNull<u8> = NonNull::new(0x1001 as _).unwrap();
+    ///
+    /// // i32 has at least 2-byte alignment, so this will fail
+    /// assert!(unaligned.try_cast_aligned::<i32>().is_none());
+    /// ```
+    #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")]
+    #[must_use = "this returns the result of the operation, \
+                  without modifying the original"]
+    #[inline]
+    pub fn try_cast_aligned<U>(self) -> Option<NonNull<U>> {
+        if self.is_aligned_to(align_of::<U>()) { Some(self.cast()) } else { None }
+    }
+
     /// Adds an offset to a pointer.
     ///
     /// `count` is in units of T; e.g., a `count` of 3 represents a pointer