-
Notifications
You must be signed in to change notification settings - Fork 338
Simplify ArrayView construction from NonNull<T> and add RawView .cast() method #734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
use std::mem; | ||
use std::ptr::NonNull; | ||
|
||
use crate::dimension::{self, stride_offset}; | ||
use crate::extension::nonnull::nonnull_debug_checked_from_ptr; | ||
use crate::imp_prelude::*; | ||
|
@@ -11,16 +14,20 @@ where | |
/// | ||
/// Unsafe because caller is responsible for ensuring that the array will | ||
/// meet all of the invariants of the `ArrayBase` type. | ||
#[inline(always)] | ||
pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { | ||
#[inline] | ||
pub(crate) unsafe fn new(ptr: NonNull<A>, dim: D, strides: D) -> Self { | ||
RawArrayView { | ||
data: RawViewRepr::new(), | ||
ptr: nonnull_debug_checked_from_ptr(ptr as *mut _), | ||
ptr, | ||
dim, | ||
strides, | ||
} | ||
} | ||
|
||
unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { | ||
Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) | ||
} | ||
|
||
/// Create an `RawArrayView<A, D>` from shape information and a raw pointer | ||
/// to the elements. | ||
/// | ||
|
@@ -76,7 +83,7 @@ where | |
/// ensure that all of the data is valid and choose the correct lifetime. | ||
#[inline] | ||
pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { | ||
ArrayView::new_(self.ptr.as_ptr(), self.dim, self.strides) | ||
ArrayView::new(self.ptr, self.dim, self.strides) | ||
} | ||
|
||
/// Split the array view along `axis` and return one array pointer strictly | ||
|
@@ -105,6 +112,32 @@ where | |
|
||
(left, right) | ||
} | ||
|
||
/// Cast the raw pointer of the raw array view to a different type | ||
/// | ||
/// **Panics** if element size is not compatible. | ||
/// | ||
/// Lack of panic does not imply it is a valid cast. The cast works the same | ||
/// way as regular raw pointer casts. | ||
/// | ||
/// While this method is safe, for the same reason as regular raw pointer | ||
/// casts are safe, access through the produced raw view is only possible | ||
/// in an unsafe block or function. | ||
pub fn cast<B>(self) -> RawArrayView<B, D> { | ||
assert_eq!( | ||
mem::size_of::<B>(), | ||
mem::size_of::<A>(), | ||
"size mismatch in raw view cast" | ||
); | ||
let ptr = self.ptr.cast::<B>(); | ||
debug_assert!( | ||
is_aligned(ptr.as_ptr()), | ||
"alignment mismatch in raw view cast" | ||
); | ||
/* Alignment checked with debug assertion: alignment could be dynamically correct, | ||
* and we don't have a check that compiles out for that. */ | ||
unsafe { RawArrayView::new(ptr, self.dim, self.strides) } | ||
} | ||
} | ||
|
||
impl<A, D> RawArrayViewMut<A, D> | ||
|
@@ -115,16 +148,20 @@ where | |
/// | ||
/// Unsafe because caller is responsible for ensuring that the array will | ||
/// meet all of the invariants of the `ArrayBase` type. | ||
#[inline(always)] | ||
pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { | ||
#[inline] | ||
pub(crate) unsafe fn new(ptr: NonNull<A>, dim: D, strides: D) -> Self { | ||
RawArrayViewMut { | ||
data: RawViewRepr::new(), | ||
ptr: nonnull_debug_checked_from_ptr(ptr), | ||
ptr, | ||
dim, | ||
strides, | ||
} | ||
} | ||
|
||
unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { | ||
Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) | ||
} | ||
|
||
/// Create an `RawArrayViewMut<A, D>` from shape information and a raw | ||
/// pointer to the elements. | ||
/// | ||
|
@@ -176,7 +213,7 @@ where | |
/// Converts to a non-mutable `RawArrayView`. | ||
#[inline] | ||
pub(crate) fn into_raw_view(self) -> RawArrayView<A, D> { | ||
unsafe { RawArrayView::new_(self.ptr.as_ptr(), self.dim, self.strides) } | ||
unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } | ||
} | ||
|
||
/// Converts to a read-only view of the array. | ||
|
@@ -186,7 +223,7 @@ where | |
/// ensure that all of the data is valid and choose the correct lifetime. | ||
#[inline] | ||
pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { | ||
ArrayView::new_(self.ptr.as_ptr(), self.dim, self.strides) | ||
ArrayView::new(self.ptr, self.dim, self.strides) | ||
} | ||
|
||
/// Converts to a mutable view of the array. | ||
|
@@ -196,7 +233,7 @@ where | |
/// ensure that all of the data is valid and choose the correct lifetime. | ||
#[inline] | ||
pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> { | ||
ArrayViewMut::new_(self.ptr.as_ptr(), self.dim, self.strides) | ||
ArrayViewMut::new(self.ptr, self.dim, self.strides) | ||
} | ||
|
||
/// Split the array view along `axis` and return one array pointer strictly | ||
|
@@ -207,9 +244,35 @@ where | |
let (left, right) = self.into_raw_view().split_at(axis, index); | ||
unsafe { | ||
( | ||
Self::new_(left.ptr.as_ptr(), left.dim, left.strides), | ||
Self::new_(right.ptr.as_ptr(), right.dim, right.strides), | ||
Self::new(left.ptr, left.dim, left.strides), | ||
Self::new(right.ptr, right.dim, right.strides), | ||
) | ||
} | ||
} | ||
|
||
/// Cast the raw pointer of the raw array view to a different type | ||
/// | ||
/// **Panics** if element size is not compatible. | ||
/// | ||
/// Lack of panic does not imply it is a valid cast. The cast works the same | ||
/// way as regular raw pointer casts. | ||
/// | ||
/// While this method is safe, for the same reason as regular raw pointer | ||
/// casts are safe, access through the produced raw view is only possible | ||
/// in an unsafe block or function. | ||
pub fn cast<B>(self) -> RawArrayViewMut<B, D> { | ||
assert_eq!( | ||
mem::size_of::<B>(), | ||
mem::size_of::<A>(), | ||
"size mismatch in raw view cast" | ||
); | ||
let ptr = self.ptr.cast::<B>(); | ||
debug_assert!( | ||
is_aligned(ptr.as_ptr()), | ||
"alignment mismatch in raw view cast" | ||
); | ||
/* Alignment checked with debug assertion: alignment could be dynamically correct, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here -- the alignment check is a safety issue, so it should be |
||
* and we don't have a check that compiles out for that. */ | ||
unsafe { RawArrayViewMut::new(ptr, self.dim, self.strides) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,8 @@ | |
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use std::ptr::NonNull; | ||
|
||
use crate::dimension; | ||
use crate::error::ShapeError; | ||
use crate::extension::nonnull::nonnull_debug_checked_from_ptr; | ||
|
@@ -200,11 +202,11 @@ where | |
|
||
/// Convert the view into an `ArrayViewMut<'b, A, D>` where `'b` is a lifetime | ||
/// outlived by `'a'`. | ||
pub fn reborrow<'b>(mut self) -> ArrayViewMut<'b, A, D> | ||
pub fn reborrow<'b>(self) -> ArrayViewMut<'b, A, D> | ||
where | ||
'a: 'b, | ||
{ | ||
unsafe { ArrayViewMut::new_(self.as_mut_ptr(), self.dim, self.strides) } | ||
unsafe { ArrayViewMut::new(self.ptr, self.dim, self.strides) } | ||
} | ||
} | ||
|
||
|
@@ -217,14 +219,24 @@ where | |
/// | ||
/// Unsafe because: `ptr` must be valid for the given dimension and strides. | ||
#[inline(always)] | ||
pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { | ||
pub(crate) unsafe fn new(ptr: NonNull<A>, dim: D, strides: D) -> Self { | ||
if cfg!(debug_assertions) { | ||
assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); | ||
dimension::max_abs_offset_check_overflow::<A, _>(&dim, &strides).unwrap(); | ||
} | ||
ArrayView { | ||
data: ViewRepr::new(), | ||
ptr: nonnull_debug_checked_from_ptr(ptr as *mut A), | ||
ptr, | ||
dim, | ||
strides, | ||
} | ||
} | ||
|
||
/// Unsafe because: `ptr` must be valid for the given dimension and strides. | ||
#[inline] | ||
pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { | ||
Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) | ||
} | ||
} | ||
|
||
impl<'a, A, D> ArrayViewMut<'a, A, D> | ||
|
@@ -235,17 +247,24 @@ where | |
/// | ||
/// Unsafe because: `ptr` must be valid for the given dimension and strides. | ||
#[inline(always)] | ||
pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { | ||
pub(crate) unsafe fn new(ptr: NonNull<A>, dim: D, strides: D) -> Self { | ||
if cfg!(debug_assertions) { | ||
assert!(!ptr.is_null(), "The pointer must be non-null."); | ||
assert!(is_aligned(ptr), "The pointer must be aligned."); | ||
assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); | ||
dimension::max_abs_offset_check_overflow::<A, _>(&dim, &strides).unwrap(); | ||
} | ||
ArrayViewMut { | ||
data: ViewRepr::new(), | ||
ptr: nonnull_debug_checked_from_ptr(ptr), | ||
ptr, | ||
dim, | ||
strides, | ||
} | ||
} | ||
|
||
/// Create a new `ArrayView` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: This should be |
||
/// | ||
/// Unsafe because: `ptr` must be valid for the given dimension and strides. | ||
#[inline(always)] | ||
pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { | ||
Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a safe function, we need to either
assert!
(notdebug_assert!
) that the pointer is aligned (which would be my preference), or we need to relax the "ptr
must always be aligned" requirement onArrayBase
.To avoid the cost of the check in most cases, we could write it like this:
or, equivalently, since "alignment is measured in bytes, and must be at least 1, and always a power of 2":
The comparison in the
if
should always compile out, and the body of theif
will remain only if the alignment ofB
is greater than the alignment ofA
. I would expect that case to be relatively rare, because it requires additional work on the part of the user (since the pointer toA
won't naturally be guaranteed to have valid alignment forB
).Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the alignment requirement come from, and doesn't it only matter if we dereference?
I'd prefer to make this function unsafe, there is basically no downside to that, instead of adding checking that does not compile out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm referring to this constraint on
ArrayBase
. From what I can tell, you're right -- the only time alignment actually matters forRawArrayView/Mut
is when dereferencing a pointer or reading/writing a pointed-to value in some way (i.e. most of the functions instd::ptr
).It sounds like the best option is to remove the alignment restriction on
RawArrayView/Mut
. All we'd need to do is update the list of restrictions in that comment aboveArrayBase
, the docs forRawArrayView/Mut::from_shape_ptr
, and the docs forderef_into_view/_mut
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've created #738 to remove the alignment restriction on
RawArrayView/Mut
.