mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 21:07:43 +00:00
refactor: better loro structure
This commit is contained in:
parent
1d38440e9f
commit
f20e135301
4 changed files with 129 additions and 55 deletions
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue