From efea3c2765cbc02fe217176d6b5422bc8178321e Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 6 May 2023 23:34:37 -0500 Subject: [PATCH 1/6] Fix feature gate --- utils/litemap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/litemap/src/lib.rs b/utils/litemap/src/lib.rs index 3647ccfcb87..85f2c435d66 100644 --- a/utils/litemap/src/lib.rs +++ b/utils/litemap/src/lib.rs @@ -51,7 +51,7 @@ mod serde; mod serde_helpers; pub mod store; -#[cfg(feature = "testing")] +#[cfg(any(test, feature = "testing"))] pub mod testing; pub use map::LiteMap; From 58c0c1ec2904105b7f0676bb739e581ef68afd42 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 6 May 2023 23:34:47 -0500 Subject: [PATCH 2/6] Move first/last impls into correct blocks --- utils/litemap/src/map.rs | 68 ++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/utils/litemap/src/map.rs b/utils/litemap/src/map.rs index bcd7eb8c963..8b1ae63009d 100644 --- a/utils/litemap/src/map.rs +++ b/utils/litemap/src/map.rs @@ -91,6 +91,40 @@ where pub fn get_indexed(&self, index: usize) -> Option<(&K, &V)> { self.values.lm_get(index) } + + /// Get the lowest-rank key/value pair from the `LiteMap`, if it exists. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// let mut map: LiteMap> = + /// LiteMap::from_iter([(1, "uno"), (3, "tres")].into_iter()); + /// + /// assert_eq!(map.first(), Some((&1, &"uno"))); + /// ``` + #[inline] + pub fn first(&self) -> Option<(&K, &V)> { + self.values.lm_get(0).map(|(k, v)| (k, v)) + } + + /// Get the highest-rank key/value pair from the `LiteMap`, if it exists. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// let mut map: LiteMap> = + /// LiteMap::from_iter([(1, "uno"), (3, "tres")].into_iter()); + /// + /// assert_eq!(map.last(), Some((&3, &"tres"))); + /// ``` + #[inline] + pub fn last(&self) -> Option<(&K, &V)> { + self.values.lm_get(self.len() - 1).map(|(k, v)| (k, v)) + } } impl LiteMap @@ -146,40 +180,6 @@ where self.find_index(key).is_ok() } - /// Get the lowest-rank key/value pair from the `LiteMap`, if it exists. - /// - /// # Examples - /// - /// ```rust - /// use litemap::LiteMap; - /// - /// let mut map: LiteMap> = - /// LiteMap::from_iter([(1, "uno"), (3, "tres")].into_iter()); - /// - /// assert_eq!(map.first(), Some((&1, &"uno"))); - /// ``` - #[inline] - pub fn first(&self) -> Option<(&K, &V)> { - self.values.lm_get(0).map(|(k, v)| (k, v)) - } - - /// Get the highest-rank key/value pair from the `LiteMap`, if it exists. - /// - /// # Examples - /// - /// ```rust - /// use litemap::LiteMap; - /// - /// let mut map: LiteMap> = - /// LiteMap::from_iter([(1, "uno"), (3, "tres")].into_iter()); - /// - /// assert_eq!(map.last(), Some((&3, &"tres"))); - /// ``` - #[inline] - pub fn last(&self) -> Option<(&K, &V)> { - self.values.lm_get(self.len() - 1).map(|(k, v)| (k, v)) - } - /// Obtain the index for a given key, or if the key is not found, the index /// at which it would be inserted. /// From f10dc7c003bc2a7cd9e94f7490ef450620cb50c7 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 6 May 2023 23:35:02 -0500 Subject: [PATCH 3/6] Add const LiteMap getter functions for common types --- utils/litemap/src/map.rs | 214 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 1 deletion(-) diff --git a/utils/litemap/src/map.rs b/utils/litemap/src/map.rs index 8b1ae63009d..02d76daf172 100644 --- a/utils/litemap/src/map.rs +++ b/utils/litemap/src/map.rs @@ -571,9 +571,209 @@ where } } +impl<'a, K, V> LiteMap { + /// Const version of [`LiteMap::len()`] for a slice store. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// ("a", 11), + /// ("b", 22), + /// ]); + /// const len: usize = map.const_len(); + /// assert_eq!(len, 2); + /// ``` + pub const fn const_len(&self) -> usize { + self.values.len() + } + + /// Const version of [`LiteMap::is_empty()`] for a slice store. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[]); + /// const is_empty: bool = map.const_is_empty(); + /// assert!(is_empty); + /// ``` + pub const fn const_is_empty(&self) -> bool { + self.values.is_empty() + } + + /// Const version of [`LiteMap::get_indexed()`] for a slice store. + /// + /// # Panics + /// + /// Panics if the index is out of bounds. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// ("a", 11), + /// ("b", 22), + /// ]); + /// const t: &(&str, usize) = map.const_get_indexed_or_panic(0); + /// assert_eq!(t.0, "a"); + /// assert_eq!(t.1, 11); + /// ``` + #[inline] + pub const fn const_get_indexed_or_panic(&self, index: usize) -> &'a (K, V) { + &self.values[index] + } +} + +const fn const_cmp_bytes(a: &[u8], b: &[u8]) -> Ordering { + let (max, default) = if a.len() == b.len() { + (a.len(), Ordering::Equal) + } else if a.len() < b.len() { + (a.len(), Ordering::Less) + } else { + (b.len(), Ordering::Greater) + }; + let mut i = 0; + while i < max { + if a[i] == b[i] { + i += 1; + continue; + } else if a[i] < b[i] { + return Ordering::Less; + } else { + return Ordering::Greater; + } + } + return default; +} + +impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { + /// Const function to get the value associated with a `&str` key, if it exists. + /// + /// Also returns the index of the value. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// ("abc", 11), + /// ("bcd", 22), + /// ("cde", 33), + /// ("def", 44), + /// ("efg", 55), + /// ]); + /// + /// const d: Option<(usize, &usize)> = map.const_get_with_index("def"); + /// assert_eq!(d, Some((3, &44))); + /// + /// const n: Option<(usize, &usize)> = map.const_get_with_index("dng"); + /// assert_eq!(n, None); + /// ``` + pub const fn const_get_with_index(&self, key: &str) -> Option<(usize, &'a V)> { + let mut i = 0; + let mut j = self.const_len(); + while i < j { + let mid = (i + j) / 2; + let x = &self.values[mid]; + match const_cmp_bytes(key.as_bytes(), x.0.as_bytes()) { + Ordering::Equal => return Some((mid, &x.1)), + Ordering::Greater => i = mid + 1, + Ordering::Less => j = mid, + }; + } + return None; + } +} + +impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { + /// Const function to get the value associated with a `&[u8]` key, if it exists. + /// + /// Also returns the index of the value. + /// + /// # Examples + /// + /// ```rust + /// use litemap::LiteMap; + /// + /// const map: LiteMap<&[u8], usize, &[(&[u8], usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// (b"abc", 11), + /// (b"bcd", 22), + /// (b"cde", 33), + /// (b"def", 44), + /// (b"efg", 55), + /// ]); + /// + /// const d: Option<(usize, &usize)> = map.const_get_with_index(b"def"); + /// assert_eq!(d, Some((3, &44))); + /// + /// const n: Option<(usize, &usize)> = map.const_get_with_index(b"dng"); + /// assert_eq!(n, None); + /// ``` + pub const fn const_get_with_index(&self, key: &[u8]) -> Option<(usize, &'a V)> { + let mut i = 0; + let mut j = self.const_len(); + while i < j { + let mid = (i + j) / 2; + let x = &self.values[mid]; + match const_cmp_bytes(key, x.0) { + Ordering::Equal => return Some((mid, &x.1)), + Ordering::Greater => i = mid + 1, + Ordering::Less => j = mid, + }; + } + return None; + } +} + +macro_rules! impl_const_get_with_index_for_integer { + ($integer:ty) => { + impl<'a, V> LiteMap<$integer, V, &'a [($integer, V)]> { + /// Const function to get the value associated with an integer key, if it exists. + /// + /// Also returns the index of the value. + pub const fn const_get_with_index(&self, key: $integer) -> Option<(usize, &'a V)> { + let mut i = 0; + let mut j = self.const_len(); + while i < j { + let mid = (i + j) / 2; + let x = &self.values[mid]; + if key == x.0 { + return Some((mid, &x.1)); + } else if key > x.0 { + i = mid + 1; + } else { + j = mid; + } + } + return None; + } + } + }; +} + +impl_const_get_with_index_for_integer!(u8); +impl_const_get_with_index_for_integer!(u16); +impl_const_get_with_index_for_integer!(u32); +impl_const_get_with_index_for_integer!(u64); +impl_const_get_with_index_for_integer!(u128); +impl_const_get_with_index_for_integer!(usize); +impl_const_get_with_index_for_integer!(i8); +impl_const_get_with_index_for_integer!(i16); +impl_const_get_with_index_for_integer!(i32); +impl_const_get_with_index_for_integer!(i64); +impl_const_get_with_index_for_integer!(i128); +impl_const_get_with_index_for_integer!(isize); + #[cfg(test)] mod test { - use crate::LiteMap; + use super::*; #[test] fn from_iterator() { @@ -655,4 +855,16 @@ mod test { .expect("Insert with conflict"); assert_eq!(map.len(), 5); } + + #[test] + fn test_const_cmp_bytes() { + let strs = &["a", "aa", "abc", "abde", "bcd", "bcde"]; + for i in 0..strs.len() { + for j in 0..strs.len() { + let a = strs[i].as_bytes(); + let b = strs[j].as_bytes(); + assert_eq!(a.cmp(b), const_cmp_bytes(a, b)); + } + } + } } From 38280fcaf049417f3034f17e658e6fd5ec5d91f4 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 6 May 2023 23:38:16 -0500 Subject: [PATCH 4/6] Use `static` instead of `const` in examples (which is better?) --- utils/litemap/src/map.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/utils/litemap/src/map.rs b/utils/litemap/src/map.rs index 02d76daf172..3f753aa5d67 100644 --- a/utils/litemap/src/map.rs +++ b/utils/litemap/src/map.rs @@ -579,11 +579,11 @@ impl<'a, K, V> LiteMap { /// ```rust /// use litemap::LiteMap; /// - /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// static map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ /// ("a", 11), /// ("b", 22), /// ]); - /// const len: usize = map.const_len(); + /// static len: usize = map.const_len(); /// assert_eq!(len, 2); /// ``` pub const fn const_len(&self) -> usize { @@ -597,8 +597,8 @@ impl<'a, K, V> LiteMap { /// ```rust /// use litemap::LiteMap; /// - /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[]); - /// const is_empty: bool = map.const_is_empty(); + /// static map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[]); + /// static is_empty: bool = map.const_is_empty(); /// assert!(is_empty); /// ``` pub const fn const_is_empty(&self) -> bool { @@ -616,11 +616,11 @@ impl<'a, K, V> LiteMap { /// ```rust /// use litemap::LiteMap; /// - /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// static map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ /// ("a", 11), /// ("b", 22), /// ]); - /// const t: &(&str, usize) = map.const_get_indexed_or_panic(0); + /// static t: &(&str, usize) = map.const_get_indexed_or_panic(0); /// assert_eq!(t.0, "a"); /// assert_eq!(t.1, 11); /// ``` @@ -662,7 +662,7 @@ impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { /// ```rust /// use litemap::LiteMap; /// - /// const map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// static map: LiteMap<&str, usize, &[(&str, usize)]> = LiteMap::from_sorted_store_unchecked(&[ /// ("abc", 11), /// ("bcd", 22), /// ("cde", 33), @@ -670,10 +670,10 @@ impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { /// ("efg", 55), /// ]); /// - /// const d: Option<(usize, &usize)> = map.const_get_with_index("def"); + /// static d: Option<(usize, &usize)> = map.const_get_with_index("def"); /// assert_eq!(d, Some((3, &44))); /// - /// const n: Option<(usize, &usize)> = map.const_get_with_index("dng"); + /// static n: Option<(usize, &usize)> = map.const_get_with_index("dng"); /// assert_eq!(n, None); /// ``` pub const fn const_get_with_index(&self, key: &str) -> Option<(usize, &'a V)> { @@ -702,7 +702,7 @@ impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { /// ```rust /// use litemap::LiteMap; /// - /// const map: LiteMap<&[u8], usize, &[(&[u8], usize)]> = LiteMap::from_sorted_store_unchecked(&[ + /// static map: LiteMap<&[u8], usize, &[(&[u8], usize)]> = LiteMap::from_sorted_store_unchecked(&[ /// (b"abc", 11), /// (b"bcd", 22), /// (b"cde", 33), @@ -710,10 +710,10 @@ impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { /// (b"efg", 55), /// ]); /// - /// const d: Option<(usize, &usize)> = map.const_get_with_index(b"def"); + /// static d: Option<(usize, &usize)> = map.const_get_with_index(b"def"); /// assert_eq!(d, Some((3, &44))); /// - /// const n: Option<(usize, &usize)> = map.const_get_with_index(b"dng"); + /// static n: Option<(usize, &usize)> = map.const_get_with_index(b"dng"); /// assert_eq!(n, None); /// ``` pub const fn const_get_with_index(&self, key: &[u8]) -> Option<(usize, &'a V)> { From 2025c0861cb2bfd48e849cc384280067d2421a4c Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sun, 7 May 2023 00:18:58 -0500 Subject: [PATCH 5/6] Clippy --- utils/litemap/src/map.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/utils/litemap/src/map.rs b/utils/litemap/src/map.rs index 3f753aa5d67..a1fc69718ea 100644 --- a/utils/litemap/src/map.rs +++ b/utils/litemap/src/map.rs @@ -586,6 +586,7 @@ impl<'a, K, V> LiteMap { /// static len: usize = map.const_len(); /// assert_eq!(len, 2); /// ``` + #[inline] pub const fn const_len(&self) -> usize { self.values.len() } @@ -601,6 +602,7 @@ impl<'a, K, V> LiteMap { /// static is_empty: bool = map.const_is_empty(); /// assert!(is_empty); /// ``` + #[inline] pub const fn const_is_empty(&self) -> bool { self.values.is_empty() } @@ -625,6 +627,7 @@ impl<'a, K, V> LiteMap { /// assert_eq!(t.1, 11); /// ``` #[inline] + #[allow(clippy::indexing_slicing)] // documented pub const fn const_get_indexed_or_panic(&self, index: usize) -> &'a (K, V) { &self.values[index] } @@ -639,6 +642,7 @@ const fn const_cmp_bytes(a: &[u8], b: &[u8]) -> Ordering { (b.len(), Ordering::Greater) }; let mut i = 0; + #[allow(clippy::indexing_slicing)] // indexes in range by above checks while i < max { if a[i] == b[i] { i += 1; @@ -649,7 +653,7 @@ const fn const_cmp_bytes(a: &[u8], b: &[u8]) -> Ordering { return Ordering::Greater; } } - return default; + default } impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { @@ -681,6 +685,7 @@ impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { let mut j = self.const_len(); while i < j { let mid = (i + j) / 2; + #[allow(clippy::indexing_slicing)] // in range let x = &self.values[mid]; match const_cmp_bytes(key.as_bytes(), x.0.as_bytes()) { Ordering::Equal => return Some((mid, &x.1)), @@ -688,7 +693,7 @@ impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { Ordering::Less => j = mid, }; } - return None; + None } } @@ -721,6 +726,7 @@ impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { let mut j = self.const_len(); while i < j { let mid = (i + j) / 2; + #[allow(clippy::indexing_slicing)] // in range let x = &self.values[mid]; match const_cmp_bytes(key, x.0) { Ordering::Equal => return Some((mid, &x.1)), @@ -728,7 +734,7 @@ impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { Ordering::Less => j = mid, }; } - return None; + None } } @@ -743,6 +749,7 @@ macro_rules! impl_const_get_with_index_for_integer { let mut j = self.const_len(); while i < j { let mid = (i + j) / 2; + #[allow(clippy::indexing_slicing)] // in range let x = &self.values[mid]; if key == x.0 { return Some((mid, &x.1)); From 4aff0cf9caea933963ad99db6cc7148e06f5ed65 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sun, 7 May 2023 14:23:34 -0500 Subject: [PATCH 6/6] Add note about const trait --- utils/litemap/src/map.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/litemap/src/map.rs b/utils/litemap/src/map.rs index a1fc69718ea..9264cbd3e04 100644 --- a/utils/litemap/src/map.rs +++ b/utils/litemap/src/map.rs @@ -574,6 +574,8 @@ where impl<'a, K, V> LiteMap { /// Const version of [`LiteMap::len()`] for a slice store. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// # Examples /// /// ```rust @@ -593,6 +595,8 @@ impl<'a, K, V> LiteMap { /// Const version of [`LiteMap::is_empty()`] for a slice store. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// # Examples /// /// ```rust @@ -609,6 +613,8 @@ impl<'a, K, V> LiteMap { /// Const version of [`LiteMap::get_indexed()`] for a slice store. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// # Panics /// /// Panics if the index is out of bounds. @@ -661,6 +667,8 @@ impl<'a, V> LiteMap<&'a str, V, &'a [(&'a str, V)]> { /// /// Also returns the index of the value. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// # Examples /// /// ```rust @@ -702,6 +710,8 @@ impl<'a, V> LiteMap<&'a [u8], V, &'a [(&'a [u8], V)]> { /// /// Also returns the index of the value. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// # Examples /// /// ```rust @@ -743,6 +753,8 @@ macro_rules! impl_const_get_with_index_for_integer { impl<'a, V> LiteMap<$integer, V, &'a [($integer, V)]> { /// Const function to get the value associated with an integer key, if it exists. /// + /// Note: This function will no longer be needed if const trait behavior is stabilized. + /// /// Also returns the index of the value. pub const fn const_get_with_index(&self, key: $integer) -> Option<(usize, &'a V)> { let mut i = 0;