From df3a708e48f0c566ae516050922f17f153ac1d83 Mon Sep 17 00:00:00 2001 From: leeeon233 Date: Mon, 21 Nov 2022 17:10:04 +0800 Subject: [PATCH] feat: add context check --- .../src/container/list/list_container.rs | 28 +++++++++++------ .../src/container/map/map_container.rs | 25 +++++++++++----- crates/loro-core/src/container/registry.rs | 19 +++++++++++- .../src/container/text/text_container.rs | 24 ++++++++++----- crates/loro-core/src/loro.rs | 30 +++++++++---------- crates/loro-core/tests/test.rs | 11 +++++++ 6 files changed, 98 insertions(+), 39 deletions(-) diff --git a/crates/loro-core/src/container/list/list_container.rs b/crates/loro-core/src/container/list/list_container.rs index 39f430aa..87eebdc0 100644 --- a/crates/loro-core/src/container/list/list_container.rs +++ b/crates/loro-core/src/container/list/list_container.rs @@ -19,7 +19,7 @@ use crate::{ Container, ContainerID, ContainerType, }, context::Context, - id::{Counter, ID}, + id::{ClientID, Counter, ID}, op::{InnerContent, Op, RemoteContent, RichOp}, value::LoroValue, version::IdSpanVector, @@ -259,19 +259,31 @@ impl Container for ListContainer { pub struct List { instance: Arc>, + client_id: ClientID, } impl Clone for List { fn clone(&self) -> Self { Self { instance: Arc::clone(&self.instance), + client_id: self.client_id, } } } impl List { + pub(crate) fn from_instance( + instance: Arc>, + client_id: ClientID, + ) -> Self { + Self { + instance, + client_id, + } + } + pub fn insert_batch(&mut self, ctx: &C, pos: usize, values: Vec) { - self.with_container(|x| x.insert_batch(ctx, pos, values)) + self.with_container_checked(ctx, |x| x.insert_batch(ctx, pos, values)) } pub fn insert>( @@ -280,7 +292,7 @@ impl List { pos: usize, value: V, ) -> Option { - self.with_container(|x| x.insert(ctx, pos, value)) + self.with_container_checked(ctx, |x| x.insert(ctx, pos, value)) } pub fn insert_obj( @@ -289,11 +301,11 @@ impl List { pos: usize, obj: ContainerType, ) -> ContainerID { - self.with_container(|x| x.insert_obj(ctx, pos, obj)) + self.with_container_checked(ctx, |x| x.insert_obj(ctx, pos, obj)) } pub fn delete(&mut self, ctx: &C, pos: usize, len: usize) -> Option { - self.with_container(|text| text.delete(ctx, pos, len)) + self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) } pub fn len(&self) -> usize { @@ -317,10 +329,8 @@ impl ContainerWrapper for List { let list = container_instance.as_list_mut().unwrap(); f(list) } -} -impl From>> for List { - fn from(text: Arc>) -> Self { - List { instance: text } + fn client_id(&self) -> ClientID { + self.client_id } } diff --git a/crates/loro-core/src/container/map/map_container.rs b/crates/loro-core/src/container/map/map_container.rs index 5bae1b13..8ef3358a 100644 --- a/crates/loro-core/src/container/map/map_container.rs +++ b/crates/loro-core/src/container/map/map_container.rs @@ -10,6 +10,7 @@ use crate::{ Container, ContainerID, ContainerType, }, context::Context, + id::ClientID, op::{InnerContent, Op, RemoteContent, RichOp}, span::HasLamport, value::LoroValue, @@ -209,19 +210,31 @@ impl Container for MapContainer { pub struct Map { instance: Arc>, + client_id: ClientID, } impl Clone for Map { fn clone(&self) -> Self { Self { instance: Arc::clone(&self.instance), + client_id: self.client_id, } } } impl Map { + pub(crate) fn from_instance( + instance: Arc>, + client_id: ClientID, + ) -> Self { + Self { + instance, + client_id, + } + } + pub fn insert>(&mut self, ctx: &C, key: &str, value: V) { - self.with_container(|map| { + self.with_container_checked(ctx, |map| { map.insert(ctx, key.into(), value); }) } @@ -232,11 +245,11 @@ impl Map { key: &str, obj: ContainerType, ) -> ContainerID { - self.with_container(|map| map.insert_obj(ctx, key.into(), obj)) + self.with_container_checked(ctx, |map| map.insert_obj(ctx, key.into(), obj)) } pub fn delete(&mut self, ctx: &C, key: &str) { - self.with_container(|map| { + self.with_container_checked(ctx, |map| { map.delete(ctx, key.into()); }) } @@ -271,10 +284,8 @@ impl ContainerWrapper for Map { let map = container_instance.as_map_mut().unwrap(); f(map) } -} -impl From>> for Map { - fn from(map: Arc>) -> Self { - Map { instance: map } + fn client_id(&self) -> ClientID { + self.client_id } } diff --git a/crates/loro-core/src/container/registry.rs b/crates/loro-core/src/container/registry.rs index f88838f4..a81e0d78 100644 --- a/crates/loro-core/src/container/registry.rs +++ b/crates/loro-core/src/container/registry.rs @@ -11,7 +11,7 @@ use smallvec::SmallVec; use crate::{ context::Context, - id::ContainerIdx, + id::{ClientID, ContainerIdx}, op::{RemoteContent, RichOp}, version::IdSpanVector, LoroValue, VersionVector, @@ -297,6 +297,23 @@ pub trait ContainerWrapper { where F: FnOnce(&mut Self::Container) -> R; + fn with_container_checked(&self, ctx: &C, f: F) -> R + where + F: FnOnce(&mut Self::Container) -> R, + { + let store_client_id = ctx.log_store().read().unwrap().this_client_id(); + if store_client_id != self.client_id() { + panic!( + "Context's client_id({}) does not match Container's client_id({})", + store_client_id, + self.client_id() + ); + } + self.with_container(f) + } + + fn client_id(&self) -> ClientID; + fn id(&self) -> ContainerID { self.with_container(|x| x.id().clone()) } diff --git a/crates/loro-core/src/container/text/text_container.rs b/crates/loro-core/src/container/text/text_container.rs index 14ab978b..658236ed 100644 --- a/crates/loro-core/src/container/text/text_container.rs +++ b/crates/loro-core/src/container/text/text_container.rs @@ -14,7 +14,7 @@ use crate::{ }, context::Context, debug_log, - id::{Counter, ID}, + id::{ClientID, Counter, ID}, op::{InnerContent, Op, RemoteContent, RichOp}, value::LoroValue, version::IdSpanVector, @@ -278,27 +278,39 @@ impl Container for TextContainer { pub struct Text { instance: Arc>, + client_id: ClientID, } impl Clone for Text { fn clone(&self) -> Self { Self { instance: Arc::clone(&self.instance), + client_id: self.client_id, } } } impl Text { + pub(crate) fn from_instance( + instance: Arc>, + client_id: ClientID, + ) -> Self { + Self { + instance, + client_id, + } + } + pub fn id(&self) -> ContainerID { self.instance.lock().unwrap().as_text().unwrap().id.clone() } pub fn insert(&mut self, ctx: &C, pos: usize, text: &str) -> Option { - self.with_container(|x| x.insert(ctx, pos, text)) + self.with_container_checked(ctx, |x| x.insert(ctx, pos, text)) } pub fn delete(&mut self, ctx: &C, pos: usize, len: usize) -> Option { - self.with_container(|text| text.delete(ctx, pos, len)) + self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) } pub fn get_value(&self) -> LoroValue { @@ -326,10 +338,8 @@ impl ContainerWrapper for Text { let text = container_instance.as_text_mut().unwrap(); f(text) } -} -impl From>> for Text { - fn from(text: Arc>) -> Self { - Text { instance: text } + fn client_id(&self) -> crate::id::ClientID { + self.client_id } } diff --git a/crates/loro-core/src/loro.rs b/crates/loro-core/src/loro.rs index 89bb2b09..9ed1cd93 100644 --- a/crates/loro-core/src/loro.rs +++ b/crates/loro-core/src/loro.rs @@ -36,34 +36,34 @@ impl LoroCore { #[inline(always)] pub fn get_list>(&mut self, id: I) -> List { let id: ContainerIdRaw = id.into(); - self.log_store - .write() - .unwrap() + let mut store = self.log_store.write().unwrap(); + let instance = store .get_or_create_container(&id.with_type(ContainerType::List)) - .clone() - .into() + .clone(); + let cid = store.this_client_id(); + List::from_instance(instance, cid) } #[inline(always)] pub fn get_map>(&mut self, id: I) -> Map { let id: ContainerIdRaw = id.into(); - self.log_store - .write() - .unwrap() + let mut store = self.log_store.write().unwrap(); + let instance = store .get_or_create_container(&id.with_type(ContainerType::Map)) - .clone() - .into() + .clone(); + let cid = store.this_client_id(); + Map::from_instance(instance, cid) } #[inline(always)] pub fn get_text>(&mut self, id: I) -> Text { let id: ContainerIdRaw = id.into(); - self.log_store - .write() - .unwrap() + let mut store = self.log_store.write().unwrap(); + let instance = store .get_or_create_container(&id.with_type(ContainerType::Text)) - .clone() - .into() + .clone(); + let cid = store.this_client_id(); + Text::from_instance(instance, cid) } pub fn export( diff --git a/crates/loro-core/tests/test.rs b/crates/loro-core/tests/test.rs index 7233c527..b884c9b5 100644 --- a/crates/loro-core/tests/test.rs +++ b/crates/loro-core/tests/test.rs @@ -136,6 +136,17 @@ fn two_client_text_sync() { assert_eq!(&**value, "abc"); } +#[test] +#[should_panic] +fn test_recursive_should_panic() { + let mut store_a = LoroCore::new(Default::default(), Some(1)); + let mut store_b = LoroCore::new(Default::default(), Some(2)); + let mut text_a = store_a.get_text("text_a"); + let mut text_b = store_b.get_text("text_b"); + text_a.insert(&store_a, 0, "012"); + text_b.insert(&store_a, 1, "34"); +} + #[ctor] fn init_color_backtrace() { color_backtrace::install();