use super::core::{trie_ref_root, TrieRefNode};
use super::snapshot::PathMapSnapshot;
use super::zipper::PathMapZipper;
use crate::iterator::DictionaryIterator;
use crate::value::DictionaryValue;
use crate::{Dictionary, MappedDictionary, SyncStrategy};
use pathmap::zipper::TrieRefOwned;
use pathmap::PathMap;
use std::sync::Arc;
use crate::sync_compat::RwLock;
#[derive(Clone, Debug)]
pub struct PathMapDictionary<V: DictionaryValue = ()> {
pub(crate) map: Arc<RwLock<PathMap<V>>>,
term_count: Arc<RwLock<usize>>,
}
impl<V: DictionaryValue> PathMapDictionary<V> {
pub fn new() -> Self
where
V: Default,
{
Self {
map: Arc::new(RwLock::new(PathMap::new())),
term_count: Arc::new(RwLock::new(0)),
}
}
pub fn from_terms<I, S>(terms: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
V: Default,
{
let mut map = PathMap::new();
let mut count = 0;
for term in terms {
let bytes = term.as_ref().as_bytes();
if map.insert(bytes, V::default()).is_none() {
count += 1;
}
}
Self {
map: Arc::new(RwLock::new(map)),
term_count: Arc::new(RwLock::new(count)),
}
}
pub fn from_terms_with_values<I, S>(terms: I) -> Self
where
I: IntoIterator<Item = (S, V)>,
S: AsRef<str>,
{
let mut map = PathMap::new();
let mut count = 0;
for (term, value) in terms {
let bytes = term.as_ref().as_bytes();
if map.insert(bytes, value).is_none() {
count += 1;
}
}
Self {
map: Arc::new(RwLock::new(map)),
term_count: Arc::new(RwLock::new(count)),
}
}
pub fn insert(&self, term: &str) -> bool
where
V: Default,
{
self.insert_with_value(term, V::default())
}
pub fn insert_with_value(&self, term: &str, value: V) -> bool {
let bytes = term.as_bytes();
let mut map = self.map.write();
let mut count = self.term_count.write();
if map.insert(bytes, value).is_none() {
*count += 1;
true
} else {
false
}
}
pub fn remove(&self, term: &str) -> bool {
let bytes = term.as_bytes();
let mut map = self.map.write();
let mut count = self.term_count.write();
if map.remove_val_at(bytes, true).is_some() {
*count = count.saturating_sub(1);
true
} else {
false
}
}
pub fn clear(&self) {
let mut map = self.map.write();
let mut count = self.term_count.write();
*map = PathMap::new();
*count = 0;
}
pub fn term_count(&self) -> usize {
*self.term_count.read()
}
pub fn serialize_paths<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
use pathmap::paths_serialization::serialize_paths;
let map = self.map.read();
serialize_paths(map.read_zipper(), writer)?;
Ok(())
}
pub fn deserialize_paths<R: std::io::Read>(reader: R) -> std::io::Result<Self>
where
V: Default,
{
use pathmap::paths_serialization::deserialize_paths;
use pathmap::zipper::ZipperIteration;
let mut map = PathMap::new();
deserialize_paths(map.write_zipper(), reader, V::default())?;
let count = {
let mut rz = map.read_zipper();
let mut count = 0;
while rz.to_next_val() {
count += 1;
}
count
};
Ok(Self {
map: Arc::new(RwLock::new(map)),
term_count: Arc::new(RwLock::new(count)),
})
}
pub fn get_value(&self, term: &str) -> Option<V> {
let bytes = term.as_bytes();
let map = self.map.read();
map.get_val_at(bytes).cloned()
}
pub fn update_or_insert<F>(&self, term: &str, default_value: V, update_fn: F) -> bool
where
F: Fn(&mut V),
{
let bytes = term.as_bytes();
let mut map = self.map.write();
let mut count = self.term_count.write();
let existed = map.get_val_at(bytes).is_some();
let value = map.get_val_or_set_mut_at(bytes, default_value);
update_fn(value);
if !existed {
*count += 1;
}
!existed
}
pub fn iter_bytes(&self) -> DictionaryIterator<PathMapZipper<V>> {
let zipper = PathMapZipper::new_from_dict(self);
DictionaryIterator::new(zipper)
}
pub fn iter(&self) -> impl Iterator<Item = (String, V)> + '_ {
self.iter_bytes()
.map(|(bytes, value)| (String::from_utf8_lossy(&bytes).into_owned(), value))
}
pub fn snapshot(&self) -> PathMapSnapshot<V> {
PathMapSnapshot::from_map(self.map.read().clone()).with_len(self.term_count())
}
}
impl<V: DictionaryValue> IntoIterator for &PathMapDictionary<V> {
type Item = (Vec<u8>, V);
type IntoIter = DictionaryIterator<PathMapZipper<V>>;
fn into_iter(self) -> Self::IntoIter {
self.iter_bytes()
}
}
impl<V: DictionaryValue + Default> Default for PathMapDictionary<V> {
fn default() -> Self {
Self::new()
}
}
impl<V: DictionaryValue> Dictionary for PathMapDictionary<V> {
type Node = PathMapNode<V>;
#[inline]
fn root(&self) -> Self::Node {
let snapshot = self.map.read().clone();
TrieRefNode::new(trie_ref_root(snapshot))
}
#[inline]
fn len(&self) -> Option<usize> {
Some(self.term_count())
}
#[inline]
fn sync_strategy(&self) -> SyncStrategy {
SyncStrategy::ExternalSync
}
}
impl<V: DictionaryValue> MappedDictionary for PathMapDictionary<V> {
type Value = V;
fn get_value(&self, term: &str) -> Option<Self::Value> {
PathMapDictionary::get_value(self, term)
}
}
impl<V: DictionaryValue + Default> crate::MutableDictionary for PathMapDictionary<V> {
fn insert(&self, term: &str) -> bool {
PathMapDictionary::insert(self, term)
}
fn remove(&self, term: &str) -> bool {
PathMapDictionary::remove(self, term)
}
}
impl<V: DictionaryValue> crate::MutableMappedDictionary for PathMapDictionary<V> {
fn insert_with_value(&self, term: &str, value: Self::Value) -> bool {
PathMapDictionary::insert_with_value(self, term, value)
}
fn update_or_insert<F>(&self, term: &str, default_value: Self::Value, update_fn: F) -> bool
where
F: Fn(&mut Self::Value),
{
PathMapDictionary::update_or_insert(self, term, default_value, update_fn)
}
fn union_with<F>(&self, other: &Self, merge_fn: F) -> usize
where
F: Fn(&Self::Value, &Self::Value) -> Self::Value,
Self::Value: Clone,
{
let other_map = other.map.read();
let mut self_map = self.map.write();
let mut self_term_count = self.term_count.write();
let mut processed = 0;
for (key_bytes, other_value) in other_map.iter() {
processed += 1;
if let Some(self_value) = self_map.get(&key_bytes) {
let merged = merge_fn(self_value, other_value);
self_map.insert(&key_bytes, merged);
} else {
self_map.insert(&key_bytes, other_value.clone());
*self_term_count += 1;
}
}
processed
}
}
pub type PathMapNode<V> = TrieRefNode<V, TrieRefOwned<V>>;
#[cfg(test)]
mod tests {
use super::*;
use crate::{DictionaryNode, MappedDictionaryNode};
#[test]
fn test_pathmap_dictionary_creation() {
let dict: PathMapDictionary<()> =
PathMapDictionary::from_terms(vec!["hello", "world", "test"]);
assert_eq!(dict.len(), Some(3));
}
#[test]
fn test_pathmap_dictionary_contains() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["hello", "world"]);
assert!(dict.contains("hello"));
assert!(dict.contains("world"));
assert!(!dict.contains("goodbye"));
}
#[test]
fn test_pathmap_node_traversal() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["test", "testing"]);
let root = dict.root();
let t = root.transition(b't').expect("should have 't'");
let e = t.transition(b'e').expect("should have 'e'");
let s = e.transition(b's').expect("should have 's'");
let t2 = s.transition(b't').expect("should have second 't'");
assert!(t2.is_final(), "'test' should be final");
let i = t2.transition(b'i').expect("should have 'i'");
assert!(!i.is_final(), "'testi' should not be final");
}
#[test]
fn test_pathmap_node_edges() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["ab", "ac", "ad"]);
let root = dict.root();
let a = root.transition(b'a').expect("should have 'a'");
let edges: Vec<_> = a.edges().map(|(byte, _)| byte).collect();
assert_eq!(edges.len(), 3);
assert!(edges.contains(&b'b'));
assert!(edges.contains(&b'c'));
assert!(edges.contains(&b'd'));
}
#[test]
fn test_pathmap_dictionary_insert() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["test"]);
assert_eq!(dict.term_count(), 1);
assert!(dict.insert("testing"));
assert_eq!(dict.term_count(), 2);
assert!(dict.contains("testing"));
assert!(!dict.insert("test"));
assert_eq!(dict.term_count(), 2);
}
#[test]
fn test_pathmap_dictionary_remove() {
let dict: PathMapDictionary<()> =
PathMapDictionary::from_terms(vec!["test", "testing", "tested"]);
assert_eq!(dict.term_count(), 3);
assert!(dict.remove("testing"));
assert_eq!(dict.term_count(), 2);
assert!(!dict.contains("testing"));
assert!(dict.contains("test"));
assert!(dict.contains("tested"));
assert!(!dict.remove("nonexistent"));
assert_eq!(dict.term_count(), 2);
}
#[test]
fn test_pathmap_dictionary_clear() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["test", "testing"]);
assert_eq!(dict.term_count(), 2);
dict.clear();
assert_eq!(dict.term_count(), 0);
assert!(!dict.contains("test"));
assert!(!dict.contains("testing"));
}
#[test]
fn test_pathmap_dictionary_concurrent_operations() {
use std::thread;
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["test"]);
let dict_clone = dict.clone();
let handle = thread::spawn(move || {
dict_clone.insert("testing");
dict_clone.insert("tested");
});
let _ = dict.contains("test");
handle.join().unwrap();
assert!(dict.contains("test"));
assert!(dict.contains("testing"));
assert!(dict.contains("tested"));
assert_eq!(dict.term_count(), 3);
}
#[test]
fn test_pathmap_dictionary_with_values() {
let terms_with_values = vec![("hello", 1u32), ("world", 2u32), ("test", 3u32)];
let dict: PathMapDictionary<u32> =
PathMapDictionary::from_terms_with_values(terms_with_values);
assert_eq!(dict.len(), Some(3));
assert!(dict.contains("hello"));
assert_eq!(dict.get_value("hello"), Some(1));
assert_eq!(dict.get_value("world"), Some(2));
assert_eq!(dict.get_value("test"), Some(3));
assert_eq!(dict.get_value("missing"), None);
}
#[test]
fn test_pathmap_dictionary_insert_with_value() {
let dict: PathMapDictionary<u32> = PathMapDictionary::new();
assert!(dict.insert_with_value("hello", 42));
assert_eq!(dict.get_value("hello"), Some(42));
assert!(!dict.insert_with_value("hello", 99));
assert_eq!(dict.get_value("hello"), Some(99));
assert_eq!(dict.term_count(), 1);
}
#[test]
fn test_pathmap_node_value() {
let terms_with_values = vec![("hello", 10u32), ("world", 20u32)];
let dict: PathMapDictionary<u32> =
PathMapDictionary::from_terms_with_values(terms_with_values);
let root = dict.root();
let h = root.transition(b'h').expect("should have 'h'");
let e = h.transition(b'e').expect("should have 'e'");
let l1 = e.transition(b'l').expect("should have first 'l'");
let l2 = l1.transition(b'l').expect("should have second 'l'");
let o = l2.transition(b'o').expect("should have 'o'");
assert!(o.is_final());
assert_eq!(o.value(), Some(10));
assert!(!h.is_final());
assert_eq!(h.value(), None);
}
#[test]
fn test_snapshot_is_consistent_and_decoupled() {
let dict: PathMapDictionary<u32> = PathMapDictionary::new();
dict.insert_with_value("alpha", 1);
let snap = dict.snapshot();
assert_eq!(snap.len(), Some(1));
assert!(snap.contains("alpha"));
assert_eq!(snap.get_value("alpha"), Some(1));
dict.insert_with_value("beta", 2);
assert!(!snap.contains("beta"));
assert!(dict.contains("beta"));
assert!(dict.root().transition(b'b').is_some());
}
#[test]
fn test_root_snapshot_isolation_during_mutation() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["cat"]);
let root_before = dict.root();
dict.insert("car");
let a = root_before
.transition(b'c')
.and_then(|c| c.transition(b'a'))
.expect("'ca'");
let mut labels: Vec<u8> = a.edges().map(|(b, _)| b).collect();
labels.sort_unstable();
assert_eq!(
labels,
vec![b't'],
"pre-mutation snapshot must not see 'car'"
);
}
}