mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +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 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue