From 1edc2b82cccdfc84864bff8dccc4c4b8c34a1574 Mon Sep 17 00:00:00 2001 From: forehalo Date: Fri, 15 Sep 2023 17:25:25 +0800 Subject: [PATCH] refactor: re-implement map type --- y-octo/benches/map_ops_benchmarks.rs | 6 +- y-octo/bin/memory_leak_test.rs | 2 +- y-octo/src/doc/codec/item.rs | 23 +- y-octo/src/doc/codec/refs.rs | 6 +- y-octo/src/doc/codec/utils/items.rs | 5 +- y-octo/src/doc/document.rs | 2 +- y-octo/src/doc/history.rs | 12 +- y-octo/src/doc/publisher.rs | 6 +- y-octo/src/doc/store.rs | 43 ++-- y-octo/src/doc/types/array.rs | 14 +- y-octo/src/doc/types/list/iterator.rs | 11 +- y-octo/src/doc/types/list/mod.rs | 5 +- y-octo/src/doc/types/map.rs | 293 +++++++++++++------------- y-octo/src/doc/types/mod.rs | 161 +------------- y-octo/src/doc/types/text.rs | 2 +- y-octo/src/doc/types/value.rs | 154 ++++++++++++++ y-octo/src/doc/types/xml.rs | 14 ++ 17 files changed, 380 insertions(+), 379 deletions(-) create mode 100644 y-octo/src/doc/types/value.rs create mode 100644 y-octo/src/doc/types/xml.rs diff --git a/y-octo/benches/map_ops_benchmarks.rs b/y-octo/benches/map_ops_benchmarks.rs index 55b0393..beb8f72 100644 --- a/y-octo/benches/map_ops_benchmarks.rs +++ b/y-octo/benches/map_ops_benchmarks.rs @@ -16,7 +16,7 @@ fn operations(c: &mut Criterion) { let doc = Doc::default(); let mut map = doc.get_or_create_map("test").unwrap(); for (idx, key) in base_text.iter().enumerate() { - map.insert(key, idx).unwrap(); + map.insert(key.to_string(), idx).unwrap(); } }); }); @@ -49,7 +49,7 @@ fn operations(c: &mut Criterion) { let doc = Doc::default(); let mut map = doc.get_or_create_map("test").unwrap(); for (idx, key) in base_text.iter().enumerate() { - map.insert(key, idx).unwrap(); + map.insert(key.to_string(), idx).unwrap(); } b.iter(|| { @@ -93,7 +93,7 @@ fn operations(c: &mut Criterion) { let doc = Doc::default(); let mut map = doc.get_or_create_map("test").unwrap(); for (idx, key) in base_text.iter().enumerate() { - map.insert(key, idx).unwrap(); + map.insert(key.to_string(), idx).unwrap(); } for key in &base_text { map.remove(key); diff --git a/y-octo/bin/memory_leak_test.rs b/y-octo/bin/memory_leak_test.rs index ab4d1f5..be49ea2 100644 --- a/y-octo/bin/memory_leak_test.rs +++ b/y-octo/bin/memory_leak_test.rs @@ -63,7 +63,7 @@ fn run_map_test() { let doc = Doc::default(); let mut map = doc.get_or_create_map("test").unwrap(); for (idx, key) in base_text.iter().enumerate() { - map.insert(key, idx).unwrap(); + map.insert(key.to_string(), idx).unwrap(); } } } diff --git a/y-octo/src/doc/codec/item.rs b/y-octo/src/doc/codec/item.rs index 7e81b81..ac5c3c8 100644 --- a/y-octo/src/doc/codec/item.rs +++ b/y-octo/src/doc/codec/item.rs @@ -1,5 +1,5 @@ use super::*; -use crate::sync::{Arc, AtomicU8, Ordering}; +use crate::sync::{AtomicU8, Ordering}; #[derive(Debug, Clone)] #[cfg_attr(test, derive(proptest_derive::Arbitrary))] @@ -118,16 +118,13 @@ pub(crate) struct Item { pub id: Id, pub origin_left_id: Option, pub origin_right_id: Option, - #[cfg_attr(all(test, not(loom)), proptest(value = "Somr::default()"))] - pub left: Somr, - #[cfg_attr(all(test, not(loom)), proptest(value = "Somr::default()"))] - pub right: Somr, + #[cfg_attr(all(test, not(loom)), proptest(value = "Somr::none()"))] + pub left: ItemRef, + #[cfg_attr(all(test, not(loom)), proptest(value = "Somr::none()"))] + pub right: ItemRef, pub parent: Option, pub parent_sub: Option, - // make content Arc, so we can share the content between items - // and item can be readonly and cloned fast. - // TODO: considering using Cow - pub content: Arc, + pub content: Content, #[cfg_attr(all(test, not(loom)), proptest(value = "ItemFlags::default()"))] pub flags: ItemFlags, } @@ -187,7 +184,7 @@ impl Default for Item { right: Somr::none(), parent: None, parent_sub: None, - content: Arc::new(Content::Deleted(0)), + content: Content::Deleted(0), flags: ItemFlags::from(0), } } @@ -216,7 +213,7 @@ impl Item { right, parent, parent_sub, - content: Arc::new(content), + content, flags, } } @@ -369,7 +366,7 @@ impl Item { // tag must not GC or Skip, this must process in parse_struct debug_assert_ne!(first_5_bit, 0); debug_assert_ne!(first_5_bit, 10); - Arc::new(Content::read(decoder, first_5_bit)?) + Content::read(decoder, first_5_bit)? }, left: Somr::none(), right: Somr::none(), @@ -380,7 +377,7 @@ impl Item { item.flags.set_countable(); } - if matches!(item.content.as_ref(), Content::Deleted(_)) { + if matches!(item.content, Content::Deleted(_)) { item.flags.set_deleted(); } diff --git a/y-octo/src/doc/codec/refs.rs b/y-octo/src/doc/codec/refs.rs index 0dc7daa..44741b7 100644 --- a/y-octo/src/doc/codec/refs.rs +++ b/y-octo/src/doc/codec/refs.rs @@ -1,5 +1,3 @@ -use sync::Arc; - use super::*; // make fields Copy + Clone without much effort @@ -81,7 +79,7 @@ impl Node { _ => { let item = Somr::new(Item::read(decoder, id, info, first_5_bit)?); - if let Content::Type(ty) = item.get().unwrap().content.as_ref() { + if let Content::Type(ty) = &item.get().unwrap().content { if let Some(mut ty) = ty.ty_mut() { ty.item = item.clone(); } @@ -271,7 +269,7 @@ impl Node { return false; } - match (Arc::make_mut(&mut litem.content), Arc::make_mut(&mut ritem.content)) { + match (&mut litem.content, &mut ritem.content) { (Content::Deleted(l), Content::Deleted(r)) => { *l += *r; } diff --git a/y-octo/src/doc/codec/utils/items.rs b/y-octo/src/doc/codec/utils/items.rs index 4b1d219..d2f1dff 100644 --- a/y-octo/src/doc/codec/utils/items.rs +++ b/y-octo/src/doc/codec/utils/items.rs @@ -1,5 +1,4 @@ use super::{item::item_flags, *}; -use crate::sync::Arc; pub(crate) struct ItemBuilder { item: Item, @@ -54,7 +53,7 @@ impl ItemBuilder { } pub fn content(mut self, content: Content) -> ItemBuilder { - self.item.content = Arc::new(content); + self.item.content = content; self } @@ -92,7 +91,7 @@ mod tests { assert_eq!(item.origin_right_id, Some(Id::new(4, 5))); assert!(matches!(item.parent, Some(Parent::String(text)) if text == "test")); assert_eq!(item.parent_sub, None); - assert_eq!(item.content, Arc::new(Content::Any(vec![Any::String("Hello".into())]))); + assert_eq!(item.content, Content::Any(vec![Any::String("Hello".into())])); }); } } diff --git a/y-octo/src/doc/document.rs b/y-octo/src/doc/document.rs index f6710d3..4145e6a 100644 --- a/y-octo/src/doc/document.rs +++ b/y-octo/src/doc/document.rs @@ -416,7 +416,7 @@ mod tests { let doc = Doc::new(); let mut map = doc.get_or_create_map("abc").unwrap(); - map.insert("a", 1).unwrap(); + map.insert("a".to_string(), 1).unwrap(); let binary = doc.encode_update_v1().unwrap(); let doc_new = Doc::new(); diff --git a/y-octo/src/doc/history.rs b/y-octo/src/doc/history.rs index 1184801..874033b 100644 --- a/y-octo/src/doc/history.rs +++ b/y-octo/src/doc/history.rs @@ -119,9 +119,7 @@ impl StoreHistory { histories.push(History { id: item.id.to_string(), parent: Self::parse_path(item, &parents), - content: Value::try_from(item.content.as_ref()) - .map(|v| v.to_string()) - .unwrap_or("unknown".to_owned()), + content: Value::from(&item.content).to_string(), }) } @@ -243,8 +241,8 @@ mod test { let doc = Doc::default(); let mut map = doc.get_or_create_map("map").unwrap(); let mut sub_map = doc.create_map().unwrap(); - map.insert("sub_map", sub_map.clone()).unwrap(); - sub_map.insert("key", "value").unwrap(); + map.insert("sub_map".to_string(), sub_map.clone()).unwrap(); + sub_map.insert("key".to_string(), "value").unwrap(); assert_eq!(doc.clients()[0], doc.client()); }); @@ -256,8 +254,8 @@ mod test { let doc = Doc::default(); let mut map = doc.get_or_create_map("map").unwrap(); let mut sub_map = doc.create_map().unwrap(); - map.insert("sub_map", sub_map.clone()).unwrap(); - sub_map.insert("key", "value").unwrap(); + map.insert("sub_map".to_string(), sub_map.clone()).unwrap(); + sub_map.insert("key".to_string(), "value").unwrap(); let history = StoreHistory::new(&doc.store); diff --git a/y-octo/src/doc/publisher.rs b/y-octo/src/doc/publisher.rs index 7171c93..b5143da 100644 --- a/y-octo/src/doc/publisher.rs +++ b/y-octo/src/doc/publisher.rs @@ -201,12 +201,12 @@ mod tests { }); let mut map = doc.get_or_create_map("test").unwrap(); - map.insert("key1", "val1").unwrap(); + map.insert("key1".to_string(), "val1").unwrap(); sleep(Duration::from_secs(1)); - map.insert("key2", "val2").unwrap(); - map.insert("key3", "val3").unwrap(); + map.insert("key2".to_string(), "val2").unwrap(); + map.insert("key3".to_string(), "val3").unwrap(); sleep(Duration::from_secs(1)); let mut array = doc.get_or_create_array("array").unwrap(); diff --git a/y-octo/src/doc/store.rs b/y-octo/src/doc/store.rs index 9490ac7..f9935da 100644 --- a/y-octo/src/doc/store.rs +++ b/y-octo/src/doc/store.rs @@ -174,7 +174,7 @@ impl DocStore { let id = (self.client(), self.get_state(self.client())).into(); let item = Somr::new(Item::new(id, content, left, right, parent, parent_sub)); - if let Content::Type(ty) = item.get().unwrap().content.as_ref() { + if let Content::Type(ty) = &item.get().unwrap().content { if let Some(mut ty) = ty.ty_mut() { ty.item = item.clone(); } @@ -375,15 +375,12 @@ impl DocStore { Some(Parent::Id(parent_id)) => { match self.get_node(*parent_id) { Some(Node::Item(parent_item)) => { - match &parent_item.get().unwrap().content.as_ref() { - Content::Type(ty) => { - item.parent.replace(Parent::Type(ty.clone())); - } - _ => { - // invalid parent, take it. - item.parent.take(); - // return Err(JwstCodecError::InvalidParent); - } + if let Content::Type(ty) = &parent_item.get().unwrap().content { + item.parent.replace(Parent::Type(ty.clone())); + } else { + // invalid parent, take it. + item.parent.take(); + // return Err(JwstCodecError::InvalidParent); } } _ => { @@ -406,7 +403,7 @@ impl DocStore { }; // assign store in ytype to ensure store exists if a ytype not has any children - if let Content::Type(ty) = Arc::make_mut(&mut item.content) { + if let Content::Type(ty) = &mut item.content { ty.store = Arc::downgrade(&store_ref); // we keep ty owner in dangling_types so the delete of any type will not make it @@ -450,7 +447,7 @@ impl DocStore { this.origin_left_id = left_ref.get().map(|left| left.last_id()); this.left = left_ref; } - this.content = Arc::new(this.content.split(offset)?.1); + this.content = this.content.split(offset)?.1; } if let Some(Parent::Type(ty)) = &this.parent { @@ -618,7 +615,7 @@ impl DocStore { } } - match item.content.as_ref() { + match &item.content { Content::Type(ty) => { if let Some(mut ty) = ty.ty_mut() { // items in ty are all refs, not owned @@ -851,7 +848,7 @@ impl DocStore { let _ = mem::replace(&mut items[idx], Node::new_gc(item.id, item.len())); } else { let mut item = unsafe { item_ref.get_mut_unchecked() }; - item.content = Arc::new(Content::Deleted(item.len())); + item.content = Content::Deleted(item.len()); item.flags.clear_countable(); debug_assert!(!item.flags.countable()); } @@ -1141,14 +1138,7 @@ mod tests { store.gc_delete_set().unwrap(); assert_eq!( - store - .get_node((1, 0)) - .unwrap() - .as_item() - .get() - .unwrap() - .content - .as_ref(), + &store.get_node((1, 0)).unwrap().as_item().get().unwrap().content, &Content::Deleted(4) ); }); @@ -1173,14 +1163,7 @@ mod tests { assert_eq!(arr.len(), 0); assert_eq!( - store - .get_node((1, 0)) - .unwrap() - .as_item() - .get() - .unwrap() - .content - .as_ref(), + &store.get_node((1, 0)).unwrap().as_item().get().unwrap().content, &Content::Deleted(1) ); diff --git a/y-octo/src/doc/types/array.rs b/y-octo/src/doc/types/array.rs index f68123d..a69181d 100644 --- a/y-octo/src/doc/types/array.rs +++ b/y-octo/src/doc/types/array.rs @@ -4,16 +4,16 @@ impl_type!(Array); impl ListType for Array {} -pub struct ArrayIter(ListIterator); +pub struct ArrayIter<'a>(ListIterator<'a>); -impl Iterator for ArrayIter { +impl Iterator for ArrayIter<'_> { type Item = Value; fn next(&mut self) -> Option { for item in self.0.by_ref() { if let Some(item) = item.get() { if item.countable() { - return Some(item.content.as_ref().try_into().unwrap()); + return Some(Value::try_from(&item.content).unwrap()); } } } @@ -39,9 +39,9 @@ impl Array { debug_assert!(offset == 0); if let Some(item) = item.get() { // TODO: rewrite to content.read(&mut [Any]) - return match item.content.as_ref() { + return match &item.content { Content::Any(any) => return any.first().map(|any| Value::Any(any.clone())), - _ => item.content.as_ref().try_into().map_or_else(|_| None, Some), + _ => Value::try_from(&item.content).map_or_else(|_| None, Some), }; } @@ -83,8 +83,6 @@ impl serde::Serialize for Array { #[cfg(test)] mod tests { - use yrs::{Array, Options, Text, Transact}; - use super::*; #[test] @@ -108,6 +106,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn test_ytext_equal() { + use yrs::{Options, Text, Transact}; let options = DocOptions::default(); let yrs_options = Options::default(); @@ -166,6 +165,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn test_yrs_array_decode() { + use yrs::{Array, Transact}; let update = { let doc = yrs::Doc::new(); let array = doc.get_or_insert_array("abc"); diff --git a/y-octo/src/doc/types/list/iterator.rs b/y-octo/src/doc/types/list/iterator.rs index 1908ae1..4e03305 100644 --- a/y-octo/src/doc/types/list/iterator.rs +++ b/y-octo/src/doc/types/list/iterator.rs @@ -1,10 +1,11 @@ use super::*; -pub(crate) struct ListIterator { +pub(crate) struct ListIterator<'a> { + pub(super) _lock: RwLockReadGuard<'a, YType>, pub(super) cur: Somr, } -impl Iterator for ListIterator { +impl Iterator for ListIterator<'_> { type Item = Somr; fn next(&mut self) -> Option { @@ -20,9 +21,3 @@ impl Iterator for ListIterator { None } } - -impl ListIterator { - pub fn new(start: Somr) -> Self { - Self { cur: start } - } -} diff --git a/y-octo/src/doc/types/list/mod.rs b/y-octo/src/doc/types/list/mod.rs index fc0055a..864fa3d 100644 --- a/y-octo/src/doc/types/list/mod.rs +++ b/y-octo/src/doc/types/list/mod.rs @@ -62,7 +62,10 @@ pub(crate) trait ListType: AsInner { fn iter_item(&self) -> ListIterator { let inner = self.as_inner().ty().unwrap(); - ListIterator::new(inner.start.clone()) + ListIterator { + cur: inner.start.clone(), + _lock: inner, + } } fn find_pos(&self, inner: &YType, index: u64) -> Option { diff --git a/y-octo/src/doc/types/map.rs b/y-octo/src/doc/types/map.rs index 22e7fb4..90b2c47 100644 --- a/y-octo/src/doc/types/map.rs +++ b/y-octo/src/doc/types/map.rs @@ -1,26 +1,24 @@ -use std::collections::HashMap; +use std::collections::hash_map::Iter; use super::*; use crate::{ doc::{AsInner, Node, Parent, YTypeRef}, - impl_type, - sync::Arc, - Content, JwstCodecResult, + impl_type, JwstCodecResult, }; impl_type!(Map); pub(crate) trait MapType: AsInner { - fn insert(&mut self, key: impl AsRef, value: impl Into) -> JwstCodecResult { + fn _insert>(&mut self, key: String, value: V) -> JwstCodecResult { if let Some((mut store, mut ty)) = self.as_inner().write() { - let left = ty.map.get(key.as_ref()).cloned(); + let left = ty.map.get(&key).cloned(); let item = store.create_item( - value.into(), + value.into().into(), left.unwrap_or(Somr::none()), Somr::none(), Some(Parent::Type(self.as_inner().clone())), - Some(key.as_ref().into()), + Some(key), ); store.integrate(Node::Item(item), 0, Some(&mut ty))?; } @@ -28,41 +26,26 @@ pub(crate) trait MapType: AsInner { Ok(()) } - fn keys(&self) -> Vec { - if let Some(ty) = self.as_inner().ty() { - ty.map.keys().cloned().collect() - } else { - vec![] - } - } - - fn get(&self, key: impl AsRef) -> Option> { + fn _get(&self, key: &str) -> Option { self.as_inner().ty().and_then(|ty| { - ty.map - .get(key.as_ref()) - .filter(|item| item.get().map(|item| !item.deleted()).unwrap_or(false)) - .map(|item| item.get().unwrap().content.clone()) - }) - } - - fn get_all(&self) -> HashMap> { - let mut ret = HashMap::default(); + ty.map.get(key).and_then(|item| { + if let Some(item) = item.get() { + if item.deleted() { + return None; + } - if let Some(ty) = self.as_inner().ty() { - for key in ty.map.keys() { - if let Some(content) = self.get(key) { - ret.insert(key.clone(), content); + Some(Value::from(&item.content)) + } else { + None } - } - } - - ret + }) + }) } - fn contains_key(&self, key: impl AsRef) -> bool { + fn _contains_key(&self, key: &str) -> bool { if let Some(ty) = self.as_inner().ty() { ty.map - .get(key.as_ref()) + .get(key) .and_then(|item| item.get()) .map_or(false, |item| !item.deleted()) } else { @@ -70,120 +53,155 @@ pub(crate) trait MapType: AsInner { } } - fn remove(&mut self, key: impl AsRef) -> bool { + fn _remove(&mut self, key: &str) { if let Some((mut store, mut ty)) = self.as_inner().write() { - let item = ty.map.get(key.as_ref()).cloned(); - if let Some(item) = item { + if let Some(item) = ty.map.get(key).cloned() { if let Some(item) = item.get() { store.delete_item(item, Some(&mut ty)); - return true; } } } + } - false + fn _len(&self) -> u64 { + self._keys().count() as u64 } - fn len(&self) -> u64 { - self.as_inner() - .ty() - .map(|ty| { - ty.map - .values() - .filter(|v| v.get().map(|item| !item.deleted()).unwrap_or(false)) - .count() as u64 - }) - .unwrap_or(0) + fn _iter(&self) -> EntriesInnerIterator { + let ty = self.as_inner().ty(); + + if let Some(ty) = ty { + let ty = Arc::new(ty); + + EntriesInnerIterator { + iter: Some(unsafe { &*Arc::as_ptr(&ty) }.map.iter()), + _lock: Some(ty), + } + } else { + EntriesInnerIterator { + _lock: None, + iter: None, + } + } + } + + fn _keys(&self) -> KeysIterator { + KeysIterator(self._iter()) } - fn iter(&self) -> MapIterator { - let inner = self.as_inner().ty().unwrap(); - let map = inner - .map - .iter() - .filter(|(_, v)| v.get().map(|item| !item.deleted()).unwrap_or(false)) - .map(|(k, v)| (k.clone(), v.clone())) - .collect::)>>(); + fn _values(&self) -> ValuesIterator { + ValuesIterator(self._iter()) + } - MapIterator { nodes: map, index: 0 } + fn _entries(&self) -> EntriesIterator { + EntriesIterator(self._iter()) } } -pub struct MapIterator { - pub(super) nodes: Vec<(String, Somr)>, - pub(super) index: usize, +pub(crate) struct EntriesInnerIterator<'a> { + _lock: Option>>, + iter: Option>, } -impl Iterator for MapIterator { - type Item = (String, Value); +pub struct KeysIterator<'a>(EntriesInnerIterator<'a>); +pub struct ValuesIterator<'a>(EntriesInnerIterator<'a>); +pub struct EntriesIterator<'a>(EntriesInnerIterator<'a>); + +impl<'a> Iterator for EntriesInnerIterator<'a> { + type Item = (&'a String, &'a Item); fn next(&mut self) -> Option { - let len = self.nodes.len(); - if self.index >= len { - return None; + if let Some(iter) = &mut self.iter { + for (k, v) in iter { + if let Some(item) = v.get() { + if !item.deleted() { + return Some((k, item)); + } + } + } + + None + } else { + None } + } +} - while self.index < len { - let (name, node) = self.nodes[self.index].clone(); - if let Some(item) = node.get() { - if item.deleted() { - continue; - } +impl<'a> Iterator for KeysIterator<'a> { + type Item = &'a String; + + fn next(&mut self) -> Option { + self.0.next().map(|(k, _)| k) + } +} - self.index += 1; +impl<'a> Iterator for ValuesIterator<'a> { + type Item = Value; - return item.content.as_ref().try_into().ok().map(|item| (name, item)); - } - } + fn next(&mut self) -> Option { + self.0.next().map(|(_, v)| Value::from(&v.content)) + } +} + +impl<'a> Iterator for EntriesIterator<'a> { + type Item = (&'a String, Value); - None + fn next(&mut self) -> Option { + self.0.next().map(|(k, v)| (k, Value::from(&v.content))) } } impl MapType for Map {} impl Map { - pub fn iter(&self) -> MapIterator { - MapType::iter(self) + #[inline(always)] + pub fn insert>(&mut self, key: String, value: V) -> JwstCodecResult { + self._insert(key, value) } - pub fn insert, V: Into>(&mut self, key: K, value: V) -> JwstCodecResult { - MapType::insert(self, key, value.into()) + #[inline(always)] + pub fn get(&self, key: &str) -> Option { + self._get(key) } - pub fn keys(&self) -> Vec { - MapType::keys(self) + #[inline(always)] + pub fn contains_key(&self, key: &str) -> bool { + self._contains_key(key) } - #[inline] - pub fn get>(&self, key: K) -> Option { - if let Some(content) = MapType::get(self, key) { - // TODO: rewrite to content.read(&mut [Any]) - return match content.as_ref() { - Content::Any(any) => return any.first().map(|any| Value::Any(any.clone())), - _ => content.as_ref().try_into().map_or_else(|_| None, Some), - }; - } + #[inline(always)] + pub fn remove(&mut self, key: &str) { + self._remove(key) + } - None + #[inline(always)] + pub fn len(&self) -> u64 { + self._len() } - #[inline] - pub fn remove>(&mut self, key: K) -> bool { - MapType::remove(self, key) + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len() == 0 } - #[inline] - pub fn contains_key>(&self, key: K) -> bool { - MapType::contains_key(self, key) + #[inline(always)] + pub fn iter(&self) -> EntriesIterator { + self._entries() } - pub fn len(&self) -> u64 { - MapType::len(self) + #[inline(always)] + pub fn entries(&self) -> EntriesIterator { + self._entries() } - pub fn is_empty(&self) -> bool { - self.len() == 0 + #[inline(always)] + pub fn keys(&self) -> KeysIterator { + self._keys() + } + + #[inline(always)] + pub fn values(&self) -> ValuesIterator { + self._values() } } @@ -209,7 +227,7 @@ mod tests { loom_model!({ let doc = Doc::new(); let mut map = doc.get_or_create_map("map").unwrap(); - map.insert("1", "value").unwrap(); + map.insert("1".to_string(), "value").unwrap(); assert_eq!(map.get("1").unwrap(), Value::Any(Any::String("value".to_string()))); assert!(!map.contains_key("nonexistent_key")); assert_eq!(map.len(), 1); @@ -225,8 +243,8 @@ mod tests { loom_model!({ let doc = Doc::new(); let mut map = doc.get_or_create_map("map").unwrap(); - map.insert("1", "value").unwrap(); - map.insert("2", false).unwrap(); + map.insert("1".to_string(), "value").unwrap(); + map.insert("2".to_string(), false).unwrap(); let binary = doc.encode_update_v1().unwrap(); let new_doc = Doc::new_from_binary(binary).unwrap(); @@ -242,50 +260,21 @@ mod tests { loom_model!({ let doc = Doc::new(); let mut map = doc.get_or_create_map("map").unwrap(); - map.insert("1", "value").unwrap(); - map.insert("1", "value2").unwrap(); + map.insert("1".to_string(), "value").unwrap(); + map.insert("1".to_string(), "value2").unwrap(); assert_eq!(map.get("1").unwrap(), Value::Any(Any::String("value2".to_string()))); assert_eq!(map.len(), 1); }); } - // #[test] - // fn test_map_iter() { - // let options = DocOptions { - // client: Some(rand::random()), - // guid: Some(nanoid::nanoid!()), - // }; - - // loom_model!({ - // let doc = Doc::with_options(options.clone()); - // let mut map = doc.get_or_create_map("map").unwrap(); - // map.insert("1", "value1").unwrap(); - // map.insert("2", "value2").unwrap(); - // let iter = map.iter(); - // assert_eq!(iter.count(), 2); - - // let iter = map.iter(); - // let mut vec: Vec<_> = iter.collect(); - // vec.sort_by(|a, b| a.id.cmp(&b.id)); - // assert_eq!( - // vec[0].content, - // Arc::new(Content::Any(vec![Any::String("value1".to_string())])) - // ); - // assert_eq!( - // vec[1].content, - // Arc::new(Content::Any(vec![Any::String("value2".to_string())])) - // ); - // }); - // } - #[test] fn test_map_re_encode() { loom_model!({ let binary = { let doc = Doc::new(); let mut map = doc.get_or_create_map("map").unwrap(); - map.insert("1", "value1").unwrap(); - map.insert("2", "value2").unwrap(); + map.insert("1".to_string(), "value1").unwrap(); + map.insert("2".to_string(), "value2").unwrap(); doc.encode_update_v1().unwrap() }; @@ -297,4 +286,26 @@ mod tests { } }); } + + #[test] + fn test_map_iter() { + loom_model!({ + let doc = Doc::new(); + let mut map = doc.get_or_create_map("map").unwrap(); + map.insert("1".to_string(), "value1").unwrap(); + map.insert("2".to_string(), "value2").unwrap(); + let mut vec = map.entries().collect::>(); + + // hashmap iteration is in random order instead of insert order + vec.sort_by(|a, b| a.0.cmp(b.0)); + + assert_eq!( + vec, + vec![ + (&"1".to_string(), Value::Any(Any::String("value1".to_string()))), + (&"2".to_string(), Value::Any(Any::String("value2".to_string()))) + ] + ) + }); + } } diff --git a/y-octo/src/doc/types/mod.rs b/y-octo/src/doc/types/mod.rs index 7d15797..f3a94fa 100644 --- a/y-octo/src/doc/types/mod.rs +++ b/y-octo/src/doc/types/mod.rs @@ -2,6 +2,8 @@ mod array; mod list; mod map; mod text; +mod value; +mod xml; use std::{ collections::{hash_map::Entry, HashMap}, @@ -12,10 +14,12 @@ pub use array::*; use list::*; pub use map::*; pub use text::*; +pub use value::*; +pub use xml::*; use super::{ store::{StoreRef, WeakStoreRef}, - Node, *, + *, }; use crate::{ sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -363,158 +367,3 @@ impl_variants!({ XMLText: 6 // Doc: 9? }); - -// TODO: move to separated impl files. -impl_type!(XMLElement); -impl_type!(XMLFragment); -impl_type!(XMLText); -impl_type!(XMLHook); - -#[derive(Debug, PartialEq)] -pub enum Value { - Any(Any), - Doc(Doc), - Array(Array), - Map(Map), - Text(Text), - XMLElement(XMLElement), - XMLFragment(XMLFragment), - XMLHook(XMLHook), - XMLText(XMLText), -} - -impl Value { - pub fn to_any(&self) -> Option { - match self { - Value::Any(any) => Some(any.clone()), - _ => None, - } - } - - pub fn to_array(&self) -> Option { - match self { - Value::Array(array) => Some(array.clone()), - _ => None, - } - } - - pub fn to_map(&self) -> Option { - match self { - Value::Map(map) => Some(map.clone()), - _ => None, - } - } - - pub fn to_text(&self) -> Option { - match self { - Value::Text(text) => Some(text.clone()), - _ => None, - } - } - - pub fn from_vec>(el: Vec) -> Self { - Value::Any(Any::Array(el.into_iter().map(|item| item.into()).collect::>())) - } -} - -impl TryFrom<&Content> for Value { - type Error = JwstCodecError; - fn try_from(value: &Content) -> Result { - Ok(match value { - Content::Any(any) => Value::Any(if any.len() == 1 { - any[0].clone() - } else { - Any::Array(any.clone()) - }), - Content::String(s) => Value::Any(Any::String(s.clone())), - Content::Json(json) => Value::Any(Any::Array( - json.iter() - .map(|item| { - if let Some(s) = item { - Any::String(s.clone()) - } else { - Any::Undefined - } - }) - .collect::>(), - )), - Content::Binary(buf) => Value::Any(Any::Binary(buf.clone())), - Content::Embed(v) => Value::Any(v.clone()), - Content::Type(ty) => match ty.ty().unwrap().kind { - YTypeKind::Array => Value::Array(Array::from_unchecked(ty.clone())), - YTypeKind::Map => Value::Map(Map::from_unchecked(ty.clone())), - YTypeKind::Text => Value::Text(Text::from_unchecked(ty.clone())), - YTypeKind::XMLElement => Value::XMLElement(XMLElement::from_unchecked(ty.clone())), - YTypeKind::XMLFragment => Value::XMLFragment(XMLFragment::from_unchecked(ty.clone())), - YTypeKind::XMLHook => Value::XMLHook(XMLHook::from_unchecked(ty.clone())), - YTypeKind::XMLText => Value::XMLText(XMLText::from_unchecked(ty.clone())), - // actually unreachable - YTypeKind::Unknown => return Err(JwstCodecError::TypeCastError("unknown")), - }, - Content::Doc { guid: _, opts } => Value::Doc(DocOptions::try_from(opts.clone())?.build()), - Content::Format { .. } => return Err(JwstCodecError::TypeCastError("unimplemented: Format")), - Content::Deleted(_) => return Err(JwstCodecError::TypeCastError("unimplemented: Deleted")), - }) - } -} - -impl From for Content { - fn from(value: Value) -> Self { - match value { - Value::Any(any) => Content::from(any), - Value::Doc(doc) => Content::Doc { - guid: doc.guid().to_owned(), - opts: Any::from(doc.options().clone()), - }, - Value::Array(v) => Content::Type(v.0), - Value::Map(v) => Content::Type(v.0), - Value::Text(v) => Content::Type(v.0), - Value::XMLElement(v) => Content::Type(v.0), - Value::XMLFragment(v) => Content::Type(v.0), - Value::XMLHook(v) => Content::Type(v.0), - Value::XMLText(v) => Content::Type(v.0), - } - } -} - -impl> From for Value { - fn from(value: T) -> Self { - Value::Any(value.into()) - } -} - -impl From for Value { - fn from(value: Doc) -> Self { - Value::Doc(value) - } -} - -impl ToString for Value { - fn to_string(&self) -> String { - match self { - Value::Any(any) => any.to_string(), - Value::Text(text) => text.to_string(), - _ => String::default(), - } - } -} - -impl serde::Serialize for Value { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - Self::Any(any) => any.serialize(serializer), - Self::Array(array) => array.serialize(serializer), - Self::Map(map) => map.serialize(serializer), - Self::Text(text) => text.serialize(serializer), - // Self::XMLElement(xml_element) => xml_element.serialize(serializer), - // Self::XMLFragment(xml_fragment) => xml_fragment.serialize(serializer), - // Self::XMLHook(xml_hook) => xml_hook.serialize(serializer), - // Self::XMLText(xml_text) => xml_text.serialize(serializer), - // Self::Doc(doc) => doc.serialize(serializer), - _ => serializer.serialize_none(), - } - } -} diff --git a/y-octo/src/doc/types/text.rs b/y-octo/src/doc/types/text.rs index 275f1f8..694affa 100644 --- a/y-octo/src/doc/types/text.rs +++ b/y-octo/src/doc/types/text.rs @@ -32,7 +32,7 @@ impl ToString for Text { let mut ret = String::with_capacity(self.len() as usize); self.iter_item().fold(&mut ret, |ret, item| { - if let Content::String(str) = item.get().unwrap().content.as_ref() { + if let Content::String(str) = &item.get().unwrap().content { ret.push_str(str); } diff --git a/y-octo/src/doc/types/value.rs b/y-octo/src/doc/types/value.rs new file mode 100644 index 0000000..de1d5d3 --- /dev/null +++ b/y-octo/src/doc/types/value.rs @@ -0,0 +1,154 @@ +use super::*; + +#[derive(Debug, PartialEq)] +pub enum Value { + Any(Any), + Doc(Doc), + Array(Array), + Map(Map), + Text(Text), + XMLElement(XMLElement), + XMLFragment(XMLFragment), + XMLHook(XMLHook), + XMLText(XMLText), +} + +impl Value { + pub fn to_any(&self) -> Option { + match self { + Value::Any(any) => Some(any.clone()), + _ => None, + } + } + + pub fn to_array(&self) -> Option { + match self { + Value::Array(array) => Some(array.clone()), + _ => None, + } + } + + pub fn to_map(&self) -> Option { + match self { + Value::Map(map) => Some(map.clone()), + _ => None, + } + } + + pub fn to_text(&self) -> Option { + match self { + Value::Text(text) => Some(text.clone()), + _ => None, + } + } + + pub fn from_vec>(el: Vec) -> Self { + Value::Any(Any::Array(el.into_iter().map(|item| item.into()).collect::>())) + } +} + +impl From<&Content> for Value { + fn from(value: &Content) -> Value { + match value { + Content::Any(any) => Value::Any(if any.len() == 1 { + any[0].clone() + } else { + Any::Array(any.clone()) + }), + Content::String(s) => Value::Any(Any::String(s.clone())), + Content::Json(json) => Value::Any(Any::Array( + json.iter() + .map(|item| { + if let Some(s) = item { + Any::String(s.clone()) + } else { + Any::Undefined + } + }) + .collect::>(), + )), + Content::Binary(buf) => Value::Any(Any::Binary(buf.clone())), + Content::Embed(v) => Value::Any(v.clone()), + Content::Type(ty) => match ty.ty().unwrap().kind { + YTypeKind::Array => Value::Array(Array::from_unchecked(ty.clone())), + YTypeKind::Map => Value::Map(Map::from_unchecked(ty.clone())), + YTypeKind::Text => Value::Text(Text::from_unchecked(ty.clone())), + YTypeKind::XMLElement => Value::XMLElement(XMLElement::from_unchecked(ty.clone())), + YTypeKind::XMLFragment => Value::XMLFragment(XMLFragment::from_unchecked(ty.clone())), + YTypeKind::XMLHook => Value::XMLHook(XMLHook::from_unchecked(ty.clone())), + YTypeKind::XMLText => Value::XMLText(XMLText::from_unchecked(ty.clone())), + // actually unreachable + YTypeKind::Unknown => Value::Any(Any::Undefined), + }, + Content::Doc { guid: _, opts } => Value::Doc( + DocOptions::try_from(opts.clone()) + .expect("Failed to parse doc options") + .build(), + ), + Content::Format { .. } => unimplemented!(), + // actually unreachable + Content::Deleted(_) => Value::Any(Any::Undefined), + } + } +} + +impl From for Content { + fn from(value: Value) -> Self { + match value { + Value::Any(any) => Content::from(any), + Value::Doc(doc) => Content::Doc { + guid: doc.guid().to_owned(), + opts: Any::from(doc.options().clone()), + }, + Value::Array(v) => Content::Type(v.0), + Value::Map(v) => Content::Type(v.0), + Value::Text(v) => Content::Type(v.0), + Value::XMLElement(v) => Content::Type(v.0), + Value::XMLFragment(v) => Content::Type(v.0), + Value::XMLHook(v) => Content::Type(v.0), + Value::XMLText(v) => Content::Type(v.0), + } + } +} + +impl> From for Value { + fn from(value: T) -> Self { + Value::Any(value.into()) + } +} + +impl From for Value { + fn from(value: Doc) -> Self { + Value::Doc(value) + } +} + +impl ToString for Value { + fn to_string(&self) -> String { + match self { + Value::Any(any) => any.to_string(), + Value::Text(text) => text.to_string(), + _ => String::default(), + } + } +} + +impl serde::Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Self::Any(any) => any.serialize(serializer), + Self::Array(array) => array.serialize(serializer), + Self::Map(map) => map.serialize(serializer), + Self::Text(text) => text.serialize(serializer), + // Self::XMLElement(xml_element) => xml_element.serialize(serializer), + // Self::XMLFragment(xml_fragment) => xml_fragment.serialize(serializer), + // Self::XMLHook(xml_hook) => xml_hook.serialize(serializer), + // Self::XMLText(xml_text) => xml_text.serialize(serializer), + // Self::Doc(doc) => doc.serialize(serializer), + _ => serializer.serialize_none(), + } + } +} diff --git a/y-octo/src/doc/types/xml.rs b/y-octo/src/doc/types/xml.rs new file mode 100644 index 0000000..49af7a0 --- /dev/null +++ b/y-octo/src/doc/types/xml.rs @@ -0,0 +1,14 @@ +use super::list::ListType; +use crate::impl_type; + +impl_type!(XMLElement); +impl ListType for XMLElement {} + +impl_type!(XMLFragment); +impl ListType for XMLFragment {} + +impl_type!(XMLText); +impl ListType for XMLText {} + +impl_type!(XMLHook); +impl ListType for XMLHook {}