diff options
| author | Ariel Ben-Yehuda <arielb1@mail.tau.ac.il> | 2017-04-05 23:01:08 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-04-05 23:01:08 +0000 |
| commit | 9d074473da5fef4b1d9ebbbb7f181edcb7a365a0 (patch) | |
| tree | 4c1ecbe25d9034e8a32979bf577c0de93e64da50 /src/libcore | |
| parent | fc5ff66b04af18b42a5278f1f74875311c957555 (diff) | |
| parent | 1f70247446914a8b58bd088f32bcca792d30d75f (diff) | |
| download | rust-9d074473da5fef4b1d9ebbbb7f181edcb7a365a0.tar.gz rust-9d074473da5fef4b1d9ebbbb7f181edcb7a365a0.zip | |
Rollup merge of #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)
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/ptr.rs | 76 | ||||
| -rw-r--r-- | src/libcore/slice/mod.rs | 7 |
2 files changed, 80 insertions, 3 deletions
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index d2830a6d00c..04480fc5d31 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -500,6 +500,44 @@ impl<T: ?Sized> *const T { intrinsics::arith_offset(self, count) } } + + /// Calculates the distance between two pointers. The returned value is in + /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// + /// If the address different between the two pointers ia not a multiple of + /// `mem::size_of::<T>()` then the result of the division is rounded towards + /// zero. + /// + /// This function returns `None` if `T` is a zero-sized typed. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(offset_to)] + /// + /// fn main() { + /// let a = [0; 5]; + /// let ptr1: *const i32 = &a[1]; + /// let ptr2: *const i32 = &a[3]; + /// assert_eq!(ptr1.offset_to(ptr2), Some(2)); + /// assert_eq!(ptr2.offset_to(ptr1), Some(-2)); + /// assert_eq!(unsafe { ptr1.offset(2) }, ptr2); + /// assert_eq!(unsafe { ptr2.offset(-2) }, ptr1); + /// } + /// ``` + #[unstable(feature = "offset_to", issue = "41079")] + #[inline] + pub fn offset_to(self, other: *const T) -> Option<isize> where T: Sized { + let size = mem::size_of::<T>(); + if size == 0 { + None + } else { + let diff = (other as isize).wrapping_sub(self as isize); + Some(diff / size as isize) + } + } } #[lang = "mut_ptr"] @@ -653,6 +691,44 @@ impl<T: ?Sized> *mut T { Some(&mut *self) } } + + /// Calculates the distance between two pointers. The returned value is in + /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// + /// If the address different between the two pointers ia not a multiple of + /// `mem::size_of::<T>()` then the result of the division is rounded towards + /// zero. + /// + /// This function returns `None` if `T` is a zero-sized typed. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(offset_to)] + /// + /// fn main() { + /// let mut a = [0; 5]; + /// let ptr1: *mut i32 = &mut a[1]; + /// let ptr2: *mut i32 = &mut a[3]; + /// assert_eq!(ptr1.offset_to(ptr2), Some(2)); + /// assert_eq!(ptr2.offset_to(ptr1), Some(-2)); + /// assert_eq!(unsafe { ptr1.offset(2) }, ptr2); + /// assert_eq!(unsafe { ptr2.offset(-2) }, ptr1); + /// } + /// ``` + #[unstable(feature = "offset_to", issue = "41079")] + #[inline] + pub fn offset_to(self, other: *const T) -> Option<isize> where T: Sized { + let size = mem::size_of::<T>(); + if size == 0 { + None + } else { + let diff = (other as isize).wrapping_sub(self as isize); + Some(diff / size as isize) + } + } } // Equality for pointers diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 45667bb4299..a425e418e42 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1498,9 +1498,10 @@ unsafe impl<'a, T> TrustedLen for IterMut<'a, T> {} // Return the arithmetic difference if `T` is zero size. #[inline(always)] fn ptrdistance<T>(start: *const T, end: *const T) -> usize { - let diff = (end as usize).wrapping_sub(start as usize); - let size = mem::size_of::<T>(); - diff / (if size == 0 { 1 } else { size }) + match start.offset_to(end) { + Some(x) => x as usize, + None => (end as usize).wrapping_sub(start as usize), + } } // Extension methods for raw pointers, used by the iterators |
