From 13374e8065e48b8ddf0ebcfd7cebd19a9e84b538 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Wed, 18 Sep 2024 21:49:18 -0700 Subject: [PATCH] Add const functions for negation / union of AsciiSet --- percent_encoding/src/lib.rs | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/percent_encoding/src/lib.rs b/percent_encoding/src/lib.rs index 34ad8f53..5cac67bc 100644 --- a/percent_encoding/src/lib.rs +++ b/percent_encoding/src/lib.rs @@ -106,17 +106,30 @@ impl AsciiSet { mask[byte as usize / BITS_PER_CHUNK] &= !(1 << (byte as usize % BITS_PER_CHUNK)); AsciiSet { mask } } + + /// Return the union of two sets. + pub const fn union(&self, other: &Self) -> Self { + let mask = [ + self.mask[0] | other.mask[0], + self.mask[1] | other.mask[1], + self.mask[2] | other.mask[2], + self.mask[3] | other.mask[3], + ]; + AsciiSet { mask } + } + + /// Return the negation of the set. + pub const fn complement(&self) -> Self { + let mask = [!self.mask[0], !self.mask[1], !self.mask[2], !self.mask[3]]; + AsciiSet { mask } + } } impl ops::Add for AsciiSet { type Output = Self; fn add(self, other: Self) -> Self { - let mut mask = self.mask.clone(); - for i in 0..mask.len() { - mask[i] |= other.mask[i]; - } - AsciiSet { mask } + self.union(&other) } } @@ -124,8 +137,7 @@ impl ops::Not for AsciiSet { type Output = Self; fn not(self) -> Self { - let mask = self.mask.map(|chunk| !chunk); - AsciiSet { mask } + self.complement() } } @@ -511,7 +523,7 @@ mod tests { use super::*; #[test] - fn add() { + fn add_op() { let left = AsciiSet::EMPTY.add(b'A'); let right = AsciiSet::EMPTY.add(b'B'); let expected = AsciiSet::EMPTY.add(b'A').add(b'B'); @@ -519,10 +531,32 @@ mod tests { } #[test] - fn not() { + fn not_op() { let set = AsciiSet::EMPTY.add(b'A').add(b'B'); let not_set = !set; assert!(!not_set.contains(b'A')); assert!(not_set.contains(b'C')); } + + /// This test ensures that we can get the union of two sets as a constant value, which is + /// useful for defining sets in a modular way. + #[test] + fn union() { + const A: AsciiSet = AsciiSet::EMPTY.add(b'A'); + const B: AsciiSet = AsciiSet::EMPTY.add(b'B'); + const UNION: AsciiSet = A.union(&B); + const EXPECTED: AsciiSet = AsciiSet::EMPTY.add(b'A').add(b'B'); + assert_eq!(UNION, EXPECTED); + } + + /// This test ensures that we can get the complement of a set as a constant value, which is + /// useful for defining sets in a modular way. + #[test] + fn complement() { + const BOTH: AsciiSet = AsciiSet::EMPTY.add(b'A').add(b'B'); + const COMPLEMENT: AsciiSet = BOTH.complement(); + assert!(!COMPLEMENT.contains(b'A')); + assert!(!COMPLEMENT.contains(b'B')); + assert!(COMPLEMENT.contains(b'C')); + } }