Skip to content

Commit

Permalink
Rollup merge of #74774 - oliver-giersch:set_data_ptr, r=dtolnay
Browse files Browse the repository at this point in the history
adds [*mut|*const] ptr::set_ptr_value

I propose the addition of these two functions to `*mut T` and `*const T`, respectively. The motivation for this is primarily byte-wise pointer arithmetic on (potentially) fat pointers, i.e. for types with a `T: ?Sized` bound. A concrete use-case has been discussed in [this](https://internals.rust-lang.org/t/byte-wise-fat-pointer-arithmetic/12739) thread.
TL;DR: Currently, byte-wise pointer arithmetic with potentially fat pointers in not possible in either stable or nightly Rust without making assumptions about the layout of fat pointers, which is currently still an implementation detail and not formally stabilized. This PR adds one function to `*mut T` and `*const T` each, allowing to circumvent this restriction without exposing any internal implementation details.
One possible alternative would be to add specific byte-wise pointer arithmetic functions to the two pointer types in addition to the already existing count-wise functions. However, I feel this fairly niche use case does not warrant adding a whole set of new functions like `add_bytes`, `offset_bytes`, `wrapping_offset_bytes`, etc. (times two, one for each pointer type) to `libcore`.
  • Loading branch information
Manishearth authored Aug 7, 2020
2 parents 63e3442 + 6c81556 commit 5f331c0
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
32 changes: 32 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,38 @@ impl<T: ?Sized> *const T {
self.wrapping_offset((count as isize).wrapping_neg())
}

/// Sets the pointer value to `ptr`.
///
/// In case `self` is a (fat) pointer to an unsized type, this operation
/// will only affect the pointer part, whereas for (thin) pointers to
/// sized types, this has the same effect as a simple assignment.
///
/// # Examples
///
/// This function is primarily useful for allowing byte-wise pointer
/// arithmetic on potentially fat pointers:
///
/// ```
/// #![feature(set_ptr_value)]
/// # use core::fmt::Debug;
/// let arr: [i32; 3] = [1, 2, 3];
/// let mut ptr = &arr[0] as *const dyn Debug;
/// let thin = ptr as *const u8;
/// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
/// assert_eq!(unsafe { *(ptr as *const i32) }, 3);
/// ```
#[unstable(feature = "set_ptr_value", issue = "75091")]
#[inline]
pub fn set_ptr_value(mut self, val: *const ()) -> Self {
let thin = &mut self as *mut *const T as *mut *const ();
// SAFETY: In case of a thin pointer, this operations is identical
// to a simple assignment. In case of a fat pointer, with the current
// fat pointer layout implementation, the first field of such a
// pointer is always the data pointer, which is likewise assigned.
unsafe { *thin = val };
self
}

/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
Expand Down
32 changes: 32 additions & 0 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,38 @@ impl<T: ?Sized> *mut T {
self.wrapping_offset((count as isize).wrapping_neg())
}

/// Sets the pointer value to `ptr`.
///
/// In case `self` is a (fat) pointer to an unsized type, this operation
/// will only affect the pointer part, whereas for (thin) pointers to
/// sized types, this has the same effect as a simple assignment.
///
/// # Examples
///
/// This function is primarily useful for allowing byte-wise pointer
/// arithmetic on potentially fat pointers:
///
/// ```
/// #![feature(set_ptr_value)]
/// # use core::fmt::Debug;
/// let mut arr: [i32; 3] = [1, 2, 3];
/// let mut ptr = &mut arr[0] as *mut dyn Debug;
/// let thin = ptr as *mut u8;
/// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
/// assert_eq!(unsafe { *(ptr as *mut i32) }, 3);
/// ```
#[unstable(feature = "set_ptr_value", issue = "75091")]
#[inline]
pub fn set_ptr_value(mut self, val: *mut ()) -> Self {
let thin = &mut self as *mut *mut T as *mut *mut ();
// SAFETY: In case of a thin pointer, this operations is identical
// to a simple assignment. In case of a fat pointer, with the current
// fat pointer layout implementation, the first field of such a
// pointer is always the data pointer, which is likewise assigned.
unsafe { *thin = val };
self
}

/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
Expand Down

0 comments on commit 5f331c0

Please sign in to comment.