usecrate::{arch::generic::memchr as generic, ext::Pointer, vector::Vector};
/// Finds all occurrences of a single byte in a haystack. #[derive(Clone, Copy, Debug)] pubstruct One { /// Used for haystacks less than 32 bytes.
sse2: generic::One<__m128i>, /// Used for haystacks bigger than 32 bytes.
avx2: generic::One<__m256i>,
}
impl One { /// Create a new searcher that finds occurrences of the needle byte given. /// /// This particular searcher is specialized to use AVX2 vector instructions /// that typically make it quite fast. (SSE2 is used for haystacks that /// are too short to accommodate an AVX2 vector.) /// /// If either SSE2 or AVX2 is unavailable in the current environment, then /// `None` is returned. #[inline] pubfn new(needle: u8) -> Option<One> { if One::is_available() { // SAFETY: we check that sse2 and avx2 are available above. unsafe { Some(One::new_unchecked(needle)) }
} else {
None
}
}
/// Create a new finder specific to AVX2 vectors and routines without /// checking that either SSE2 or AVX2 is available. /// /// # Safety /// /// Callers must guarantee that it is safe to execute both `sse2` and /// `avx2` instructions in the current environment. /// /// Note that it is a common misconception that if one compiles for an /// `x86_64` target, then they therefore automatically have access to SSE2 /// instructions. While this is almost always the case, it isn't true in /// 100% of cases. #[target_feature(enable = "sse2", enable = "avx2")] #[inline] pubunsafefn new_unchecked(needle: u8) -> One {
One {
sse2: generic::One::new(needle),
avx2: generic::One::new(needle),
}
}
/// Returns true when this implementation is available in the current /// environment. /// /// When this is true, it is guaranteed that [`One::new`] will return /// a `Some` value. Similarly, when it is false, it is guaranteed that /// `One::new` will return a `None` value. /// /// Note also that for the lifetime of a single program, if this returns /// true then it will always return true. #[inline] pubfn is_available() -> bool { #[cfg(not(target_feature = "sse2"))]
{ false
} #[cfg(target_feature = "sse2")]
{ #[cfg(target_feature = "avx2")]
{ true
} #[cfg(not(target_feature = "avx2"))]
{ #[cfg(feature = "std")]
{
std::is_x86_feature_detected!("avx2")
} #[cfg(not(feature = "std"))]
{ false
}
}
}
}
/// Return the first occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn find(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.find_raw(s, e)
})
}
}
/// Return the last occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn rfind(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.rfind_raw(s, e)
})
}
}
/// Counts all occurrences of this byte in the given haystack. #[inline] pubfn count(&self, haystack: &[u8]) -> usize { // SAFETY: All of our pointers are derived directly from a borrowed // slice, which is guaranteed to be valid. unsafe { let start = haystack.as_ptr(); let end = start.add(haystack.len()); self.count_raw(start, end)
}
}
/// Like `find`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn find_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::fwd_byte_by_byte(start, end, |b| {
b == self.sse2.needle1()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.find_raw_sse2(start, end)
};
} // SAFETY: Building a `One` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // Note that we could call `self.avx2.find_raw` directly here. But that // means we'd have to annotate this routine with `target_feature`. // Which is fine, because this routine is `unsafe` anyway and the // `target_feature` obligation is met by virtue of building a `One`. // The real problem is that a routine with a `target_feature` // annotation generally can't be inlined into caller code unless // the caller code has the same target feature annotations. Namely, // the common case (at time of writing) is for calling code to not // have the `avx2` target feature enabled *at compile time*. Without // `target_feature` on this routine, it can be inlined which will // handle some of the short-haystack cases above without touching the // architecture specific code. self.find_raw_avx2(start, end)
}
/// Like `rfind`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn rfind_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::rev_byte_by_byte(start, end, |b| {
b == self.sse2.needle1()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.rfind_raw_sse2(start, end)
};
} // SAFETY: Building a `One` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // See note in forward routine above for why we don't just call // `self.avx2.rfind_raw` directly here. self.rfind_raw_avx2(start, end)
}
/// Counts all occurrences of this byte in the given haystack represented /// by raw pointers. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `0` will always be returned. #[inline] pubunsafefn count_raw(&self, start: *const u8, end: *const u8) -> usize { if start >= end { return0;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::count_byte_by_byte(start, end, |b| {
b == self.sse2.needle1()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.count_raw_sse2(start, end)
};
} // SAFETY: Building a `One` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. self.count_raw_avx2(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`One::find_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn find_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.find_raw(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`One::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn rfind_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.rfind_raw(start, end)
}
/// Execute a count using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`One::count_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn count_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> usize { self.sse2.count_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`One::find_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn find_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.find_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`One::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn rfind_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.rfind_raw(start, end)
}
/// Execute a count using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`One::count_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `One`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn count_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> usize { self.avx2.count_raw(start, end)
}
/// Returns an iterator over all occurrences of the needle byte in the /// given haystack. /// /// The iterator returned implements `DoubleEndedIterator`. This means it /// can also be used to find occurrences in reverse order. #[inline] pubfn iter<'a, 'h>(&'a self, haystack: &'h [u8]) -> OneIter<'a, 'h> {
OneIter { searcher: self, it: generic::Iter::new(haystack) }
}
}
/// An iterator over all occurrences of a single byte in a haystack. /// /// This iterator implements `DoubleEndedIterator`, which means it can also be /// used to find occurrences in reverse order. /// /// This iterator is created by the [`One::iter`] method. /// /// The lifetime parameters are as follows: /// /// * `'a` refers to the lifetime of the underlying [`One`] searcher. /// * `'h` refers to the lifetime of the haystack being searched. #[derive(Clone, Debug)] pubstruct OneIter<'a, 'h> {
searcher: &'a One,
it: generic::Iter<'h>,
}
impl<'a, 'h> Iterator for OneIter<'a, 'h> { type Item = usize;
#[inline] fn next(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'find_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next(|s, e| self.searcher.find_raw(s, e)) }
}
#[inline] fn count(self) -> usize { self.it.count(|s, e| { // SAFETY: We rely on our generic iterator to return valid start // and end pointers. unsafe { self.searcher.count_raw(s, e) }
})
}
impl<'a, 'h> DoubleEndedIterator for OneIter<'a, 'h> { #[inline] fn next_back(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'rfind_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next_back(|s, e| self.searcher.rfind_raw(s, e)) }
}
}
impl<'a, 'h> core::iter::FusedIterator for OneIter<'a, 'h> {}
/// Finds all occurrences of two bytes in a haystack. /// /// That is, this reports matches of one of two possible bytes. For example, /// searching for `a` or `b` in `afoobar` would report matches at offsets `0`, /// `4` and `5`. #[derive(Clone, Copy, Debug)] pubstruct Two { /// Used for haystacks less than 32 bytes.
sse2: generic::Two<__m128i>, /// Used for haystacks bigger than 32 bytes.
avx2: generic::Two<__m256i>,
}
impl Two { /// Create a new searcher that finds occurrences of the needle bytes given. /// /// This particular searcher is specialized to use AVX2 vector instructions /// that typically make it quite fast. (SSE2 is used for haystacks that /// are too short to accommodate an AVX2 vector.) /// /// If either SSE2 or AVX2 is unavailable in the current environment, then /// `None` is returned. #[inline] pubfn new(needle1: u8, needle2: u8) -> Option<Two> { if Two::is_available() { // SAFETY: we check that sse2 and avx2 are available above. unsafe { Some(Two::new_unchecked(needle1, needle2)) }
} else {
None
}
}
/// Create a new finder specific to AVX2 vectors and routines without /// checking that either SSE2 or AVX2 is available. /// /// # Safety /// /// Callers must guarantee that it is safe to execute both `sse2` and /// `avx2` instructions in the current environment. /// /// Note that it is a common misconception that if one compiles for an /// `x86_64` target, then they therefore automatically have access to SSE2 /// instructions. While this is almost always the case, it isn't true in /// 100% of cases. #[target_feature(enable = "sse2", enable = "avx2")] #[inline] pubunsafefn new_unchecked(needle1: u8, needle2: u8) -> Two {
Two {
sse2: generic::Two::new(needle1, needle2),
avx2: generic::Two::new(needle1, needle2),
}
}
/// Returns true when this implementation is available in the current /// environment. /// /// When this is true, it is guaranteed that [`Two::new`] will return /// a `Some` value. Similarly, when it is false, it is guaranteed that /// `Two::new` will return a `None` value. /// /// Note also that for the lifetime of a single program, if this returns /// true then it will always return true. #[inline] pubfn is_available() -> bool { #[cfg(not(target_feature = "sse2"))]
{ false
} #[cfg(target_feature = "sse2")]
{ #[cfg(target_feature = "avx2")]
{ true
} #[cfg(not(target_feature = "avx2"))]
{ #[cfg(feature = "std")]
{
std::is_x86_feature_detected!("avx2")
} #[cfg(not(feature = "std"))]
{ false
}
}
}
}
/// Return the first occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn find(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.find_raw(s, e)
})
}
}
/// Return the last occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn rfind(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.rfind_raw(s, e)
})
}
}
/// Like `find`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn find_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::fwd_byte_by_byte(start, end, |b| {
b == self.sse2.needle1() || b == self.sse2.needle2()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.find_raw_sse2(start, end)
};
} // SAFETY: Building a `Two` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // Note that we could call `self.avx2.find_raw` directly here. But that // means we'd have to annotate this routine with `target_feature`. // Which is fine, because this routine is `unsafe` anyway and the // `target_feature` obligation is met by virtue of building a `Two`. // The real problem is that a routine with a `target_feature` // annotation generally can't be inlined into caller code unless // the caller code has the same target feature annotations. Namely, // the common case (at time of writing) is for calling code to not // have the `avx2` target feature enabled *at compile time*. Without // `target_feature` on this routine, it can be inlined which will // handle some of the short-haystack cases above without touching the // architecture specific code. self.find_raw_avx2(start, end)
}
/// Like `rfind`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn rfind_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::rev_byte_by_byte(start, end, |b| {
b == self.sse2.needle1() || b == self.sse2.needle2()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.rfind_raw_sse2(start, end)
};
} // SAFETY: Building a `Two` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // See note in forward routine above for why we don't just call // `self.avx2.rfind_raw` directly here. self.rfind_raw_avx2(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`Two::find_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Two`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn find_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.find_raw(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`Two::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Two`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn rfind_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.rfind_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`Two::find_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Two`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn find_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.find_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`Two::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Two`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn rfind_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.rfind_raw(start, end)
}
/// Returns an iterator over all occurrences of the needle bytes in the /// given haystack. /// /// The iterator returned implements `DoubleEndedIterator`. This means it /// can also be used to find occurrences in reverse order. #[inline] pubfn iter<'a, 'h>(&'a self, haystack: &'h [u8]) -> TwoIter<'a, 'h> {
TwoIter { searcher: self, it: generic::Iter::new(haystack) }
}
}
/// An iterator over all occurrences of two possible bytes in a haystack. /// /// This iterator implements `DoubleEndedIterator`, which means it can also be /// used to find occurrences in reverse order. /// /// This iterator is created by the [`Two::iter`] method. /// /// The lifetime parameters are as follows: /// /// * `'a` refers to the lifetime of the underlying [`Two`] searcher. /// * `'h` refers to the lifetime of the haystack being searched. #[derive(Clone, Debug)] pubstruct TwoIter<'a, 'h> {
searcher: &'a Two,
it: generic::Iter<'h>,
}
impl<'a, 'h> Iterator for TwoIter<'a, 'h> { type Item = usize;
#[inline] fn next(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'find_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next(|s, e| self.searcher.find_raw(s, e)) }
}
impl<'a, 'h> DoubleEndedIterator for TwoIter<'a, 'h> { #[inline] fn next_back(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'rfind_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next_back(|s, e| self.searcher.rfind_raw(s, e)) }
}
}
impl<'a, 'h> core::iter::FusedIterator for TwoIter<'a, 'h> {}
/// Finds all occurrences of three bytes in a haystack. /// /// That is, this reports matches of one of three possible bytes. For example, /// searching for `a`, `b` or `o` in `afoobar` would report matches at offsets /// `0`, `2`, `3`, `4` and `5`. #[derive(Clone, Copy, Debug)] pubstruct Three { /// Used for haystacks less than 32 bytes.
sse2: generic::Three<__m128i>, /// Used for haystacks bigger than 32 bytes.
avx2: generic::Three<__m256i>,
}
impl Three { /// Create a new searcher that finds occurrences of the needle bytes given. /// /// This particular searcher is specialized to use AVX2 vector instructions /// that typically make it quite fast. (SSE2 is used for haystacks that /// are too short to accommodate an AVX2 vector.) /// /// If either SSE2 or AVX2 is unavailable in the current environment, then /// `None` is returned. #[inline] pubfn new(needle1: u8, needle2: u8, needle3: u8) -> Option<Three> { if Three::is_available() { // SAFETY: we check that sse2 and avx2 are available above. unsafe { Some(Three::new_unchecked(needle1, needle2, needle3)) }
} else {
None
}
}
/// Create a new finder specific to AVX2 vectors and routines without /// checking that either SSE2 or AVX2 is available. /// /// # Safety /// /// Callers must guarantee that it is safe to execute both `sse2` and /// `avx2` instructions in the current environment. /// /// Note that it is a common misconception that if one compiles for an /// `x86_64` target, then they therefore automatically have access to SSE2 /// instructions. While this is almost always the case, it isn't true in /// 100% of cases. #[target_feature(enable = "sse2", enable = "avx2")] #[inline] pubunsafefn new_unchecked(
needle1: u8,
needle2: u8,
needle3: u8,
) -> Three {
Three {
sse2: generic::Three::new(needle1, needle2, needle3),
avx2: generic::Three::new(needle1, needle2, needle3),
}
}
/// Returns true when this implementation is available in the current /// environment. /// /// When this is true, it is guaranteed that [`Three::new`] will return /// a `Some` value. Similarly, when it is false, it is guaranteed that /// `Three::new` will return a `None` value. /// /// Note also that for the lifetime of a single program, if this returns /// true then it will always return true. #[inline] pubfn is_available() -> bool { #[cfg(not(target_feature = "sse2"))]
{ false
} #[cfg(target_feature = "sse2")]
{ #[cfg(target_feature = "avx2")]
{ true
} #[cfg(not(target_feature = "avx2"))]
{ #[cfg(feature = "std")]
{
std::is_x86_feature_detected!("avx2")
} #[cfg(not(feature = "std"))]
{ false
}
}
}
}
/// Return the first occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn find(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.find_raw(s, e)
})
}
}
/// Return the last occurrence of one of the needle bytes in the given /// haystack. If no such occurrence exists, then `None` is returned. /// /// The occurrence is reported as an offset into `haystack`. Its maximum /// value is `haystack.len() - 1`. #[inline] pubfn rfind(&self, haystack: &[u8]) -> Option<usize> { // SAFETY: `find_raw` guarantees that if a pointer is returned, it // falls within the bounds of the start and end pointers. unsafe {
generic::search_slice_with_raw(haystack, |s, e| { self.rfind_raw(s, e)
})
}
}
/// Like `find`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn find_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::fwd_byte_by_byte(start, end, |b| {
b == self.sse2.needle1()
|| b == self.sse2.needle2()
|| b == self.sse2.needle3()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.find_raw_sse2(start, end)
};
} // SAFETY: Building a `Three` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // Note that we could call `self.avx2.find_raw` directly here. But that // means we'd have to annotate this routine with `target_feature`. // Which is fine, because this routine is `unsafe` anyway and the // `target_feature` obligation is met by virtue of building a `Three`. // The real problem is that a routine with a `target_feature` // annotation generally can't be inlined into caller code unless // the caller code has the same target feature annotations. Namely, // the common case (at time of writing) is for calling code to not // have the `avx2` target feature enabled *at compile time*. Without // `target_feature` on this routine, it can be inlined which will // handle some of the short-haystack cases above without touching the // architecture specific code. self.find_raw_avx2(start, end)
}
/// Like `rfind`, but accepts and returns raw pointers. /// /// When a match is found, the pointer returned is guaranteed to be /// `>= start` and `< end`. /// /// This routine is useful if you're already using raw pointers and would /// like to avoid converting back to a slice before executing a search. /// /// # Safety /// /// * Both `start` and `end` must be valid for reads. /// * Both `start` and `end` must point to an initialized value. /// * Both `start` and `end` must point to the same allocated object and /// must either be in bounds or at most one byte past the end of the /// allocated object. /// * Both `start` and `end` must be _derived from_ a pointer to the same /// object. /// * The distance between `start` and `end` must not overflow `isize`. /// * The distance being in bounds must not rely on "wrapping around" the /// address space. /// /// Note that callers may pass a pair of pointers such that `start >= end`. /// In that case, `None` will always be returned. #[inline] pubunsafefn rfind_raw(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { if start >= end { return None;
} let len = end.distance(start); if len < __m256i::BYTES { returnif len < __m128i::BYTES { // SAFETY: We require the caller to pass valid start/end // pointers.
generic::rev_byte_by_byte(start, end, |b| {
b == self.sse2.needle1()
|| b == self.sse2.needle2()
|| b == self.sse2.needle3()
})
} else { // SAFETY: We require the caller to pass valid start/end // pointers. self.rfind_raw_sse2(start, end)
};
} // SAFETY: Building a `Three` means it's safe to call both 'sse2' and // 'avx2' routines. Also, we've checked that our haystack is big // enough to run on the vector routine. Pointer validity is caller's // responsibility. // // See note in forward routine above for why we don't just call // `self.avx2.rfind_raw` directly here. self.rfind_raw_avx2(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`Three::find_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Three`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn find_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.find_raw(start, end)
}
/// Execute a search using SSE2 vectors and routines. /// /// # Safety /// /// Same as [`Three::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an SSE2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Three`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "sse2")] #[inline] unsafefn rfind_raw_sse2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.sse2.rfind_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`Three::find_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Three`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn find_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.find_raw(start, end)
}
/// Execute a search using AVX2 vectors and routines. /// /// # Safety /// /// Same as [`Three::rfind_raw`], except the distance between `start` and /// `end` must be at least the size of an AVX2 vector (in bytes). /// /// (The target feature safety obligation is automatically fulfilled by /// virtue of being a method on `Three`, which can only be constructed /// when it is safe to call `sse2`/`avx2` routines.) #[target_feature(enable = "avx2")] #[inline] unsafefn rfind_raw_avx2(
&self,
start: *const u8,
end: *const u8,
) -> Option<*const u8> { self.avx2.rfind_raw(start, end)
}
/// Returns an iterator over all occurrences of the needle bytes in the /// given haystack. /// /// The iterator returned implements `DoubleEndedIterator`. This means it /// can also be used to find occurrences in reverse order. #[inline] pubfn iter<'a, 'h>(&'a self, haystack: &'h [u8]) -> ThreeIter<'a, 'h> {
ThreeIter { searcher: self, it: generic::Iter::new(haystack) }
}
}
/// An iterator over all occurrences of three possible bytes in a haystack. /// /// This iterator implements `DoubleEndedIterator`, which means it can also be /// used to find occurrences in reverse order. /// /// This iterator is created by the [`Three::iter`] method. /// /// The lifetime parameters are as follows: /// /// * `'a` refers to the lifetime of the underlying [`Three`] searcher. /// * `'h` refers to the lifetime of the haystack being searched. #[derive(Clone, Debug)] pubstruct ThreeIter<'a, 'h> {
searcher: &'a Three,
it: generic::Iter<'h>,
}
impl<'a, 'h> Iterator for ThreeIter<'a, 'h> { type Item = usize;
#[inline] fn next(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'find_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next(|s, e| self.searcher.find_raw(s, e)) }
}
impl<'a, 'h> DoubleEndedIterator for ThreeIter<'a, 'h> { #[inline] fn next_back(&mutself) -> Option<usize> { // SAFETY: We rely on the generic iterator to provide valid start // and end pointers, but we guarantee that any pointer returned by // 'rfind_raw' falls within the bounds of the start and end pointer. unsafe { self.it.next_back(|s, e| self.searcher.rfind_raw(s, e)) }
}
}
impl<'a, 'h> core::iter::FusedIterator for ThreeIter<'a, 'h> {}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.