/rust/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.4.1/src/memmem/rarebytes.rs
Line | Count | Source |
1 | | /// A heuristic frequency based detection of rare bytes for substring search. |
2 | | /// |
3 | | /// This detector attempts to pick out two bytes in a needle that are predicted |
4 | | /// to occur least frequently. The purpose is to use these bytes to implement |
5 | | /// fast candidate search using vectorized code. |
6 | | /// |
7 | | /// A set of offsets is only computed for needles of length 2 or greater. |
8 | | /// Smaller needles should be special cased by the substring search algorithm |
9 | | /// in use. (e.g., Use memchr for single byte needles.) |
10 | | /// |
11 | | /// Note that we use `u8` to represent the offsets of the rare bytes in a |
12 | | /// needle to reduce space usage. This means that rare byte occurring after the |
13 | | /// first 255 bytes in a needle will never be used. |
14 | | #[derive(Clone, Copy, Debug, Default)] |
15 | | pub(crate) struct RareNeedleBytes { |
16 | | /// The leftmost offset of the rarest byte in the needle, according to |
17 | | /// pre-computed frequency analysis. The "leftmost offset" means that |
18 | | /// rare1i <= i for all i where needle[i] == needle[rare1i]. |
19 | | rare1i: u8, |
20 | | /// The leftmost offset of the second rarest byte in the needle, according |
21 | | /// to pre-computed frequency analysis. The "leftmost offset" means that |
22 | | /// rare2i <= i for all i where needle[i] == needle[rare2i]. |
23 | | /// |
24 | | /// The second rarest byte is used as a type of guard for quickly detecting |
25 | | /// a mismatch if the first byte matches. This is a hedge against |
26 | | /// pathological cases where the pre-computed frequency analysis may be |
27 | | /// off. (But of course, does not prevent *all* pathological cases.) |
28 | | /// |
29 | | /// In general, rare1i != rare2i by construction, although there is no hard |
30 | | /// requirement that they be different. However, since the case of a single |
31 | | /// byte needle is handled specially by memchr itself, rare2i generally |
32 | | /// always should be different from rare1i since it would otherwise be |
33 | | /// ineffective as a guard. |
34 | | rare2i: u8, |
35 | | } |
36 | | |
37 | | impl RareNeedleBytes { |
38 | | /// Create a new pair of rare needle bytes with the given offsets. This is |
39 | | /// only used in tests for generating input data. |
40 | | #[cfg(all(test, feature = "std"))] |
41 | | pub(crate) fn new(rare1i: u8, rare2i: u8) -> RareNeedleBytes { |
42 | | RareNeedleBytes { rare1i, rare2i } |
43 | | } |
44 | | |
45 | | /// Detect the leftmost offsets of the two rarest bytes in the given |
46 | | /// needle. |
47 | 60 | pub(crate) fn forward(needle: &[u8]) -> RareNeedleBytes { |
48 | 60 | if needle.len() <= 1 || needle.len() > core::u8::MAX as usize { |
49 | | // For needles bigger than u8::MAX, our offsets aren't big enough. |
50 | | // (We make our offsets small to reduce stack copying.) |
51 | | // If you have a use case for it, please file an issue. In that |
52 | | // case, we should probably just adjust the routine below to pick |
53 | | // some rare bytes from the first 255 bytes of the needle. |
54 | | // |
55 | | // Also note that for needles of size 0 or 1, they are special |
56 | | // cased in Two-Way. |
57 | | // |
58 | | // TODO: Benchmar this. |
59 | 60 | return RareNeedleBytes { rare1i: 0, rare2i: 0 }; |
60 | 0 | } |
61 | | |
62 | | // Find the rarest two bytes. We make them distinct by construction. |
63 | 0 | let (mut rare1, mut rare1i) = (needle[0], 0); |
64 | 0 | let (mut rare2, mut rare2i) = (needle[1], 1); |
65 | 0 | if rank(rare2) < rank(rare1) { |
66 | 0 | core::mem::swap(&mut rare1, &mut rare2); |
67 | 0 | core::mem::swap(&mut rare1i, &mut rare2i); |
68 | 0 | } |
69 | 0 | for (i, &b) in needle.iter().enumerate().skip(2) { |
70 | 0 | if rank(b) < rank(rare1) { |
71 | 0 | rare2 = rare1; |
72 | 0 | rare2i = rare1i; |
73 | 0 | rare1 = b; |
74 | 0 | rare1i = i as u8; |
75 | 0 | } else if b != rare1 && rank(b) < rank(rare2) { |
76 | 0 | rare2 = b; |
77 | 0 | rare2i = i as u8; |
78 | 0 | } |
79 | | } |
80 | | // While not strictly required, we really don't want these to be |
81 | | // equivalent. If they were, it would reduce the effectiveness of |
82 | | // candidate searching using these rare bytes by increasing the rate of |
83 | | // false positives. |
84 | 0 | assert_ne!(rare1i, rare2i); |
85 | 0 | RareNeedleBytes { rare1i, rare2i } |
86 | 60 | } |
87 | | |
88 | | /// Return the rare bytes in the given needle in the forward direction. |
89 | | /// The needle given must be the same one given to the RareNeedleBytes |
90 | | /// constructor. |
91 | 0 | pub(crate) fn as_rare_bytes(&self, needle: &[u8]) -> (u8, u8) { |
92 | 0 | (needle[self.rare1i as usize], needle[self.rare2i as usize]) |
93 | 0 | } |
94 | | |
95 | | /// Return the rare offsets such that the first offset is always <= to the |
96 | | /// second offset. This is useful when the caller doesn't care whether |
97 | | /// rare1 is rarer than rare2, but just wants to ensure that they are |
98 | | /// ordered with respect to one another. |
99 | | #[cfg(memchr_runtime_simd)] |
100 | 0 | pub(crate) fn as_rare_ordered_usize(&self) -> (usize, usize) { |
101 | 0 | let (rare1i, rare2i) = self.as_rare_ordered_u8(); |
102 | 0 | (rare1i as usize, rare2i as usize) |
103 | 0 | } |
104 | | |
105 | | /// Like as_rare_ordered_usize, but returns the offsets as their native |
106 | | /// u8 values. |
107 | | #[cfg(memchr_runtime_simd)] |
108 | 0 | pub(crate) fn as_rare_ordered_u8(&self) -> (u8, u8) { |
109 | 0 | if self.rare1i <= self.rare2i { |
110 | 0 | (self.rare1i, self.rare2i) |
111 | | } else { |
112 | 0 | (self.rare2i, self.rare1i) |
113 | | } |
114 | 0 | } |
115 | | |
116 | | /// Return the rare offsets as usize values in the order in which they were |
117 | | /// constructed. rare1, for example, is constructed as the "rarer" byte, |
118 | | /// and thus, callers may want to treat it differently from rare2. |
119 | 0 | pub(crate) fn as_rare_usize(&self) -> (usize, usize) { |
120 | 0 | (self.rare1i as usize, self.rare2i as usize) |
121 | 0 | } |
122 | | |
123 | | /// Return the byte frequency rank of each byte. The higher the rank, the |
124 | | /// more frequency the byte is predicted to be. The needle given must be |
125 | | /// the same one given to the RareNeedleBytes constructor. |
126 | 0 | pub(crate) fn as_ranks(&self, needle: &[u8]) -> (usize, usize) { |
127 | 0 | let (b1, b2) = self.as_rare_bytes(needle); |
128 | 0 | (rank(b1), rank(b2)) |
129 | 0 | } |
130 | | } |
131 | | |
132 | | /// Return the heuristical frequency rank of the given byte. A lower rank |
133 | | /// means the byte is believed to occur less frequently. |
134 | 0 | fn rank(b: u8) -> usize { |
135 | 0 | crate::memmem::byte_frequencies::BYTE_FREQUENCIES[b as usize] as usize |
136 | 0 | } |