1#[inline]
20pub fn find_crlf(buf: &[u8], start: usize) -> Option<usize> {
21 #[cfg(target_arch = "x86_64")]
22 {
23 if has_avx2() {
24 return unsafe { find_crlf_avx2(buf, start) };
28 }
29 }
30 #[cfg(target_arch = "aarch64")]
31 {
32 return unsafe { find_crlf_neon(buf, start) };
36 }
37 #[allow(unreachable_code)]
38 find_crlf_swar(buf, start)
39}
40
41#[cfg(target_arch = "x86_64")]
42fn has_avx2() -> bool {
43 use core::sync::atomic::{AtomicI8, Ordering};
44 static CACHED: AtomicI8 = AtomicI8::new(-1);
45 let c = CACHED.load(Ordering::Relaxed);
46 if c >= 0 {
47 return c == 1;
48 }
49 let detected = std::is_x86_feature_detected!("avx2");
50 CACHED.store(i8::from(detected), Ordering::Relaxed);
51 detected
52}
53
54#[cfg(target_arch = "x86_64")]
55#[target_feature(enable = "avx2")]
56unsafe fn find_crlf_avx2(buf: &[u8], start: usize) -> Option<usize> {
57 use core::arch::x86_64::{
58 __m256i, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, _mm256_set1_epi8,
59 };
60
61 let n = buf.len();
62 if start + 1 >= n {
63 return None;
64 }
65 let mut i = start;
66 let cr = _mm256_set1_epi8(0x0D);
68 while i + 32 < n {
72 let chunk =
75 unsafe { _mm256_loadu_si256(buf.as_ptr().add(i) as *const __m256i) };
76 let mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, cr)) as u32;
78 if mask != 0 {
79 let bit = mask.trailing_zeros() as usize;
80 let pos = i + bit;
81 if buf[pos + 1] == b'\n' {
83 return Some(pos);
84 }
85 i = pos + 1;
87 continue;
88 }
89 i += 32;
90 }
91 find_crlf_swar(buf, i)
93}
94
95#[cfg(target_arch = "aarch64")]
96#[target_feature(enable = "neon")]
97unsafe fn find_crlf_neon(buf: &[u8], start: usize) -> Option<usize> {
98 use core::arch::aarch64::{vceqq_u8, vdupq_n_u8, vld1q_u8, vmaxvq_u8};
99
100 let n = buf.len();
101 if start + 1 >= n {
102 return None;
103 }
104 let mut i = start;
105 let cr = vdupq_n_u8(0x0D);
107 while i + 16 < n {
110 let chunk = unsafe { vld1q_u8(buf.as_ptr().add(i)) };
112 let eq = vceqq_u8(chunk, cr);
114 let any = vmaxvq_u8(eq);
115 if any != 0 {
116 for j in 0..16 {
121 if buf[i + j] == b'\r' {
122 let pos = i + j;
123 if buf[pos + 1] == b'\n' {
125 return Some(pos);
126 }
127 return find_crlf_swar(buf, pos + 1);
131 }
132 }
133 }
134 i += 16;
135 }
136 find_crlf_swar(buf, i)
137}
138
139pub(crate) fn find_crlf_swar(buf: &[u8], start: usize) -> Option<usize> {
142 const CR_BCAST: u64 = 0x0D0D_0D0D_0D0D_0D0D_u64;
143 const ONES: u64 = 0x0101_0101_0101_0101_u64;
144 const HIGH: u64 = 0x8080_8080_8080_8080_u64;
145
146 let n = buf.len();
147 let mut i = start;
148 if i + 1 >= n {
149 return None;
150 }
151 while i + 8 < n {
152 let word = u64::from_le_bytes(buf[i..i + 8].try_into().expect("8 bytes"));
153 let x = word ^ CR_BCAST;
154 let zeroed = x.wrapping_sub(ONES) & !x & HIGH;
155 if zeroed != 0 {
156 let bit_idx = zeroed.trailing_zeros();
157 let pos = i + (bit_idx / 8) as usize;
158 if buf[pos + 1] == b'\n' {
159 return Some(pos);
160 }
161 i = pos + 1;
162 continue;
163 }
164 i += 8;
165 }
166 while i + 1 < n {
167 if buf[i] == b'\r' && buf[i + 1] == b'\n' {
168 return Some(i);
169 }
170 i += 1;
171 }
172 None
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 fn assert_matches_swar(buf: &[u8]) {
182 for start in 0..=buf.len() {
183 assert_eq!(
184 find_crlf(buf, start),
185 find_crlf_swar(buf, start),
186 "mismatch at buf={buf:?} start={start}",
187 );
188 }
189 }
190
191 #[test]
192 fn empty_and_short_buffers() {
193 for buf in &[b"" as &[u8], b"a", b"\r", b"\n", b"ab"] {
194 assert_matches_swar(buf);
195 }
196 }
197
198 #[test]
199 fn crlf_at_every_offset() {
200 for off in 0..=80 {
202 let mut buf = vec![b'X'; off + 2];
203 buf[off] = b'\r';
204 buf[off + 1] = b'\n';
205 assert_eq!(find_crlf(&buf, 0), Some(off), "off={off}");
206 }
207 }
208
209 #[test]
210 fn lone_cr_does_not_terminate() {
211 let mut buf = vec![b'X'; 50];
213 buf[10] = b'\r';
214 assert_eq!(find_crlf(&buf, 0), None);
216 buf[30] = b'\r';
218 buf[31] = b'\n';
219 assert_eq!(find_crlf(&buf, 0), Some(30));
220 }
221
222 #[test]
223 fn multiple_crs_in_a_row() {
224 let buf = b"X\r\r\r\nY";
225 assert_eq!(find_crlf(buf, 0), Some(3));
226 }
227
228 #[test]
229 fn start_past_crlf_finds_next() {
230 let buf = b"AAA\r\nBBB\r\nCCC";
231 assert_eq!(find_crlf(buf, 0), Some(3));
232 assert_eq!(find_crlf(buf, 4), Some(8));
233 assert_eq!(find_crlf(buf, 9), None);
234 }
235
236 #[test]
237 fn cross_tier_oracle_random_shapes() {
238 let shapes: &[&[u8]] = &[
241 b"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
242 b"*1\r\n$4\r\nPING\r\n",
243 b"PING\r\n",
244 b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n", b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n", b"\r\nXX\r\nXXXXXX\r\n", b"XXXXXXXXXXXXXXXX\rXXXXXXXXXXXXXXXX\r\n", b"only-text-no-newline-at-all-just-bytes-here-XXXX",
249 ];
250 for buf in shapes {
251 assert_matches_swar(buf);
252 }
253 }
254
255 #[test]
256 fn returns_none_when_only_one_byte_after_start() {
257 let buf = b"AAAAA";
258 assert_eq!(find_crlf(buf, 4), None);
259 assert_eq!(find_crlf(buf, 5), None);
260 }
261}