-
Notifications
You must be signed in to change notification settings - Fork 22
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
fn unaligned_lvl_slice
: Safely encapsulate "unaligned" slices of lvl: &[[u8; 4]]
, as previous accesses were UB
#731
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//! Unstable `fn`s copied directly from `std`, with the following differences: | ||
//! * They are free `fn`s now, not methods. | ||
//! * `self` is replaced by `this`. | ||
//! * Things only accessible by `std` are replaced with stable counterparts, such as: | ||
//! * `exact_div` => `/` | ||
//! * `.unchecked_mul` => `*` | ||
//! * `const` `.expect` => `match` and `panic!` | ||
|
||
use std::mem; | ||
use std::slice::from_raw_parts; | ||
|
||
/// From `1.75.0`. | ||
pub const fn flatten<const N: usize, T>(this: &[[T; N]]) -> &[T] { | ||
let len = if mem::size_of::<T>() == 0 { | ||
match this.len().checked_mul(N) { | ||
None => panic!("slice len overflow"), | ||
Some(it) => it, | ||
} | ||
} else { | ||
// SAFETY: `this.len() * N` cannot overflow because `self` is | ||
// already in the address space. | ||
/* unsafe */ | ||
this.len() * N | ||
}; | ||
// SAFETY: `[T]` is layout-identical to `[T; N]` | ||
unsafe { from_raw_parts(this.as_ptr().cast(), len) } | ||
} | ||
|
||
/// From `1.75.0`. | ||
#[inline] | ||
#[must_use] | ||
pub const unsafe fn as_chunks_unchecked<const N: usize, T>(this: &[T]) -> &[[T; N]] { | ||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length | ||
let new_len = /* unsafe */ { | ||
assert!(N != 0 && this.len() % N == 0); | ||
this.len() / N | ||
}; | ||
// SAFETY: We cast a slice of `new_len * N` elements into | ||
// a slice of `new_len` many `N` elements chunks. | ||
unsafe { from_raw_parts(this.as_ptr().cast(), new_len) } | ||
} | ||
|
||
/// From `1.75.0`. | ||
#[inline] | ||
#[track_caller] | ||
#[must_use] | ||
pub const fn as_chunks<const N: usize, T>(this: &[T]) -> (&[[T; N]], &[T]) { | ||
assert!(N != 0, "chunk size must be non-zero"); | ||
let len = this.len() / N; | ||
let (multiple_of_n, remainder) = this.split_at(len * N); | ||
// SAFETY: We already panicked for zero, and ensured by construction | ||
// that the length of the subslice is a multiple of N. | ||
let array_slice = unsafe { as_chunks_unchecked(multiple_of_n) }; | ||
(array_slice, remainder) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
If enforcing a minimum slice size, this would be something like
lvl[x as usize..][..((endy4 - starty4 - 1) as isize * b4_stride + 1) as usize].as_ptr()
. However, index-1
of such as slice can also be read, so more work on slice definitions will be needed. But that's probably outside the scope of this PRThere 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 was hoping to structure things as, at this point in the code, we don't care about checking the length, as it is up to the inner
fn
to correctly bounds check when reading elements. That's of course not true for the asmfn
s, though it will be eventually true (once we make them safe) for the C/Rust fallbacks. If we do check the length eagerly here, the only real safety that gives us in documentation, that is, if we check that the asmfn
claims it will access X elements and here we check for X elements, we check that the actual length matches the docs, but it doesn't enforce anything on the asm. Not sure if that made sense.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.
Yes, that makes sense. If, down the road, we want the C/Rust fallbacks to check bounds, then we'll probably have to change
fn
interfaces to avoid casting to raw pointers and recreating slices in the fallbackfn
s? One potential wrinkle here, is that an asmfn
may access memory locations that are different from the corresponding fallbackfn
(because asm may read a 128-bit word where the fallback reads a couple 16-bit words; I can't immediately think of a case where that would also apply a write). I don't know whether we need to consider that.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.
We can't change the existing
fn
ptr args without modifying the asm, which we don't want to do. But what we can do is is pass extra args that asm ignores (which we already do for passingbitdepth_max: c_int
for 8bpcfn
s). So we can split a slice into the ptr and len, pass the len as an extra arg, and then recreate the slice in the fallback C/Rustfn
. Where it will be technicallyunsafe
, but quite safe in practice.As for asm
fn
s accessing more memory locations than the fallbacks, we could eagerly checks the bounds, but it's still ultimately up to the asm not to access any more than that. It's always going to be inherently veryunsafe
.