refactor: better loro structure

This commit is contained in:
Zixuan Chen 2022-10-19 16:59:01 +08:00
parent 1d38440e9f
commit f20e135301
4 changed files with 129 additions and 55 deletions

View file

@ -1,49 +1,117 @@
use std::ptr::NonNull; use std::ptr::NonNull;
use enum_as_inner::EnumAsInner;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use crate::LogStore; 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<MapContainer>),
Text(Box<TextContainer>),
Dyn(Box<dyn Container>),
}
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 // TODO: containers snapshot: we need to resolve each container's parent even
// if its creation op is not in the logStore // if its creation op is not in the logStore
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct ContainerManager { pub struct ContainerManager {
pub(crate) containers: FxHashMap<ContainerID, Box<dyn Container>>, pub(crate) containers: FxHashMap<ContainerID, ContainerInstance>,
pub(crate) store: NonNull<LogStore>, pub(crate) store: NonNull<LogStore>,
} }
impl ContainerManager { impl ContainerManager {
#[inline] #[inline]
pub fn create(&mut self, id: ContainerID, container_type: ContainerType) -> Box<dyn Container> { pub fn create(&mut self, id: ContainerID, container_type: ContainerType) -> ContainerInstance {
match container_type { match container_type {
ContainerType::Map => Box::new(MapContainer::new(id)), ContainerType::Map => ContainerInstance::Map(Box::new(MapContainer::new(id))),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
#[inline] #[inline]
pub fn get(&self, id: ContainerID) -> Option<&dyn Container> { pub fn get(&self, id: ContainerID) -> Option<&ContainerInstance> {
self.containers.get(&id).map(|c| c.as_ref()) self.containers.get(&id)
} }
#[inline] #[inline]
pub fn get_mut(&mut self, id: &ContainerID) -> Option<&mut Box<dyn Container>> { pub fn get_mut(&mut self, id: &ContainerID) -> Option<&mut ContainerInstance> {
self.containers.get_mut(id) self.containers.get_mut(id)
} }
#[inline] #[inline]
fn insert(&mut self, id: ContainerID, container: Box<dyn Container>) { fn insert(&mut self, id: ContainerID, container: ContainerInstance) {
self.containers.insert(id, container); 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) { if !self.containers.contains_key(id) {
let container = self.create(id.clone(), id.container_type()); let container = self.create(id.clone(), id.container_type());
self.insert(id.clone(), container); self.insert(id.clone(), container);
} }
self.get_mut(id).unwrap().as_mut() self.get_mut(id).unwrap()
} }
} }

View file

@ -1,21 +1,24 @@
#![cfg(test)] #![cfg(test)]
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use proptest::prelude::*; use proptest::prelude::*;
use proptest::proptest; use proptest::proptest;
use crate::container::Container;
use crate::value::proptest::gen_insert_value; 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] #[test]
fn basic() { fn basic() {
let mut loro = LoroCore::default(); let mut loro = LoroCore::default();
let accessor = loro.store.get_accessor(); let weak = Arc::downgrade(&loro.store);
let container = loro.get_map_container("map".into()).unwrap(); let mut a = loro.get_map_container("map".into());
container.insert("haha".into(), InsertValue::Int32(1), &accessor); let container = a.as_mut();
container.insert("haha".into(), InsertValue::Int32(1), weak);
let ans = fx_map!( let ans = fx_map!(
"haha".into() => LoroValue::Integer(1) "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) value in prop::collection::vec(gen_insert_value(), 0..10 * PROPTEST_FACTOR_10)
) { ) {
let mut loro = LoroCore::default(); let mut loro = LoroCore::default();
let accessor = loro.store.get_accessor(); let weak = Arc::downgrade(&loro.store);
let container = loro.get_map_container("map".into()).unwrap(); let mut a = loro.get_map_container("map".into());
let container = a.as_mut();
let mut map: HashMap<String, InsertValue> = HashMap::new(); let mut map: HashMap<String, InsertValue> = HashMap::new();
for (k, v) in key.iter().zip(value.iter()) { for (k, v) in key.iter().zip(value.iter()) {
map.insert(k.clone(), v.clone()); 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(); let snapshot = container.get_value();
for (key, value) in snapshot.as_map().unwrap().iter() { for (key, value) in snapshot.as_map().unwrap().iter() {
assert_eq!(map.get(&key.to_string()).map(|x|x.clone().into()), Some(value.clone())); assert_eq!(map.get(&key.to_string()).map(|x|x.clone().into()), Some(value.clone()));

View file

@ -4,7 +4,6 @@
mod iter; mod iter;
use std::{ use std::{
marker::PhantomPinned, marker::PhantomPinned,
ptr::NonNull,
sync::{Arc, RwLock, Weak}, sync::{Arc, RwLock, Weak},
}; };
@ -16,7 +15,7 @@ use smallvec::SmallVec;
use crate::{ use crate::{
change::{Change, ChangeMergeCfg}, change::{Change, ChangeMergeCfg},
configure::Configure, configure::Configure,
container::{manager::ContainerManager, text::string_pool::StringPool}, container::{manager::ContainerManager, text::string_pool::StringPool, Container},
id::{ClientID, Counter}, id::{ClientID, Counter},
op::OpProxy, op::OpProxy,
Lamport, Op, Timestamp, ID, Lamport, Op, Timestamp, ID,
@ -59,14 +58,18 @@ pub struct LogStore {
pub(crate) this_client_id: ClientID, pub(crate) this_client_id: ClientID,
frontier: SmallVec<[ID; 2]>, frontier: SmallVec<[ID; 2]>,
/// CRDT container manager /// CRDT container manager
pub(crate) container: ContainerManager,
pub(crate) raw_str: StringPool, pub(crate) raw_str: StringPool,
pub container: Arc<RwLock<ContainerManager>>,
_pin: PhantomPinned, _pin: PhantomPinned,
} }
impl LogStore { impl LogStore {
pub fn new(mut cfg: Configure, client_id: Option<ClientID>) -> Arc<RwLock<Self>> { pub fn new(
mut cfg: Configure,
client_id: Option<ClientID>,
container: Arc<RwLock<ContainerManager>>,
) -> Arc<RwLock<Self>> {
let this_client_id = client_id.unwrap_or_else(|| cfg.rand.next_u64()); let this_client_id = client_id.unwrap_or_else(|| cfg.rand.next_u64());
let mut this = Arc::new(RwLock::new(Self { let mut this = Arc::new(RwLock::new(Self {
cfg, cfg,
@ -74,12 +77,9 @@ impl LogStore {
changes: FxHashMap::default(), changes: FxHashMap::default(),
latest_lamport: 0, latest_lamport: 0,
latest_timestamp: 0, latest_timestamp: 0,
container: ContainerManager {
containers: Default::default(),
store: NonNull::dangling(),
},
frontier: Default::default(), frontier: Default::default(),
raw_str: StringPool::default(), raw_str: StringPool::default(),
container,
_pin: PhantomPinned, _pin: PhantomPinned,
})); }));
@ -194,7 +194,8 @@ impl LogStore {
/// this function assume op is not included in the log, and its deps are included. /// this function assume op is not included in the log, and its deps are included.
#[inline] #[inline]
fn apply_remote_op(&mut self, change: &Change, op: &Op) { 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)); container.apply(&OpProxy::new(change, op, None));
} }

View file

@ -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::{ use crate::{
configure::Configure, configure::Configure,
container::{map::MapContainer, Cast, Container, ContainerID, ContainerType}, container::{
manager::{ContainerInstance, ContainerManager},
map::MapContainer,
ContainerID, ContainerType,
},
id::ClientID, id::ClientID,
InternalString, LogStore, InternalString, LogStore,
}; };
pub struct LoroCore { pub struct LoroCore {
pub store: Arc<RwLock<LogStore>>, pub store: Arc<RwLock<LogStore>>,
pub container: Arc<RwLock<ContainerManager>>,
} }
impl Default for LoroCore { impl Default for LoroCore {
@ -21,41 +29,34 @@ impl Default for LoroCore {
impl LoroCore { impl LoroCore {
pub fn new(cfg: Configure, client_id: Option<ClientID>) -> Self { pub fn new(cfg: Configure, client_id: Option<ClientID>) -> Self {
let container = Arc::new(RwLock::new(ContainerManager {
containers: Default::default(),
store: NonNull::dangling(),
}));
Self { Self {
store: LogStore::new(cfg, client_id), store: LogStore::new(cfg, client_id, container.clone()),
container,
} }
} }
pub fn get_container<'a>( pub fn get_container(
&'a mut self, &mut self,
name: InternalString, name: InternalString,
container: ContainerType, container: ContainerType,
) -> OwningRefMut<RwLockWriteGuard<LogStore>, dyn Container + 'a> { ) -> OwningRefMut<RwLockWriteGuard<ContainerManager>, ContainerInstance> {
if let Ok(store) = self.store.write() { let a = OwningRefMut::new(self.container.write().unwrap());
OwningRefMut::new(store).map_mut(|store| { a.map_mut(|x| x.get_or_create(&ContainerID::new_root(name, container)))
let r = store
.container
.get_or_create(&ContainerID::new_root(name, container));
r
})
} else {
todo!()
}
} }
pub fn get_map_container( pub fn get_map_container(
&mut self, &mut self,
name: InternalString, name: InternalString,
) -> OwningRefMut<RwLockWriteGuard<LogStore>, MapContainer> { ) -> OwningRefMut<RwLockWriteGuard<ContainerManager>, Box<MapContainer>> {
if let Ok(store) = self.store.write() { let a = OwningRefMut::new(self.container.write().unwrap());
OwningRefMut::new(store).map_mut(|store| { a.map_mut(|x| {
let r = store x.get_or_create(&ContainerID::new_root(name, ContainerType::Map))
.container .as_map_mut()
.get_or_create(&ContainerID::new_root(name, ContainerType::Map)); .unwrap()
r.cast_mut().unwrap() })
})
} else {
todo!()
}
} }
} }