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