Skip to main content

fionn_diff/
simd_compare.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! SIMD-accelerated comparison utilities for JSON diff operations.
3//!
4//! Provides fast byte-level comparison for detecting differences between
5//! JSON values without full parsing.
6
7#[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/// Cached SIMD feature detection.
20#[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/// SIMD-accelerated byte slice equality check.
39///
40/// Returns `true` if both slices are equal, `false` otherwise.
41/// Uses SIMD to compare 16-32 bytes at a time.
42#[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    // Scalar fallback
71    a == b
72}
73
74/// Find the first position where two byte slices differ.
75///
76/// Returns `None` if the slices are equal, or `Some(index)` of the first difference.
77/// If slices have different lengths, returns the length of the shorter slice
78/// if the common prefix is equal.
79#[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            // Check if lengths differ
96            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    // Scalar fallback
131    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/// SSE2 implementation of byte equality.
145#[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        // Process 16 bytes at a time
153        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            // All 16 bytes must be equal (mask = 0xFFFF)
160            if mask != 0xFFFF {
161                return false;
162            }
163
164            i += 16;
165        }
166
167        // Check remaining bytes
168        a[i..] == b[i..]
169    }
170}
171
172/// AVX2 implementation of byte equality.
173#[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        // Process 32 bytes at a time
181        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            // All 32 bytes must be equal (mask = -1 as i32, or 0xFFFFFFFF)
188            if mask != -1 {
189                return false;
190            }
191
192            i += 32;
193        }
194
195        // Handle remaining with SSE2 or scalar
196        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        // Check remaining bytes
209        a[i..] == b[i..]
210    }
211}
212
213/// NEON implementation of byte equality.
214#[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        // Process 16 bytes at a time
221        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            // Check if all bytes are equal (all comparison results are 0xFF)
227            let min_val = vminvq_u8(cmp);
228            if min_val != 0xFF {
229                return false;
230            }
231
232            i += 16;
233        }
234
235        // Check remaining bytes
236        a[i..] == b[i..]
237    }
238}
239
240/// SSE2 implementation of finding first difference.
241#[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                // Found a difference - find which byte
255                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        // Check remaining bytes
264        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/// AVX2 implementation of finding first difference.
275#[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                // Found a difference - find which byte
289                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        // Handle remaining with SSE2 or scalar
298        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        // Check remaining bytes
313        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/// NEON implementation of finding first difference.
324#[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            // Check if all bytes are equal
335            let min_val = vminvq_u8(cmp);
336            if min_val != 0xFF {
337                // Found a difference - find which byte
338                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        // Check remaining bytes
350        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/// Compare two JSON string values for equality.
361///
362/// This is optimized for JSON strings which are typically short.
363/// Uses SIMD for strings >= 16 bytes.
364#[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/// Compare two JSON number representations for equality.
371///
372/// Note: This does byte comparison, so "1.0" != "1" even though
373/// they represent the same value. For semantic equality, parse first.
374#[inline]
375#[must_use]
376pub fn json_numbers_equal(a: &str, b: &str) -> bool {
377    // Numbers are typically short, use direct comparison
378    a == b
379}
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384
385    // =========================================================================
386    // simd_bytes_equal Tests
387    // =========================================================================
388
389    #[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        // Test with data longer than 32 bytes to trigger AVX2
420        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        // Test SSE2 boundary (16 bytes)
432        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        // Test AVX2 boundary (32 bytes)
443        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        // Test between SSE2 boundaries (16 < 17 < 32)
454        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        // Test between AVX2 boundaries (32 < 33 < 48)
465        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        // Test 48 bytes (32 + 16)
476        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        // Test 64 bytes (2 x 32)
488        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    // =========================================================================
518    // simd_find_first_difference Tests
519    // =========================================================================
520
521    #[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        // Difference in first 16 bytes
596        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        // Difference in second 16 bytes (for SSE path)
608        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        // Difference in first 32 bytes
620        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        // Difference in second 32 bytes
632        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        // 48 bytes: 32 (AVX) + 16 (SSE remainder)
644        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        // 50 bytes: 32 (AVX) + 16 (SSE) + 2 (scalar)
656        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        // Longer a
672        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        // Very long equal slices (tests multiple SIMD iterations)
680        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    // =========================================================================
697    // json_strings_equal Tests
698    // =========================================================================
699
700    #[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    // =========================================================================
720    // json_numbers_equal Tests
721    // =========================================================================
722
723    #[test]
724    fn test_json_numbers_equal() {
725        assert!(json_numbers_equal("42", "42"));
726        assert!(json_numbers_equal("3.14", "3.14"));
727        // Note: These are semantically equal but byte-different
728        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")); // Case sensitive
741    }
742
743    // =========================================================================
744    // Feature Detection Tests
745    // =========================================================================
746
747    #[cfg(target_arch = "x86_64")]
748    #[test]
749    fn test_has_sse2() {
750        // SSE2 is baseline for x86_64
751        assert!(has_sse2());
752    }
753
754    #[cfg(target_arch = "x86_64")]
755    #[test]
756    fn test_has_avx2() {
757        // Just check it doesn't panic
758        let _ = has_avx2();
759    }
760
761    // =========================================================================
762    // Scalar Fallback Tests (small inputs)
763    // =========================================================================
764
765    #[test]
766    fn test_simd_bytes_equal_scalar_fallback() {
767        // Very short inputs use scalar path
768        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        // Very short inputs use scalar path
779        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}