Skip to main content

cadmpeg_core/
bytes.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Shared byte-slice search over untrusted decode windows.
3//!
4//! Empty needles are never a match. That matches the codec helpers this
5//! module replaces and avoids `memchr`'s empty-needle-at-zero behavior.
6
7/// First offset of `needle` in `haystack`, or `None` when `needle` is empty.
8pub fn find(haystack: &[u8], needle: &[u8]) -> Option<usize> {
9    if needle.is_empty() {
10        return None;
11    }
12    memchr::memmem::find(haystack, needle)
13}
14
15/// First offset of `needle` at or after `from`, or `None` when `needle` is empty.
16pub fn find_from(haystack: &[u8], needle: &[u8], from: usize) -> Option<usize> {
17    let tail = haystack.get(from..)?;
18    find(tail, needle).map(|relative| from + relative)
19}
20
21/// First offset of `needle` in `haystack[start..end]`, returned as an absolute offset.
22pub fn find_in(haystack: &[u8], needle: &[u8], start: usize, end: usize) -> Option<usize> {
23    let window = haystack.get(start..end)?;
24    find(window, needle).map(|relative| start + relative)
25}
26
27/// Whether `needle` occurs in `haystack`. Empty needles are absent.
28pub fn contains(haystack: &[u8], needle: &[u8]) -> bool {
29    find(haystack, needle).is_some()
30}
31
32/// Offsets of every non-overlapping occurrence of `needle`.
33///
34/// An empty needle yields no offsets.
35pub fn find_iter<'a>(haystack: &'a [u8], needle: &'a [u8]) -> impl Iterator<Item = usize> + 'a {
36    let search = if needle.is_empty() {
37        None
38    } else {
39        Some(memchr::memmem::find_iter(haystack, needle))
40    };
41    search.into_iter().flatten()
42}
43
44#[cfg(test)]
45mod tests {
46    use super::{contains, find, find_from, find_in, find_iter};
47
48    #[test]
49    fn empty_needle_is_never_a_match() {
50        assert_eq!(find(b"abc", b""), None);
51        assert_eq!(find_from(b"abc", b"", 1), None);
52        assert_eq!(find_in(b"abc", b"", 0, 3), None);
53        assert!(!contains(b"abc", b""));
54        assert_eq!(
55            find_iter(b"abc", b"").collect::<Vec<_>>(),
56            Vec::<usize>::new()
57        );
58    }
59
60    #[test]
61    fn finds_absolute_and_ranged_offsets() {
62        let haystack = b"xxabcxxabc";
63        assert_eq!(find(haystack, b"abc"), Some(2));
64        assert_eq!(find_from(haystack, b"abc", 3), Some(7));
65        assert_eq!(find_in(haystack, b"abc", 3, 10), Some(7));
66        assert_eq!(find_in(haystack, b"abc", 3, 6), None);
67        assert!(contains(haystack, b"abc"));
68        assert_eq!(find_iter(haystack, b"abc").collect::<Vec<_>>(), [2, 7]);
69    }
70}