fix: remove temp, add checker

fix: map lamport order
This commit is contained in:
leeeon233 2023-03-07 10:49:24 +08:00
parent 707fdc5b55
commit 4f5f809bb6
15 changed files with 765 additions and 370 deletions

View file

@ -29,7 +29,6 @@ use self::{pool_mapping::StateContent, registry::ContainerIdx};
mod checker; mod checker;
pub mod pool_mapping; pub mod pool_mapping;
pub mod registry; pub mod registry;
pub mod temp;
pub mod list; pub mod list;
pub mod map; pub mod map;
@ -199,7 +198,7 @@ pub trait ContainerTrait: Debug + Any + Unpin + Send + Sync {
hierarchy.unsubscribe(subscription); hierarchy.unsubscribe(subscription);
} }
fn apply_txn_op(&mut self, store: &mut LogStore, op: &TransactionOp) -> Vec<Op>; fn apply_txn_op(&mut self, store: &mut LogStore, op: TransactionOp) -> Vec<Op>;
} }
/// [ContainerID] includes the Op's [ID] and the type. So it's impossible to have /// [ContainerID] includes the Op's [ID] and the type. So it's impossible to have

View file

@ -1,9 +1,8 @@
use fxhash::FxHashSet; use fxhash::FxHashSet;
use crate::delta::DeltaItem;
use crate::{container::registry::ContainerIdx, InternalString, LoroError}; use crate::{container::registry::ContainerIdx, InternalString, LoroError};
use crate::transaction::op::{ListTxnOps, MapTxnOps, TextTxnOps}; use crate::transaction::op::MapTxnOps;
/// [ListChecker] maintains the length of all list container during one transaction, /// [ListChecker] maintains the length of all list container during one transaction,
/// when a op is be inserted, it will check whether the position or the length of deletion is valid. /// when a op is be inserted, it will check whether the position or the length of deletion is valid.
@ -41,81 +40,41 @@ impl ListChecker {
} }
} }
pub(crate) fn check(&mut self, ops: &ListTxnOps) -> Result<(), LoroError> { pub(crate) fn check_insert(&mut self, pos: usize, len: usize) -> Result<(), LoroError> {
let mut index = 0; if pos > self.current_length {
for op in ops.items() {
match op {
DeltaItem::Insert { value, .. } => {
index += value.len();
self.current_length += value.len()
}
DeltaItem::Retain { len, .. } => {
index += len;
if *len > self.current_length {
return Err(LoroError::TransactionError( return Err(LoroError::TransactionError(
format!("`List-{:?}` index out of bounds: the len is {} but the index is {}", self.idx, self.current_length, len).into(), format!(
"`ContainerIdx-{:?}` index out of bounds: the len is {} but the index is {}",
self.idx, self.current_length, pos
)
.into(),
)); ));
} }
self.current_length += len;
Ok(())
} }
DeltaItem::Delete(l) => {
if index + *l > self.current_length { pub(crate) fn check_delete(&mut self, pos: usize, len: usize) -> Result<(), LoroError> {
if pos > self.current_length {
return Err(LoroError::TransactionError( return Err(LoroError::TransactionError(
format!("`List-{:?}` can not apply delete op: the current len is {} but the delete range is {:?}", self.idx, self.current_length, index..index+*l).into(), format!(
"`ContainerIdx-{:?}` index out of bounds: the len is {} but the index is {}",
self.idx, self.current_length, pos
)
.into(),
)); ));
} }
self.current_length -= *l; if pos + len > self.current_length {
} return Err(LoroError::TransactionError(
} format!("`ContainerIdx-{:?}` can not apply delete op: the current len is {} but the delete range is {:?}", self.idx, self.current_length, pos..pos+len).into(),
} ));
Ok(())
}
}
impl TextChecker {
pub(crate) fn new(idx: ContainerIdx, current_length: usize) -> Self {
Self {
idx,
current_length,
}
}
pub(crate) fn from_idx(idx: ContainerIdx) -> Self {
Self {
idx,
current_length: 0,
}
}
pub(crate) fn check(&mut self, ops: &TextTxnOps) -> Result<(), LoroError> {
// TODO utf-16
let mut index = 0;
for op in ops.items() {
match op {
DeltaItem::Insert { value, .. } => {
index += value.len();
self.current_length += value.len()
}
DeltaItem::Retain { len, .. } => {
index += len;
if *len > self.current_length {
return Err(LoroError::TransactionError(
format!("`Text-{:?}` index out of bounds: the len is {} but the index is {}", self.idx, self.current_length, len).into(),
));
}
}
DeltaItem::Delete(l) => {
if index + *l > self.current_length {
return Err(LoroError::TransactionError(
format!("`Text-{:?}` can not apply delete op: the current len is {} but the delete range is {:?}", self.idx, self.current_length, index..index+*l).into(),
));
}
self.current_length -= *l;
}
}
} }
self.current_length -= len;
Ok(()) Ok(())
} }
} }
// TODO
impl MapChecker { impl MapChecker {
pub(crate) fn new(idx: ContainerIdx, keys: FxHashSet<InternalString>) -> Self { pub(crate) fn new(idx: ContainerIdx, keys: FxHashSet<InternalString>) -> Self {
Self { idx, keys } Self { idx, keys }
@ -127,7 +86,7 @@ impl MapChecker {
keys: Default::default(), keys: Default::default(),
} }
} }
pub(crate) fn check(&mut self, ops: &MapTxnOps) -> Result<(), LoroError> { pub(crate) fn check_insert(&mut self, ops: &MapTxnOps) -> Result<(), LoroError> {
self.keys.extend(ops.added.keys().cloned()); self.keys.extend(ops.added.keys().cloned());
self.keys.retain(|k| !ops.deleted.contains(k)); self.keys.retain(|k| !ops.deleted.contains(k));
Ok(()) Ok(())

View file

@ -9,20 +9,19 @@ use smallvec::SmallVec;
use crate::{ use crate::{
container::{ container::{
checker::ListChecker,
list::list_op::ListOp, list::list_op::ListOp,
pool, pool,
pool_mapping::{PoolMapping, StateContent}, pool_mapping::{PoolMapping, StateContent},
registry::{ registry::{
ContainerIdx, ContainerInner, ContainerInstance, ContainerRegistry, ContainerWrapper, ContainerIdx, ContainerInner, ContainerInstance, ContainerRegistry, ContainerWrapper,
}, },
temp::ContainerTemp,
text::{ text::{
text_content::{ListSlice, SliceRange}, text_content::{ListSlice, SliceRange},
tracker::{Effect, Tracker}, tracker::{Effect, Tracker},
}, },
ContainerID, ContainerTrait, ContainerType, ContainerID, ContainerTrait, ContainerType,
}, },
context::Context,
delta::{Delta, DeltaItem}, delta::{Delta, DeltaItem},
event::{Diff, Index}, event::{Diff, Index},
hierarchy::Hierarchy, hierarchy::Hierarchy,
@ -62,14 +61,13 @@ impl ListContainer {
} }
} }
fn apply_txn_op_impl(&mut self, store: &mut LogStore, op: &ListTxnOps) -> Vec<Op> { fn apply_txn_op_impl(&mut self, store: &mut LogStore, op: ListTxnOps) -> Vec<Op> {
let mut index = 0; let mut index = 0;
let mut ops = Vec::new(); let mut ops = Vec::new();
let id = store.next_id(); let id = store.next_id();
let mut offset = 0; let mut offset = 0;
for item in op.items() { for item in op.inner() {
// TODO avoid clone let item = item.into_event_format();
let item = item.clone().into_event_format();
match item { match item {
DeltaItem::Retain { len, .. } => index += len, DeltaItem::Retain { len, .. } => index += len,
DeltaItem::Insert { value, .. } => { DeltaItem::Insert { value, .. } => {
@ -482,8 +480,8 @@ impl ContainerTrait for ListContainer {
} }
} }
fn apply_txn_op(&mut self, store: &mut LogStore, op: &TransactionOp) -> Vec<Op> { fn apply_txn_op(&mut self, store: &mut LogStore, op: TransactionOp) -> Vec<Op> {
let op = op.as_list().unwrap().1; let op = op.list_inner();
self.apply_txn_op_impl(store, op) self.apply_txn_op_impl(store, op)
} }
} }
@ -493,6 +491,7 @@ pub struct List {
container: ContainerInner, container: ContainerInner,
client_id: ClientID, client_id: ClientID,
container_idx: ContainerIdx, container_idx: ContainerIdx,
checker: ListChecker,
} }
impl List { impl List {
@ -500,19 +499,25 @@ impl List {
instance: Weak<Mutex<ContainerInstance>>, instance: Weak<Mutex<ContainerInstance>>,
client_id: ClientID, client_id: ClientID,
) -> Self { ) -> Self {
let container_idx = instance.upgrade().unwrap().try_lock().unwrap().idx(); let (container_idx, current_length) = {
let list = instance.upgrade().unwrap();
let list = list.try_lock().unwrap();
(list.idx(), list.as_list().unwrap().values_len())
};
Self { Self {
container: ContainerInner::from(instance), container: ContainerInner::from(instance),
client_id, client_id,
container_idx, container_idx,
checker: ListChecker::new(container_idx, current_length),
} }
} }
pub(crate) fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self { pub(crate) fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self {
Self { Self {
container: ContainerInner::from(ContainerTemp::new(idx, ContainerType::List)), container: ContainerInner::from(idx),
client_id, client_id,
container_idx: idx, container_idx: idx,
checker: ListChecker::from_idx(idx),
} }
} }
@ -528,15 +533,14 @@ impl List {
pos: usize, pos: usize,
value: P, value: P,
) -> Result<Option<Container>, LoroError> { ) -> Result<Option<Container>, LoroError> {
self.checker.check_insert(pos, 1)?;
self.with_transaction_checked(txn, |txn, _x| { self.with_transaction_checked(txn, |txn, _x| {
let (value, maybe_container) = value.convert_value()?; let (value, maybe_container) = value.convert_value()?;
if let Some(prelim) = maybe_container { if let Some(prelim) = maybe_container {
let type_ = value.into_container().unwrap(); let type_ = value.into_container().unwrap();
let idx = txn.next_container_idx(); let idx = txn.next_container_idx();
txn.push( let op = TransactionOp::insert_list_container(self.idx(), pos, type_, idx);
TransactionOp::insert_list_container(self.idx(), pos, type_, idx), txn.push(op, Some(idx))?;
Some(idx),
)?;
prelim.integrate(txn, idx)?; prelim.integrate(txn, idx)?;
let container = match type_ { let container = match type_ {
ContainerType::List => Container::from(List::from_idx(idx, self.client_id)), ContainerType::List => Container::from(List::from_idx(idx, self.client_id)),
@ -562,6 +566,7 @@ impl List {
pos: usize, pos: usize,
values: Vec<LoroValue>, values: Vec<LoroValue>,
) -> Result<(), LoroError> { ) -> Result<(), LoroError> {
self.checker.check_insert(pos, values.len())?;
self.with_transaction_checked(txn, |txn, _| { self.with_transaction_checked(txn, |txn, _| {
txn.push( txn.push(
TransactionOp::insert_list_batch_value(self.idx(), pos, values), TransactionOp::insert_list_batch_value(self.idx(), pos, values),
@ -608,6 +613,7 @@ impl List {
pos: usize, pos: usize,
len: usize, len: usize,
) -> Result<(), LoroError> { ) -> Result<(), LoroError> {
self.checker.check_delete(pos, len)?;
self.with_transaction_checked(txn, |txn, _x| { self.with_transaction_checked(txn, |txn, _x| {
txn.push(TransactionOp::delete_list(self.idx(), pos, len), None) txn.push(TransactionOp::delete_list(self.idx(), pos, len), None)
})? })?
@ -631,10 +637,7 @@ impl List {
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match &self.container { self.checker.current_length
ContainerInner::Instance(_) => self.with_container(|x| x.values_len()).unwrap(),
ContainerInner::Temp(temp) => temp.as_list().unwrap().len(),
}
} }
// pub fn for_each<F: FnMut((usize, &LoroValue))>(&self, f: F) { // pub fn for_each<F: FnMut((usize, &LoroValue))>(&self, f: F) {

View file

@ -5,7 +5,6 @@ use crate::{
container::{ container::{
pool_mapping::{MapPoolMapping, StateContent}, pool_mapping::{MapPoolMapping, StateContent},
registry::{ContainerIdx, ContainerInner, ContainerRegistry}, registry::{ContainerIdx, ContainerInner, ContainerRegistry},
temp::ContainerTemp,
}, },
delta::MapDiff, delta::MapDiff,
id::ID, id::ID,
@ -97,8 +96,7 @@ impl MapContainer {
} }
} }
fn apply_txn_op_impl(&mut self, store: &mut LogStore, ops: &MapTxnOps) -> Vec<Op> { fn apply_txn_op_impl(&mut self, store: &mut LogStore, ops: MapTxnOps) -> Vec<Op> {
let ops = ops.clone();
let mut store_ops = Vec::with_capacity(ops.added.len() + ops.deleted.len()); let mut store_ops = Vec::with_capacity(ops.added.len() + ops.deleted.len());
let mut offset = 0; let mut offset = 0;
let id = store.next_id(); let id = store.next_id();
@ -295,6 +293,7 @@ impl ContainerTrait for MapContainer {
}, },
); );
} }
println!("");
} }
fn track_apply(&mut self, _: &mut Hierarchy, op: &RichOp, _: &mut ImportContext) { fn track_apply(&mut self, _: &mut Hierarchy, op: &RichOp, _: &mut ImportContext) {
@ -392,8 +391,8 @@ impl ContainerTrait for MapContainer {
} }
} }
fn apply_txn_op(&mut self, store: &mut LogStore, op: &TransactionOp) -> Vec<Op> { fn apply_txn_op(&mut self, store: &mut LogStore, op: TransactionOp) -> Vec<Op> {
let op = op.as_map().unwrap().1; let op = op.map_inner();
self.apply_txn_op_impl(store, op) self.apply_txn_op_impl(store, op)
} }
} }
@ -420,7 +419,7 @@ impl Map {
pub(crate) fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self { pub(crate) fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self {
Self { Self {
container: ContainerInner::from(ContainerTemp::new(idx, ContainerType::Map)), container: ContainerInner::from(idx),
client_id, client_id,
container_idx: idx, container_idx: idx,
} }
@ -486,7 +485,7 @@ impl Map {
pub fn keys(&self) -> Vec<InternalString> { pub fn keys(&self) -> Vec<InternalString> {
match &self.container { match &self.container {
ContainerInner::Instance(_) => self.with_container(|x| x.keys()).unwrap(), ContainerInner::Instance(_) => self.with_container(|x| x.keys()).unwrap(),
ContainerInner::Temp(temp) => temp.as_map().unwrap().keys(), ContainerInner::Temp(idx) => unimplemented!(),
} }
} }
@ -521,7 +520,7 @@ impl Map {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match &self.container { match &self.container {
ContainerInner::Instance(_) => self.with_container(|x| x.len()).unwrap(), ContainerInner::Instance(_) => self.with_container(|x| x.len()).unwrap(),
ContainerInner::Temp(temp) => temp.as_map().unwrap().len(), ContainerInner::Temp(idx) => unimplemented!(),
} }
} }

View file

@ -26,21 +26,21 @@ use crate::{
}; };
use super::{ use super::{
list::ListContainer, map::MapContainer, pool_mapping::StateContent, temp::ContainerTemp, list::ListContainer, map::MapContainer, pool_mapping::StateContent, text::TextContainer,
text::TextContainer, ContainerID, ContainerTrait, ContainerType, ContainerID, ContainerTrait, ContainerType,
}; };
#[derive(Debug, Clone, EnumAsInner)] #[derive(Debug, Clone, EnumAsInner)]
pub enum ContainerInner { pub enum ContainerInner {
Instance(Weak<Mutex<ContainerInstance>>), Instance(Weak<Mutex<ContainerInstance>>),
Temp(ContainerTemp), Temp(ContainerIdx),
} }
impl ContainerInner { impl ContainerInner {
pub fn idx(&self) -> ContainerIdx { pub fn idx(&self) -> ContainerIdx {
match self { match self {
ContainerInner::Instance(i) => i.upgrade().unwrap().try_lock().unwrap().idx(), ContainerInner::Instance(i) => i.upgrade().unwrap().try_lock().unwrap().idx(),
ContainerInner::Temp(t) => t.idx(), ContainerInner::Temp(idx) => *idx,
} }
} }
} }
@ -51,8 +51,8 @@ impl From<Weak<Mutex<ContainerInstance>>> for ContainerInner {
} }
} }
impl From<ContainerTemp> for ContainerInner { impl From<ContainerIdx> for ContainerInner {
fn from(value: ContainerTemp) -> Self { fn from(value: ContainerIdx) -> Self {
Self::Temp(value) Self::Temp(value)
} }
} }
@ -245,7 +245,7 @@ impl ContainerTrait for ContainerInstance {
} }
} }
fn apply_txn_op(&mut self, store: &mut LogStore, op: &TransactionOp) -> Vec<Op> { fn apply_txn_op(&mut self, store: &mut LogStore, op: TransactionOp) -> Vec<Op> {
match self { match self {
ContainerInstance::List(x) => x.apply_txn_op(store, op), ContainerInstance::List(x) => x.apply_txn_op(store, op),
ContainerInstance::Map(x) => x.apply_txn_op(store, op), ContainerInstance::Map(x) => x.apply_txn_op(store, op),

View file

@ -1,101 +0,0 @@
use enum_as_inner::EnumAsInner;
use fxhash::FxHashSet;
use crate::{ContainerType, InternalString};
use super::{
checker::{ListChecker, MapChecker, TextChecker},
registry::ContainerIdx,
};
#[derive(Debug, Clone, EnumAsInner)]
pub enum ContainerTemp {
List(ListTemp),
Map(MapTemp),
Text(TextTemp),
}
impl ContainerTemp {
pub(crate) fn new(idx: ContainerIdx, type_: ContainerType) -> Self {
match type_ {
ContainerType::List => Self::List(ListTemp::from_idx(idx)),
ContainerType::Map => Self::Map(MapTemp::from_idx(idx)),
ContainerType::Text => Self::Text(TextTemp::from_idx(idx)),
}
}
pub fn idx(&self) -> ContainerIdx {
match self {
ContainerTemp::List(x) => x.idx,
ContainerTemp::Map(x) => x.idx,
ContainerTemp::Text(x) => x.idx,
}
}
pub fn type_(&self) -> ContainerType {
match self {
ContainerTemp::List(_) => ContainerType::List,
ContainerTemp::Map(_) => ContainerType::Map,
ContainerTemp::Text(_) => ContainerType::Text,
}
}
}
#[derive(Debug, Clone)]
pub struct ListTemp {
idx: ContainerIdx,
checker: ListChecker,
}
#[derive(Debug, Clone)]
pub struct MapTemp {
idx: ContainerIdx,
checker: MapChecker,
}
#[derive(Debug, Clone)]
pub struct TextTemp {
idx: ContainerIdx,
checker: TextChecker,
}
impl ListTemp {
fn from_idx(idx: ContainerIdx) -> Self {
ListTemp {
idx,
checker: ListChecker::from_idx(idx),
}
}
pub(crate) fn len(&self) -> usize {
self.checker.current_length
}
}
impl MapTemp {
fn from_idx(idx: ContainerIdx) -> Self {
MapTemp {
idx,
checker: MapChecker::from_idx(idx),
}
}
pub(crate) fn keys(&self) -> Vec<InternalString> {
self.checker.keys.iter().cloned().collect()
}
pub(crate) fn len(&self) -> usize {
self.checker.keys.len()
}
}
impl TextTemp {
fn from_idx(idx: ContainerIdx) -> Self {
TextTemp {
idx,
checker: TextChecker::from_idx(idx),
}
}
pub(crate) fn len(&self) -> usize {
self.checker.current_length
}
}

View file

@ -7,10 +7,10 @@ use tracing::instrument;
use crate::{ use crate::{
container::{ container::{
checker::ListChecker,
list::list_op::{InnerListOp, ListOp}, list::list_op::{InnerListOp, ListOp},
pool_mapping::{PoolMapping, StateContent}, pool_mapping::{PoolMapping, StateContent},
registry::{ContainerIdx, ContainerInner, ContainerInstance, ContainerWrapper}, registry::{ContainerIdx, ContainerInner, ContainerInstance, ContainerWrapper},
temp::ContainerTemp,
ContainerID, ContainerTrait, ContainerType, ContainerID, ContainerTrait, ContainerType,
}, },
delta::{Delta, DeltaItem}, delta::{Delta, DeltaItem},
@ -54,26 +54,26 @@ impl TextContainer {
} }
} }
pub(crate) fn apply_txn_op_impl(&mut self, store: &mut LogStore, op: &TextTxnOps) -> Vec<Op> { pub(crate) fn apply_txn_op_impl(&mut self, store: &mut LogStore, op: TextTxnOps) -> Vec<Op> {
let mut index = 0; let mut index = 0;
let mut ops = Vec::new(); let mut ops = Vec::new();
let mut offset = 0; let mut offset = 0;
let id = store.next_id(); let id = store.next_id();
for item in op.items() { for item in op.inner() {
match item { match item {
DeltaItem::Retain { len, .. } => index += len, DeltaItem::Retain { len, .. } => index += len,
DeltaItem::Insert { value, .. } => { DeltaItem::Insert { value, .. } => {
let len = value.len(); let len = value.len();
let id = id.inc(offset); let id = id.inc(offset);
offset += len as i32; offset += len as i32;
let op = self.apply_insert(index, value, id); let op = self.apply_insert(index, &value, id);
index += len; index += len;
ops.push(op); ops.push(op);
} }
DeltaItem::Delete(len) => { DeltaItem::Delete(len) => {
let id = id.inc(offset); let id = id.inc(offset);
offset += *len as i32; offset += len as i32;
let op = self.apply_delete(index, *len, id); let op = self.apply_delete(index, len, id);
ops.push(op); ops.push(op);
} }
} }
@ -450,8 +450,8 @@ impl ContainerTrait for TextContainer {
} }
} }
fn apply_txn_op(&mut self, store: &mut LogStore, op: &TransactionOp) -> Vec<Op> { fn apply_txn_op(&mut self, store: &mut LogStore, op: TransactionOp) -> Vec<Op> {
let op = op.as_text().unwrap().1; let op = op.text_inner();
self.apply_txn_op_impl(store, op) self.apply_txn_op_impl(store, op)
} }
} }
@ -461,23 +461,31 @@ pub struct Text {
container: ContainerInner, container: ContainerInner,
client_id: ClientID, client_id: ClientID,
container_idx: ContainerIdx, container_idx: ContainerIdx,
// TODO: use text checker
checker: ListChecker,
} }
impl Text { impl Text {
pub fn from_instance(instance: Weak<Mutex<ContainerInstance>>, client_id: ClientID) -> Self { pub fn from_instance(instance: Weak<Mutex<ContainerInstance>>, client_id: ClientID) -> Self {
let container_idx = instance.upgrade().unwrap().try_lock().unwrap().idx(); let (container_idx, current_length) = {
let x = instance.upgrade().unwrap();
let x = x.try_lock().unwrap();
(x.idx(), x.as_text().unwrap().text_len())
};
Self { Self {
container: ContainerInner::from(instance), container: ContainerInner::from(instance),
client_id, client_id,
container_idx, container_idx,
checker: ListChecker::new(container_idx, current_length),
} }
} }
pub fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self { pub fn from_idx(idx: ContainerIdx, client_id: ClientID) -> Self {
Self { Self {
container: ContainerInner::from(ContainerTemp::new(idx, ContainerType::Text)), container: ContainerInner::from(idx),
client_id, client_id,
container_idx: idx, container_idx: idx,
checker: ListChecker::from_idx(idx),
} }
} }
@ -502,6 +510,7 @@ impl Text {
if text.is_empty() { if text.is_empty() {
return Ok(()); return Ok(());
} }
self.checker.check_insert(pos, text.len())?;
self.with_transaction_checked(txn, |txn, _| { self.with_transaction_checked(txn, |txn, _| {
txn.push(TransactionOp::insert_text(self.idx(), pos, text), None) txn.push(TransactionOp::insert_text(self.idx(), pos, text), None)
})? })?
@ -522,10 +531,11 @@ impl Text {
pos: usize, pos: usize,
len: usize, len: usize,
) -> Result<(), crate::LoroError> { ) -> Result<(), crate::LoroError> {
self.with_transaction_checked(txn, |txn, _| {
if len == 0 { if len == 0 {
return Ok(()); return Ok(());
} }
self.checker.check_delete(pos, len)?;
self.with_transaction_checked(txn, |txn, _| {
txn.push(TransactionOp::delete_text(self.idx(), pos, len), None) txn.push(TransactionOp::delete_text(self.idx(), pos, len), None)
})? })?
} }
@ -555,7 +565,8 @@ impl Text {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
// TODO // TODO
self.with_container(|x| x.text_len()).unwrap() // self.with_container(|x| x.text_len()).unwrap()
self.checker.current_length
} }
#[must_use] #[must_use]

View file

@ -8,7 +8,7 @@ use enum_as_inner::EnumAsInner;
use rle::{ use rle::{
range_map::RangeMap, range_map::RangeMap,
rle_tree::{node::LeafNode, BumpMode, Position, SafeCursor, SafeCursorMut, UnsafeCursor}, rle_tree::{node::LeafNode, HeapMode, Position, SafeCursor, SafeCursorMut, UnsafeCursor},
HasLength, Mergable, RleVecWithLen, Sliceable, ZeroElement, HasLength, Mergable, RleVecWithLen, Sliceable, ZeroElement,
}; };
@ -195,10 +195,10 @@ impl Mergable for Marker {
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(super) struct CursorMap(RangeMap<u128, Marker, BumpMode>); pub(super) struct CursorMap(RangeMap<u128, Marker, HeapMode>);
impl Deref for CursorMap { impl Deref for CursorMap {
type Target = RangeMap<u128, Marker, BumpMode>; type Target = RangeMap<u128, Marker, HeapMode>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0

View file

@ -7,12 +7,12 @@ use crate::{
ContentType, InsertContentTrait, ID, ContentType, InsertContentTrait, ID,
}; };
use rle::{ use rle::{
rle_tree::{tree_trait::CumulateTreeTrait, BumpMode}, rle_tree::{tree_trait::CumulateTreeTrait, HeapMode},
HasLength, Mergable, Sliceable, HasLength, Mergable, Sliceable,
}; };
const MAX_CHILDREN_SIZE: usize = 16; const MAX_CHILDREN_SIZE: usize = 16;
pub(super) type YSpanTreeTrait = CumulateTreeTrait<YSpan, MAX_CHILDREN_SIZE, BumpMode>; pub(super) type YSpanTreeTrait = CumulateTreeTrait<YSpan, MAX_CHILDREN_SIZE, HeapMode>;
/// 80 bytes /// 80 bytes
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -8,7 +8,7 @@ use crdt_list::{
use fxhash::FxHashSet; use fxhash::FxHashSet;
use rle::{ use rle::{
range_map::{RangeMap, WithStartEnd}, range_map::{RangeMap, WithStartEnd},
rle_tree::{iter::IterMut, node::LeafNode, BumpMode, SafeCursorMut}, rle_tree::{iter::IterMut, node::LeafNode, HeapMode, SafeCursorMut},
HasLength, HasLength,
}; };

View file

@ -11,7 +11,7 @@ use tabled::{TableIteratorExt, Tabled};
use crate::{ use crate::{
array_mut_ref, array_mut_ref,
container::{temp::ContainerTemp, ContainerID}, container::ContainerID,
delta::DeltaItem, delta::DeltaItem,
event::{Diff, Observer}, event::{Diff, Observer},
id::ClientID, id::ClientID,

View file

@ -79,7 +79,13 @@ impl Actor {
let root_value = Arc::clone(&actor.value_tracker); let root_value = Arc::clone(&actor.value_tracker);
actor.loro.subscribe_deep(Box::new(move |event| { actor.loro.subscribe_deep(Box::new(move |event| {
let mut root_value = root_value.lock().unwrap(); let mut root_value = root_value.lock().unwrap();
if id == 0 {
println!("event {:?}\n\nvalue {:?}", event, root_value);
}
root_value.apply(&event.relative_path, &event.diff); root_value.apply(&event.relative_path, &event.diff);
if id == 0 {
println!("after value {:?}\n", root_value);
}
})); }));
let log_store = actor.loro.log_store.write().unwrap(); let log_store = actor.loro.log_store.write().unwrap();
@ -1312,31 +1318,33 @@ mod failed_tests {
} }
#[test] #[test]
fn list_slice_err() { fn failed() {
test_multi_sites( test_multi_sites(
5, 5,
&mut [ &mut [
Text { Map {
site: 2, site: 0,
container_idx: 0, container_idx: 0,
pos: 0, key: 0,
value: 39064, value: Container(C::Text),
is_del: false,
}, },
Sync { from: 2, to: 3 }, Map {
Text { site: 4,
site: 2,
container_idx: 0, container_idx: 0,
pos: 2, key: 0,
value: 39064, value: I32(-2021161081),
is_del: false,
}, },
Text { Map {
site: 2, site: 0,
container_idx: 0, container_idx: 0,
pos: 0, key: 255,
value: 39064, value: Container(C::List),
is_del: false, },
List {
site: 0,
container_idx: 1,
key: 0,
value: I32(1),
}, },
], ],
) )
@ -1348,84 +1356,600 @@ mod failed_tests {
minify_error( minify_error(
5, 5,
vec![ vec![
Text { Map {
site: 2, site: 0,
container_idx: 0,
pos: 0,
value: 39064,
is_del: false,
},
Text {
site: 2,
container_idx: 0,
pos: 5,
value: 152,
is_del: false,
},
Sync { from: 2, to: 3 },
Text {
site: 3,
container_idx: 0,
pos: 10,
value: 2,
is_del: true,
},
Text {
site: 2,
container_idx: 0,
pos: 0,
value: 39064,
is_del: false,
},
Sync { from: 2, to: 3 },
Text {
site: 2,
container_idx: 0,
pos: 16,
value: 39064,
is_del: false,
},
Text {
site: 2,
container_idx: 0,
pos: 8,
value: 39064,
is_del: false,
},
Text {
site: 2,
container_idx: 0,
pos: 28,
value: 39064,
is_del: false,
},
Text {
site: 2,
container_idx: 0,
pos: 0,
value: 39064,
is_del: false,
},
Text {
site: 2,
container_idx: 0,
pos: 41,
value: 45232,
is_del: false,
},
Sync { from: 1, to: 2 },
Text {
site: 2,
container_idx: 0,
pos: 48,
value: 39064,
is_del: false,
},
List {
site: 1,
container_idx: 0, container_idx: 0,
key: 0, key: 0,
value: I32(-1734829928), value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Container(C::Text),
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 14,
container_idx: 14,
key: 0,
value: I32(-2021161081),
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Sync { from: 5, to: 0 },
Map {
site: 0,
container_idx: 0,
key: 170,
value: I32(10),
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 14,
container_idx: 14,
key: 14,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 14,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 92,
value: Null,
},
Map {
site: 255,
container_idx: 255,
key: 255,
value: Container(C::List),
},
List {
site: 255,
container_idx: 255,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 4,
container_idx: 4,
key: 4,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 4,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Container(C::Text),
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
},
Map {
site: 0,
container_idx: 0,
key: 0,
value: Null,
}, },
], ],
test_multi_sites, test_multi_sites,

View file

@ -379,7 +379,9 @@ pub(super) fn decode_changes_to_inner_format(
} }
} }
} }
if store.this_client_id == 0 && changes_ans.len()==1{
println!("################################");
}
// TODO: using the one with fewer changes to import // TODO: using the one with fewer changes to import
Ok(changes_ans) Ok(changes_ans)
} }

View file

@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use crate::{ use crate::{
container::{registry::ContainerIdx, temp::ContainerTemp, ContainerID}, container::{registry::ContainerIdx, ContainerID},
context::Context, context::Context,
event::{ObserverHandler, RawEvent}, event::{ObserverHandler, RawEvent},
hierarchy::Hierarchy, hierarchy::Hierarchy,

View file

@ -121,60 +121,59 @@ impl Transaction {
let mut events = Vec::with_capacity(compressed_op.len()); let mut events = Vec::with_capacity(compressed_op.len());
for op in compressed_op { for op in compressed_op {
let idx = op.container_idx(); let idx = op.container_idx();
let type_ = op.container_type();
// TODO: diff remove vec!
let diff = vec![match type_ {
ContainerType::List => {
Diff::List(op.as_list().unwrap().1.clone().into_event_format())
}
ContainerType::Map => {
let container = store.reg.get_by_idx(&idx).unwrap();
let map = Map::from_instance(container, store.this_client_id);
Diff::Map(op.as_map().unwrap().1.clone().into_event_format(&map))
}
ContainerType::Text => Diff::Text(op.as_text().unwrap().1.clone()),
}];
let container = store.reg.get_by_idx(&idx).unwrap(); let container = store.reg.get_by_idx(&idx).unwrap();
let container = container.upgrade().unwrap(); let container = container.upgrade().unwrap();
let mut container = container.try_lock().unwrap(); let mut container = container.try_lock().unwrap();
let container_id = container.id().clone(); let container_id = container.id().clone();
let type_ = container_id.container_type(); let store_ops = container.apply_txn_op(store, op);
let store_ops = container.apply_txn_op(store, &op);
drop(container); drop(container);
let (old_version, new_version) = store.append_local_ops(&store_ops); let (old_version, new_version) = store.append_local_ops(&store_ops);
let new_version = new_version.into(); let new_version = new_version.into();
let event = if hierarchy.should_notify(&container_id) { let event = if hierarchy.should_notify(&container_id) {
match type_ { match type_ {
ContainerType::List => { ContainerType::List => hierarchy
let delta = op.into_list().unwrap().1.into_event_format();
hierarchy
.get_abs_path(&store.reg, &container_id) .get_abs_path(&store.reg, &container_id)
.map(|abs_path| RawEvent { .map(|abs_path| RawEvent {
container_id, container_id,
old_version, old_version,
new_version, new_version,
diff: vec![Diff::List(delta)], diff,
local: true, local: true,
abs_path, abs_path,
}) }),
} ContainerType::Text => hierarchy
ContainerType::Text => {
let delta = op.into_text().unwrap().1;
hierarchy
.get_abs_path(&store.reg, &container_id) .get_abs_path(&store.reg, &container_id)
.map(|abs_path| RawEvent { .map(|abs_path| RawEvent {
container_id, container_id,
old_version, old_version,
new_version, new_version,
diff: vec![Diff::Text(delta)], diff,
local: true, local: true,
abs_path, abs_path,
}) }),
} ContainerType::Map => hierarchy
ContainerType::Map => {
let delta = {
let map = store.get_container(&container_id).unwrap();
let map = Map::from_instance(map, store.this_client_id);
op.into_map().unwrap().1.into_event_format(&map)
};
hierarchy
.get_abs_path(&store.reg, &container_id) .get_abs_path(&store.reg, &container_id)
.map(|abs_path| RawEvent { .map(|abs_path| RawEvent {
container_id, container_id,
old_version, old_version,
new_version, new_version,
diff: vec![Diff::Map(delta)], diff,
local: true, local: true,
abs_path, abs_path,
}) }),
}
} }
} else { } else {
None None