Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0374ea2

Browse files
committedJul 23, 2024
Add elem_offset and related methods
1 parent 9a945fd commit 0374ea2

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed
 

‎core/src/slice/mod.rs

+115
Original file line numberDiff line numberDiff line change
@@ -4540,6 +4540,121 @@ impl<T> [T] {
45404540
// are disjunct and in bounds.
45414541
unsafe { Ok(self.get_many_unchecked_mut(indices)) }
45424542
}
4543+
4544+
/// Returns the index that an element reference points to.
4545+
///
4546+
/// Returns `None` if `element` does not point within the slice or if it points between elements.
4547+
///
4548+
/// This method is useful for extending slice iterators like [`slice::split`].
4549+
///
4550+
/// Note that this uses pointer arithmetic and **does not compare elements**.
4551+
/// To find the index of an element via comparison, use
4552+
/// [`.iter().position()`](crate::iter::Iterator::position) instead.
4553+
///
4554+
/// # Panics
4555+
/// Panics if `T` is zero-sized.
4556+
///
4557+
/// # Examples
4558+
/// Basic usage:
4559+
/// ```
4560+
/// #![feature(substr_range)]
4561+
///
4562+
/// let nums: &[u32] = &[1, 7, 1, 1];
4563+
/// let num = &nums[2];
4564+
///
4565+
/// assert_eq!(num, &1);
4566+
/// assert_eq!(nums.elem_offset(num), Some(2));
4567+
/// ```
4568+
/// Returning `None` with an in-between element:
4569+
/// ```
4570+
/// #![feature(substr_range)]
4571+
///
4572+
/// let arr: &[[u32; 2]] = &[[0, 1], [2, 3]];
4573+
/// let flat_arr: &[u32] = arr.as_flattened();
4574+
///
4575+
/// let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap();
4576+
/// let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap();
4577+
///
4578+
/// assert_eq!(ok_elm, &[0, 1]);
4579+
/// assert_eq!(weird_elm, &[1, 2]);
4580+
///
4581+
/// assert_eq!(arr.elem_offset(ok_elm), Some(0)); // Points to element 0
4582+
/// assert_eq!(arr.elem_offset(weird_elm), None); // Points between element 0 and 1
4583+
/// ```
4584+
#[must_use]
4585+
#[unstable(feature = "substr_range", issue = "126769")]
4586+
pub fn elem_offset(&self, element: &T) -> Option<usize> {
4587+
if T::IS_ZST {
4588+
panic!("elements are zero-sized");
4589+
}
4590+
4591+
let self_start = self.as_ptr() as usize;
4592+
let elem_start = element as *const T as usize;
4593+
4594+
let byte_offset = elem_start.wrapping_sub(self_start);
4595+
4596+
if byte_offset % mem::size_of::<T>() != 0 {
4597+
return None;
4598+
}
4599+
4600+
let offset = byte_offset / mem::size_of::<T>();
4601+
4602+
if offset < self.len() { Some(offset) } else { None }
4603+
}
4604+
4605+
/// Returns the range of indices that a subslice points to.
4606+
///
4607+
/// Returns `None` if `subslice` does not point within the slice or if it points between elements.
4608+
///
4609+
/// This method **does not compare elements**. Instead, this method finds the location in the slice that
4610+
/// `subslice` was obtained from. To find the index of a subslice via comparison, instead use
4611+
/// [`.windows()`](slice::windows)[`.position()`](crate::iter::Iterator::position).
4612+
///
4613+
/// This method is useful for extending slice iterators like [`slice::split`].
4614+
///
4615+
/// Note that this may return a false positive (either `Some(0..0)` or `Some(self.len()..self.len())`)
4616+
/// if `subslice` has a length of zero and points to the beginning or end of another, separate, slice.
4617+
///
4618+
/// # Panics
4619+
/// Panics if `T` is zero-sized.
4620+
///
4621+
/// # Examples
4622+
/// Basic usage:
4623+
/// ```
4624+
/// #![feature(substr_range)]
4625+
///
4626+
/// let nums = &[0, 5, 10, 0, 0, 5];
4627+
///
4628+
/// let mut iter = nums
4629+
/// .split(|t| *t == 0)
4630+
/// .map(|n| nums.subslice_range(n).unwrap());
4631+
///
4632+
/// assert_eq!(iter.next(), Some(0..0));
4633+
/// assert_eq!(iter.next(), Some(1..3));
4634+
/// assert_eq!(iter.next(), Some(4..4));
4635+
/// assert_eq!(iter.next(), Some(5..6));
4636+
/// ```
4637+
#[must_use]
4638+
#[unstable(feature = "substr_range", issue = "126769")]
4639+
pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>> {
4640+
if T::IS_ZST {
4641+
panic!("elements are zero-sized");
4642+
}
4643+
4644+
let self_start = self.as_ptr() as usize;
4645+
let subslice_start = subslice.as_ptr() as usize;
4646+
4647+
let byte_start = subslice_start.wrapping_sub(self_start);
4648+
4649+
if byte_start % core::mem::size_of::<T>() != 0 {
4650+
return None;
4651+
}
4652+
4653+
let start = byte_start / core::mem::size_of::<T>();
4654+
let end = start.wrapping_add(subslice.len());
4655+
4656+
if start <= self.len() && end <= self.len() { Some(start..end) } else { None }
4657+
}
45434658
}
45444659

45454660
impl<T, const N: usize> [[T; N]] {

‎core/src/str/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher};
1919
use crate::ascii;
2020
use crate::char::{self, EscapeDebugExtArgs};
2121
use crate::mem;
22+
use crate::ops::Range;
2223
use crate::slice::{self, SliceIndex};
2324

2425
pub mod pattern;
@@ -2721,6 +2722,39 @@ impl str {
27212722
pub fn escape_unicode(&self) -> EscapeUnicode<'_> {
27222723
EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) }
27232724
}
2725+
2726+
/// Returns the range that a substring points to.
2727+
///
2728+
/// Returns `None` if `substr` does not point within `self`.
2729+
///
2730+
/// Unlike [`str::find`], **this does not search through the string**.
2731+
/// Instead, it uses pointer arithmetic to find where in the string
2732+
/// `substr` is derived from.
2733+
///
2734+
/// This is useful for extending [`str::split`] and similar methods.
2735+
///
2736+
/// Note that this method may return false positives (typically either
2737+
/// `Some(0..0)` or `Some(self.len()..self.len())`) if `substr` is a
2738+
/// zero-length `str` that points at the beginning or end of another,
2739+
/// independent, `str`.
2740+
///
2741+
/// # Examples
2742+
/// ```
2743+
/// #![feature(substr_range)]
2744+
///
2745+
/// let data = "a, b, b, a";
2746+
/// let mut iter = data.split(", ").map(|s| data.substr_range(s).unwrap());
2747+
///
2748+
/// assert_eq!(iter.next(), Some(0..1));
2749+
/// assert_eq!(iter.next(), Some(3..4));
2750+
/// assert_eq!(iter.next(), Some(6..7));
2751+
/// assert_eq!(iter.next(), Some(9..10));
2752+
/// ```
2753+
#[must_use]
2754+
#[unstable(feature = "substr_range", issue = "126769")]
2755+
pub fn substr_range(&self, substr: &str) -> Option<Range<usize>> {
2756+
self.as_bytes().subslice_range(substr.as_bytes())
2757+
}
27242758
}
27252759

27262760
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)
Failed to load comments.