From f20e135301a30004ba8b6c1cfbb174c55c7a0b13 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Wed, 19 Oct 2022 16:59:01 +0800 Subject: [PATCH] refactor: better loro structure --- crates/loro-core/src/container/manager.rs | 90 ++++++++++++++++++--- crates/loro-core/src/container/map/tests.rs | 18 +++-- crates/loro-core/src/log_store.rs | 19 ++--- crates/loro-core/src/loro.rs | 57 ++++++------- 4 files changed, 129 insertions(+), 55 deletions(-) diff --git a/crates/loro-core/src/container/manager.rs b/crates/loro-core/src/container/manager.rs index 1cb27857..a9cf9f98 100644 --- a/crates/loro-core/src/container/manager.rs +++ b/crates/loro-core/src/container/manager.rs @@ -1,49 +1,117 @@ use std::ptr::NonNull; +use enum_as_inner::EnumAsInner; use fxhash::FxHashMap; use crate::LogStore; -use super::{map::MapContainer, Container, ContainerID, ContainerType}; +use super::{ + map::MapContainer, text::text_container::TextContainer, Container, ContainerID, ContainerType, +}; + +#[derive(Debug, EnumAsInner)] +pub enum ContainerInstance { + Map(Box), + Text(Box), + Dyn(Box), +} + +impl Container for ContainerInstance { + fn id(&self) -> &ContainerID { + match self { + ContainerInstance::Map(x) => x.id(), + ContainerInstance::Text(x) => x.id(), + ContainerInstance::Dyn(x) => x.id(), + } + } + + fn type_(&self) -> ContainerType { + match self { + ContainerInstance::Map(_) => ContainerType::Map, + ContainerInstance::Text(_) => ContainerType::Text, + ContainerInstance::Dyn(x) => x.type_(), + } + } + + fn apply(&mut self, op: &crate::op::OpProxy) { + match self { + ContainerInstance::Map(x) => x.apply(op), + ContainerInstance::Text(x) => x.apply(op), + ContainerInstance::Dyn(x) => x.apply(op), + } + } + + fn checkout_version(&mut self, vv: &crate::VersionVector) { + match self { + ContainerInstance::Map(x) => x.checkout_version(vv), + ContainerInstance::Text(x) => x.checkout_version(vv), + ContainerInstance::Dyn(x) => x.checkout_version(vv), + } + } + + fn get_value(&mut self) -> &crate::LoroValue { + match self { + ContainerInstance::Map(x) => x.get_value(), + ContainerInstance::Text(x) => x.get_value(), + ContainerInstance::Dyn(x) => x.get_value(), + } + } + + fn as_any(&self) -> &dyn std::any::Any { + match self { + ContainerInstance::Map(x) => x.as_any(), + ContainerInstance::Text(x) => x.as_any(), + ContainerInstance::Dyn(x) => x.as_any(), + } + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + match self { + ContainerInstance::Map(x) => x.as_any_mut(), + ContainerInstance::Text(x) => x.as_any_mut(), + ContainerInstance::Dyn(x) => x.as_any_mut(), + } + } +} // TODO: containers snapshot: we need to resolve each container's parent even // if its creation op is not in the logStore #[derive(Debug)] -pub(crate) struct ContainerManager { - pub(crate) containers: FxHashMap>, +pub struct ContainerManager { + pub(crate) containers: FxHashMap, pub(crate) store: NonNull, } impl ContainerManager { #[inline] - pub fn create(&mut self, id: ContainerID, container_type: ContainerType) -> Box { + pub fn create(&mut self, id: ContainerID, container_type: ContainerType) -> ContainerInstance { match container_type { - ContainerType::Map => Box::new(MapContainer::new(id)), + ContainerType::Map => ContainerInstance::Map(Box::new(MapContainer::new(id))), _ => unimplemented!(), } } #[inline] - pub fn get(&self, id: ContainerID) -> Option<&dyn Container> { - self.containers.get(&id).map(|c| c.as_ref()) + pub fn get(&self, id: ContainerID) -> Option<&ContainerInstance> { + self.containers.get(&id) } #[inline] - pub fn get_mut(&mut self, id: &ContainerID) -> Option<&mut Box> { + pub fn get_mut(&mut self, id: &ContainerID) -> Option<&mut ContainerInstance> { self.containers.get_mut(id) } #[inline] - fn insert(&mut self, id: ContainerID, container: Box) { + fn insert(&mut self, id: ContainerID, container: ContainerInstance) { self.containers.insert(id, container); } - pub fn get_or_create(&mut self, id: &ContainerID) -> &mut (dyn Container + 'static) { + pub fn get_or_create(&mut self, id: &ContainerID) -> &mut ContainerInstance { if !self.containers.contains_key(id) { let container = self.create(id.clone(), id.container_type()); self.insert(id.clone(), container); } - self.get_mut(id).unwrap().as_mut() + self.get_mut(id).unwrap() } } diff --git a/crates/loro-core/src/container/map/tests.rs b/crates/loro-core/src/container/map/tests.rs index dcbd0274..51d91a7d 100644 --- a/crates/loro-core/src/container/map/tests.rs +++ b/crates/loro-core/src/container/map/tests.rs @@ -1,21 +1,24 @@ #![cfg(test)] use std::collections::HashMap; +use std::sync::Arc; use fxhash::FxHashMap; use proptest::prelude::*; use proptest::proptest; +use crate::container::Container; use crate::value::proptest::gen_insert_value; -use crate::{container::Container, fx_map, value::InsertValue, LoroCore, LoroValue}; +use crate::{fx_map, value::InsertValue, LoroCore, LoroValue}; #[test] fn basic() { let mut loro = LoroCore::default(); - let accessor = loro.store.get_accessor(); - let container = loro.get_map_container("map".into()).unwrap(); - container.insert("haha".into(), InsertValue::Int32(1), &accessor); + let weak = Arc::downgrade(&loro.store); + let mut a = loro.get_map_container("map".into()); + let container = a.as_mut(); + container.insert("haha".into(), InsertValue::Int32(1), weak); let ans = fx_map!( "haha".into() => LoroValue::Integer(1) ); @@ -35,12 +38,13 @@ mod map_proptest { value in prop::collection::vec(gen_insert_value(), 0..10 * PROPTEST_FACTOR_10) ) { let mut loro = LoroCore::default(); - let accessor = loro.store.get_accessor(); - let container = loro.get_map_container("map".into()).unwrap(); + let weak = Arc::downgrade(&loro.store); + let mut a = loro.get_map_container("map".into()); + let container = a.as_mut(); let mut map: HashMap = HashMap::new(); for (k, v) in key.iter().zip(value.iter()) { map.insert(k.clone(), v.clone()); - container.insert(k.clone().into(), v.clone(), &accessor); + container.insert(k.clone().into(), v.clone(), weak.clone()); let snapshot = container.get_value(); for (key, value) in snapshot.as_map().unwrap().iter() { assert_eq!(map.get(&key.to_string()).map(|x|x.clone().into()), Some(value.clone())); diff --git a/crates/loro-core/src/log_store.rs b/crates/loro-core/src/log_store.rs index ad8e9793..6d6f73b9 100644 --- a/crates/loro-core/src/log_store.rs +++ b/crates/loro-core/src/log_store.rs @@ -4,7 +4,6 @@ mod iter; use std::{ marker::PhantomPinned, - ptr::NonNull, sync::{Arc, RwLock, Weak}, }; @@ -16,7 +15,7 @@ use smallvec::SmallVec; use crate::{ change::{Change, ChangeMergeCfg}, configure::Configure, - container::{manager::ContainerManager, text::string_pool::StringPool}, + container::{manager::ContainerManager, text::string_pool::StringPool, Container}, id::{ClientID, Counter}, op::OpProxy, Lamport, Op, Timestamp, ID, @@ -59,14 +58,18 @@ pub struct LogStore { pub(crate) this_client_id: ClientID, frontier: SmallVec<[ID; 2]>, /// CRDT container manager - pub(crate) container: ContainerManager, pub(crate) raw_str: StringPool, + pub container: Arc>, _pin: PhantomPinned, } impl LogStore { - pub fn new(mut cfg: Configure, client_id: Option) -> Arc> { + pub fn new( + mut cfg: Configure, + client_id: Option, + container: Arc>, + ) -> Arc> { let this_client_id = client_id.unwrap_or_else(|| cfg.rand.next_u64()); let mut this = Arc::new(RwLock::new(Self { cfg, @@ -74,12 +77,9 @@ impl LogStore { changes: FxHashMap::default(), latest_lamport: 0, latest_timestamp: 0, - container: ContainerManager { - containers: Default::default(), - store: NonNull::dangling(), - }, frontier: Default::default(), raw_str: StringPool::default(), + container, _pin: PhantomPinned, })); @@ -194,7 +194,8 @@ impl LogStore { /// this function assume op is not included in the log, and its deps are included. #[inline] fn apply_remote_op(&mut self, change: &Change, op: &Op) { - let container = self.container.get_or_create(op.container()); + let mut container = self.container.write().unwrap(); + let container = container.get_or_create(op.container()); container.apply(&OpProxy::new(change, op, None)); } diff --git a/crates/loro-core/src/loro.rs b/crates/loro-core/src/loro.rs index 6a30d6ae..913d372a 100644 --- a/crates/loro-core/src/loro.rs +++ b/crates/loro-core/src/loro.rs @@ -1,16 +1,24 @@ -use std::sync::{Arc, RwLock, RwLockWriteGuard}; +use std::{ + ptr::NonNull, + sync::{Arc, RwLock, RwLockWriteGuard}, +}; -use owning_ref::{OwningRef, OwningRefMut}; +use owning_ref::OwningRefMut; use crate::{ configure::Configure, - container::{map::MapContainer, Cast, Container, ContainerID, ContainerType}, + container::{ + manager::{ContainerInstance, ContainerManager}, + map::MapContainer, + ContainerID, ContainerType, + }, id::ClientID, InternalString, LogStore, }; pub struct LoroCore { pub store: Arc>, + pub container: Arc>, } impl Default for LoroCore { @@ -21,41 +29,34 @@ impl Default for LoroCore { impl LoroCore { pub fn new(cfg: Configure, client_id: Option) -> Self { + let container = Arc::new(RwLock::new(ContainerManager { + containers: Default::default(), + store: NonNull::dangling(), + })); Self { - store: LogStore::new(cfg, client_id), + store: LogStore::new(cfg, client_id, container.clone()), + container, } } - pub fn get_container<'a>( - &'a mut self, + pub fn get_container( + &mut self, name: InternalString, container: ContainerType, - ) -> OwningRefMut, dyn Container + 'a> { - if let Ok(store) = self.store.write() { - OwningRefMut::new(store).map_mut(|store| { - let r = store - .container - .get_or_create(&ContainerID::new_root(name, container)); - r - }) - } else { - todo!() - } + ) -> OwningRefMut, ContainerInstance> { + let a = OwningRefMut::new(self.container.write().unwrap()); + a.map_mut(|x| x.get_or_create(&ContainerID::new_root(name, container))) } pub fn get_map_container( &mut self, name: InternalString, - ) -> OwningRefMut, MapContainer> { - if let Ok(store) = self.store.write() { - OwningRefMut::new(store).map_mut(|store| { - let r = store - .container - .get_or_create(&ContainerID::new_root(name, ContainerType::Map)); - r.cast_mut().unwrap() - }) - } else { - todo!() - } + ) -> OwningRefMut, Box> { + let a = OwningRefMut::new(self.container.write().unwrap()); + a.map_mut(|x| { + x.get_or_create(&ContainerID::new_root(name, ContainerType::Map)) + .as_map_mut() + .unwrap() + }) } }