oceanpkg_shared/ext/bytes.rs
1/// Extended functionality for
2/// [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html).
3pub trait BytesExt {
4 /// Returns whether `self` matches case-insensitively to `other`, which only
5 /// contains [ASCII] characters with the `0b100000` (lowercase) bit set.
6 ///
7 /// This method can often be used in place of
8 /// [`eq_ignore_ascii_case`](https://doc.rust-lang.org/std/primitive.slice.html#method.eq_ignore_ascii_case)
9 /// and is more performant since this uses a simple bitwise OR instead of a
10 /// lookup table. The main restriction is that only the following [ASCII]
11 /// characters may be in `other`:
12 ///
13 /// | Type | Values |
14 /// | :------------ | :----- |
15 /// | Alphanumeric | `a`-`z`, `0`-`9` |
16 /// | Punctuation | `!`, `?`, `.`, `,`, `:`, `;`, `'`, `` ` ``, `\`, `/`, `#`, `$`, `&`, <code>|</code>, `~` |
17 /// | Brackets | `<`, `>`, `(`, `)`, `{`, `}` |
18 /// | Math | `+`, `-`, `*`, `%`, `=` |
19 /// | Non-Graphical | `SPACE`, `DELETE` |
20 ///
21 /// # Examples
22 ///
23 /// This method can be used to match against filesystem paths:
24 ///
25 /// ```rust
26 /// use oceanpkg_shared::ext::BytesExt;
27 ///
28 /// let lower = b"../hello.txt";
29 /// let upper = b"../HELLO.TXT";
30 ///
31 /// assert!(upper.matches_special_lowercase(lower));
32 /// assert!(lower.matches_special_lowercase(lower));
33 /// assert!(!lower.matches_special_lowercase(upper));
34 /// assert!(!upper.matches_special_lowercase(upper));
35 /// ```
36 ///
37 /// [ASCII]: https://en.wikipedia.org/wiki/ASCII
38 fn matches_special_lowercase<B: AsRef<[u8]>>(self, other: B) -> bool;
39}
40
41// Monomorphized form
42fn matches_special_lowercase_imp(a: &[u8], b: &[u8]) -> bool {
43 a.len() == b.len() && a.iter().zip(b).all(|(&a, &b)| { a | 0b100000 == b })
44}
45
46impl BytesExt for &[u8] {
47 fn matches_special_lowercase<B: AsRef<[u8]>>(self, other: B) -> bool {
48 matches_special_lowercase_imp(self.as_ref(), other.as_ref())
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn matches_special_lowercase() {
58 let cases = [
59 (["ocean.toml", "ocean.toml"], true),
60 (["OCEAN.toMl", "ocean.toml"], true),
61 (["ocean.toml", "OCEAN.toml"], false),
62 (["ocean.tom", "ocean.toml"], false),
63 ];
64 for &([a, b], cond) in cases.iter() {
65 assert_eq!(a.as_bytes().matches_special_lowercase(b), cond);
66 }
67 }
68}