Skip to content

Commit d8a3fc4

Browse files
committed
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`
2 parents cd282d7 + 03d4569 commit d8a3fc4

File tree

2 files changed

+337
-2
lines changed

2 files changed

+337
-2
lines changed

library/core/src/ptr/const_ptr.rs

+167-1
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,26 @@ impl<T: ?Sized> *const T {
455455
unsafe { intrinsics::offset(self, count) }
456456
}
457457

458+
/// Calculates the offset from a pointer in bytes.
459+
///
460+
/// `count` is in units of **bytes**.
461+
///
462+
/// This is purely a convenience for casting to a `u8` pointer and
463+
/// using [offset][pointer::offset] on it. See that method for documentation
464+
/// and safety requirements.
465+
///
466+
/// For non-`Sized` pointees this operation changes only the data pointer,
467+
/// leaving the metadata untouched.
468+
#[must_use]
469+
#[inline(always)]
470+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
471+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
472+
pub const unsafe fn byte_offset(self, count: isize) -> Self {
473+
// SAFETY: the caller must uphold the safety contract for `offset`.
474+
let this = unsafe { self.cast::<u8>().offset(count).cast::<()>() };
475+
from_raw_parts::<T>(this, metadata(self))
476+
}
477+
458478
/// Calculates the offset from a pointer using wrapping arithmetic.
459479
///
460480
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
@@ -517,6 +537,24 @@ impl<T: ?Sized> *const T {
517537
unsafe { intrinsics::arith_offset(self, count) }
518538
}
519539

540+
/// Calculates the offset from a pointer in bytes using wrapping arithmetic.
541+
///
542+
/// `count` is in units of **bytes**.
543+
///
544+
/// This is purely a convenience for casting to a `u8` pointer and
545+
/// using [wrapping_offset][pointer::wrapping_offset] on it. See that method
546+
/// for documentation.
547+
///
548+
/// For non-`Sized` pointees this operation changes only the data pointer,
549+
/// leaving the metadata untouched.
550+
#[must_use]
551+
#[inline(always)]
552+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
553+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
554+
pub const fn wrapping_byte_offset(self, count: isize) -> Self {
555+
from_raw_parts::<T>(self.cast::<u8>().wrapping_offset(count).cast::<()>(), metadata(self))
556+
}
557+
520558
/// Calculates the distance between two pointers. The returned value is in
521559
/// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
522560
///
@@ -611,6 +649,23 @@ impl<T: ?Sized> *const T {
611649
unsafe { intrinsics::ptr_offset_from(self, origin) }
612650
}
613651

652+
/// Calculates the distance between two pointers. The returned value is in
653+
/// units of **bytes**.
654+
///
655+
/// This is purely a convenience for casting to a `u8` pointer and
656+
/// using [offset_from][pointer::offset_from] on it. See that method for
657+
/// documentation and safety requirements.
658+
///
659+
/// For non-`Sized` pointees this operation considers only the data pointers,
660+
/// ignoring the metadata.
661+
#[inline(always)]
662+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
663+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
664+
pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
665+
// SAFETY: the caller must uphold the safety contract for `offset_from`.
666+
unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
667+
}
668+
614669
/// Calculates the distance between two pointers, *where it's known that
615670
/// `self` is equal to or greater than `origin`*. The returned value is in
616671
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
@@ -813,6 +868,26 @@ impl<T: ?Sized> *const T {
813868
unsafe { self.offset(count as isize) }
814869
}
815870

871+
/// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
872+
///
873+
/// `count` is in units of bytes.
874+
///
875+
/// This is purely a convenience for casting to a `u8` pointer and
876+
/// using [add][pointer::add] on it. See that method for documentation
877+
/// and safety requirements.
878+
///
879+
/// For non-`Sized` pointees this operation changes only the data pointer,
880+
/// leaving the metadata untouched.
881+
#[must_use]
882+
#[inline(always)]
883+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
884+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
885+
pub const unsafe fn byte_add(self, count: usize) -> Self {
886+
// SAFETY: the caller must uphold the safety contract for `add`.
887+
let this = unsafe { self.cast::<u8>().add(count).cast::<()>() };
888+
from_raw_parts::<T>(this, metadata(self))
889+
}
890+
816891
/// Calculates the offset from a pointer (convenience for
817892
/// `.offset((count as isize).wrapping_neg())`).
818893
///
@@ -877,6 +952,27 @@ impl<T: ?Sized> *const T {
877952
unsafe { self.offset((count as isize).wrapping_neg()) }
878953
}
879954

955+
/// Calculates the offset from a pointer in bytes (convenience for
956+
/// `.byte_offset((count as isize).wrapping_neg())`).
957+
///
958+
/// `count` is in units of bytes.
959+
///
960+
/// This is purely a convenience for casting to a `u8` pointer and
961+
/// using [sub][pointer::sub] on it. See that method for documentation
962+
/// and safety requirements.
963+
///
964+
/// For non-`Sized` pointees this operation changes only the data pointer,
965+
/// leaving the metadata untouched.
966+
#[must_use]
967+
#[inline(always)]
968+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
969+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
970+
pub const unsafe fn byte_sub(self, count: usize) -> Self {
971+
// SAFETY: the caller must uphold the safety contract for `sub`.
972+
let this = unsafe { self.cast::<u8>().sub(count).cast::<()>() };
973+
from_raw_parts::<T>(this, metadata(self))
974+
}
975+
880976
/// Calculates the offset from a pointer using wrapping arithmetic.
881977
/// (convenience for `.wrapping_offset(count as isize)`)
882978
///
@@ -939,6 +1035,24 @@ impl<T: ?Sized> *const T {
9391035
self.wrapping_offset(count as isize)
9401036
}
9411037

1038+
/// Calculates the offset from a pointer in bytes using wrapping arithmetic.
1039+
/// (convenience for `.wrapping_byte_offset(count as isize)`)
1040+
///
1041+
/// `count` is in units of bytes.
1042+
///
1043+
/// This is purely a convenience for casting to a `u8` pointer and
1044+
/// using [wrapping_add][pointer::wrapping_add] on it. See that method for documentation.
1045+
///
1046+
/// For non-`Sized` pointees this operation changes only the data pointer,
1047+
/// leaving the metadata untouched.
1048+
#[must_use]
1049+
#[inline(always)]
1050+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
1051+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
1052+
pub const fn wrapping_byte_add(self, count: usize) -> Self {
1053+
from_raw_parts::<T>(self.cast::<u8>().wrapping_add(count).cast::<()>(), metadata(self))
1054+
}
1055+
9421056
/// Calculates the offset from a pointer using wrapping arithmetic.
9431057
/// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
9441058
///
@@ -1001,6 +1115,24 @@ impl<T: ?Sized> *const T {
10011115
self.wrapping_offset((count as isize).wrapping_neg())
10021116
}
10031117

1118+
/// Calculates the offset from a pointer in bytes using wrapping arithmetic.
1119+
/// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`)
1120+
///
1121+
/// `count` is in units of bytes.
1122+
///
1123+
/// This is purely a convenience for casting to a `u8` pointer and
1124+
/// using [wrapping_sub][pointer::wrapping_sub] on it. See that method for documentation.
1125+
///
1126+
/// For non-`Sized` pointees this operation changes only the data pointer,
1127+
/// leaving the metadata untouched.
1128+
#[must_use]
1129+
#[inline(always)]
1130+
#[unstable(feature = "pointer_byte_offsets", issue = "96283")]
1131+
#[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
1132+
pub const fn wrapping_byte_sub(self, count: usize) -> Self {
1133+
from_raw_parts::<T>(self.cast::<u8>().wrapping_sub(count).cast::<()>(), metadata(self))
1134+
}
1135+
10041136
/// Reads the value from `self` without moving it. This leaves the
10051137
/// memory in `self` unchanged.
10061138
///
@@ -1154,12 +1286,46 @@ impl<T: ?Sized> *const T {
11541286
}
11551287

11561288
// SAFETY:
1157-
// It is permisseble for `align_offset` to always return `usize::MAX`,
1289+
// It is permissible for `align_offset` to always return `usize::MAX`,
11581290
// algorithm correctness can not depend on `align_offset` returning non-max values.
11591291
//
11601292
// As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
11611293
unsafe { intrinsics::const_eval_select((self, align), ctfe_impl, rt_impl) }
11621294
}
1295+
1296+
/// Returns whether the pointer is properly aligned for `T`.
1297+
#[must_use]
1298+
#[inline]
1299+
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
1300+
pub fn is_aligned(self) -> bool
1301+
where
1302+
T: Sized,
1303+
{
1304+
self.is_aligned_to(core::mem::align_of::<T>())
1305+
}
1306+
1307+
/// Returns whether the pointer is aligned to `align`.
1308+
///
1309+
/// For non-`Sized` pointees this operation considers only the data pointer,
1310+
/// ignoring the metadata.
1311+
///
1312+
/// # Panics
1313+
///
1314+
/// The function panics if `align` is not a power-of-two (this includes 0).
1315+
#[must_use]
1316+
#[inline]
1317+
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
1318+
pub fn is_aligned_to(self, align: usize) -> bool {
1319+
if !align.is_power_of_two() {
1320+
panic!("is_aligned_to: align is not a power-of-two");
1321+
}
1322+
1323+
// SAFETY: `is_power_of_two()` will return `false` for zero.
1324+
unsafe { core::intrinsics::assume(align != 0) };
1325+
1326+
// Cast is needed for `T: !Sized`
1327+
self.cast::<u8>().addr() % align == 0
1328+
}
11631329
}
11641330

11651331
impl<T> *const [T] {

0 commit comments

Comments
 (0)