Skip to content

Commit 9d07447

Browse files
author
Ariel Ben-Yehuda
authored
Rollup merge of rust-lang#40943 - Amanieu:offset_to, r=alexcrichton
Add ptr::offset_to This PR adds a method to calculate the signed distance (in number of elements) between two pointers. The resulting value can then be passed to `offset` to get one pointer from the other. This is similar to pointer subtraction in C/C++. There are 2 special cases: - If the distance is not a multiple of the element size then the result is rounded towards zero. (in C/C++ this is UB) - ZST return `None`, while normal types return `Some(isize)`. This forces the user to handle the ZST case in unsafe code. (C/C++ doesn't have ZSTs)
2 parents fc5ff66 + 1f70247 commit 9d07447

File tree

6 files changed

+93
-11
lines changed

6 files changed

+93
-11
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
- [no_debug](no-debug.md)
124124
- [non_ascii_idents](non-ascii-idents.md)
125125
- [nonzero](nonzero.md)
126+
- [offset_to](offset-to.md)
126127
- [omit_gdb_pretty_printer_section](omit-gdb-pretty-printer-section.md)
127128
- [on_unimplemented](on-unimplemented.md)
128129
- [once_poison](once-poison.md)
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# `offset_to`
2+
3+
The tracking issue for this feature is: [#41079]
4+
5+
[#41079]: https://github.com/rust-lang/rust/issues/41079
6+
7+
------------------------

src/libcollections/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#![feature(untagged_unions)]
6363
#![cfg_attr(not(test), feature(str_checked_slicing))]
6464
#![cfg_attr(test, feature(rand, test))]
65+
#![feature(offset_to)]
6566

6667
#![no_std]
6768

src/libcollections/vec.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -2074,14 +2074,10 @@ impl<T> Iterator for IntoIter<T> {
20742074

20752075
#[inline]
20762076
fn size_hint(&self) -> (usize, Option<usize>) {
2077-
let diff = (self.end as usize) - (self.ptr as usize);
2078-
let size = mem::size_of::<T>();
2079-
let exact = diff /
2080-
(if size == 0 {
2081-
1
2082-
} else {
2083-
size
2084-
});
2077+
let exact = match self.ptr.offset_to(self.end) {
2078+
Some(x) => x as usize,
2079+
None => (self.end as usize).wrapping_sub(self.ptr as usize),
2080+
};
20852081
(exact, Some(exact))
20862082
}
20872083

src/libcore/ptr.rs

+76
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,44 @@ impl<T: ?Sized> *const T {
500500
intrinsics::arith_offset(self, count)
501501
}
502502
}
503+
504+
/// Calculates the distance between two pointers. The returned value is in
505+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
506+
///
507+
/// If the address different between the two pointers ia not a multiple of
508+
/// `mem::size_of::<T>()` then the result of the division is rounded towards
509+
/// zero.
510+
///
511+
/// This function returns `None` if `T` is a zero-sized typed.
512+
///
513+
/// # Examples
514+
///
515+
/// Basic usage:
516+
///
517+
/// ```
518+
/// #![feature(offset_to)]
519+
///
520+
/// fn main() {
521+
/// let a = [0; 5];
522+
/// let ptr1: *const i32 = &a[1];
523+
/// let ptr2: *const i32 = &a[3];
524+
/// assert_eq!(ptr1.offset_to(ptr2), Some(2));
525+
/// assert_eq!(ptr2.offset_to(ptr1), Some(-2));
526+
/// assert_eq!(unsafe { ptr1.offset(2) }, ptr2);
527+
/// assert_eq!(unsafe { ptr2.offset(-2) }, ptr1);
528+
/// }
529+
/// ```
530+
#[unstable(feature = "offset_to", issue = "41079")]
531+
#[inline]
532+
pub fn offset_to(self, other: *const T) -> Option<isize> where T: Sized {
533+
let size = mem::size_of::<T>();
534+
if size == 0 {
535+
None
536+
} else {
537+
let diff = (other as isize).wrapping_sub(self as isize);
538+
Some(diff / size as isize)
539+
}
540+
}
503541
}
504542

505543
#[lang = "mut_ptr"]
@@ -653,6 +691,44 @@ impl<T: ?Sized> *mut T {
653691
Some(&mut *self)
654692
}
655693
}
694+
695+
/// Calculates the distance between two pointers. The returned value is in
696+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
697+
///
698+
/// If the address different between the two pointers ia not a multiple of
699+
/// `mem::size_of::<T>()` then the result of the division is rounded towards
700+
/// zero.
701+
///
702+
/// This function returns `None` if `T` is a zero-sized typed.
703+
///
704+
/// # Examples
705+
///
706+
/// Basic usage:
707+
///
708+
/// ```
709+
/// #![feature(offset_to)]
710+
///
711+
/// fn main() {
712+
/// let mut a = [0; 5];
713+
/// let ptr1: *mut i32 = &mut a[1];
714+
/// let ptr2: *mut i32 = &mut a[3];
715+
/// assert_eq!(ptr1.offset_to(ptr2), Some(2));
716+
/// assert_eq!(ptr2.offset_to(ptr1), Some(-2));
717+
/// assert_eq!(unsafe { ptr1.offset(2) }, ptr2);
718+
/// assert_eq!(unsafe { ptr2.offset(-2) }, ptr1);
719+
/// }
720+
/// ```
721+
#[unstable(feature = "offset_to", issue = "41079")]
722+
#[inline]
723+
pub fn offset_to(self, other: *const T) -> Option<isize> where T: Sized {
724+
let size = mem::size_of::<T>();
725+
if size == 0 {
726+
None
727+
} else {
728+
let diff = (other as isize).wrapping_sub(self as isize);
729+
Some(diff / size as isize)
730+
}
731+
}
656732
}
657733

658734
// Equality for pointers

src/libcore/slice/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1498,9 +1498,10 @@ unsafe impl<'a, T> TrustedLen for IterMut<'a, T> {}
14981498
// Return the arithmetic difference if `T` is zero size.
14991499
#[inline(always)]
15001500
fn ptrdistance<T>(start: *const T, end: *const T) -> usize {
1501-
let diff = (end as usize).wrapping_sub(start as usize);
1502-
let size = mem::size_of::<T>();
1503-
diff / (if size == 0 { 1 } else { size })
1501+
match start.offset_to(end) {
1502+
Some(x) => x as usize,
1503+
None => (end as usize).wrapping_sub(start as usize),
1504+
}
15041505
}
15051506

15061507
// Extension methods for raw pointers, used by the iterators

0 commit comments

Comments
 (0)