Skip to content

Commit 5c8ce35

Browse files
committed
Add unsigned_offset_from on pointers
Like we have `add`/`sub` which are the `usize` version of `offset`, this adds the `usize` equivalent of `offset_from`. Like how `.add(d)` replaced a whole bunch of `.offset(d as isize)`, you can see from the changes here that it's fairly common that code actually knows the order between the pointers and *wants* a `usize`, not an `isize`. As a bonus, this can do `sub nuw`+`udiv exact`, rather than `sub`+`sdiv exact`, which can be optimized slightly better because it doesn't have to worry about negatives. That's why the slice iterators weren't using `offset_from`, though I haven't updated that code in this PR because slices are so perf-critical that I'll do it as its own change. This is an intrinsic, like `offset_from`, so that it can eventually be allowed in CTFE. It also allows checking the extra safety condition -- see the test confirming that CTFE catches it if you pass the pointers in the wrong order.
1 parent ccb7b42 commit 5c8ce35

File tree

10 files changed

+142
-7
lines changed

10 files changed

+142
-7
lines changed

alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
#![feature(pattern)]
128128
#![feature(ptr_internals)]
129129
#![feature(ptr_metadata)]
130+
#![feature(ptr_unsigned_offset_from)]
130131
#![feature(receiver_trait)]
131132
#![feature(set_ptr_value)]
132133
#![feature(slice_group_by)]

alloc/src/slice.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ where
10561056
fn drop(&mut self) {
10571057
// `T` is not a zero-sized type, and these are pointers into a slice's elements.
10581058
unsafe {
1059-
let len = self.end.offset_from(self.start) as usize;
1059+
let len = self.end.unsigned_offset_from(self.start);
10601060
ptr::copy_nonoverlapping(self.start, self.dest, len);
10611061
}
10621062
}

alloc/src/vec/drain.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
163163
// it from the original vec but also avoid creating a &mut to the front since that could
164164
// invalidate raw pointers to it which some unsafe code might rely on.
165165
let vec_ptr = vec.as_mut().as_mut_ptr();
166-
let drop_offset = drop_ptr.offset_from(vec_ptr) as usize;
166+
let drop_offset = drop_ptr.unsigned_offset_from(vec_ptr);
167167
let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len);
168168
ptr::drop_in_place(to_drop);
169169
}

alloc/src/vec/in_place_collect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ where
250250
let sink =
251251
self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap();
252252
// iteration succeeded, don't drop head
253-
unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize }
253+
unsafe { ManuallyDrop::new(sink).dst.unsigned_offset_from(dst_buf) }
254254
}
255255
}
256256

alloc/src/vec/in_place_drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub(super) struct InPlaceDrop<T> {
1010

1111
impl<T> InPlaceDrop<T> {
1212
fn len(&self) -> usize {
13-
unsafe { self.dst.offset_from(self.inner) as usize }
13+
unsafe { self.dst.unsigned_offset_from(self.inner) }
1414
}
1515
}
1616

alloc/src/vec/into_iter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
169169
let exact = if mem::size_of::<T>() == 0 {
170170
self.end.addr().wrapping_sub(self.ptr.addr())
171171
} else {
172-
unsafe { self.end.offset_from(self.ptr) as usize }
172+
unsafe { self.end.unsigned_offset_from(self.ptr) }
173173
};
174174
(exact, Some(exact))
175175
}

core/src/intrinsics.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,6 +1903,11 @@ extern "rust-intrinsic" {
19031903
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
19041904
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
19051905

1906+
/// See documentation of `<*const T>::unsigned_offset_from` for details.
1907+
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
1908+
#[cfg(not(bootstrap))]
1909+
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
1910+
19061911
/// See documentation of `<*const T>::guaranteed_eq` for details.
19071912
///
19081913
/// Note that, unlike most intrinsics, this is safe to call;
@@ -2385,3 +2390,11 @@ where
23852390
{
23862391
called_in_const.call_once(arg)
23872392
}
2393+
2394+
/// Bootstrap polyfill
2395+
#[cfg(bootstrap)]
2396+
pub const unsafe fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize {
2397+
// SAFETY: we have stricter preconditions than `ptr_offset_from`, so can
2398+
// call it, and its output has to be positive, so we can just cast.
2399+
unsafe { ptr_offset_from(ptr, base) as _ }
2400+
}

core/src/ptr/const_ptr.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,67 @@ impl<T: ?Sized> *const T {
611611
unsafe { intrinsics::ptr_offset_from(self, origin) }
612612
}
613613

614+
/// Calculates the distance between two pointers, *where it's known that
615+
/// `self` is equal to or greater than `origin`*. The returned value is in
616+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
617+
///
618+
/// This computes the same value that [`offset_from`](#method.offset_from)
619+
/// would compute, but with the added precondition that that the offset is
620+
/// guaranteed to be non-negative. This method is equivalent to
621+
/// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
622+
/// but it provides slightly more information to the optimizer, which can
623+
/// sometimes allow it to optimize slightly better with some backends.
624+
///
625+
/// This method is the inverse of [`add`](#method.add) (and, with the parameters
626+
/// in the other order, of [`sub`](#method.sub)).
627+
///
628+
/// # Safety
629+
///
630+
/// - The distance between the pointers must be non-negative (`self >= origin`)
631+
///
632+
/// - *All* the safety conditions of [`offset_from`](#method.offset_from)
633+
/// apply to this method as well; see it for the full details.
634+
///
635+
/// Importantly, despite the return type of this method being able to represent
636+
/// a larger offset, it's still *not permitted* to pass pointers which differ
637+
/// by more than `isize::MAX` *bytes*. As such, the result of this method will
638+
/// always be less than or equal to `isize::MAX as usize`.
639+
///
640+
/// # Panics
641+
///
642+
/// This function panics if `T` is a Zero-Sized Type ("ZST").
643+
///
644+
/// # Examples
645+
///
646+
/// ```
647+
/// #![feature(ptr_unsigned_offset_from)]
648+
///
649+
/// let a = [0; 5];
650+
/// let ptr1: *const i32 = &a[1];
651+
/// let ptr2: *const i32 = &a[3];
652+
/// unsafe {
653+
/// assert_eq!(ptr2.unsigned_offset_from(ptr1), 2);
654+
/// assert_eq!(ptr1.add(2), ptr2);
655+
/// assert_eq!(ptr2.sub(2), ptr1);
656+
/// assert_eq!(ptr2.unsigned_offset_from(ptr2), 0);
657+
/// }
658+
///
659+
/// // This would be incorrect, as the pointers are not correctly ordered:
660+
/// // ptr1.offset_from(ptr2)
661+
/// ```
662+
#[unstable(feature = "ptr_unsigned_offset_from", issue = "88888888")]
663+
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
664+
#[inline]
665+
pub const unsafe fn unsigned_offset_from(self, origin: *const T) -> usize
666+
where
667+
T: Sized,
668+
{
669+
let pointee_size = mem::size_of::<T>();
670+
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
671+
// SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`.
672+
unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
673+
}
674+
614675
/// Returns whether two pointers are guaranteed to be equal.
615676
///
616677
/// At runtime this function behaves like `self == other`.

core/src/ptr/mut_ptr.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,66 @@ impl<T: ?Sized> *mut T {
787787
unsafe { (self as *const T).offset_from(origin) }
788788
}
789789

790+
/// Calculates the distance between two pointers, *where it's known that
791+
/// `self` is equal to or greater than `origin`*. The returned value is in
792+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
793+
///
794+
/// This computes the same value that [`offset_from`](#method.offset_from)
795+
/// would compute, but with the added precondition that that the offset is
796+
/// guaranteed to be non-negative. This method is equivalent to
797+
/// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
798+
/// but it provides slightly more information to the optimizer, which can
799+
/// sometimes allow it to optimize slightly better with some backends.
800+
///
801+
/// This method is the inverse of [`add`](#method.add) (and, with the parameters
802+
/// in the other order, of [`sub`](#method.sub)).
803+
///
804+
/// # Safety
805+
///
806+
/// - The distance between the pointers must be non-negative (`self >= origin`)
807+
///
808+
/// - *All* the safety conditions of [`offset_from`](#method.offset_from)
809+
/// apply to this method as well; see it for the full details.
810+
///
811+
/// Importantly, despite the return type of this method being able to represent
812+
/// a larger offset, it's still *not permitted* to pass pointers which differ
813+
/// by more than `isize::MAX` *bytes*. As such, the result of this method will
814+
/// always be less than or equal to `isize::MAX as usize`.
815+
///
816+
/// # Panics
817+
///
818+
/// This function panics if `T` is a Zero-Sized Type ("ZST").
819+
///
820+
/// # Examples
821+
///
822+
/// ```
823+
/// #![feature(ptr_unsigned_offset_from)]
824+
///
825+
/// let mut a = [0; 5];
826+
/// let p: *mut i32 = a.as_mut_ptr();
827+
/// unsafe {
828+
/// let ptr1: *mut i32 = p.add(1);
829+
/// let ptr2: *mut i32 = p.add(3);
830+
///
831+
/// assert_eq!(ptr2.unsigned_offset_from(ptr1), 2);
832+
/// assert_eq!(ptr1.add(2), ptr2);
833+
/// assert_eq!(ptr2.sub(2), ptr1);
834+
/// assert_eq!(ptr2.unsigned_offset_from(ptr2), 0);
835+
/// }
836+
///
837+
/// // This would be incorrect, as the pointers are not correctly ordered:
838+
/// // ptr1.offset_from(ptr2)
839+
#[unstable(feature = "ptr_unsigned_offset_from", issue = "88888888")]
840+
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
841+
#[inline]
842+
pub const unsafe fn unsigned_offset_from(self, origin: *const T) -> usize
843+
where
844+
T: Sized,
845+
{
846+
// SAFETY: the caller must uphold the safety contract for `unsigned_offset_from`.
847+
unsafe { (self as *const T).unsigned_offset_from(origin) }
848+
}
849+
790850
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
791851
///
792852
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer

core/src/slice/raw.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T] {
215215
#[unstable(feature = "slice_from_ptr_range", issue = "89792")]
216216
pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
217217
// SAFETY: the caller must uphold the safety contract for `from_ptr_range`.
218-
unsafe { from_raw_parts(range.start, range.end.offset_from(range.start) as usize) }
218+
unsafe { from_raw_parts(range.start, range.end.unsigned_offset_from(range.start)) }
219219
}
220220

221221
/// Performs the same functionality as [`from_ptr_range`], except that a
@@ -265,5 +265,5 @@ pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
265265
#[unstable(feature = "slice_from_ptr_range", issue = "89792")]
266266
pub unsafe fn from_mut_ptr_range<'a, T>(range: Range<*mut T>) -> &'a mut [T] {
267267
// SAFETY: the caller must uphold the safety contract for `from_mut_ptr_range`.
268-
unsafe { from_raw_parts_mut(range.start, range.end.offset_from(range.start) as usize) }
268+
unsafe { from_raw_parts_mut(range.start, range.end.unsigned_offset_from(range.start)) }
269269
}

0 commit comments

Comments
 (0)