1#[cfg(target_arch = "x86_64")]
8use std::arch::x86_64::{
9 _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm256_cmpeq_epi8, _mm256_loadu_si256,
10 _mm256_movemask_epi8,
11};
12
13#[cfg(target_arch = "aarch64")]
14use std::arch::aarch64::{uint8x16_t, vceqq_u8, vld1q_u8, vminvq_u8};
15
16use std::sync::OnceLock;
17
18#[cfg(target_arch = "x86_64")]
20static HAS_SSE2: OnceLock<bool> = OnceLock::new();
21
22#[cfg(target_arch = "x86_64")]
23static HAS_AVX2: OnceLock<bool> = OnceLock::new();
24
25#[cfg(target_arch = "x86_64")]
26#[inline]
27fn has_sse2() -> bool {
28 *HAS_SSE2.get_or_init(|| is_x86_feature_detected!("sse2"))
29}
30
31#[cfg(target_arch = "x86_64")]
32#[inline]
33fn has_avx2() -> bool {
34 *HAS_AVX2.get_or_init(|| is_x86_feature_detected!("avx2"))
35}
36
37#[inline]
42#[must_use]
43pub fn simd_bytes_equal(a: &[u8], b: &[u8]) -> bool {
44 if a.len() != b.len() {
45 return false;
46 }
47
48 if a.is_empty() {
49 return true;
50 }
51
52 #[cfg(target_arch = "x86_64")]
53 {
54 if a.len() >= 32 && has_avx2() {
55 return unsafe { simd_bytes_equal_avx2(a, b) };
56 }
57 if a.len() >= 16 && has_sse2() {
58 return unsafe { simd_bytes_equal_sse2(a, b) };
59 }
60 }
61
62 #[cfg(target_arch = "aarch64")]
63 {
64 if a.len() >= 16 {
65 return unsafe { simd_bytes_equal_neon(a, b) };
66 }
67 }
68
69 a == b
71}
72
73#[inline]
79#[must_use]
80pub fn simd_find_first_difference(a: &[u8], b: &[u8]) -> Option<usize> {
81 let min_len = a.len().min(b.len());
82
83 if min_len == 0 {
84 return if a.len() == b.len() { None } else { Some(0) };
85 }
86
87 #[cfg(target_arch = "x86_64")]
88 {
89 if min_len >= 32 && has_avx2() {
90 let diff = unsafe { find_first_difference_avx2(a, b, min_len) };
91 if let Some(pos) = diff {
92 return Some(pos);
93 }
94 return if a.len() == b.len() {
96 None
97 } else {
98 Some(min_len)
99 };
100 }
101 if min_len >= 16 && has_sse2() {
102 let diff = unsafe { find_first_difference_sse2(a, b, min_len) };
103 if let Some(pos) = diff {
104 return Some(pos);
105 }
106 return if a.len() == b.len() {
107 None
108 } else {
109 Some(min_len)
110 };
111 }
112 }
113
114 #[cfg(target_arch = "aarch64")]
115 {
116 if min_len >= 16 {
117 let diff = unsafe { find_first_difference_neon(a, b, min_len) };
118 if let Some(pos) = diff {
119 return Some(pos);
120 }
121 return if a.len() == b.len() {
122 None
123 } else {
124 Some(min_len)
125 };
126 }
127 }
128
129 for (i, (&byte_a, &byte_b)) in a.iter().zip(b.iter()).enumerate() {
131 if byte_a != byte_b {
132 return Some(i);
133 }
134 }
135
136 if a.len() == b.len() {
137 None
138 } else {
139 Some(min_len)
140 }
141}
142
143#[cfg(target_arch = "x86_64")]
145#[target_feature(enable = "sse2")]
146unsafe fn simd_bytes_equal_sse2(a: &[u8], b: &[u8]) -> bool {
147 unsafe {
148 let len = a.len();
149 let mut i = 0;
150
151 while i + 16 <= len {
153 let chunk_a = _mm_loadu_si128(a[i..].as_ptr().cast());
154 let chunk_b = _mm_loadu_si128(b[i..].as_ptr().cast());
155 let cmp = _mm_cmpeq_epi8(chunk_a, chunk_b);
156 let mask = _mm_movemask_epi8(cmp);
157
158 if mask != 0xFFFF {
160 return false;
161 }
162
163 i += 16;
164 }
165
166 a[i..] == b[i..]
168 }
169}
170
171#[cfg(target_arch = "x86_64")]
173#[target_feature(enable = "avx2")]
174unsafe fn simd_bytes_equal_avx2(a: &[u8], b: &[u8]) -> bool {
175 unsafe {
176 let len = a.len();
177 let mut i = 0;
178
179 while i + 32 <= len {
181 let chunk_a = _mm256_loadu_si256(a[i..].as_ptr().cast());
182 let chunk_b = _mm256_loadu_si256(b[i..].as_ptr().cast());
183 let cmp = _mm256_cmpeq_epi8(chunk_a, chunk_b);
184 let mask = _mm256_movemask_epi8(cmp);
185
186 if mask != -1 {
188 return false;
189 }
190
191 i += 32;
192 }
193
194 if i + 16 <= len && has_sse2() {
196 let chunk_a = _mm_loadu_si128(a[i..].as_ptr().cast());
197 let chunk_b = _mm_loadu_si128(b[i..].as_ptr().cast());
198 let cmp = _mm_cmpeq_epi8(chunk_a, chunk_b);
199 let mask = _mm_movemask_epi8(cmp);
200
201 if mask != 0xFFFF {
202 return false;
203 }
204 i += 16;
205 }
206
207 a[i..] == b[i..]
209 }
210}
211
212#[cfg(target_arch = "aarch64")]
214unsafe fn simd_bytes_equal_neon(a: &[u8], b: &[u8]) -> bool {
215 unsafe {
216 let len = a.len();
217 let mut i = 0;
218
219 while i + 16 <= len {
221 let chunk_a = vld1q_u8(a[i..].as_ptr());
222 let chunk_b = vld1q_u8(b[i..].as_ptr());
223 let cmp = vceqq_u8(chunk_a, chunk_b);
224
225 let min_val = vminvq_u8(cmp);
227 if min_val != 0xFF {
228 return false;
229 }
230
231 i += 16;
232 }
233
234 a[i..] == b[i..]
236 }
237}
238
239#[cfg(target_arch = "x86_64")]
241#[target_feature(enable = "sse2")]
242unsafe fn find_first_difference_sse2(a: &[u8], b: &[u8], min_len: usize) -> Option<usize> {
243 unsafe {
244 let mut i = 0;
245
246 while i + 16 <= min_len {
247 let chunk_a = _mm_loadu_si128(a[i..].as_ptr().cast());
248 let chunk_b = _mm_loadu_si128(b[i..].as_ptr().cast());
249 let cmp = _mm_cmpeq_epi8(chunk_a, chunk_b);
250 let mask = _mm_movemask_epi8(cmp);
251
252 if mask != 0xFFFF {
253 let diff_mask = !mask & 0xFFFF;
255 let offset = diff_mask.trailing_zeros() as usize;
256 return Some(i + offset);
257 }
258
259 i += 16;
260 }
261
262 for j in i..min_len {
264 if a[j] != b[j] {
265 return Some(j);
266 }
267 }
268
269 None
270 }
271}
272
273#[cfg(target_arch = "x86_64")]
275#[target_feature(enable = "avx2")]
276unsafe fn find_first_difference_avx2(a: &[u8], b: &[u8], min_len: usize) -> Option<usize> {
277 unsafe {
278 let mut i = 0;
279
280 while i + 32 <= min_len {
281 let chunk_a = _mm256_loadu_si256(a[i..].as_ptr().cast());
282 let chunk_b = _mm256_loadu_si256(b[i..].as_ptr().cast());
283 let cmp = _mm256_cmpeq_epi8(chunk_a, chunk_b);
284 let mask = _mm256_movemask_epi8(cmp);
285
286 if mask != -1 {
287 let diff_mask = (!mask).cast_unsigned();
289 let offset = diff_mask.trailing_zeros() as usize;
290 return Some(i + offset);
291 }
292
293 i += 32;
294 }
295
296 if i + 16 <= min_len && has_sse2() {
298 let chunk_a = _mm_loadu_si128(a[i..].as_ptr().cast());
299 let chunk_b = _mm_loadu_si128(b[i..].as_ptr().cast());
300 let cmp = _mm_cmpeq_epi8(chunk_a, chunk_b);
301 let mask = _mm_movemask_epi8(cmp);
302
303 if mask != 0xFFFF {
304 let diff_mask = !mask & 0xFFFF;
305 let offset = diff_mask.trailing_zeros() as usize;
306 return Some(i + offset);
307 }
308 i += 16;
309 }
310
311 for j in i..min_len {
313 if a[j] != b[j] {
314 return Some(j);
315 }
316 }
317
318 None
319 }
320}
321
322#[cfg(target_arch = "aarch64")]
324unsafe fn find_first_difference_neon(a: &[u8], b: &[u8], min_len: usize) -> Option<usize> {
325 unsafe {
326 let mut i = 0;
327
328 while i + 16 <= min_len {
329 let chunk_a = vld1q_u8(a[i..].as_ptr());
330 let chunk_b = vld1q_u8(b[i..].as_ptr());
331 let cmp = vceqq_u8(chunk_a, chunk_b);
332
333 let min_val = vminvq_u8(cmp);
335 if min_val != 0xFF {
336 let arr: [u8; 16] = std::mem::transmute(cmp);
338 for (offset, &val) in arr.iter().enumerate() {
339 if val != 0xFF {
340 return Some(i + offset);
341 }
342 }
343 }
344
345 i += 16;
346 }
347
348 for j in i..min_len {
350 if a[j] != b[j] {
351 return Some(j);
352 }
353 }
354
355 None
356 }
357}
358
359#[inline]
364#[must_use]
365pub fn json_strings_equal(a: &str, b: &str) -> bool {
366 simd_bytes_equal(a.as_bytes(), b.as_bytes())
367}
368
369#[inline]
374#[must_use]
375pub fn json_numbers_equal(a: &str, b: &str) -> bool {
376 a == b
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
389 fn test_simd_bytes_equal_same() {
390 let a = b"hello world";
391 let b = b"hello world";
392 assert!(simd_bytes_equal(a, b));
393 }
394
395 #[test]
396 fn test_simd_bytes_equal_different() {
397 let a = b"hello world";
398 let b = b"hello worle";
399 assert!(!simd_bytes_equal(a, b));
400 }
401
402 #[test]
403 fn test_simd_bytes_equal_different_length() {
404 let a = b"hello";
405 let b = b"hello world";
406 assert!(!simd_bytes_equal(a, b));
407 }
408
409 #[test]
410 fn test_simd_bytes_equal_empty() {
411 let a: &[u8] = b"";
412 let b: &[u8] = b"";
413 assert!(simd_bytes_equal(a, b));
414 }
415
416 #[test]
417 fn test_simd_bytes_equal_long() {
418 let a = "a".repeat(100);
420 let b = "a".repeat(100);
421 assert!(simd_bytes_equal(a.as_bytes(), b.as_bytes()));
422
423 let mut c = "a".repeat(100);
424 c.replace_range(50..51, "b");
425 assert!(!simd_bytes_equal(a.as_bytes(), c.as_bytes()));
426 }
427
428 #[test]
429 fn test_simd_bytes_equal_exactly_16() {
430 let a = b"1234567890123456";
432 let b = b"1234567890123456";
433 assert!(simd_bytes_equal(a, b));
434
435 let c = b"1234567890123457";
436 assert!(!simd_bytes_equal(a, c));
437 }
438
439 #[test]
440 fn test_simd_bytes_equal_exactly_32() {
441 let a = b"12345678901234567890123456789012";
443 let b = b"12345678901234567890123456789012";
444 assert!(simd_bytes_equal(a, b));
445
446 let c = b"12345678901234567890123456789013";
447 assert!(!simd_bytes_equal(a, c));
448 }
449
450 #[test]
451 fn test_simd_bytes_equal_17_bytes() {
452 let a = b"12345678901234567";
454 let b = b"12345678901234567";
455 assert!(simd_bytes_equal(a, b));
456
457 let c = b"12345678901234568";
458 assert!(!simd_bytes_equal(a, c));
459 }
460
461 #[test]
462 fn test_simd_bytes_equal_33_bytes() {
463 let a = b"123456789012345678901234567890123";
465 let b = b"123456789012345678901234567890123";
466 assert!(simd_bytes_equal(a, b));
467
468 let c = b"123456789012345678901234567890124";
469 assert!(!simd_bytes_equal(a, c));
470 }
471
472 #[test]
473 fn test_simd_bytes_equal_48_bytes() {
474 let a = "x".repeat(48);
476 let b = "x".repeat(48);
477 assert!(simd_bytes_equal(a.as_bytes(), b.as_bytes()));
478
479 let mut c = "x".repeat(48);
480 c.replace_range(47..48, "y");
481 assert!(!simd_bytes_equal(a.as_bytes(), c.as_bytes()));
482 }
483
484 #[test]
485 fn test_simd_bytes_equal_64_bytes() {
486 let a = "z".repeat(64);
488 let b = "z".repeat(64);
489 assert!(simd_bytes_equal(a.as_bytes(), b.as_bytes()));
490 }
491
492 #[test]
493 fn test_simd_bytes_equal_difference_in_first_chunk() {
494 let a = "x".repeat(64);
495 let mut b = "x".repeat(64);
496 b.replace_range(5..6, "y");
497 assert!(!simd_bytes_equal(a.as_bytes(), b.as_bytes()));
498 }
499
500 #[test]
501 fn test_simd_bytes_equal_difference_in_second_chunk() {
502 let a = "x".repeat(64);
503 let mut b = "x".repeat(64);
504 b.replace_range(35..36, "y");
505 assert!(!simd_bytes_equal(a.as_bytes(), b.as_bytes()));
506 }
507
508 #[test]
509 fn test_simd_bytes_equal_difference_in_remainder() {
510 let a = "x".repeat(50);
511 let mut b = "x".repeat(50);
512 b.replace_range(49..50, "y");
513 assert!(!simd_bytes_equal(a.as_bytes(), b.as_bytes()));
514 }
515
516 #[test]
521 fn test_find_first_difference_none() {
522 let a = b"hello world";
523 let b = b"hello world";
524 assert_eq!(simd_find_first_difference(a, b), None);
525 }
526
527 #[test]
528 fn test_find_first_difference_at_start() {
529 let a = b"hello";
530 let b = b"jello";
531 assert_eq!(simd_find_first_difference(a, b), Some(0));
532 }
533
534 #[test]
535 fn test_find_first_difference_at_end() {
536 let a = b"hello";
537 let b = b"hellp";
538 assert_eq!(simd_find_first_difference(a, b), Some(4));
539 }
540
541 #[test]
542 fn test_find_first_difference_length() {
543 let a = b"hello";
544 let b = b"hello world";
545 assert_eq!(simd_find_first_difference(a, b), Some(5));
546 }
547
548 #[test]
549 fn test_find_first_difference_empty() {
550 let a: &[u8] = b"";
551 let b: &[u8] = b"";
552 assert_eq!(simd_find_first_difference(a, b), None);
553
554 let c = b"hello";
555 assert_eq!(simd_find_first_difference(a, c), Some(0));
556 }
557
558 #[test]
559 fn test_find_first_difference_long() {
560 let a = "a".repeat(100);
561 let b = "a".repeat(100);
562 assert_eq!(simd_find_first_difference(a.as_bytes(), b.as_bytes()), None);
563
564 let mut c = "a".repeat(100);
565 c.replace_range(75..76, "b");
566 assert_eq!(
567 simd_find_first_difference(a.as_bytes(), c.as_bytes()),
568 Some(75)
569 );
570 }
571
572 #[test]
573 fn test_find_first_difference_16_bytes() {
574 let a = b"1234567890123456";
575 let b = b"1234567890123456";
576 assert_eq!(simd_find_first_difference(a, b), None);
577
578 let c = b"1234567890123457";
579 assert_eq!(simd_find_first_difference(a, c), Some(15));
580 }
581
582 #[test]
583 fn test_find_first_difference_32_bytes() {
584 let a = b"12345678901234567890123456789012";
585 let b = b"12345678901234567890123456789012";
586 assert_eq!(simd_find_first_difference(a, b), None);
587
588 let c = b"12345678901234567890123456789013";
589 assert_eq!(simd_find_first_difference(a, c), Some(31));
590 }
591
592 #[test]
593 fn test_find_first_difference_in_first_sse_chunk() {
594 let a = "x".repeat(32);
596 let mut b = "x".repeat(32);
597 b.replace_range(3..4, "y");
598 assert_eq!(
599 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
600 Some(3)
601 );
602 }
603
604 #[test]
605 fn test_find_first_difference_in_second_sse_chunk() {
606 let a = "x".repeat(32);
608 let mut b = "x".repeat(32);
609 b.replace_range(20..21, "y");
610 assert_eq!(
611 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
612 Some(20)
613 );
614 }
615
616 #[test]
617 fn test_find_first_difference_in_first_avx_chunk() {
618 let a = "x".repeat(64);
620 let mut b = "x".repeat(64);
621 b.replace_range(10..11, "y");
622 assert_eq!(
623 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
624 Some(10)
625 );
626 }
627
628 #[test]
629 fn test_find_first_difference_in_second_avx_chunk() {
630 let a = "x".repeat(64);
632 let mut b = "x".repeat(64);
633 b.replace_range(40..41, "y");
634 assert_eq!(
635 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
636 Some(40)
637 );
638 }
639
640 #[test]
641 fn test_find_first_difference_in_sse_remainder_of_avx() {
642 let a = "x".repeat(48);
644 let mut b = "x".repeat(48);
645 b.replace_range(35..36, "y");
646 assert_eq!(
647 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
648 Some(35)
649 );
650 }
651
652 #[test]
653 fn test_find_first_difference_in_scalar_remainder() {
654 let a = "x".repeat(50);
656 let mut b = "x".repeat(50);
657 b.replace_range(49..50, "y");
658 assert_eq!(
659 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
660 Some(49)
661 );
662 }
663
664 #[test]
665 fn test_find_first_difference_different_lengths_same_prefix() {
666 let a = b"hello";
667 let b = b"hello world";
668 assert_eq!(simd_find_first_difference(a, b), Some(5));
669
670 let a = b"hello world";
672 let b = b"hello";
673 assert_eq!(simd_find_first_difference(a, b), Some(5));
674 }
675
676 #[test]
677 fn test_find_first_difference_long_equal_slices() {
678 let a = "x".repeat(500);
680 let b = "x".repeat(500);
681 assert_eq!(simd_find_first_difference(a.as_bytes(), b.as_bytes()), None);
682 }
683
684 #[test]
685 fn test_find_first_difference_long_difference_at_end() {
686 let a = "x".repeat(500);
687 let mut b = "x".repeat(500);
688 b.replace_range(499..500, "y");
689 assert_eq!(
690 simd_find_first_difference(a.as_bytes(), b.as_bytes()),
691 Some(499)
692 );
693 }
694
695 #[test]
700 fn test_json_strings_equal() {
701 assert!(json_strings_equal("hello", "hello"));
702 assert!(!json_strings_equal("hello", "world"));
703 assert!(json_strings_equal("", ""));
704 }
705
706 #[test]
707 fn test_json_strings_equal_long() {
708 let s = "a".repeat(100);
709 assert!(json_strings_equal(&s, &s));
710 }
711
712 #[test]
713 fn test_json_strings_equal_unicode() {
714 assert!(json_strings_equal("日本語", "日本語"));
715 assert!(!json_strings_equal("日本語", "中文"));
716 }
717
718 #[test]
723 fn test_json_numbers_equal() {
724 assert!(json_numbers_equal("42", "42"));
725 assert!(json_numbers_equal("3.14", "3.14"));
726 assert!(!json_numbers_equal("1.0", "1"));
728 }
729
730 #[test]
731 fn test_json_numbers_equal_negative() {
732 assert!(json_numbers_equal("-42", "-42"));
733 assert!(!json_numbers_equal("-42", "42"));
734 }
735
736 #[test]
737 fn test_json_numbers_equal_exponent() {
738 assert!(json_numbers_equal("1e10", "1e10"));
739 assert!(!json_numbers_equal("1e10", "1E10")); }
741
742 #[cfg(target_arch = "x86_64")]
747 #[test]
748 fn test_has_sse2() {
749 assert!(has_sse2());
751 }
752
753 #[cfg(target_arch = "x86_64")]
754 #[test]
755 fn test_has_avx2() {
756 let _ = has_avx2();
758 }
759
760 #[test]
765 fn test_simd_bytes_equal_scalar_fallback() {
766 let a = b"hi";
768 let b = b"hi";
769 assert!(simd_bytes_equal(a, b));
770
771 let c = b"ho";
772 assert!(!simd_bytes_equal(a, c));
773 }
774
775 #[test]
776 fn test_find_first_difference_scalar_fallback() {
777 let a = b"abc";
779 let b = b"abc";
780 assert_eq!(simd_find_first_difference(a, b), None);
781
782 let c = b"abd";
783 assert_eq!(simd_find_first_difference(a, c), Some(2));
784 }
785
786 #[test]
787 fn test_simd_bytes_equal_single_byte() {
788 assert!(simd_bytes_equal(b"a", b"a"));
789 assert!(!simd_bytes_equal(b"a", b"b"));
790 }
791
792 #[test]
793 fn test_find_first_difference_single_byte() {
794 assert_eq!(simd_find_first_difference(b"a", b"a"), None);
795 assert_eq!(simd_find_first_difference(b"a", b"b"), Some(0));
796 }
797}