about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-05-18 23:18:03 +0000
committerbors <bors@rust-lang.org>2022-05-18 23:18:03 +0000
commitd8a3fc4d71bae720cc2534ff5b97164f47622e12 (patch)
tree683bdc9c71844589da8f028471e762eb03d77dd3
parentcd282d7f75da9080fda0f1740a729516e7fbec68 (diff)
parent03d45699390421d6a27f5ae2dc10077b38d950fe (diff)
downloadrust-d8a3fc4d71bae720cc2534ff5b97164f47622e12.tar.gz
rust-d8a3fc4d71bae720cc2534ff5b97164f47622e12.zip
Auto merge of #95643 - WaffleLapkin:ptr_convenience, r=joshtriplett
Add convenience byte offset/check align functions to pointers

This PR adds the following APIs:
```rust
impl *const T {
    // feature gates `pointer_byte_offsets` and `const_pointer_byte_offsets
    pub const unsafe fn byte_offset(self, count: isize) -> Self;
    pub const fn wrapping_byte_offset(self, count: isize) -> Self;
    pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize;
    pub const unsafe fn byte_add(self, count: usize) -> Self;
    pub const unsafe fn byte_sub(self, count: usize) -> Self;
    pub const fn wrapping_byte_add(self, count: usize) -> Self;
    pub const fn wrapping_byte_sub(self, count: usize) -> Self;

    // feature gate `pointer_is_aligned`
    pub fn is_aligned(self) -> bool where T: Sized;
    pub fn is_aligned_to(self, align: usize) -> bool;
}
// ... and the same for` *mut T`
```

Note that all functions except `is_aligned` do **not** require `T: Sized` as their pointee-sized-offset counterparts.

cc `@oli-obk` (you may want to check that I've correctly placed `const`s)
cc `@RalfJung`
-rw-r--r--library/core/src/ptr/const_ptr.rs168
-rw-r--r--library/core/src/ptr/mut_ptr.rs171
2 files changed, 337 insertions, 2 deletions
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 028adc796e5..f26fdc74ce1 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -455,6 +455,26 @@ impl<T: ?Sized> *const T {
         unsafe { intrinsics::offset(self, count) }
     }
 
+    /// Calculates the offset from a pointer in bytes.
+    ///
+    /// `count` is in units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [offset][pointer::offset] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_offset(self, count: isize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `offset`.
+        let this = unsafe { self.cast::<u8>().offset(count).cast::<()>() };
+        from_raw_parts::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     ///
     /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
@@ -517,6 +537,24 @@ impl<T: ?Sized> *const T {
         unsafe { intrinsics::arith_offset(self, count) }
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    ///
+    /// `count` is in units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_offset][pointer::wrapping_offset] on it. See that method
+    /// for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_offset(self, count: isize) -> Self {
+        from_raw_parts::<T>(self.cast::<u8>().wrapping_offset(count).cast::<()>(), metadata(self))
+    }
+
     /// Calculates the distance between two pointers. The returned value is in
     /// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
     ///
@@ -611,6 +649,23 @@ impl<T: ?Sized> *const T {
         unsafe { intrinsics::ptr_offset_from(self, origin) }
     }
 
+    /// Calculates the distance between two pointers. The returned value is in
+    /// units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [offset_from][pointer::offset_from] on it. See that method for
+    /// documentation and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation considers only the data pointers,
+    /// ignoring the metadata.
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+        // SAFETY: the caller must uphold the safety contract for `offset_from`.
+        unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
+    }
+
     /// Calculates the distance between two pointers, *where it's known that
     /// `self` is equal to or greater than `origin`*. The returned value is in
     /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
@@ -813,6 +868,26 @@ impl<T: ?Sized> *const T {
         unsafe { self.offset(count as isize) }
     }
 
+    /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [add][pointer::add] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_add(self, count: usize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `add`.
+        let this = unsafe { self.cast::<u8>().add(count).cast::<()>() };
+        from_raw_parts::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer (convenience for
     /// `.offset((count as isize).wrapping_neg())`).
     ///
@@ -877,6 +952,27 @@ impl<T: ?Sized> *const T {
         unsafe { self.offset((count as isize).wrapping_neg()) }
     }
 
+    /// Calculates the offset from a pointer in bytes (convenience for
+    /// `.byte_offset((count as isize).wrapping_neg())`).
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [sub][pointer::sub] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_sub(self, count: usize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `sub`.
+        let this = unsafe { self.cast::<u8>().sub(count).cast::<()>() };
+        from_raw_parts::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     /// (convenience for `.wrapping_offset(count as isize)`)
     ///
@@ -939,6 +1035,24 @@ impl<T: ?Sized> *const T {
         self.wrapping_offset(count as isize)
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    /// (convenience for `.wrapping_byte_offset(count as isize)`)
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_add][pointer::wrapping_add] on it. See that method for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_add(self, count: usize) -> Self {
+        from_raw_parts::<T>(self.cast::<u8>().wrapping_add(count).cast::<()>(), metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
     ///
@@ -1001,6 +1115,24 @@ impl<T: ?Sized> *const T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_sub][pointer::wrapping_sub] on it. See that method for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_sub(self, count: usize) -> Self {
+        from_raw_parts::<T>(self.cast::<u8>().wrapping_sub(count).cast::<()>(), metadata(self))
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
@@ -1154,12 +1286,46 @@ impl<T: ?Sized> *const T {
         }
 
         // SAFETY:
-        // It is permisseble for `align_offset` to always return `usize::MAX`,
+        // It is permissible for `align_offset` to always return `usize::MAX`,
         // algorithm correctness can not depend on `align_offset` returning non-max values.
         //
         // As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
         unsafe { intrinsics::const_eval_select((self, align), ctfe_impl, rt_impl) }
     }
+
+    /// Returns whether the pointer is properly aligned for `T`.
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+    pub fn is_aligned(self) -> bool
+    where
+        T: Sized,
+    {
+        self.is_aligned_to(core::mem::align_of::<T>())
+    }
+
+    /// Returns whether the pointer is aligned to `align`.
+    ///
+    /// For non-`Sized` pointees this operation considers only the data pointer,
+    /// ignoring the metadata.
+    ///
+    /// # Panics
+    ///
+    /// The function panics if `align` is not a power-of-two (this includes 0).
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+    pub fn is_aligned_to(self, align: usize) -> bool {
+        if !align.is_power_of_two() {
+            panic!("is_aligned_to: align is not a power-of-two");
+        }
+
+        // SAFETY: `is_power_of_two()` will return `false` for zero.
+        unsafe { core::intrinsics::assume(align != 0) };
+
+        // Cast is needed for `T: !Sized`
+        self.cast::<u8>().addr() % align == 0
+    }
 }
 
 impl<T> *const [T] {
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 1a32dd62dfd..37cf2ef4bfb 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -467,6 +467,26 @@ impl<T: ?Sized> *mut T {
         unsafe { intrinsics::offset(self, count) as *mut T }
     }
 
+    /// Calculates the offset from a pointer in bytes.
+    ///
+    /// `count` is in units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [offset][pointer::offset] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_offset(self, count: isize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `offset`.
+        let this = unsafe { self.cast::<u8>().offset(count).cast::<()>() };
+        from_raw_parts_mut::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
     /// offset of `3 * size_of::<T>()` bytes.
@@ -528,6 +548,27 @@ impl<T: ?Sized> *mut T {
         unsafe { intrinsics::arith_offset(self, count) as *mut T }
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    ///
+    /// `count` is in units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_offset][pointer::wrapping_offset] on it. See that method
+    /// for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_offset(self, count: isize) -> Self {
+        from_raw_parts_mut::<T>(
+            self.cast::<u8>().wrapping_offset(count).cast::<()>(),
+            metadata(self),
+        )
+    }
+
     /// Returns `None` if the pointer is null, or else returns a unique reference to
     /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`]
     /// must be used instead.
@@ -787,6 +828,23 @@ impl<T: ?Sized> *mut T {
         unsafe { (self as *const T).offset_from(origin) }
     }
 
+    /// Calculates the distance between two pointers. The returned value is in
+    /// units of **bytes**.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [offset_from][pointer::offset_from] on it. See that method for
+    /// documentation and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation considers only the data pointers,
+    /// ignoring the metadata.
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+        // SAFETY: the caller must uphold the safety contract for `offset_from`.
+        unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
+    }
+
     /// Calculates the distance between two pointers, *where it's known that
     /// `self` is equal to or greater than `origin`*. The returned value is in
     /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
@@ -922,6 +980,26 @@ impl<T: ?Sized> *mut T {
         unsafe { self.offset(count as isize) }
     }
 
+    /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [add][pointer::add] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_add(self, count: usize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `add`.
+        let this = unsafe { self.cast::<u8>().add(count).cast::<()>() };
+        from_raw_parts_mut::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer (convenience for
     /// `.offset((count as isize).wrapping_neg())`).
     ///
@@ -986,6 +1064,27 @@ impl<T: ?Sized> *mut T {
         unsafe { self.offset((count as isize).wrapping_neg()) }
     }
 
+    /// Calculates the offset from a pointer in bytes (convenience for
+    /// `.byte_offset((count as isize).wrapping_neg())`).
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [sub][pointer::sub] on it. See that method for documentation
+    /// and safety requirements.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const unsafe fn byte_sub(self, count: usize) -> Self {
+        // SAFETY: the caller must uphold the safety contract for `sub`.
+        let this = unsafe { self.cast::<u8>().sub(count).cast::<()>() };
+        from_raw_parts_mut::<T>(this, metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     /// (convenience for `.wrapping_offset(count as isize)`)
     ///
@@ -1048,6 +1147,24 @@ impl<T: ?Sized> *mut T {
         self.wrapping_offset(count as isize)
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    /// (convenience for `.wrapping_byte_offset(count as isize)`)
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_add][pointer::wrapping_add] on it. See that method for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_add(self, count: usize) -> Self {
+        from_raw_parts_mut::<T>(self.cast::<u8>().wrapping_add(count).cast::<()>(), metadata(self))
+    }
+
     /// Calculates the offset from a pointer using wrapping arithmetic.
     /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
     ///
@@ -1110,6 +1227,24 @@ impl<T: ?Sized> *mut T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Calculates the offset from a pointer in bytes using wrapping arithmetic.
+    /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
+    ///
+    /// `count` is in units of bytes.
+    ///
+    /// This is purely a convenience for casting to a `u8` pointer and
+    /// using [wrapping_sub][pointer::wrapping_sub] on it. See that method for documentation.
+    ///
+    /// For non-`Sized` pointees this operation changes only the data pointer,
+    /// leaving the metadata untouched.
+    #[must_use]
+    #[inline(always)]
+    #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
+    #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
+    pub const fn wrapping_byte_sub(self, count: usize) -> Self {
+        from_raw_parts_mut::<T>(self.cast::<u8>().wrapping_sub(count).cast::<()>(), metadata(self))
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
@@ -1420,12 +1555,46 @@ impl<T: ?Sized> *mut T {
         }
 
         // SAFETY:
-        // It is permisseble for `align_offset` to always return `usize::MAX`,
+        // It is permissible for `align_offset` to always return `usize::MAX`,
         // algorithm correctness can not depend on `align_offset` returning non-max values.
         //
         // As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
         unsafe { intrinsics::const_eval_select((self, align), ctfe_impl, rt_impl) }
     }
+
+    /// Returns whether the pointer is properly aligned for `T`.
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+    pub fn is_aligned(self) -> bool
+    where
+        T: Sized,
+    {
+        self.is_aligned_to(core::mem::align_of::<T>())
+    }
+
+    /// Returns whether the pointer is aligned to `align`.
+    ///
+    /// For non-`Sized` pointees this operation considers only the data pointer,
+    /// ignoring the metadata.
+    ///
+    /// # Panics
+    ///
+    /// The function panics if `align` is not a power-of-two (this includes 0).
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "pointer_is_aligned", issue = "96284")]
+    pub fn is_aligned_to(self, align: usize) -> bool {
+        if !align.is_power_of_two() {
+            panic!("is_aligned_to: align is not a power-of-two");
+        }
+
+        // SAFETY: `is_power_of_two()` will return `false` for zero.
+        unsafe { core::intrinsics::assume(align != 0) };
+
+        // Cast is needed for `T: !Sized`
+        self.cast::<u8>().addr() % align == 0
+    }
 }
 
 impl<T> *mut [T] {