mirror of
https://github.com/loro-dev/loro.git
synced 2024-12-01 04:28:18 +00:00
fix: changes traveler bug
should iter every change between two version, even when a and b are parallel to each other
This commit is contained in:
parent
7ab8ccff64
commit
3551bc4e99
7 changed files with 209 additions and 20 deletions
|
@ -136,13 +136,6 @@ impl SharedArena {
|
|||
}
|
||||
|
||||
pub fn set_parent(&self, child: ContainerIdx, parent: Option<ContainerIdx>) {
|
||||
debug_log::debug_log!(
|
||||
"set parent {:?} {:?} {:?} {:?}",
|
||||
child,
|
||||
parent,
|
||||
self.get_container_id(child),
|
||||
parent.map(|x| self.get_container_id(x))
|
||||
);
|
||||
self.inner.parents.lock().unwrap().insert(child, parent);
|
||||
}
|
||||
|
||||
|
|
|
@ -453,6 +453,15 @@ impl ListHandler {
|
|||
.get_container_deep_value(self.container_idx)
|
||||
}
|
||||
|
||||
pub fn get_deep_value_with_id(&self) -> LoroValue {
|
||||
self.state
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_container_deep_value_with_id(self.container_idx, None)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ContainerID {
|
||||
self.state
|
||||
.upgrade()
|
||||
|
@ -614,6 +623,15 @@ impl MapHandler {
|
|||
.get_container_deep_value(self.container_idx)
|
||||
}
|
||||
|
||||
pub fn get_deep_value_with_id(&self) -> LoroValue {
|
||||
self.state
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_container_deep_value_with_id(self.container_idx, None)
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<LoroValue> {
|
||||
self.state
|
||||
.upgrade()
|
||||
|
|
|
@ -119,13 +119,14 @@ impl LoroDoc {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_txn<F>(&self, f: F) -> LoroResult<()>
|
||||
pub fn with_txn<F, R>(&self, f: F) -> LoroResult<R>
|
||||
where
|
||||
F: Fn(&mut Transaction) -> LoroResult<()>,
|
||||
F: FnOnce(&mut Transaction) -> LoroResult<R>,
|
||||
{
|
||||
let mut txn = self.txn().unwrap();
|
||||
f(&mut txn)?;
|
||||
txn.commit()
|
||||
let v = f(&mut txn)?;
|
||||
txn.commit()?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Create a new transaction with specified origin.
|
||||
|
@ -346,6 +347,11 @@ impl LoroDoc {
|
|||
self.state.lock().unwrap().get_deep_value()
|
||||
}
|
||||
|
||||
/// Get deep value of the document with container id
|
||||
pub fn get_deep_value_with_id(&self) -> LoroValue {
|
||||
self.state.lock().unwrap().get_deep_value_with_id()
|
||||
}
|
||||
|
||||
pub fn checkout_to_latest(&mut self) {
|
||||
let f = self.oplog_frontiers();
|
||||
self.checkout(&f);
|
||||
|
@ -371,6 +377,7 @@ impl LoroDoc {
|
|||
Some(frontiers),
|
||||
);
|
||||
|
||||
debug_dbg!(&diff);
|
||||
state.apply_diff(InternalDocDiff {
|
||||
origin: "checkout".into(),
|
||||
local: true,
|
||||
|
|
|
@ -432,20 +432,24 @@ impl OpLog {
|
|||
decode_oplog(self, data)
|
||||
}
|
||||
|
||||
/// Iterates over all changes between `from` and `to` peer by peer
|
||||
/// Iterates over all changes between `a` and `b` peer by peer (not in causal order, fast)
|
||||
pub(crate) fn for_each_change_within(
|
||||
&self,
|
||||
from: &VersionVector,
|
||||
to: &VersionVector,
|
||||
a: &VersionVector,
|
||||
b: &VersionVector,
|
||||
mut f: impl FnMut(&Change),
|
||||
) {
|
||||
for (peer, changes) in self.changes.iter() {
|
||||
let from_cnt = from.get(peer).copied().unwrap_or(0);
|
||||
let to_cnt = to.get(peer).copied().unwrap_or(0);
|
||||
let mut from_cnt = a.get(peer).copied().unwrap_or(0);
|
||||
let mut to_cnt = b.get(peer).copied().unwrap_or(0);
|
||||
if from_cnt == to_cnt {
|
||||
continue;
|
||||
}
|
||||
|
||||
if to_cnt < from_cnt {
|
||||
std::mem::swap(&mut from_cnt, &mut to_cnt);
|
||||
}
|
||||
|
||||
let Some(result) = changes.get_by_atom_index(from_cnt) else { continue };
|
||||
for i in result.merged_index..changes.vec().len() {
|
||||
f(&changes.vec()[i])
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
delta::{Delta, DeltaItem},
|
||||
event::InternalContainerDiff,
|
||||
event::{Diff, Index},
|
||||
fx_map,
|
||||
id::PeerID,
|
||||
op::RawOp,
|
||||
version::Frontiers,
|
||||
|
@ -412,6 +413,93 @@ impl DocState {
|
|||
LoroValue::Map(Arc::new(ans))
|
||||
}
|
||||
|
||||
pub fn get_deep_value_with_id(&self) -> LoroValue {
|
||||
let roots = self.arena.root_containers();
|
||||
let mut ans = FxHashMap::with_capacity_and_hasher(roots.len(), Default::default());
|
||||
for root_idx in roots {
|
||||
let id = self.arena.idx_to_id(root_idx).unwrap();
|
||||
match id.clone() {
|
||||
loro_common::ContainerID::Root { name, .. } => {
|
||||
ans.insert(
|
||||
name.to_string(),
|
||||
self.get_container_deep_value_with_id(root_idx, Some(id)),
|
||||
);
|
||||
}
|
||||
loro_common::ContainerID::Normal { .. } => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::Map(Arc::new(ans))
|
||||
}
|
||||
|
||||
pub(crate) fn get_container_deep_value_with_id(
|
||||
&self,
|
||||
container: ContainerIdx,
|
||||
id: Option<ContainerID>,
|
||||
) -> LoroValue {
|
||||
let id = id.unwrap_or_else(|| self.arena.idx_to_id(container).unwrap());
|
||||
let Some(state) = self.states.get(&container) else {
|
||||
return container.get_type().default_value();
|
||||
};
|
||||
let value = state.get_value();
|
||||
let cid_str =
|
||||
LoroValue::String(Arc::new(format!("idx:{}, id:{}", container.to_index(), id)));
|
||||
match value {
|
||||
LoroValue::Container(_) => unreachable!(),
|
||||
LoroValue::List(mut list) => {
|
||||
if list.iter().all(|x| !x.is_container()) {
|
||||
return LoroValue::Map(Arc::new(fx_map!(
|
||||
"cid".into() => cid_str,
|
||||
"value".into() => LoroValue::List(list)
|
||||
)));
|
||||
}
|
||||
|
||||
let list_mut = Arc::make_mut(&mut list);
|
||||
for item in list_mut.iter_mut() {
|
||||
if item.is_container() {
|
||||
let container = item.as_container().unwrap();
|
||||
let container_idx = self.arena.register_container(container);
|
||||
let value = self.get_container_deep_value_with_id(
|
||||
container_idx,
|
||||
Some(container.clone()),
|
||||
);
|
||||
*item = value;
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::Map(Arc::new(fx_map!(
|
||||
"cid".into() => cid_str,
|
||||
"value".into() => LoroValue::List(list)
|
||||
)))
|
||||
}
|
||||
LoroValue::Map(mut map) => {
|
||||
let map_mut = Arc::make_mut(&mut map);
|
||||
for (_key, value) in map_mut.iter_mut() {
|
||||
if value.is_container() {
|
||||
let container = value.as_container().unwrap();
|
||||
let container_idx = self.arena.register_container(container);
|
||||
let new_value = self.get_container_deep_value_with_id(
|
||||
container_idx,
|
||||
Some(container.clone()),
|
||||
);
|
||||
*value = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::Map(Arc::new(fx_map!(
|
||||
"cid".into() => cid_str,
|
||||
"value".into() => LoroValue::Map(map)
|
||||
)))
|
||||
}
|
||||
_ => LoroValue::Map(Arc::new(fx_map!(
|
||||
"cid".into() => cid_str,
|
||||
"value".into() => value
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_container_deep_value(&self, container: ContainerIdx) -> LoroValue {
|
||||
let Some(state) = self.states.get(&container) else {
|
||||
return container.get_type().default_value();
|
||||
|
|
|
@ -260,7 +260,6 @@ impl ListState {
|
|||
|
||||
impl ContainerState for ListState {
|
||||
fn apply_diff(&mut self, diff: &mut Diff, arena: &SharedArena) {
|
||||
debug_log::debug_log!("Apply List Diff {:#?}", diff);
|
||||
match diff {
|
||||
Diff::List(delta) => {
|
||||
let mut index = 0;
|
||||
|
@ -300,7 +299,6 @@ impl ContainerState for ListState {
|
|||
for slices in value.0.iter() {
|
||||
for i in slices.0.start..slices.0.end {
|
||||
let value = arena.get_value(i as usize).unwrap();
|
||||
debug_dbg!(&value);
|
||||
if value.is_container() {
|
||||
let c = value.as_container().unwrap();
|
||||
let idx = arena.register_container(c);
|
||||
|
@ -321,8 +319,6 @@ impl ContainerState for ListState {
|
|||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
debug_dbg!(&self.idx);
|
||||
debug_dbg!(&self.get_value());
|
||||
}
|
||||
|
||||
fn apply_op(&mut self, op: RawOp, arena: &SharedArena) {
|
||||
|
|
|
@ -100,6 +100,89 @@ fn map_checkout() {
|
|||
assert_eq!(meta.get_deep_value().to_json(), r#"{"key":1}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_list_of_map_checkout() {
|
||||
let mut doc = LoroDoc::new();
|
||||
let entry = doc.get_map("entry");
|
||||
let (list, sub) = doc
|
||||
.with_txn(|txn| {
|
||||
let list = entry
|
||||
.insert_container(txn, "list", loro_common::ContainerType::List)?
|
||||
.into_list()
|
||||
.unwrap();
|
||||
let sub_map = list
|
||||
.insert_container(txn, 0, loro_common::ContainerType::Map)?
|
||||
.into_map()
|
||||
.unwrap();
|
||||
sub_map.insert(txn, "x", 100.into())?;
|
||||
sub_map.insert(txn, "y", 1000.into())?;
|
||||
Ok((list, sub_map))
|
||||
})
|
||||
.unwrap();
|
||||
let v0 = doc.oplog_frontiers();
|
||||
let d0 = doc.get_deep_value().to_json();
|
||||
doc.with_txn(|txn| {
|
||||
list.insert(txn, 0, 3.into())?;
|
||||
list.push(txn, 4.into())?;
|
||||
list.insert_container(txn, 2, loro_common::ContainerType::Map)?;
|
||||
list.insert_container(txn, 3, loro_common::ContainerType::Map)?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
doc.with_txn(|txn| {
|
||||
list.delete(txn, 2, 1)?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
doc.with_txn(|txn| {
|
||||
sub.insert(txn, "x", 9.into())?;
|
||||
sub.insert(txn, "y", 9.into())?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
doc.with_txn(|txn| {
|
||||
sub.insert(txn, "z", 9.into())?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
let v1 = doc.oplog_frontiers();
|
||||
let d1 = doc.get_deep_value().to_json();
|
||||
doc.with_txn(|txn| {
|
||||
sub.insert(txn, "x", 77.into())?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
doc.with_txn(|txn| {
|
||||
sub.insert(txn, "y", 88.into())?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
doc.with_txn(|txn| {
|
||||
list.delete(txn, 0, 1)?;
|
||||
list.insert(txn, 0, 123.into())?;
|
||||
list.push(txn, 99.into())?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
let v2 = doc.oplog_frontiers();
|
||||
let d2 = doc.get_deep_value().to_json();
|
||||
|
||||
doc.checkout(&v0);
|
||||
assert_eq!(doc.get_deep_value().to_json(), d0);
|
||||
doc.checkout(&v1);
|
||||
assert_eq!(doc.get_deep_value().to_json(), d1);
|
||||
doc.checkout(&v2);
|
||||
println!("{}", doc.get_deep_value_with_id().to_json_pretty());
|
||||
assert_eq!(doc.get_deep_value().to_json(), d2);
|
||||
debug_log::group!("checking out v1");
|
||||
doc.checkout(&v1);
|
||||
debug_log::group_end!();
|
||||
println!("{}", doc.get_deep_value_with_id().to_json_pretty());
|
||||
assert_eq!(doc.get_deep_value().to_json(), d1);
|
||||
doc.checkout(&v0);
|
||||
assert_eq!(doc.get_deep_value().to_json(), d0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_concurrent_checkout() {
|
||||
let mut doc_a = LoroDoc::new();
|
||||
|
|
Loading…
Reference in a new issue