Skip to content

Commit

Permalink
Rollup merge of rust-lang#40943 - Amanieu:offset_to, r=alexcrichton
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
frewsxcv authored Apr 5, 2017
2 parents 30f3a38 + 1f70247 commit 9f88677
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/doc/unstable-book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
- [no_debug](no-debug.md)
- [non_ascii_idents](non-ascii-idents.md)
- [nonzero](nonzero.md)
- [offset_to](offset-to.md)
- [omit_gdb_pretty_printer_section](omit-gdb-pretty-printer-section.md)
- [on_unimplemented](on-unimplemented.md)
- [once_poison](once-poison.md)
Expand Down
7 changes: 7 additions & 0 deletions src/doc/unstable-book/src/offset-to.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `offset_to`

The tracking issue for this feature is: [#41079]

[#41079]: https://github.com/rust-lang/rust/issues/41079

------------------------
1 change: 1 addition & 0 deletions src/libcollections/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#![feature(untagged_unions)]
#![cfg_attr(not(test), feature(str_checked_slicing))]
#![cfg_attr(test, feature(rand, test))]
#![feature(offset_to)]

#![no_std]

Expand Down
12 changes: 4 additions & 8 deletions src/libcollections/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2074,14 +2074,10 @@ impl<T> Iterator for IntoIter<T> {

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let diff = (self.end as usize) - (self.ptr as usize);
let size = mem::size_of::<T>();
let exact = diff /
(if size == 0 {
1
} else {
size
});
let exact = match self.ptr.offset_to(self.end) {
Some(x) => x as usize,
None => (self.end as usize).wrapping_sub(self.ptr as usize),
};
(exact, Some(exact))
}

Expand Down
76 changes: 76 additions & 0 deletions src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9f88677

Please sign in to comment.