diff --git a/src/index_map.rs b/src/index_map.rs index daa79bc..fb16614 100644 --- a/src/index_map.rs +++ b/src/index_map.rs @@ -34,6 +34,24 @@ impl FrozenIndexMap { impl FrozenIndexMap { // these should never return &K or &V // these should never delete any entries + // + /// If the key exists in the map, returns a reference to the corresponding + /// value, otherwise inserts a new entry in the map for that key + /// and returns a reference to the generated value. + /// + /// Existing values are never overwritten. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Example + /// ``` + /// use elsa::index_map::FrozenIndexMap; + /// let map = FrozenIndexMap::new(); + /// assert_eq!(map.insert(1, Box::new("a")), &"a"); + /// assert_eq!(map.insert(1, Box::new("b")), &"a"); + /// ``` pub fn insert(&self, k: K, v: V) -> &V::Target { assert!(!self.in_use.get()); self.in_use.set(true); @@ -47,6 +65,24 @@ impl FrozenIndexMap { // these should never return &K or &V // these should never delete any entries + // + /// If the key exists in the map, returns a reference to the corresponding + /// value and its index, otherwise inserts a new entry in the map for that key + /// and returns a reference to the generated value and its index. + /// + /// Existing values are never overwritten. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Example + /// ``` + /// use elsa::index_map::FrozenIndexMap; + /// let map = FrozenIndexMap::new(); + /// assert_eq!(map.insert_full(12, Box::new("a")), (0, &"a")); + /// assert_eq!(map.insert_full(12, Box::new("b")), (0, &"a")); + /// ``` pub fn insert_full(&self, k: K, v: V) -> (usize, &V::Target) { assert!(!self.in_use.get()); self.in_use.set(true); @@ -91,6 +127,23 @@ impl FrozenIndexMap { ret } + /// Returns a reference to the key-value mapping corresponding to an index. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use elsa::index_map::FrozenIndexMap; + /// + /// let map = FrozenIndexMap::new(); + /// let (idx, _ref) = map.insert_full(Box::new("foo"), Box::new("a")); + /// assert_eq!(idx, 0); + /// assert_eq!(map.get_index(idx), Some((&"foo", &"a"))); + /// assert_eq!(map.get_index(idx + 1), None); + /// ``` pub fn get_index(&self, index: usize) -> Option<(&K::Target, &V::Target)> where K: StableDeref, diff --git a/src/index_set.rs b/src/index_set.rs index f0fa6ea..735810b 100644 --- a/src/index_set.rs +++ b/src/index_set.rs @@ -31,6 +31,24 @@ impl FrozenIndexSet { impl FrozenIndexSet { // these should never return &T // these should never delete any entries + // + /// If the value exists in the set, returns a reference to the corresponding + /// value, otherwise inserts a new entry in the set for that value + /// and returns a reference to it. + /// + /// Existing values are never overwritten. + /// + /// # Example + /// ``` + /// use elsa::index_set::FrozenIndexSet; + /// let set = FrozenIndexSet::new(); + /// let a_ref = set.insert(Box::new("a")); + /// let aa = "a"; + /// let other_a_ref = unsafe { aa.as_ptr() as *const &str}; + /// let other_a = Box::new(aa); + /// assert!(!std::ptr::eq(a_ref, other_a_ref)); + /// assert!(std::ptr::eq(a_ref, set.insert(other_a))); + /// ``` pub fn insert(&self, value: T) -> &T::Target { assert!(!self.in_use.get()); self.in_use.set(true); @@ -45,6 +63,19 @@ impl FrozenIndexSet { // these should never return &T // these should never delete any entries + /// If the key exists in the set, returns a reference to the corresponding + /// value and its index, otherwise inserts a new entry in the set for that value + /// and returns a reference to it and its index. + /// + /// Existing values are never overwritten. + /// + /// # Example + /// ``` + /// use elsa::index_set::FrozenIndexSet; + /// let map = FrozenIndexSet::new(); + /// assert_eq!(map.insert_full(Box::new("a")), (0, &"a")); + /// assert_eq!(map.insert_full(Box::new("b")), (1, &"b")); + /// ``` pub fn insert_full(&self, value: T) -> (usize, &T::Target) { assert!(!self.in_use.get()); self.in_use.set(true); @@ -84,6 +115,18 @@ impl FrozenIndexSet { // } // } + /// Returns a reference to the value passed as argument if present in the set. + /// + /// # Examples + /// + /// ``` + /// use elsa::index_set::FrozenIndexSet; + /// + /// let set = FrozenIndexSet::new(); + /// set.insert(Box::new("a")); + /// assert_eq!(set.get(&Box::new("a")), Some(&"a")); + /// assert_eq!(set.get(&Box::new("b")), None); + /// ``` pub fn get(&self, k: &Q) -> Option<&T::Target> where T: Borrow, @@ -99,6 +142,19 @@ impl FrozenIndexSet { ret } + /// Returns a reference to the value passed as argument if present in the set, + /// along with its index + /// + /// # Examples + /// + /// ``` + /// use elsa::index_set::FrozenIndexSet; + /// + /// let set = FrozenIndexSet::new(); + /// set.insert(Box::new("a")); + /// assert_eq!(set.get_full(&Box::new("a")), Some((0, &"a"))); + /// assert_eq!(set.get_full(&Box::new("b")), None); + /// ``` pub fn get_full(&self, k: &Q) -> Option<(usize, &T::Target)> where T: Borrow, @@ -114,6 +170,19 @@ impl FrozenIndexSet { ret } + /// Returns a reference to value at the index passed as argument, if + /// present in the set. + /// + /// # Examples + /// + /// ``` + /// use elsa::index_set::FrozenIndexSet; + /// + /// let set = FrozenIndexSet::new(); + /// set.insert(Box::new("a")); + /// assert_eq!(set.get_index(0), Some(&"a")); + /// assert_eq!(set.get_index(1), None); + /// ``` pub fn get_index(&self, index: usize) -> Option<&T::Target> { assert!(!self.in_use.get()); self.in_use.set(true); diff --git a/src/sync.rs b/src/sync.rs index 44cdab8..1b32369 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -12,10 +12,12 @@ use std::borrow::Borrow; use std::cmp::Eq; use std::collections::BTreeMap; use std::collections::HashMap; +use std::fmt; use std::hash::Hash; use std::iter::{FromIterator, IntoIterator}; use std::ops::Index; +use std::sync::TryLockError; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicPtr; use std::sync::atomic::AtomicUsize; @@ -28,6 +30,28 @@ pub struct FrozenMap { map: RwLock>, } +impl fmt::Debug for FrozenMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.map.try_read() { + Ok(guard) => { + guard.fmt(f) + }, + Err(TryLockError::Poisoned(err)) => { + f.debug_tuple("FrozenMap").field(&&**err.get_ref()).finish() + } + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + f.debug_tuple("FrozenMap").field(&LockedPlaceholder).finish() + }, + } + } +} + impl Default for FrozenMap { fn default() -> Self { Self { @@ -42,6 +66,15 @@ impl FrozenMap { } } +impl From> for FrozenVec { + fn from(vec: Vec) -> Self { + Self { + vec: RwLock::new(vec), + } + } +} + + impl FrozenMap { // these should never return &K or &V // these should never delete any entries @@ -396,6 +429,28 @@ pub struct FrozenVec { vec: RwLock>, } +impl fmt::Debug for FrozenVec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.vec.try_read() { + Ok(guard) => { + guard.fmt(f) + }, + Err(TryLockError::Poisoned(err)) => { + f.debug_tuple("FrozenMap").field(&&**err.get_ref()).finish() + } + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + f.debug_tuple("FrozenMap").field(&LockedPlaceholder).finish() + }, + } + } +} + impl FrozenVec { /// Returns the number of elements in the vector. pub fn len(&self) -> usize { @@ -455,6 +510,53 @@ impl FrozenVec { let vec = self.vec.read().unwrap(); unsafe { vec.get(index).map(|x| &*(&**x as *const T::Target)) } } + + /// Returns an iterator over the vector. + pub fn iter(&self) -> Iter<'_, T> { + self.into_iter() + } +} + +/// Iterator over FrozenVec, obtained via `.iter()` +/// +/// It is safe to push to the vector during iteration +#[derive(Debug)] +pub struct Iter<'a, T> { + vec: &'a FrozenVec, + idx: usize, +} + +impl<'a, T: StableDeref> Iterator for Iter<'a, T> { + type Item = &'a T::Target; + fn next(&mut self) -> Option<&'a T::Target> { + if let Some(ret) = self.vec.get(self.idx) { + self.idx += 1; + Some(ret) + } else { + None + } + } +} + +impl<'a, T: StableDeref> IntoIterator for &'a FrozenVec { + type Item = &'a T::Target; + type IntoIter = Iter<'a, T>; + fn into_iter(self) -> Iter<'a, T> { + Iter { vec: self, idx: 0 } + } +} + +#[test] +fn test_iteration() { + let vec = vec!["a", "b", "c", "d"]; + let frozen: FrozenVec<_> = vec.clone().into(); + + assert_eq!(vec, frozen.iter().collect::>()); + for (e1, e2) in vec.iter().zip(frozen.iter()) { + assert_eq!(*e1, e2); + } + + assert_eq!(vec.len(), frozen.iter().count()) } impl FrozenVec {