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 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<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
// if its creation op is not in the logStore
#[derive(Debug)]
pub(crate) struct ContainerManager {
pub(crate) containers: FxHashMap<ContainerID, Box<dyn Container>>,
pub struct ContainerManager {
pub(crate) containers: FxHashMap<ContainerID, ContainerInstance>,
pub(crate) store: NonNull<LogStore>,
}
impl ContainerManager {
#[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 {
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<dyn Container>> {
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<dyn Container>) {
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()
}
}

View file

@ -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<String, InsertValue> = 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()));

View file

@ -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<RwLock<ContainerManager>>,
_pin: PhantomPinned,
}
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 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));
}

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::{
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<RwLock<LogStore>>,
pub container: Arc<RwLock<ContainerManager>>,
}
impl Default for LoroCore {
@ -21,41 +29,34 @@ impl Default for LoroCore {
impl LoroCore {
pub fn new(cfg: Configure, client_id: Option<ClientID>) -> 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<RwLockWriteGuard<LogStore>, 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<RwLockWriteGuard<ContainerManager>, 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<RwLockWriteGuard<LogStore>, 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<RwLockWriteGuard<ContainerManager>, Box<MapContainer>> {
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()
})
}
}