mesh_loader/utils/
bytes.rs

1#[cfg(feature = "obj")]
2use std::{borrow::Cow, ffi::OsStr, path::Path, str};
3
4// This is the same as s.starts_with(needle), but faster if the length of the
5// needle is known at compile time.
6#[inline(always)] // Ensure the code getting the length of the needle is inlined.
7pub(crate) fn starts_with(mut s: &[u8], mut needle: &'static [u8]) -> bool {
8    if s.len() < needle.len() {
9        return false;
10    }
11    if needle.len() < 4 {
12        return s.starts_with(needle);
13    }
14    if needle.len() < 8 {
15        // u32 (4 bytes) + 0-3 bytes
16        return u32::from_ne_bytes(needle[..4].try_into().unwrap())
17            == u32::from_ne_bytes(s[..4].try_into().unwrap())
18            && s[4..].starts_with(&needle[4..]);
19    }
20    if needle.len() < 12 {
21        // u64 (8 bytes) + 0-3 bytes
22        return u64::from_ne_bytes(needle[..8].try_into().unwrap())
23            == u64::from_ne_bytes(s[..8].try_into().unwrap())
24            && s[8..].starts_with(&needle[8..]);
25    }
26    if needle.len() < 16 {
27        // u64 (8 bytes) + u32 (4 bytes) + 0-3 bytes
28        return u64::from_ne_bytes(needle[..8].try_into().unwrap())
29            == u64::from_ne_bytes(s[..8].try_into().unwrap())
30            && u32::from_ne_bytes(needle[8..12].try_into().unwrap())
31                == u32::from_ne_bytes(s[8..12].try_into().unwrap())
32            && s[12..].starts_with(&needle[12..]);
33    }
34    // u64 (8 bytes) + u64 (8 bytes) + N bytes
35    while needle.len() >= 8 {
36        if u64::from_ne_bytes(needle[..8].try_into().unwrap())
37            != u64::from_ne_bytes(s[..8].try_into().unwrap())
38        {
39            return false;
40        }
41        needle = &needle[8..];
42        s = &s[8..];
43    }
44    s.starts_with(needle)
45}
46
47#[cfg(any(feature = "collada", feature = "obj"))]
48#[inline]
49pub(crate) const fn memchr_naive(needle: u8, mut s: &[u8]) -> Option<usize> {
50    let start = s;
51    while let Some((&b, s_next)) = s.split_first() {
52        if b == needle {
53            return Some(start.len() - s.len());
54        }
55        s = s_next;
56    }
57    None
58}
59
60#[cfg(any(feature = "obj", feature = "stl"))]
61#[inline]
62pub(crate) const fn memchr_naive_table(
63    needle_mask: u8,
64    table: &[u8; 256],
65    mut s: &[u8],
66) -> Option<usize> {
67    let start = s;
68    while let Some((&b, s_next)) = s.split_first() {
69        if table[b as usize] & needle_mask != 0 {
70            return Some(start.len() - s.len());
71        }
72        s = s_next;
73    }
74    None
75}
76
77#[cfg(any(feature = "obj", feature = "stl"))]
78#[inline]
79pub(crate) const fn memrchr_naive(needle: u8, mut s: &[u8]) -> Option<usize> {
80    let start = s;
81    while let Some((&b, s_next)) = s.split_last() {
82        if b == needle {
83            return Some(start.len() - s.len());
84        }
85        s = s_next;
86    }
87    None
88}
89
90#[cfg(any(feature = "obj", feature = "stl"))]
91#[inline]
92pub(crate) const fn bytecount_naive(needle: u8, mut s: &[u8]) -> usize {
93    let mut n = 0;
94    while let Some((&b, s_next)) = s.split_first() {
95        n += (b == needle) as usize;
96        s = s_next;
97    }
98    n
99}
100
101#[cfg(feature = "obj")]
102#[allow(clippy::unnecessary_wraps)] // clippy bug: this lint doesn't consider cfg attribute
103pub(crate) fn os_str_from_bytes(bytes: &[u8]) -> Result<&OsStr, std::str::Utf8Error> {
104    #[cfg(any(unix, target_os = "wasi"))]
105    {
106        #[cfg(unix)]
107        use std::os::unix::ffi::OsStrExt as _;
108        #[cfg(target_os = "wasi")]
109        use std::os::wasi::ffi::OsStrExt as _;
110        Ok(OsStr::from_bytes(bytes))
111    }
112    #[cfg(not(any(unix, target_os = "wasi")))]
113    {
114        std::str::from_utf8(bytes).map(OsStr::new)
115    }
116}
117#[cfg(feature = "obj")]
118pub(crate) fn path_from_bytes(bytes: &[u8]) -> Result<&Path, std::str::Utf8Error> {
119    os_str_from_bytes(bytes).map(Path::new)
120}
121
122// Ideally, we want to use Utf8Chunks here, but it is unstable.
123#[cfg(feature = "obj")]
124#[inline]
125pub(crate) fn from_utf8_lossy(mut bytes: &[u8]) -> Cow<'_, str> {
126    let mut base = String::new();
127    loop {
128        match str::from_utf8(bytes) {
129            Ok(s) => {
130                if base.is_empty() {
131                    return s.into();
132                }
133                base.push_str(s);
134                return base.into();
135            }
136            Err(e) => {
137                let valid_up_to = e.valid_up_to();
138                let s = str::from_utf8(&bytes[..valid_up_to]).unwrap();
139                base.push_str(s);
140                base.push(char::REPLACEMENT_CHARACTER);
141                if let Some(error_len) = e.error_len() {
142                    bytes = &bytes[valid_up_to + error_len..];
143                } else {
144                    return base.into();
145                }
146            }
147        }
148    }
149}