diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/memchr/src/arch/generic/memchr.rs | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/memchr/src/arch/generic/memchr.rs')
-rw-r--r-- | vendor/memchr/src/arch/generic/memchr.rs | 1214 |
1 files changed, 0 insertions, 1214 deletions
diff --git a/vendor/memchr/src/arch/generic/memchr.rs b/vendor/memchr/src/arch/generic/memchr.rs deleted file mode 100644 index 580b3cc..0000000 --- a/vendor/memchr/src/arch/generic/memchr.rs +++ /dev/null @@ -1,1214 +0,0 @@ -/*! -Generic crate-internal routines for the `memchr` family of functions. -*/ - -// What follows is a vector algorithm generic over the specific vector -// type to detect the position of one, two or three needles in a haystack. -// From what I know, this is a "classic" algorithm, although I don't -// believe it has been published in any peer reviewed journal. I believe -// it can be found in places like glibc and Go's standard library. It -// appears to be well known and is elaborated on in more detail here: -// https://gms.tf/stdfind-and-memchr-optimizations.html -// -// While the routine below is fairly long and perhaps intimidating, the basic -// idea is actually very simple and can be expressed straight-forwardly in -// pseudo code. The psuedo code below is written for 128 bit vectors, but the -// actual code below works for anything that implements the Vector trait. -// -// needle = (n1 << 15) | (n1 << 14) | ... | (n1 << 1) | n1 -// // Note: shift amount is in bytes -// -// while i <= haystack.len() - 16: -// // A 16 byte vector. Each byte in chunk corresponds to a byte in -// // the haystack. -// chunk = haystack[i:i+16] -// // Compare bytes in needle with bytes in chunk. The result is a 16 -// // byte chunk where each byte is 0xFF if the corresponding bytes -// // in needle and chunk were equal, or 0x00 otherwise. -// eqs = cmpeq(needle, chunk) -// // Return a 32 bit integer where the most significant 16 bits -// // are always 0 and the lower 16 bits correspond to whether the -// // most significant bit in the correspond byte in `eqs` is set. -// // In other words, `mask as u16` has bit i set if and only if -// // needle[i] == chunk[i]. -// mask = movemask(eqs) -// -// // Mask is 0 if there is no match, and non-zero otherwise. -// if mask != 0: -// // trailing_zeros tells us the position of the least significant -// // bit that is set. -// return i + trailing_zeros(mask) -// -// // haystack length may not be a multiple of 16, so search the rest. -// while i < haystack.len(): -// if haystack[i] == n1: -// return i -// -// // No match found. -// return NULL -// -// In fact, we could loosely translate the above code to Rust line-for-line -// and it would be a pretty fast algorithm. But, we pull out all the stops -// to go as fast as possible: -// -// 1. We use aligned loads. That is, we do some finagling to make sure our -// primary loop not only proceeds in increments of 16 bytes, but that -// the address of haystack's pointer that we dereference is aligned to -// 16 bytes. 16 is a magic number here because it is the size of SSE2 -// 128-bit vector. (For the AVX2 algorithm, 32 is the magic number.) -// Therefore, to get aligned loads, our pointer's address must be evenly -// divisible by 16. -// 2. Our primary loop proceeds 64 bytes at a time instead of 16. It's -// kind of like loop unrolling, but we combine the equality comparisons -// using a vector OR such that we only need to extract a single mask to -// determine whether a match exists or not. If so, then we do some -// book-keeping to determine the precise location but otherwise mush on. -// 3. We use our "chunk" comparison routine in as many places as possible, -// even if it means using unaligned loads. In particular, if haystack -// starts with an unaligned address, then we do an unaligned load to -// search the first 16 bytes. We then start our primary loop at the -// smallest subsequent aligned address, which will actually overlap with -// previously searched bytes. But we're OK with that. We do a similar -// dance at the end of our primary loop. Finally, to avoid a -// byte-at-a-time loop at the end, we do a final 16 byte unaligned load -// that may overlap with a previous load. This is OK because it converts -// a loop into a small number of very fast vector instructions. The overlap -// is OK because we know the place where the overlap occurs does not -// contain a match. -// -// And that's pretty all there is to it. Note that since the below is -// generic and since it's meant to be inlined into routines with a -// `#[target_feature(enable = "...")]` annotation, we must mark all routines as -// both unsafe and `#[inline(always)]`. -// -// The fact that the code below is generic does somewhat inhibit us. For -// example, I've noticed that introducing an unlineable `#[cold]` function to -// handle the match case in the loop generates tighter assembly, but there is -// no way to do this in the generic code below because the generic code doesn't -// know what `target_feature` annotation to apply to the unlineable function. -// We could make such functions part of the `Vector` trait, but we instead live -// with the slightly sub-optimal codegen for now since it doesn't seem to have -// a noticeable perf difference. - -use crate::{ - ext::Pointer, - vector::{MoveMask, Vector}, -}; - -/// Finds all occurrences of a single byte in a haystack. -#[derive(Clone, Copy, Debug)] -pub(crate) struct One<V> { - s1: u8, - v1: V, -} - -impl<V: Vector> One<V> { - /// The number of bytes we examine per each iteration of our search loop. - const LOOP_SIZE: usize = 4 * V::BYTES; - - /// Create a new searcher that finds occurrences of the byte given. - #[inline(always)] - pub(crate) unsafe fn new(needle: u8) -> One<V> { - One { s1: needle, v1: V::splat(needle) } - } - - /// Returns the needle given to `One::new`. - #[inline(always)] - pub(crate) fn needle1(&self) -> u8 { - self.s1 - } - - /// Return a pointer to the first occurrence of the needle in the given - /// haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn find_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::first_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - // Search a possibly unaligned chunk at `start`. This covers any part - // of the haystack prior to where aligned loads can start. - if let Some(cur) = self.search_chunk(start, topos) { - return Some(cur); - } - // Set `cur` to the first V-aligned pointer greater than `start`. - let mut cur = start.add(V::BYTES - (start.as_usize() & V::ALIGN)); - debug_assert!(cur > start && end.sub(V::BYTES) >= start); - if len >= Self::LOOP_SIZE { - while cur <= end.sub(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(1 * V::BYTES)); - let c = V::load_aligned(cur.add(2 * V::BYTES)); - let d = V::load_aligned(cur.add(3 * V::BYTES)); - let eqa = self.v1.cmpeq(a); - let eqb = self.v1.cmpeq(b); - let eqc = self.v1.cmpeq(c); - let eqd = self.v1.cmpeq(d); - let or1 = eqa.or(eqb); - let or2 = eqc.or(eqd); - let or3 = or1.or(or2); - if or3.movemask_will_have_non_zero() { - let mask = eqa.movemask(); - if mask.has_non_zero() { - return Some(cur.add(topos(mask))); - } - - let mask = eqb.movemask(); - if mask.has_non_zero() { - return Some(cur.add(1 * V::BYTES).add(topos(mask))); - } - - let mask = eqc.movemask(); - if mask.has_non_zero() { - return Some(cur.add(2 * V::BYTES).add(topos(mask))); - } - - let mask = eqd.movemask(); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(3 * V::BYTES).add(topos(mask))); - } - cur = cur.add(Self::LOOP_SIZE); - } - } - // Handle any leftovers after the aligned loop above. We use unaligned - // loads here, but I believe we are guaranteed that they are aligned - // since `cur` is aligned. - while cur <= end.sub(V::BYTES) { - debug_assert!(end.distance(cur) >= V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - cur = cur.add(V::BYTES); - } - // Finally handle any remaining bytes less than the size of V. In this - // case, our pointer may indeed be unaligned and the load may overlap - // with the previous one. But that's okay since we know the previous - // load didn't lead to a match (otherwise we wouldn't be here). - if cur < end { - debug_assert!(end.distance(cur) < V::BYTES); - cur = cur.sub(V::BYTES - end.distance(cur)); - debug_assert_eq!(end.distance(cur), V::BYTES); - return self.search_chunk(cur, topos); - } - None - } - - /// Return a pointer to the last occurrence of the needle in the given - /// haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn rfind_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::last_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - if let Some(cur) = self.search_chunk(end.sub(V::BYTES), topos) { - return Some(cur); - } - let mut cur = end.sub(end.as_usize() & V::ALIGN); - debug_assert!(start <= cur && cur <= end); - if len >= Self::LOOP_SIZE { - while cur >= start.add(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - cur = cur.sub(Self::LOOP_SIZE); - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(1 * V::BYTES)); - let c = V::load_aligned(cur.add(2 * V::BYTES)); - let d = V::load_aligned(cur.add(3 * V::BYTES)); - let eqa = self.v1.cmpeq(a); - let eqb = self.v1.cmpeq(b); - let eqc = self.v1.cmpeq(c); - let eqd = self.v1.cmpeq(d); - let or1 = eqa.or(eqb); - let or2 = eqc.or(eqd); - let or3 = or1.or(or2); - if or3.movemask_will_have_non_zero() { - let mask = eqd.movemask(); - if mask.has_non_zero() { - return Some(cur.add(3 * V::BYTES).add(topos(mask))); - } - - let mask = eqc.movemask(); - if mask.has_non_zero() { - return Some(cur.add(2 * V::BYTES).add(topos(mask))); - } - - let mask = eqb.movemask(); - if mask.has_non_zero() { - return Some(cur.add(1 * V::BYTES).add(topos(mask))); - } - - let mask = eqa.movemask(); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(topos(mask))); - } - } - } - while cur >= start.add(V::BYTES) { - debug_assert!(cur.distance(start) >= V::BYTES); - cur = cur.sub(V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - } - if cur > start { - debug_assert!(cur.distance(start) < V::BYTES); - return self.search_chunk(start, topos); - } - None - } - - /// Return a count of all matching bytes in the given haystack. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn count_raw( - &self, - start: *const u8, - end: *const u8, - ) -> usize { - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let confirm = |b| b == self.needle1(); - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - // Set `cur` to the first V-aligned pointer greater than `start`. - let mut cur = start.add(V::BYTES - (start.as_usize() & V::ALIGN)); - // Count any matching bytes before we start our aligned loop. - let mut count = count_byte_by_byte(start, cur, confirm); - debug_assert!(cur > start && end.sub(V::BYTES) >= start); - if len >= Self::LOOP_SIZE { - while cur <= end.sub(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(1 * V::BYTES)); - let c = V::load_aligned(cur.add(2 * V::BYTES)); - let d = V::load_aligned(cur.add(3 * V::BYTES)); - let eqa = self.v1.cmpeq(a); - let eqb = self.v1.cmpeq(b); - let eqc = self.v1.cmpeq(c); - let eqd = self.v1.cmpeq(d); - count += eqa.movemask().count_ones(); - count += eqb.movemask().count_ones(); - count += eqc.movemask().count_ones(); - count += eqd.movemask().count_ones(); - cur = cur.add(Self::LOOP_SIZE); - } - } - // Handle any leftovers after the aligned loop above. We use unaligned - // loads here, but I believe we are guaranteed that they are aligned - // since `cur` is aligned. - while cur <= end.sub(V::BYTES) { - debug_assert!(end.distance(cur) >= V::BYTES); - let chunk = V::load_unaligned(cur); - count += self.v1.cmpeq(chunk).movemask().count_ones(); - cur = cur.add(V::BYTES); - } - // And finally count any leftovers that weren't caught above. - count += count_byte_by_byte(cur, end, confirm); - count - } - - /// Search `V::BYTES` starting at `cur` via an unaligned load. - /// - /// `mask_to_offset` should be a function that converts a `movemask` to - /// an offset such that `cur.add(offset)` corresponds to a pointer to the - /// match location if one is found. Generally it is expected to use either - /// `mask_to_first_offset` or `mask_to_last_offset`, depending on whether - /// one is implementing a forward or reverse search, respectively. - /// - /// # Safety - /// - /// `cur` must be a valid pointer and it must be valid to do an unaligned - /// load of size `V::BYTES` at `cur`. - #[inline(always)] - unsafe fn search_chunk( - &self, - cur: *const u8, - mask_to_offset: impl Fn(V::Mask) -> usize, - ) -> Option<*const u8> { - let chunk = V::load_unaligned(cur); - let mask = self.v1.cmpeq(chunk).movemask(); - if mask.has_non_zero() { - Some(cur.add(mask_to_offset(mask))) - } else { - None - } - } -} - -/// 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)] -pub(crate) struct Two<V> { - s1: u8, - s2: u8, - v1: V, - v2: V, -} - -impl<V: Vector> Two<V> { - /// The number of bytes we examine per each iteration of our search loop. - const LOOP_SIZE: usize = 2 * V::BYTES; - - /// Create a new searcher that finds occurrences of the byte given. - #[inline(always)] - pub(crate) unsafe fn new(needle1: u8, needle2: u8) -> Two<V> { - Two { - s1: needle1, - s2: needle2, - v1: V::splat(needle1), - v2: V::splat(needle2), - } - } - - /// Returns the first needle given to `Two::new`. - #[inline(always)] - pub(crate) fn needle1(&self) -> u8 { - self.s1 - } - - /// Returns the second needle given to `Two::new`. - #[inline(always)] - pub(crate) fn needle2(&self) -> u8 { - self.s2 - } - - /// Return a pointer to the first occurrence of one of the needles in the - /// given haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn find_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::first_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - // Search a possibly unaligned chunk at `start`. This covers any part - // of the haystack prior to where aligned loads can start. - if let Some(cur) = self.search_chunk(start, topos) { - return Some(cur); - } - // Set `cur` to the first V-aligned pointer greater than `start`. - let mut cur = start.add(V::BYTES - (start.as_usize() & V::ALIGN)); - debug_assert!(cur > start && end.sub(V::BYTES) >= start); - if len >= Self::LOOP_SIZE { - while cur <= end.sub(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(V::BYTES)); - let eqa1 = self.v1.cmpeq(a); - let eqb1 = self.v1.cmpeq(b); - let eqa2 = self.v2.cmpeq(a); - let eqb2 = self.v2.cmpeq(b); - let or1 = eqa1.or(eqb1); - let or2 = eqa2.or(eqb2); - let or3 = or1.or(or2); - if or3.movemask_will_have_non_zero() { - let mask = eqa1.movemask().or(eqa2.movemask()); - if mask.has_non_zero() { - return Some(cur.add(topos(mask))); - } - - let mask = eqb1.movemask().or(eqb2.movemask()); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(V::BYTES).add(topos(mask))); - } - cur = cur.add(Self::LOOP_SIZE); - } - } - // Handle any leftovers after the aligned loop above. We use unaligned - // loads here, but I believe we are guaranteed that they are aligned - // since `cur` is aligned. - while cur <= end.sub(V::BYTES) { - debug_assert!(end.distance(cur) >= V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - cur = cur.add(V::BYTES); - } - // Finally handle any remaining bytes less than the size of V. In this - // case, our pointer may indeed be unaligned and the load may overlap - // with the previous one. But that's okay since we know the previous - // load didn't lead to a match (otherwise we wouldn't be here). - if cur < end { - debug_assert!(end.distance(cur) < V::BYTES); - cur = cur.sub(V::BYTES - end.distance(cur)); - debug_assert_eq!(end.distance(cur), V::BYTES); - return self.search_chunk(cur, topos); - } - None - } - - /// Return a pointer to the last occurrence of the needle in the given - /// haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn rfind_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::last_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - if let Some(cur) = self.search_chunk(end.sub(V::BYTES), topos) { - return Some(cur); - } - let mut cur = end.sub(end.as_usize() & V::ALIGN); - debug_assert!(start <= cur && cur <= end); - if len >= Self::LOOP_SIZE { - while cur >= start.add(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - cur = cur.sub(Self::LOOP_SIZE); - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(V::BYTES)); - let eqa1 = self.v1.cmpeq(a); - let eqb1 = self.v1.cmpeq(b); - let eqa2 = self.v2.cmpeq(a); - let eqb2 = self.v2.cmpeq(b); - let or1 = eqa1.or(eqb1); - let or2 = eqa2.or(eqb2); - let or3 = or1.or(or2); - if or3.movemask_will_have_non_zero() { - let mask = eqb1.movemask().or(eqb2.movemask()); - if mask.has_non_zero() { - return Some(cur.add(V::BYTES).add(topos(mask))); - } - - let mask = eqa1.movemask().or(eqa2.movemask()); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(topos(mask))); - } - } - } - while cur >= start.add(V::BYTES) { - debug_assert!(cur.distance(start) >= V::BYTES); - cur = cur.sub(V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - } - if cur > start { - debug_assert!(cur.distance(start) < V::BYTES); - return self.search_chunk(start, topos); - } - None - } - - /// Search `V::BYTES` starting at `cur` via an unaligned load. - /// - /// `mask_to_offset` should be a function that converts a `movemask` to - /// an offset such that `cur.add(offset)` corresponds to a pointer to the - /// match location if one is found. Generally it is expected to use either - /// `mask_to_first_offset` or `mask_to_last_offset`, depending on whether - /// one is implementing a forward or reverse search, respectively. - /// - /// # Safety - /// - /// `cur` must be a valid pointer and it must be valid to do an unaligned - /// load of size `V::BYTES` at `cur`. - #[inline(always)] - unsafe fn search_chunk( - &self, - cur: *const u8, - mask_to_offset: impl Fn(V::Mask) -> usize, - ) -> Option<*const u8> { - let chunk = V::load_unaligned(cur); - let eq1 = self.v1.cmpeq(chunk); - let eq2 = self.v2.cmpeq(chunk); - let mask = eq1.or(eq2).movemask(); - if mask.has_non_zero() { - let mask1 = eq1.movemask(); - let mask2 = eq2.movemask(); - Some(cur.add(mask_to_offset(mask1.or(mask2)))) - } else { - None - } - } -} - -/// 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)] -pub(crate) struct Three<V> { - s1: u8, - s2: u8, - s3: u8, - v1: V, - v2: V, - v3: V, -} - -impl<V: Vector> Three<V> { - /// The number of bytes we examine per each iteration of our search loop. - const LOOP_SIZE: usize = 2 * V::BYTES; - - /// Create a new searcher that finds occurrences of the byte given. - #[inline(always)] - pub(crate) unsafe fn new( - needle1: u8, - needle2: u8, - needle3: u8, - ) -> Three<V> { - Three { - s1: needle1, - s2: needle2, - s3: needle3, - v1: V::splat(needle1), - v2: V::splat(needle2), - v3: V::splat(needle3), - } - } - - /// Returns the first needle given to `Three::new`. - #[inline(always)] - pub(crate) fn needle1(&self) -> u8 { - self.s1 - } - - /// Returns the second needle given to `Three::new`. - #[inline(always)] - pub(crate) fn needle2(&self) -> u8 { - self.s2 - } - - /// Returns the third needle given to `Three::new`. - #[inline(always)] - pub(crate) fn needle3(&self) -> u8 { - self.s3 - } - - /// Return a pointer to the first occurrence of one of the needles in the - /// given haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn find_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::first_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - // Search a possibly unaligned chunk at `start`. This covers any part - // of the haystack prior to where aligned loads can start. - if let Some(cur) = self.search_chunk(start, topos) { - return Some(cur); - } - // Set `cur` to the first V-aligned pointer greater than `start`. - let mut cur = start.add(V::BYTES - (start.as_usize() & V::ALIGN)); - debug_assert!(cur > start && end.sub(V::BYTES) >= start); - if len >= Self::LOOP_SIZE { - while cur <= end.sub(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(V::BYTES)); - let eqa1 = self.v1.cmpeq(a); - let eqb1 = self.v1.cmpeq(b); - let eqa2 = self.v2.cmpeq(a); - let eqb2 = self.v2.cmpeq(b); - let eqa3 = self.v3.cmpeq(a); - let eqb3 = self.v3.cmpeq(b); - let or1 = eqa1.or(eqb1); - let or2 = eqa2.or(eqb2); - let or3 = eqa3.or(eqb3); - let or4 = or1.or(or2); - let or5 = or3.or(or4); - if or5.movemask_will_have_non_zero() { - let mask = eqa1 - .movemask() - .or(eqa2.movemask()) - .or(eqa3.movemask()); - if mask.has_non_zero() { - return Some(cur.add(topos(mask))); - } - - let mask = eqb1 - .movemask() - .or(eqb2.movemask()) - .or(eqb3.movemask()); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(V::BYTES).add(topos(mask))); - } - cur = cur.add(Self::LOOP_SIZE); - } - } - // Handle any leftovers after the aligned loop above. We use unaligned - // loads here, but I believe we are guaranteed that they are aligned - // since `cur` is aligned. - while cur <= end.sub(V::BYTES) { - debug_assert!(end.distance(cur) >= V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - cur = cur.add(V::BYTES); - } - // Finally handle any remaining bytes less than the size of V. In this - // case, our pointer may indeed be unaligned and the load may overlap - // with the previous one. But that's okay since we know the previous - // load didn't lead to a match (otherwise we wouldn't be here). - if cur < end { - debug_assert!(end.distance(cur) < V::BYTES); - cur = cur.sub(V::BYTES - end.distance(cur)); - debug_assert_eq!(end.distance(cur), V::BYTES); - return self.search_chunk(cur, topos); - } - None - } - - /// Return a pointer to the last occurrence of the needle in the given - /// haystack. If no such occurrence exists, then `None` is returned. - /// - /// When a match is found, the pointer returned is guaranteed to be - /// `>= start` and `< end`. - /// - /// # Safety - /// - /// * It must be the case that `start < end` and that the distance between - /// them is at least equal to `V::BYTES`. That is, it must always be valid - /// to do at least an unaligned load of `V` at `start`. - /// * 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. - #[inline(always)] - pub(crate) unsafe fn rfind_raw( - &self, - start: *const u8, - end: *const u8, - ) -> Option<*const u8> { - // If we want to support vectors bigger than 256 bits, we probably - // need to move up to using a u64 for the masks used below. Currently - // they are 32 bits, which means we're SOL for vectors that need masks - // bigger than 32 bits. Overall unclear until there's a use case. - debug_assert!(V::BYTES <= 32, "vector cannot be bigger than 32 bytes"); - - let topos = V::Mask::last_offset; - let len = end.distance(start); - debug_assert!( - len >= V::BYTES, - "haystack has length {}, but must be at least {}", - len, - V::BYTES - ); - - if let Some(cur) = self.search_chunk(end.sub(V::BYTES), topos) { - return Some(cur); - } - let mut cur = end.sub(end.as_usize() & V::ALIGN); - debug_assert!(start <= cur && cur <= end); - if len >= Self::LOOP_SIZE { - while cur >= start.add(Self::LOOP_SIZE) { - debug_assert_eq!(0, cur.as_usize() % V::BYTES); - - cur = cur.sub(Self::LOOP_SIZE); - let a = V::load_aligned(cur); - let b = V::load_aligned(cur.add(V::BYTES)); - let eqa1 = self.v1.cmpeq(a); - let eqb1 = self.v1.cmpeq(b); - let eqa2 = self.v2.cmpeq(a); - let eqb2 = self.v2.cmpeq(b); - let eqa3 = self.v3.cmpeq(a); - let eqb3 = self.v3.cmpeq(b); - let or1 = eqa1.or(eqb1); - let or2 = eqa2.or(eqb2); - let or3 = eqa3.or(eqb3); - let or4 = or1.or(or2); - let or5 = or3.or(or4); - if or5.movemask_will_have_non_zero() { - let mask = eqb1 - .movemask() - .or(eqb2.movemask()) - .or(eqb3.movemask()); - if mask.has_non_zero() { - return Some(cur.add(V::BYTES).add(topos(mask))); - } - - let mask = eqa1 - .movemask() - .or(eqa2.movemask()) - .or(eqa3.movemask()); - debug_assert!(mask.has_non_zero()); - return Some(cur.add(topos(mask))); - } - } - } - while cur >= start.add(V::BYTES) { - debug_assert!(cur.distance(start) >= V::BYTES); - cur = cur.sub(V::BYTES); - if let Some(cur) = self.search_chunk(cur, topos) { - return Some(cur); - } - } - if cur > start { - debug_assert!(cur.distance(start) < V::BYTES); - return self.search_chunk(start, topos); - } - None - } - - /// Search `V::BYTES` starting at `cur` via an unaligned load. - /// - /// `mask_to_offset` should be a function that converts a `movemask` to - /// an offset such that `cur.add(offset)` corresponds to a pointer to the - /// match location if one is found. Generally it is expected to use either - /// `mask_to_first_offset` or `mask_to_last_offset`, depending on whether - /// one is implementing a forward or reverse search, respectively. - /// - /// # Safety - /// - /// `cur` must be a valid pointer and it must be valid to do an unaligned - /// load of size `V::BYTES` at `cur`. - #[inline(always)] - unsafe fn search_chunk( - &self, - cur: *const u8, - mask_to_offset: impl Fn(V::Mask) -> usize, - ) -> Option<*const u8> { - let chunk = V::load_unaligned(cur); - let eq1 = self.v1.cmpeq(chunk); - let eq2 = self.v2.cmpeq(chunk); - let eq3 = self.v3.cmpeq(chunk); - let mask = eq1.or(eq2).or(eq3).movemask(); - if mask.has_non_zero() { - let mask1 = eq1.movemask(); - let mask2 = eq2.movemask(); - let mask3 = eq3.movemask(); - Some(cur.add(mask_to_offset(mask1.or(mask2).or(mask3)))) - } else { - None - } - } -} - -/// An iterator over all occurrences of a set of bytes in a haystack. -/// -/// This iterator implements the routines necessary to provide a -/// `DoubleEndedIterator` impl, which means it can also be used to find -/// occurrences in reverse order. -/// -/// The lifetime parameters are as follows: -/// -/// * `'h` refers to the lifetime of the haystack being searched. -/// -/// This type is intended to be used to implement all iterators for the -/// `memchr` family of functions. It handles a tiny bit of marginally tricky -/// raw pointer math, but otherwise expects the caller to provide `find_raw` -/// and `rfind_raw` routines for each call of `next` and `next_back`, -/// respectively. -#[derive(Clone, Debug)] -pub(crate) struct Iter<'h> { - /// The original starting point into the haystack. We use this to convert - /// pointers to offsets. - original_start: *const u8, - /// The current starting point into the haystack. That is, where the next - /// search will begin. - start: *const u8, - /// The current ending point into the haystack. That is, where the next - /// reverse search will begin. - end: *const u8, - /// A marker for tracking the lifetime of the start/cur_start/cur_end - /// pointers above, which all point into the haystack. - haystack: core::marker::PhantomData<&'h [u8]>, -} - -// SAFETY: Iter contains no shared references to anything that performs any -// interior mutations. Also, the lifetime guarantees that Iter will not outlive -// the haystack. -unsafe impl<'h> Send for Iter<'h> {} - -// SAFETY: Iter perform no interior mutations, therefore no explicit -// synchronization is necessary. Also, the lifetime guarantees that Iter will -// not outlive the haystack. -unsafe impl<'h> Sync for Iter<'h> {} - -impl<'h> Iter<'h> { - /// Create a new generic memchr iterator. - #[inline(always)] - pub(crate) fn new(haystack: &'h [u8]) -> Iter<'h> { - Iter { - original_start: haystack.as_ptr(), - start: haystack.as_ptr(), - end: haystack.as_ptr().wrapping_add(haystack.len()), - haystack: core::marker::PhantomData, - } - } - - /// Returns the next occurrence in the forward direction. - /// - /// # Safety - /// - /// Callers must ensure that if a pointer is returned from the closure - /// provided, then it must be greater than or equal to the start pointer - /// and less than the end pointer. - #[inline(always)] - pub(crate) unsafe fn next( - &mut self, - mut find_raw: impl FnMut(*const u8, *const u8) -> Option<*const u8>, - ) -> Option<usize> { - // SAFETY: Pointers are derived directly from the same &[u8] haystack. - // We only ever modify start/end corresponding to a matching offset - // found between start and end. Thus all changes to start/end maintain - // our safety requirements. - // - // The only other assumption we rely on is that the pointer returned - // by `find_raw` satisfies `self.start <= found < self.end`, and that - // safety contract is forwarded to the caller. - let found = find_raw(self.start, self.end)?; - let result = found.distance(self.original_start); - self.start = found.add(1); - Some(result) - } - - /// Returns the number of remaining elements in this iterator. - #[inline(always)] - pub(crate) fn count( - self, - mut count_raw: impl FnMut(*const u8, *const u8) -> usize, - ) -> usize { - // SAFETY: Pointers are derived directly from the same &[u8] haystack. - // We only ever modify start/end corresponding to a matching offset - // found between start and end. Thus all changes to start/end maintain - // our safety requirements. - count_raw(self.start, self.end) - } - - /// Returns the next occurrence in reverse. - /// - /// # Safety - /// - /// Callers must ensure that if a pointer is returned from the closure - /// provided, then it must be greater than or equal to the start pointer - /// and less than the end pointer. - #[inline(always)] - pub(crate) unsafe fn next_back( - &mut self, - mut rfind_raw: impl FnMut(*const u8, *const u8) -> Option<*const u8>, - ) -> Option<usize> { - // SAFETY: Pointers are derived directly from the same &[u8] haystack. - // We only ever modify start/end corresponding to a matching offset - // found between start and end. Thus all changes to start/end maintain - // our safety requirements. - // - // The only other assumption we rely on is that the pointer returned - // by `rfind_raw` satisfies `self.start <= found < self.end`, and that - // safety contract is forwarded to the caller. - let found = rfind_raw(self.start, self.end)?; - let result = found.distance(self.original_start); - self.end = found; - Some(result) - } - - /// Provides an implementation of `Iterator::size_hint`. - #[inline(always)] - pub(crate) fn size_hint(&self) -> (usize, Option<usize>) { - (0, Some(self.end.as_usize().saturating_sub(self.start.as_usize()))) - } -} - -/// Search a slice using a function that operates on raw pointers. -/// -/// Given a function to search a contiguous sequence of memory for the location -/// of a non-empty set of bytes, this will execute that search on a slice of -/// bytes. The pointer returned by the given function will be converted to an -/// offset relative to the starting point of the given slice. That is, if a -/// match is found, the offset returned by this routine is guaranteed to be a -/// valid index into `haystack`. -/// -/// Callers may use this for a forward or reverse search. -/// -/// # Safety -/// -/// Callers must ensure that if a pointer is returned by `find_raw`, then the -/// pointer must be greater than or equal to the starting pointer and less than -/// the end pointer. -#[inline(always)] -pub(crate) unsafe fn search_slice_with_raw( - haystack: &[u8], - mut find_raw: impl FnMut(*const u8, *const u8) -> Option<*const u8>, -) -> Option<usize> { - // SAFETY: We rely on `find_raw` to return a correct and valid pointer, but - // otherwise, `start` and `end` are valid due to the guarantees provided by - // a &[u8]. - let start = haystack.as_ptr(); - let end = start.add(haystack.len()); - let found = find_raw(start, end)?; - Some(found.distance(start)) -} - -/// Performs a forward byte-at-a-time loop until either `ptr >= end_ptr` or -/// until `confirm(*ptr)` returns `true`. If the former occurs, then `None` is -/// returned. If the latter occurs, then the pointer at which `confirm` returns -/// `true` is returned. -/// -/// # Safety -/// -/// Callers must provide valid pointers and they must satisfy `start_ptr <= -/// ptr` and `ptr <= end_ptr`. -#[inline(always)] -pub(crate) unsafe fn fwd_byte_by_byte<F: Fn(u8) -> bool>( - start: *const u8, - end: *const u8, - confirm: F, -) -> Option<*const u8> { - debug_assert!(start <= end); - let mut ptr = start; - while ptr < end { - if confirm(*ptr) { - return Some(ptr); - } - ptr = ptr.offset(1); - } - None -} - -/// Performs a reverse byte-at-a-time loop until either `ptr < start_ptr` or -/// until `confirm(*ptr)` returns `true`. If the former occurs, then `None` is -/// returned. If the latter occurs, then the pointer at which `confirm` returns -/// `true` is returned. -/// -/// # Safety -/// -/// Callers must provide valid pointers and they must satisfy `start_ptr <= -/// ptr` and `ptr <= end_ptr`. -#[inline(always)] -pub(crate) unsafe fn rev_byte_by_byte<F: Fn(u8) -> bool>( - start: *const u8, - end: *const u8, - confirm: F, -) -> Option<*const u8> { - debug_assert!(start <= end); - - let mut ptr = end; - while ptr > start { - ptr = ptr.offset(-1); - if confirm(*ptr) { - return Some(ptr); - } - } - None -} - -/// Performs a forward byte-at-a-time loop until `ptr >= end_ptr` and returns -/// the number of times `confirm(*ptr)` returns `true`. -/// -/// # Safety -/// -/// Callers must provide valid pointers and they must satisfy `start_ptr <= -/// ptr` and `ptr <= end_ptr`. -#[inline(always)] -pub(crate) unsafe fn count_byte_by_byte<F: Fn(u8) -> bool>( - start: *const u8, - end: *const u8, - confirm: F, -) -> usize { - debug_assert!(start <= end); - let mut ptr = start; - let mut count = 0; - while ptr < end { - if confirm(*ptr) { - count += 1; - } - ptr = ptr.offset(1); - } - count -} |