diff --git a/automerge-wasm/src/lib.rs b/automerge-wasm/src/lib.rs index 80a3d65f..97a3fb24 100644 --- a/automerge-wasm/src/lib.rs +++ b/automerge-wasm/src/lib.rs @@ -1,6 +1,6 @@ extern crate web_sys; use automerge as am; -use automerge::{Change, ChangeHash, Prop, Value}; +use automerge::{Change, ChangeHash, ObjId, Prop, Value}; use js_sys::{Array, Object, Reflect, Uint8Array}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -151,9 +151,9 @@ impl Automerge { pub fn keys(&mut self, obj: JsValue, heads: JsValue) -> Result { let obj = self.import(obj)?; let result = if let Some(heads) = get_heads(heads) { - self.0.keys_at(obj, &heads) + self.0.keys_at(&obj, &heads) } else { - self.0.keys(obj) + self.0.keys(&obj) } .iter() .map(|s| JsValue::from_str(s)) @@ -164,9 +164,9 @@ impl Automerge { pub fn text(&mut self, obj: JsValue, heads: JsValue) -> Result { let obj = self.import(obj)?; if let Some(heads) = get_heads(heads) { - self.0.text_at(obj, &heads) + self.0.text_at(&obj, &heads) } else { - self.0.text(obj) + self.0.text(&obj) } .map_err(to_js_err) .map(|t| t.into()) @@ -185,7 +185,7 @@ impl Automerge { let mut vals = vec![]; if let Some(t) = text.as_string() { self.0 - .splice_text(obj, start, delete_count, &t) + .splice_text(&obj, start, delete_count, &t) .map_err(to_js_err)?; } else { if let Ok(array) = text.dyn_into::() { @@ -201,7 +201,7 @@ impl Automerge { } } self.0 - .splice(obj, start, delete_count, vals) + .splice(&obj, start, delete_count, vals) .map_err(to_js_err)?; } Ok(()) @@ -223,7 +223,7 @@ impl Automerge { let value = self.import_value(value, datatype)?; let opid = self .0 - .insert(obj, index as usize, value) + .insert(&obj, index as usize, value) .map_err(to_js_err)?; Ok(self.export(opid)) } @@ -238,7 +238,7 @@ impl Automerge { let obj = self.import(obj)?; let prop = self.import_prop(prop)?; let value = self.import_value(value, datatype)?; - let opid = self.0.set(obj, prop, value).map_err(to_js_err)?; + let opid = self.0.set(&obj, prop, value).map_err(to_js_err)?; match opid { Some(opid) => Ok(self.export(opid)), None => Ok(JsValue::null()), @@ -252,7 +252,7 @@ impl Automerge { .as_f64() .ok_or("inc needs a numberic value") .map_err(to_js_err)?; - self.0.inc(obj, prop, value as i64).map_err(to_js_err)?; + self.0.inc(&obj, prop, value as i64).map_err(to_js_err)?; Ok(()) } @@ -263,9 +263,9 @@ impl Automerge { let heads = get_heads(heads); if let Ok(prop) = prop { let value = if let Some(h) = heads { - self.0.value_at(obj, prop, &h) + self.0.value_at(&obj, prop, &h) } else { - self.0.value(obj, prop) + self.0.value(&obj, prop) } .map_err(to_js_err)?; match value { @@ -289,9 +289,9 @@ impl Automerge { let prop = to_prop(arg); if let Ok(prop) = prop { let values = if let Some(heads) = get_heads(heads) { - self.0.values_at(obj, prop, &heads) + self.0.values_at(&obj, prop, &heads) } else { - self.0.values(obj, prop) + self.0.values(&obj, prop) } .map_err(to_js_err)?; for value in values { @@ -318,16 +318,16 @@ impl Automerge { pub fn length(&mut self, obj: JsValue, heads: JsValue) -> Result { let obj = self.import(obj)?; if let Some(heads) = get_heads(heads) { - Ok((self.0.length_at(obj, &heads) as f64).into()) + Ok((self.0.length_at(&obj, &heads) as f64).into()) } else { - Ok((self.0.length(obj) as f64).into()) + Ok((self.0.length(&obj) as f64).into()) } } pub fn del(&mut self, obj: JsValue, prop: JsValue) -> Result<(), JsValue> { let obj = self.import(obj)?; let prop = to_prop(prop)?; - self.0.del(obj, prop).map_err(to_js_err)?; + self.0.del(&obj, prop).map_err(to_js_err)?; Ok(()) } @@ -442,15 +442,12 @@ impl Automerge { } } - fn export(&self, val: E) -> JsValue { - self.0.export(val).into() + fn export(&self, id: ObjId) -> JsValue { + id.to_string().into() } - fn import(&self, id: JsValue) -> Result { - let id_str = id - .as_string() - .ok_or("invalid opid/objid/elemid") - .map_err(to_js_err)?; + fn import(&self, id: JsValue) -> Result { + let id_str = id.as_string().ok_or("invalid opid").map_err(to_js_err)?; self.0.import(&id_str).map_err(to_js_err) } diff --git a/automerge/src/change.rs b/automerge/src/change.rs index 4d3984e5..93a9ecda 100644 --- a/automerge/src/change.rs +++ b/automerge/src/change.rs @@ -6,10 +6,7 @@ use crate::decoding; use crate::decoding::{Decodable, InvalidChangeError}; use crate::encoding::{Encodable, DEFLATE_MIN_SIZE}; use crate::legacy as amp; -use crate::{ - ActorId, AutomergeError, ElemId, IndexedCache, Key, ObjId, Op, OpId, OpType, Transaction, HEAD, - ROOT, -}; +use crate::{ActorId, AutomergeError, Op, OpType, Transaction}; use core::ops::Range; use flate2::{ bufread::{DeflateDecoder, DeflateEncoder}, @@ -50,33 +47,15 @@ fn get_heads(changes: &[amp::Change]) -> HashSet { pub(crate) fn encode_document( changes: &[amp::Change], doc_ops: &[Op], - actors_index: &IndexedCache, - props: &[String], + actors: &[ActorId], ) -> Result, AutomergeError> { let mut bytes: Vec = Vec::new(); let heads = get_heads(changes); - let actors_map = actors_index.encode_index(); - let actors = actors_index.sorted(); + let (change_bytes, change_info) = ChangeEncoder::encode_changes(changes, actors); - /* - // this assumes that all actor_ids referenced are seen in changes.actor_id which is true - // so long as we have a full history - let mut actors: Vec<_> = changes - .iter() - .map(|c| &c.actor) - .unique() - .sorted() - .cloned() - .collect(); - */ - - let (change_bytes, change_info) = ChangeEncoder::encode_changes(changes, &actors); - - //let doc_ops = group_doc_ops(changes, &actors); - - let (ops_bytes, ops_info) = DocOpEncoder::encode_doc_ops(doc_ops, &actors_map, props); + let (ops_bytes, ops_info) = DocOpEncoder::encode_doc_ops(doc_ops, actors); bytes.extend(&MAGIC_BYTES); bytes.extend(vec![0, 0, 0, 0]); // we dont know the hash yet so fill in a fake @@ -86,7 +65,7 @@ pub(crate) fn encode_document( actors.len().encode(&mut chunk)?; - for a in actors.into_iter() { + for a in actors.iter() { a.to_bytes().encode(&mut chunk)?; } @@ -200,7 +179,8 @@ fn encode_chunk(change: &::Change, deps: &[amp::ChangeHash]) -> ChunkIntermed } // encode first actor - let mut actors = vec![change.actor_id.clone()]; + //let mut actors = vec![change.actor_id.clone()]; + let mut actors = change.actors(); change.actor_id.to_bytes().encode(&mut bytes).unwrap(); // encode seq, start_op, time, message @@ -416,61 +396,16 @@ fn increment_range_map(ranges: &mut HashMap>, len: usize) { } } -fn export_objid(id: &ObjId, actors: &IndexedCache) -> amp::ObjectId { - if id.0 == ROOT { - amp::ObjectId::Root - } else { - export_opid(&id.0, actors).into() - } -} - -fn export_elemid(id: &ElemId, actors: &IndexedCache) -> amp::ElementId { - if id == &HEAD { - amp::ElementId::Head - } else { - export_opid(&id.0, actors).into() - } -} - -fn export_opid(id: &OpId, actors: &IndexedCache) -> amp::OpId { - amp::OpId(id.0, actors.get(id.1).clone()) -} - -fn export_op(op: &Op, actors: &IndexedCache, props: &IndexedCache) -> amp::Op { - let action = op.action.clone(); - let key = match &op.key { - Key::Map(n) => amp::Key::Map(props.get(*n).clone().into()), - Key::Seq(id) => amp::Key::Seq(export_elemid(id, actors)), - }; - let obj = export_objid(&op.obj, actors); - let pred = op.pred.iter().map(|id| export_opid(id, actors)).collect(); - amp::Op { - action, - obj, - insert: op.insert, - pred, - key, - } -} - -pub(crate) fn export_change( - change: &Transaction, - actors: &IndexedCache, - props: &IndexedCache, -) -> Change { +pub(crate) fn export_change(change: &Transaction) -> Change { amp::Change { - actor_id: actors.get(change.actor).clone(), + actor_id: change.actor.as_ref().clone(), seq: change.seq, start_op: change.start_op, time: change.time, deps: change.deps.clone(), message: change.message.clone(), hash: change.hash, - operations: change - .operations - .iter() - .map(|op| export_op(op, actors, props)) - .collect(), + operations: change.operations.iter().map(|op| op.into()).collect(), extra_bytes: change.extra_bytes.clone(), } .into() diff --git a/automerge/src/clock.rs b/automerge/src/clock.rs index 979885b3..18381370 100644 --- a/automerge/src/clock.rs +++ b/automerge/src/clock.rs @@ -1,26 +1,25 @@ -use crate::OpId; -use fxhash::FxBuildHasher; +use crate::{ActorId, OpId}; use std::cmp; use std::collections::HashMap; #[derive(Debug, Clone, PartialEq)] -pub(crate) struct Clock(HashMap); +pub(crate) struct Clock(HashMap); impl Clock { pub fn new() -> Self { - Clock(Default::default()) + Clock(HashMap::new()) } - pub fn include(&mut self, key: usize, n: u64) { + pub fn include(&mut self, key: &ActorId, n: u64) { self.0 - .entry(key) + .entry(key.clone()) .and_modify(|m| *m = cmp::max(n, *m)) .or_insert(n); } pub fn covers(&self, id: &OpId) -> bool { - if let Some(val) = self.0.get(&id.1) { - val >= &id.0 + if let Some(val) = self.0.get(&id.actor) { + val >= &id.counter } else { false } @@ -34,19 +33,22 @@ mod tests { #[test] fn covers() { let mut clock = Clock::new(); + let a1 = ActorId::random(); + let a2 = ActorId::random(); + let a3 = ActorId::random(); - clock.include(1, 20); - clock.include(2, 10); + clock.include(&a1, 20); + clock.include(&a2, 10); - assert!(clock.covers(&OpId(10, 1))); - assert!(clock.covers(&OpId(20, 1))); - assert!(!clock.covers(&OpId(30, 1))); + assert!(clock.covers(&OpId::at(10, &a1))); + assert!(clock.covers(&OpId::at(20, &a1))); + assert!(!clock.covers(&OpId::at(30, &a1))); - assert!(clock.covers(&OpId(5, 2))); - assert!(clock.covers(&OpId(10, 2))); - assert!(!clock.covers(&OpId(15, 2))); + assert!(clock.covers(&OpId::at(5, &a2))); + assert!(clock.covers(&OpId::at(10, &a2))); + assert!(!clock.covers(&OpId::at(15, &a2))); - assert!(!clock.covers(&OpId(1, 3))); - assert!(!clock.covers(&OpId(100, 3))); + assert!(!clock.covers(&OpId::at(1, &a3))); + assert!(!clock.covers(&OpId::at(100, &a3))); } } diff --git a/automerge/src/columnar.rs b/automerge/src/columnar.rs index 3a1df3cb..821b9157 100644 --- a/automerge/src/columnar.rs +++ b/automerge/src/columnar.rs @@ -11,7 +11,6 @@ use std::{ str, }; -use crate::ROOT; use crate::{ActorId, ElemId, Key, ObjId, ObjType, OpId, OpType, ScalarValue}; use crate::legacy as amp; @@ -23,7 +22,7 @@ use tracing::instrument; use crate::{ decoding::{BooleanDecoder, Decodable, Decoder, DeltaDecoder, RleDecoder}, encoding::{BooleanEncoder, ColData, DeltaEncoder, Encodable, RleEncoder}, - IndexedCache, Op, + Op, }; impl Encodable for Action { @@ -42,12 +41,14 @@ impl Encodable for [ActorId] { } } -fn map_actor(actor: &ActorId, actors: &mut Vec) -> usize { +fn map_actor(actor: &ActorId, actors: &[ActorId]) -> usize { if let Some(pos) = actors.iter().position(|a| a == actor) { pos } else { - actors.push(actor.clone()); - actors.len() - 1 + panic!( + "map_actor cant find actor! seeking={} actors={:?}", + actor, actors + ); } } @@ -558,7 +559,7 @@ impl ValEncoder { } } - fn append_value(&mut self, val: &ScalarValue, actors: &[usize]) { + fn append_value(&mut self, val: &ScalarValue, actors: &[ActorId]) { // It may seem weird to have two consecutive matches on the same value. The reason is so // that we don't have to repeat the `append_null` calls on ref_actor and ref_counter in // every arm of the next match @@ -679,22 +680,21 @@ impl KeyEncoder { } } - fn append(&mut self, key: Key, actors: &[usize], props: &[String]) { + fn append(&mut self, key: Key, actors: &[ActorId]) { match key { Key::Map(i) => { self.actor.append_null(); self.ctr.append_null(); - self.str.append_value(props[i].clone()); + self.str.append_value(i); } - Key::Seq(ElemId(OpId(0, 0))) => { - // HEAD + Key::Seq(ElemId::Head) => { self.actor.append_null(); self.ctr.append_value(0); self.str.append_null(); } - Key::Seq(ElemId(OpId(ctr, actor))) => { - self.actor.append_value(actors[actor]); - self.ctr.append_value(ctr); + Key::Seq(ElemId::Id(OpId { counter, actor })) => { + self.actor.append_value(map_actor(&actor, actors)); + self.ctr.append_value(counter); self.str.append_null(); } } @@ -770,11 +770,11 @@ impl SuccEncoder { } } - fn append(&mut self, succ: &[OpId], actors: &[usize]) { + fn append(&mut self, succ: &[OpId], actors: &[ActorId]) { self.num.append_value(succ.len()); for s in succ.iter() { - self.ctr.append_value(s.0); - self.actor.append_value(actors[s.1]); + self.ctr.append_value(s.counter); + self.actor.append_value(map_actor(&s.actor, actors)); } } @@ -844,15 +844,15 @@ impl ObjEncoder { } } - fn append(&mut self, obj: &ObjId, actors: &[usize]) { - match obj.0 { - ROOT => { + fn append(&mut self, obj: &ObjId, actors: &[ActorId]) { + match obj { + ObjId::Root => { self.actor.append_null(); self.ctr.append_null(); } - OpId(ctr, actor) => { - self.actor.append_value(actors[actor]); - self.ctr.append_value(ctr); + ObjId::Id(id) => { + self.actor.append_value(map_actor(&id.actor, actors)); + self.ctr.append_value(id.counter); } } } @@ -915,10 +915,7 @@ pub(crate) struct ChangeEncoder { impl ChangeEncoder { #[instrument(level = "debug", skip(changes, actors))] - pub fn encode_changes<'a, 'b, I>( - changes: I, - actors: &'a IndexedCache, - ) -> (Vec, Vec) + pub fn encode_changes<'a, 'b, I>(changes: I, actors: &'a [ActorId]) -> (Vec, Vec) where I: IntoIterator, { @@ -941,7 +938,7 @@ impl ChangeEncoder { } } - fn encode<'a, 'b, 'c, I>(&'a mut self, changes: I, actors: &'b IndexedCache) + fn encode<'a, 'b, 'c, I>(&'a mut self, changes: I, actors: &'b [ActorId]) where I: IntoIterator, { @@ -950,8 +947,7 @@ impl ChangeEncoder { if let Some(hash) = change.hash { index_by_hash.insert(hash, index); } - self.actor - .append_value(actors.lookup(change.actor_id.clone()).unwrap()); //actors.iter().position(|a| a == &change.actor_id).unwrap()); + self.actor.append_value(map_actor(&change.actor_id, actors)); self.seq.append_value(change.seq); // FIXME iterops.count is crazy slow self.max_op @@ -1024,16 +1020,12 @@ pub(crate) struct DocOpEncoder { impl DocOpEncoder { #[instrument(level = "debug", skip(ops, actors))] - pub(crate) fn encode_doc_ops<'a, I>( - ops: I, - actors: &'a [usize], - props: &'a [String], - ) -> (Vec, Vec) + pub(crate) fn encode_doc_ops<'a, I>(ops: I, actors: &'a [ActorId]) -> (Vec, Vec) where I: IntoIterator, { let mut e = Self::new(); - e.encode(ops, actors, props); + e.encode(ops, actors); e.finish() } @@ -1050,15 +1042,15 @@ impl DocOpEncoder { } } - fn encode<'a, I>(&mut self, ops: I, actors: &[usize], props: &[String]) + fn encode<'a, I>(&mut self, ops: I, actors: &[ActorId]) where I: IntoIterator, { for op in ops { - self.actor.append_value(actors[op.id.actor()]); - self.ctr.append_value(op.id.counter()); + self.actor.append_value(map_actor(&op.id.actor, actors)); + self.ctr.append_value(op.id.counter); self.obj.append(&op.obj, actors); - self.key.append(op.key, actors, props); + self.key.append(op.key.clone(), actors); self.insert.append(op.insert); self.succ.append(&op.succ, actors); let action = match &op.action { diff --git a/automerge/src/legacy/mod.rs b/automerge/src/legacy/mod.rs index 0968d290..11d56e5f 100644 --- a/automerge/src/legacy/mod.rs +++ b/automerge/src/legacy/mod.rs @@ -5,8 +5,10 @@ use std::iter::FromIterator; pub(crate) use crate::value::DataType; pub(crate) use crate::{ActorId, ChangeHash, ObjType, OpType, ScalarValue}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use smol_str::SmolStr; +use std::collections::HashSet; #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Copy, Hash)] #[cfg_attr(feature = "derive-arbitrary", derive(arbitrary::Arbitrary))] @@ -251,6 +253,29 @@ pub struct Change { pub extra_bytes: Vec, } +impl Change { + pub(crate) fn actors(&self) -> Vec { + let first = self.actor_id.clone(); + let mut set = HashSet::new(); + for o in &self.operations { + if let ObjectId::Id(OpId(_, a)) = &o.obj { + set.insert(a.clone()); + } + if let Key::Seq(ElementId::Id(OpId(_, a))) = &o.key { + set.insert(a.clone()); + } + for p in o.pred.iter() { + set.insert(p.1.clone()); + } + } + set.remove(&first); + let mut result = vec![first]; + let set: Vec = set.iter().sorted().cloned().collect(); + result.extend(set); + result + } +} + impl PartialEq for Change { // everything but hash (its computed and not always present) fn eq(&self, other: &Self) -> bool { diff --git a/automerge/src/lib.rs b/automerge/src/lib.rs index c2595c68..f6b2da61 100644 --- a/automerge/src/lib.rs +++ b/automerge/src/lib.rs @@ -33,7 +33,6 @@ mod clock; mod columnar; mod decoding; mod encoding; -mod indexed_cache; mod legacy; mod sync; #[cfg(feature = "optree-visualisation")] @@ -46,34 +45,36 @@ mod query; mod types; mod value; +use crate::legacy as amp; use change::{encode_document, export_change}; use clock::Clock; -use indexed_cache::IndexedCache; +use itertools::Itertools; use op_set::OpSet; use std::collections::{HashMap, HashSet, VecDeque}; -use types::{ElemId, Key, ObjId, Op, HEAD}; +use std::rc::Rc; +use types::{ElemId, Key, Op}; use unicode_segmentation::UnicodeSegmentation; +pub const ROOT: ObjId = ObjId::Root; + pub use change::{decode_change, Change}; pub use error::AutomergeError; pub use legacy::Change as ExpandedChange; pub use sync::{BloomFilter, SyncHave, SyncMessage, SyncState}; -pub use types::{ - ActorId, ChangeHash, Export, Exportable, Importable, ObjType, OpId, OpType, Patch, Peer, Prop, - ROOT, -}; +pub use types::{ActorId, ChangeHash, ObjId, ObjType, OpId, OpType, Patch, Peer, Prop}; pub use value::{ScalarValue, Value}; #[derive(Debug, Clone)] pub struct Automerge { queue: Vec, history: Vec, + actors: HashMap>, history_index: HashMap, - states: HashMap>, + states: HashMap, Vec>, deps: HashSet, saved: Vec, ops: OpSet, - actor: Option, + actor: Option>, max_op: u64, transaction: Option, } @@ -84,6 +85,7 @@ impl Automerge { queue: vec![], history: vec![], history_index: HashMap::new(), + actors: HashMap::new(), states: HashMap::new(), ops: Default::default(), deps: Default::default(), @@ -94,36 +96,57 @@ impl Automerge { } } - pub fn set_actor(&mut self, actor: ActorId) { - self.ensure_transaction_closed(); - self.actor = Some(self.ops.m.actors.cache(actor)) + pub fn import(&self, s: &str) -> Result { + if s == "_root" { + Ok(ObjId::Root) + } else { + let n = s + .find('@') + .ok_or_else(|| AutomergeError::InvalidOpId(s.to_owned()))?; + let counter = s[0..n] + .parse() + .map_err(|_| AutomergeError::InvalidOpId(s.to_owned()))?; + let actor = ActorId::from(hex::decode(&s[(n + 1)..]).unwrap()); + if let Some(actor) = self.actors.get(&actor).cloned() { + Ok(ObjId::Id(OpId { counter, actor })) + } else { + Err(AutomergeError::InvalidOpId(s.to_owned())) + } + } } - fn random_actor(&mut self) -> ActorId { + fn import_actor(&mut self, actor: &ActorId) -> Rc { + if let Some(a) = self.actors.get(actor) { + a.clone() + } else { + let a = Rc::new(actor.clone()); + self.actors.insert(actor.clone(), a.clone()); + a + } + } + + pub fn set_actor(&mut self, actor: ActorId) { + self.ensure_transaction_closed(); + self.actor = Some(self.import_actor(&actor)); + } + + fn random_actor(&mut self) -> Rc { let actor = ActorId::from(uuid::Uuid::new_v4().as_bytes().to_vec()); - self.actor = Some(self.ops.m.actors.cache(actor.clone())); + let actor = self.import_actor(&actor); + self.actor = Some(actor.clone()); actor } - pub fn get_actor(&mut self) -> ActorId { - if let Some(actor) = self.actor { - self.ops.m.actors[actor].clone() + pub fn get_actor(&mut self) -> Rc { + if let Some(actor) = &self.actor { + actor.clone() } else { self.random_actor() } } - pub fn maybe_get_actor(&self) -> Option { - self.actor.map(|i| self.ops.m.actors[i].clone()) - } - - fn get_actor_index(&mut self) -> usize { - if let Some(actor) = self.actor { - actor - } else { - self.random_actor(); - self.actor.unwrap() // random_actor always sets actor to is_some() - } + pub fn maybe_get_actor(&self) -> Option> { + self.actor.clone() } pub fn new_with_actor_id(actor: ActorId) -> Self { @@ -131,6 +154,7 @@ impl Automerge { queue: vec![], history: vec![], history_index: HashMap::new(), + actors: HashMap::new(), states: HashMap::new(), ops: Default::default(), deps: Default::default(), @@ -139,7 +163,7 @@ impl Automerge { max_op: 0, transaction: None, }; - am.actor = Some(am.ops.m.actors.cache(actor)); + am.set_actor(actor); am } @@ -152,12 +176,12 @@ impl Automerge { fn tx(&mut self) -> &mut Transaction { if self.transaction.is_none() { - let actor = self.get_actor_index(); + let actor = self.get_actor(); - let seq = self.states.entry(actor).or_default().len() as u64 + 1; + let seq = self.states.entry(actor.clone()).or_default().len() as u64 + 1; let mut deps = self.get_heads(); if seq > 1 { - let last_hash = self.get_hash(actor, seq - 1).unwrap(); + let last_hash = self.get_hash(&actor, seq - 1).unwrap(); if !deps.contains(&last_hash) { deps.push(last_hash); } @@ -199,7 +223,8 @@ impl Automerge { pub fn ensure_transaction_closed(&mut self) { if let Some(tx) = self.transaction.take() { - self.update_history(export_change(&tx, &self.ops.m.actors, &self.ops.m.props)); + //self.update_history(export_change(&tx, &self.ops.m.actors, &self.ops.m.props)); + self.update_history(export_change(&tx)); } } @@ -211,11 +236,11 @@ impl Automerge { // FIXME - use query to make this fast if let Some(p) = self.ops.iter().position(|o| o.id == *pred_id) { self.ops - .replace(op.obj, p, |o| o.succ.retain(|i| i != pred_id)); + .replace(&op.obj, p, |o| o.succ.retain(|i| i != pred_id)); } } if let Some(pos) = self.ops.iter().position(|o| o.id == op.id) { - self.ops.remove(op.obj, pos); + self.ops.remove(&op.obj, pos); } } num @@ -226,13 +251,16 @@ impl Automerge { fn next_id(&mut self) -> OpId { let tx = self.tx(); - OpId(tx.start_op + tx.operations.len() as u64, tx.actor) + OpId { + counter: tx.start_op + tx.operations.len() as u64, + actor: tx.actor.clone(), + } } fn insert_local_op(&mut self, op: Op, pos: usize, succ_pos: &[usize]) { for succ in succ_pos { - self.ops.replace(op.obj, *succ, |old_op| { - old_op.succ.push(op.id); + self.ops.replace(&op.obj, *succ, |old_op| { + old_op.succ.push(op.id.clone()); }); } @@ -244,11 +272,11 @@ impl Automerge { } fn insert_op(&mut self, op: Op) -> Op { - let q = self.ops.search(op.obj, query::SeekOp::new(&op)); + let q = self.ops.search(&op.obj, query::SeekOp::new(&op)); for i in q.succ { self.ops - .replace(op.obj, i, |old_op| old_op.succ.push(op.id)); + .replace(&op.obj, i, |old_op| old_op.succ.push(op.id.clone())); } if !op.is_del() { @@ -262,24 +290,24 @@ impl Automerge { // PropAt::() // NthAt::() - pub fn keys(&self, obj: OpId) -> Vec { - let q = self.ops.search(obj.into(), query::Keys::new()); - q.keys.iter().map(|k| self.export(*k)).collect() + pub fn keys(&self, obj: &ObjId) -> Vec { + let q = self.ops.search(obj, query::Keys::new()); + q.keys.iter().map(|k| k.to_string()).collect() } - pub fn keys_at(&self, obj: OpId, heads: &[ChangeHash]) -> Vec { + pub fn keys_at(&self, obj: &ObjId, heads: &[ChangeHash]) -> Vec { let clock = self.clock_at(heads); - let q = self.ops.search(obj.into(), query::KeysAt::new(clock)); - q.keys.iter().map(|k| self.export(*k)).collect() + let q = self.ops.search(obj, query::KeysAt::new(clock)); + q.keys.iter().map(|k| k.to_string()).collect() } - pub fn length(&self, obj: OpId) -> usize { - self.ops.search(obj.into(), query::Len::new(obj.into())).len + pub fn length(&self, obj: &ObjId) -> usize { + self.ops.search(obj, query::Len::new()).len } - pub fn length_at(&self, obj: OpId, heads: &[ChangeHash]) -> usize { + pub fn length_at(&self, obj: &ObjId, heads: &[ChangeHash]) -> usize { let clock = self.clock_at(heads); - self.ops.search(obj.into(), query::LenAt::new(clock)).len + self.ops.search(obj, query::LenAt::new(clock)).len } // set(obj, prop, value) - value can be scalar or objtype @@ -302,21 +330,20 @@ impl Automerge { /// - The key does not exist in the object pub fn set, V: Into>( &mut self, - obj: OpId, + obj: &ObjId, prop: P, value: V, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { let value = value.into(); - self.local_op(obj.into(), prop.into(), value.into()) + self.local_op(obj.clone(), prop.into(), value.into()) } pub fn insert>( &mut self, - obj: OpId, + obj: &ObjId, index: usize, value: V, - ) -> Result { - let obj = obj.into(); + ) -> Result { let id = self.next_id(); let query = self.ops.search(obj, query::InsertNth::new(index)); @@ -326,9 +353,9 @@ impl Automerge { let op = Op { change: self.history.len(), - id, + id: id.clone(), action: value.into(), - obj, + obj: obj.clone(), key, succ: Default::default(), pred: Default::default(), @@ -338,16 +365,16 @@ impl Automerge { self.ops.insert(query.pos, op.clone()); self.tx().operations.push(op); - Ok(id) + Ok(id.into()) } pub fn inc>( &mut self, - obj: OpId, + obj: &ObjId, prop: P, value: i64, - ) -> Result { - match self.local_op(obj.into(), prop.into(), OpType::Inc(value))? { + ) -> Result { + match self.local_op(obj.clone(), prop.into(), OpType::Inc(value))? { Some(opid) => Ok(opid), None => { panic!("increment should always create a new op") @@ -355,9 +382,9 @@ impl Automerge { } } - pub fn del>(&mut self, obj: OpId, prop: P) -> Result { + pub fn del>(&mut self, obj: &ObjId, prop: P) -> Result { // TODO: Should we also no-op multiple delete operations? - match self.local_op(obj.into(), prop.into(), OpType::Del)? { + match self.local_op(obj.clone(), prop.into(), OpType::Del)? { Some(opid) => Ok(opid), None => { panic!("delete should always create a new op") @@ -369,11 +396,11 @@ impl Automerge { /// the new elements pub fn splice( &mut self, - obj: OpId, + obj: &ObjId, mut pos: usize, del: usize, vals: Vec, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { for _ in 0..del { self.del(obj, pos)?; } @@ -387,11 +414,11 @@ impl Automerge { pub fn splice_text( &mut self, - obj: OpId, + obj: &ObjId, pos: usize, del: usize, text: &str, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { let mut vals = vec![]; for c in text.to_owned().graphemes(true) { vals.push(c.into()); @@ -399,9 +426,8 @@ impl Automerge { self.splice(obj, pos, del, vals) } - pub fn text(&self, obj: OpId) -> Result { - let obj = obj.into(); - let query = self.ops.search(obj, query::ListVals::new(obj)); + pub fn text(&self, obj: &ObjId) -> Result { + let query = self.ops.search(obj, query::ListVals::new()); let mut buffer = String::new(); for q in &query.ops { if let OpType::Set(ScalarValue::Str(s)) = &q.action { @@ -411,9 +437,8 @@ impl Automerge { Ok(buffer) } - pub fn text_at(&self, obj: OpId, heads: &[ChangeHash]) -> Result { + pub fn text_at(&self, obj: &ObjId, heads: &[ChangeHash]) -> Result { let clock = self.clock_at(heads); - let obj = obj.into(); let query = self.ops.search(obj, query::ListValsAt::new(clock)); let mut buffer = String::new(); for q in &query.ops { @@ -429,41 +454,34 @@ impl Automerge { // Something better? pub fn value>( &self, - obj: OpId, + obj: &ObjId, prop: P, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { Ok(self.values(obj, prop.into())?.first().cloned()) } pub fn value_at>( &self, - obj: OpId, + obj: &ObjId, prop: P, heads: &[ChangeHash], - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { Ok(self.values_at(obj, prop, heads)?.first().cloned()) } pub fn values>( &self, - obj: OpId, + obj: &ObjId, prop: P, - ) -> Result, AutomergeError> { - let obj = obj.into(); + ) -> Result, AutomergeError> { let result = match prop.into() { - Prop::Map(p) => { - let prop = self.ops.m.props.lookup(p); - if let Some(p) = prop { - self.ops - .search(obj, query::Prop::new(obj, p)) - .ops - .into_iter() - .map(|o| o.into()) - .collect() - } else { - vec![] - } - } + Prop::Map(p) => self + .ops + .search(obj, query::Prop::new(p)) + .ops + .into_iter() + .map(|o| o.into()) + .collect(), Prop::Seq(n) => self .ops .search(obj, query::Nth::new(n)) @@ -477,27 +495,20 @@ impl Automerge { pub fn values_at>( &self, - obj: OpId, + obj: &ObjId, prop: P, heads: &[ChangeHash], - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { let prop = prop.into(); - let obj = obj.into(); let clock = self.clock_at(heads); let result = match prop { - Prop::Map(p) => { - let prop = self.ops.m.props.lookup(p); - if let Some(p) = prop { - self.ops - .search(obj, query::PropAt::new(p, clock)) - .ops - .into_iter() - .map(|o| o.into()) - .collect() - } else { - vec![] - } - } + Prop::Map(p) => self + .ops + .search(obj, query::PropAt::new(p, clock)) + .ops + .into_iter() + .map(|o| o.into()) + .collect(), Prop::Seq(n) => self .ops .search(obj, query::NthAt::new(n, clock)) @@ -555,7 +566,7 @@ impl Automerge { obj: ObjId, prop: Prop, action: OpType, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { match prop { Prop::Map(s) => self.local_map_op(obj, s, action), Prop::Seq(n) => self.local_list_op(obj, n, action), @@ -567,14 +578,13 @@ impl Automerge { obj: ObjId, prop: String, action: OpType, - ) -> Result, AutomergeError> { + ) -> Result, AutomergeError> { if prop.is_empty() { return Err(AutomergeError::EmptyStringKey); } let id = self.next_id(); - let prop = self.ops.m.props.cache(prop); - let query = self.ops.search(obj, query::Prop::new(obj, prop)); + let query = self.ops.search(&obj, query::Prop::new(prop.clone())); match (&query.ops[..], &action) { // If there are no conflicts for this value and the old operation and the new operation are @@ -591,11 +601,11 @@ impl Automerge { _ => {} } - let pred = query.ops.iter().map(|op| op.id).collect(); + let pred = query.ops.iter().map(|op| op.id.clone()).collect(); let op = Op { change: self.history.len(), - id, + id: id.clone(), action, obj, key: Key::Map(prop), @@ -606,7 +616,7 @@ impl Automerge { self.insert_local_op(op, query.pos, &query.ops_pos); - Ok(Some(id)) + Ok(Some(id.into())) } fn local_list_op( @@ -614,11 +624,11 @@ impl Automerge { obj: ObjId, index: usize, action: OpType, - ) -> Result, AutomergeError> { - let query = self.ops.search(obj, query::Nth::new(index)); + ) -> Result, AutomergeError> { + let query = self.ops.search(&obj, query::Nth::new(index)); let id = self.next_id(); - let pred = query.ops.iter().map(|op| op.id).collect(); + let pred = query.ops.iter().map(|op| op.id.clone()).collect(); let key = query.key()?; match (&query.ops[..], &action) { @@ -638,7 +648,7 @@ impl Automerge { let op = Op { change: self.history.len(), - id, + id: id.clone(), action, obj, key, @@ -649,7 +659,7 @@ impl Automerge { self.insert_local_op(op, query.pos, &query.ops_pos); - Ok(Some(id)) + Ok(Some(id.into())) } fn is_causally_ready(&self, change: &Change) -> bool { @@ -675,21 +685,33 @@ impl Automerge { .iter_ops() .enumerate() .map(|(i, c)| { - let actor = self.ops.m.actors.cache(change.actor_id().clone()); - let id = OpId(change.start_op + i as u64, actor); - // FIXME dont need to_string() - let obj: ObjId = self.import(&c.obj.to_string()).unwrap(); + let id = OpId { + counter: change.start_op + i as u64, + actor: self.import_actor(change.actor_id()), + }; + let obj: ObjId = match &c.obj { + amp::ObjectId::Root => ObjId::Root, + amp::ObjectId::Id(amp::OpId(c, a)) => ObjId::Id(OpId { + counter: *c, + actor: self.import_actor(a), + }), + }; let pred = c .pred .iter() - .map(|i| self.import(&i.to_string()).unwrap()) + .map(|amp::OpId(c, a)| OpId { + counter: *c, + actor: self.import_actor(a), + }) .collect(); let key = match &c.key { - legacy::Key::Map(n) => Key::Map(self.ops.m.props.cache(n.to_string())), - legacy::Key::Seq(legacy::ElementId::Head) => Key::Seq(HEAD), - // FIXME dont need to_string() - legacy::Key::Seq(legacy::ElementId::Id(i)) => { - Key::Seq(self.import(&i.to_string()).unwrap()) + amp::Key::Map(n) => Key::Map(n.to_string()), + amp::Key::Seq(amp::ElementId::Head) => Key::Seq(ElemId::Head), + amp::Key::Seq(amp::ElementId::Id(amp::OpId(c, a))) => { + Key::Seq(ElemId::Id(OpId { + counter: *c, + actor: self.import_actor(a), + })) } }; Op { @@ -724,12 +746,8 @@ impl Automerge { let c: Vec<_> = self.history.iter().map(|c| c.decode()).collect(); let ops: Vec<_> = self.ops.iter().cloned().collect(); // TODO - can we make encode_document error free - let bytes = encode_document( - &c, - ops.as_slice(), - &self.ops.m.actors, - &self.ops.m.props.cache, - ); + let actors: Vec<_> = self.actors.keys().cloned().sorted().collect(); + let bytes = encode_document(&c, ops.as_slice(), &actors); if bytes.is_ok() { self.saved = self.get_heads().iter().copied().collect(); } @@ -899,8 +917,11 @@ impl Automerge { pub fn get_last_local_change(&mut self) -> Option<&Change> { self.ensure_transaction_closed(); if let Some(actor) = &self.actor { - let actor = &self.ops.m.actors[*actor]; - return self.history.iter().rev().find(|c| c.actor_id() == actor); + return self + .history + .iter() + .rev() + .find(|c| c.actor_id() == actor.as_ref()); } None } @@ -930,8 +951,8 @@ impl Automerge { to_see.push(*h); } } - let actor = self.ops.m.actors.lookup(c.actor_id().clone()).unwrap(); - clock.include(actor, c.max_op()); + //let actor = self.ops.m.actors.lookup(c.actor_id().clone()).unwrap(); + clock.include(c.actor_id(), c.max_op()); seen.insert(hash); } } @@ -989,9 +1010,9 @@ impl Automerge { deps } - fn get_hash(&mut self, actor: usize, seq: u64) -> Result { + fn get_hash(&mut self, actor: &ActorId, seq: u64) -> Result { self.states - .get(&actor) + .get(actor) .and_then(|v| v.get(seq as usize - 1)) .and_then(|&i| self.history.get(i)) .map(|c| c.hash) @@ -1005,10 +1026,8 @@ impl Automerge { let history_index = self.history.len(); - self.states - .entry(self.ops.m.actors.cache(change.actor_id().clone())) - .or_default() - .push(history_index); + let actor = self.import_actor(change.actor_id()); + self.states.entry(actor).or_default().push(history_index); self.history_index.insert(change.hash, history_index); self.history.push(change); @@ -1023,35 +1042,6 @@ impl Automerge { self.deps.insert(change.hash); } - pub fn import(&self, s: &str) -> Result { - if let Some(x) = I::from(s) { - Ok(x) - } else { - let n = s - .find('@') - .ok_or_else(|| AutomergeError::InvalidOpId(s.to_owned()))?; - let counter = s[0..n] - .parse() - .map_err(|_| AutomergeError::InvalidOpId(s.to_owned()))?; - let actor = ActorId::from(hex::decode(&s[(n + 1)..]).unwrap()); - let actor = self - .ops - .m - .actors - .lookup(actor) - .ok_or_else(|| AutomergeError::InvalidOpId(s.to_owned()))?; - Ok(I::wrap(OpId(counter, actor))) - } - } - - pub fn export(&self, id: E) -> String { - match id.export() { - Export::Id(id) => format!("{}@{}", id.counter(), self.ops.m.actors[id.actor()]), - Export::Prop(index) => self.ops.m.props[index].clone(), - Export::Special(s) => s, - } - } - pub fn dump(&self) { log!( " {:12} {:12} {:12} {} {} {}", @@ -1063,20 +1053,17 @@ impl Automerge { "succ" ); for i in self.ops.iter() { - let id = self.export(i.id); - let obj = self.export(i.obj); - let key = match i.key { - Key::Map(n) => self.ops.m.props[n].clone(), - Key::Seq(n) => self.export(n), - }; + let id = &i.id; + let obj = &i.obj; + let key = &i.key; let value: String = match &i.action { OpType::Set(value) => format!("{}", value), OpType::Make(obj) => format!("make{}", obj), OpType::Inc(obj) => format!("inc{}", obj), OpType::Del => format!("del{}", 0), }; - let pred: Vec<_> = i.pred.iter().map(|id| self.export(*id)).collect(); - let succ: Vec<_> = i.succ.iter().map(|id| self.export(*id)).collect(); + let pred = &i.pred; + let succ = &i.succ; log!( " {:12} {:12} {:12} {} {:?} {:?}", id, @@ -1097,7 +1084,7 @@ impl Automerge { #[derive(Debug, Clone)] pub(crate) struct Transaction { - pub actor: usize, + pub actor: Rc, pub seq: u64, pub start_op: u64, pub time: i64, @@ -1123,9 +1110,9 @@ mod tests { fn insert_op() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); doc.set_actor(ActorId::random()); - doc.set(ROOT, "hello", "world")?; + doc.set(&ROOT, "hello", "world")?; assert!(doc.pending_ops() == 1); - doc.value(ROOT, "hello")?; + doc.value(&ROOT, "hello")?; Ok(()) } @@ -1133,18 +1120,18 @@ mod tests { fn test_list() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); doc.set_actor(ActorId::random()); - let list_id = doc.set(ROOT, "items", Value::list())?.unwrap(); - doc.set(ROOT, "zzz", "zzzval")?; - assert!(doc.value(ROOT, "items")?.unwrap().1 == list_id); - doc.insert(list_id, 0, "a")?; - doc.insert(list_id, 0, "b")?; - doc.insert(list_id, 2, "c")?; - doc.insert(list_id, 1, "d")?; - assert!(doc.value(list_id, 0)?.unwrap().0 == "b".into()); - assert!(doc.value(list_id, 1)?.unwrap().0 == "d".into()); - assert!(doc.value(list_id, 2)?.unwrap().0 == "a".into()); - assert!(doc.value(list_id, 3)?.unwrap().0 == "c".into()); - assert!(doc.length(list_id) == 4); + let list_id = doc.set(&ROOT, "items", Value::list())?.unwrap(); + doc.set(&ROOT, "zzz", "zzzval")?; + assert!(doc.value(&ROOT, "items")?.unwrap().1 == list_id); + doc.insert(&list_id, 0, "a")?; + doc.insert(&list_id, 0, "b")?; + doc.insert(&list_id, 2, "c")?; + doc.insert(&list_id, 1, "d")?; + assert!(doc.value(&list_id, 0)?.unwrap().0 == "b".into()); + assert!(doc.value(&list_id, 1)?.unwrap().0 == "d".into()); + assert!(doc.value(&list_id, 2)?.unwrap().0 == "a".into()); + assert!(doc.value(&list_id, 3)?.unwrap().0 == "c".into()); + assert!(doc.length(&list_id) == 4); doc.save()?; Ok(()) } @@ -1153,22 +1140,22 @@ mod tests { fn test_del() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); doc.set_actor(ActorId::random()); - doc.set(ROOT, "xxx", "xxx")?; - assert!(!doc.values(ROOT, "xxx")?.is_empty()); - doc.del(ROOT, "xxx")?; - assert!(doc.values(ROOT, "xxx")?.is_empty()); + doc.set(&ROOT, "xxx", "xxx")?; + assert!(!doc.values(&ROOT, "xxx")?.is_empty()); + doc.del(&ROOT, "xxx")?; + assert!(doc.values(&ROOT, "xxx")?.is_empty()); Ok(()) } #[test] fn test_inc() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); - let id = doc.set(ROOT, "counter", Value::counter(10))?.unwrap(); - assert!(doc.value(ROOT, "counter")? == Some((Value::counter(10), id))); - doc.inc(ROOT, "counter", 10)?; - assert!(doc.value(ROOT, "counter")? == Some((Value::counter(20), id))); - doc.inc(ROOT, "counter", -5)?; - assert!(doc.value(ROOT, "counter")? == Some((Value::counter(15), id))); + let id = doc.set(&ROOT, "counter", Value::counter(10))?.unwrap(); + assert!(doc.value(&ROOT, "counter")? == Some((Value::counter(10), id.clone()))); + doc.inc(&ROOT, "counter", 10)?; + assert!(doc.value(&ROOT, "counter")? == Some((Value::counter(20), id.clone()))); + doc.inc(&ROOT, "counter", -5)?; + assert!(doc.value(&ROOT, "counter")? == Some((Value::counter(15), id.clone()))); Ok(()) } @@ -1176,15 +1163,15 @@ mod tests { fn test_save_incremental() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); - doc.set(ROOT, "foo", 1)?; + doc.set(&ROOT, "foo", 1)?; let save1 = doc.save().unwrap(); - doc.set(ROOT, "bar", 2)?; + doc.set(&ROOT, "bar", 2)?; let save2 = doc.save_incremental(); - doc.set(ROOT, "baz", 3)?; + doc.set(&ROOT, "baz", 3)?; let save3 = doc.save_incremental(); @@ -1202,7 +1189,7 @@ mod tests { let mut doc_a = Automerge::load(&save_a)?; let mut doc_b = Automerge::load(&save_b)?; - assert!(doc_a.values(ROOT, "baz")? == doc_b.values(ROOT, "baz")?); + assert!(doc_a.values(&ROOT, "baz")? == doc_b.values(&ROOT, "baz")?); assert!(doc_a.save().unwrap() == doc_b.save().unwrap()); @@ -1212,17 +1199,17 @@ mod tests { #[test] fn test_save_text() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); - let text = doc.set(ROOT, "text", Value::text())?.unwrap(); + let text = doc.set(&ROOT, "text", Value::text())?.unwrap(); let heads1 = doc.commit(None, None); - doc.splice_text(text, 0, 0, "hello world")?; + doc.splice_text(&text, 0, 0, "hello world")?; let heads2 = doc.commit(None, None); - doc.splice_text(text, 6, 0, "big bad ")?; + doc.splice_text(&text, 6, 0, "big bad ")?; let heads3 = doc.commit(None, None); - assert!(&doc.text(text)? == "hello big bad world"); - assert!(&doc.text_at(text, &heads1)?.is_empty()); - assert!(&doc.text_at(text, &heads2)? == "hello world"); - assert!(&doc.text_at(text, &heads3)? == "hello big bad world"); + assert!(&doc.text(&text)? == "hello big bad world"); + assert!(&doc.text_at(&text, &heads1)?.is_empty()); + assert!(&doc.text_at(&text, &heads2)? == "hello world"); + assert!(&doc.text_at(&text, &heads3)? == "hello big bad world"); Ok(()) } @@ -1231,50 +1218,50 @@ mod tests { fn test_props_vals_at() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); doc.set_actor("aaaa".try_into().unwrap()); - doc.set(ROOT, "prop1", "val1")?; + doc.set(&ROOT, "prop1", "val1")?; doc.commit(None, None); let heads1 = doc.get_heads(); - doc.set(ROOT, "prop1", "val2")?; + doc.set(&ROOT, "prop1", "val2")?; doc.commit(None, None); let heads2 = doc.get_heads(); - doc.set(ROOT, "prop2", "val3")?; + doc.set(&ROOT, "prop2", "val3")?; doc.commit(None, None); let heads3 = doc.get_heads(); - doc.del(ROOT, "prop1")?; + doc.del(&ROOT, "prop1")?; doc.commit(None, None); let heads4 = doc.get_heads(); - doc.set(ROOT, "prop3", "val4")?; + doc.set(&ROOT, "prop3", "val4")?; doc.commit(None, None); let heads5 = doc.get_heads(); - assert!(doc.keys_at(ROOT, &heads1) == vec!["prop1".to_owned()]); - assert!(doc.value_at(ROOT, "prop1", &heads1)?.unwrap().0 == Value::str("val1")); - assert!(doc.value_at(ROOT, "prop2", &heads1)? == None); - assert!(doc.value_at(ROOT, "prop3", &heads1)? == None); + assert!(doc.keys_at(&ROOT, &heads1) == vec!["prop1".to_owned()]); + assert!(doc.value_at(&ROOT, "prop1", &heads1)?.unwrap().0 == Value::str("val1")); + assert!(doc.value_at(&ROOT, "prop2", &heads1)? == None); + assert!(doc.value_at(&ROOT, "prop3", &heads1)? == None); - assert!(doc.keys_at(ROOT, &heads2) == vec!["prop1".to_owned()]); - assert!(doc.value_at(ROOT, "prop1", &heads2)?.unwrap().0 == Value::str("val2")); - assert!(doc.value_at(ROOT, "prop2", &heads2)? == None); - assert!(doc.value_at(ROOT, "prop3", &heads2)? == None); + assert!(doc.keys_at(&ROOT, &heads2) == vec!["prop1".to_owned()]); + assert!(doc.value_at(&ROOT, "prop1", &heads2)?.unwrap().0 == Value::str("val2")); + assert!(doc.value_at(&ROOT, "prop2", &heads2)? == None); + assert!(doc.value_at(&ROOT, "prop3", &heads2)? == None); - assert!(doc.keys_at(ROOT, &heads3) == vec!["prop1".to_owned(), "prop2".to_owned()]); - assert!(doc.value_at(ROOT, "prop1", &heads3)?.unwrap().0 == Value::str("val2")); - assert!(doc.value_at(ROOT, "prop2", &heads3)?.unwrap().0 == Value::str("val3")); - assert!(doc.value_at(ROOT, "prop3", &heads3)? == None); + assert!(doc.keys_at(&ROOT, &heads3) == vec!["prop1".to_owned(), "prop2".to_owned()]); + assert!(doc.value_at(&ROOT, "prop1", &heads3)?.unwrap().0 == Value::str("val2")); + assert!(doc.value_at(&ROOT, "prop2", &heads3)?.unwrap().0 == Value::str("val3")); + assert!(doc.value_at(&ROOT, "prop3", &heads3)? == None); - assert!(doc.keys_at(ROOT, &heads4) == vec!["prop2".to_owned()]); - assert!(doc.value_at(ROOT, "prop1", &heads4)? == None); - assert!(doc.value_at(ROOT, "prop2", &heads4)?.unwrap().0 == Value::str("val3")); - assert!(doc.value_at(ROOT, "prop3", &heads4)? == None); + assert!(doc.keys_at(&ROOT, &heads4) == vec!["prop2".to_owned()]); + assert!(doc.value_at(&ROOT, "prop1", &heads4)? == None); + assert!(doc.value_at(&ROOT, "prop2", &heads4)?.unwrap().0 == Value::str("val3")); + assert!(doc.value_at(&ROOT, "prop3", &heads4)? == None); - assert!(doc.keys_at(ROOT, &heads5) == vec!["prop2".to_owned(), "prop3".to_owned()]); - assert!(doc.value_at(ROOT, "prop1", &heads5)? == None); - assert!(doc.value_at(ROOT, "prop2", &heads5)?.unwrap().0 == Value::str("val3")); - assert!(doc.value_at(ROOT, "prop3", &heads5)?.unwrap().0 == Value::str("val4")); + assert!(doc.keys_at(&ROOT, &heads5) == vec!["prop2".to_owned(), "prop3".to_owned()]); + assert!(doc.value_at(&ROOT, "prop1", &heads5)? == None); + assert!(doc.value_at(&ROOT, "prop2", &heads5)?.unwrap().0 == Value::str("val3")); + assert!(doc.value_at(&ROOT, "prop3", &heads5)?.unwrap().0 == Value::str("val4")); - assert!(doc.keys_at(ROOT, &[]).is_empty()); - assert!(doc.value_at(ROOT, "prop1", &[])? == None); - assert!(doc.value_at(ROOT, "prop2", &[])? == None); - assert!(doc.value_at(ROOT, "prop3", &[])? == None); + assert!(doc.keys_at(&ROOT, &[]).is_empty()); + assert!(doc.value_at(&ROOT, "prop1", &[])? == None); + assert!(doc.value_at(&ROOT, "prop2", &[])? == None); + assert!(doc.value_at(&ROOT, "prop3", &[])? == None); Ok(()) } @@ -1283,47 +1270,47 @@ mod tests { let mut doc = Automerge::new(); doc.set_actor("aaaa".try_into().unwrap()); - let list = doc.set(ROOT, "list", Value::list())?.unwrap(); + let list = doc.set(&ObjId::Root, "list", Value::list())?.unwrap(); let heads1 = doc.commit(None, None); - doc.insert(list, 0, Value::int(10))?; + doc.insert(&list, 0, Value::int(10))?; let heads2 = doc.commit(None, None); - doc.set(list, 0, Value::int(20))?; - doc.insert(list, 0, Value::int(30))?; + doc.set(&list, 0, Value::int(20))?; + doc.insert(&list, 0, Value::int(30))?; let heads3 = doc.commit(None, None); - doc.set(list, 1, Value::int(40))?; - doc.insert(list, 1, Value::int(50))?; + doc.set(&list, 1, Value::int(40))?; + doc.insert(&list, 1, Value::int(50))?; let heads4 = doc.commit(None, None); - doc.del(list, 2)?; + doc.del(&list, 2)?; let heads5 = doc.commit(None, None); - doc.del(list, 0)?; + doc.del(&list, 0)?; let heads6 = doc.commit(None, None); - assert!(doc.length_at(list, &heads1) == 0); - assert!(doc.value_at(list, 0, &heads1)?.is_none()); + assert!(doc.length_at(&list, &heads1) == 0); + assert!(doc.value_at(&list, 0, &heads1)?.is_none()); - assert!(doc.length_at(list, &heads2) == 1); - assert!(doc.value_at(list, 0, &heads2)?.unwrap().0 == Value::int(10)); + assert!(doc.length_at(&list, &heads2) == 1); + assert!(doc.value_at(&list, 0, &heads2)?.unwrap().0 == Value::int(10)); - assert!(doc.length_at(list, &heads3) == 2); - assert!(doc.value_at(list, 0, &heads3)?.unwrap().0 == Value::int(30)); - assert!(doc.value_at(list, 1, &heads3)?.unwrap().0 == Value::int(20)); + assert!(doc.length_at(&list, &heads3) == 2); + assert!(doc.value_at(&list, 0, &heads3)?.unwrap().0 == Value::int(30)); + assert!(doc.value_at(&list, 1, &heads3)?.unwrap().0 == Value::int(20)); - assert!(doc.length_at(list, &heads4) == 3); - assert!(doc.value_at(list, 0, &heads4)?.unwrap().0 == Value::int(30)); - assert!(doc.value_at(list, 1, &heads4)?.unwrap().0 == Value::int(50)); - assert!(doc.value_at(list, 2, &heads4)?.unwrap().0 == Value::int(40)); + assert!(doc.length_at(&list, &heads4) == 3); + assert!(doc.value_at(&list, 0, &heads4)?.unwrap().0 == Value::int(30)); + assert!(doc.value_at(&list, 1, &heads4)?.unwrap().0 == Value::int(50)); + assert!(doc.value_at(&list, 2, &heads4)?.unwrap().0 == Value::int(40)); - assert!(doc.length_at(list, &heads5) == 2); - assert!(doc.value_at(list, 0, &heads5)?.unwrap().0 == Value::int(30)); - assert!(doc.value_at(list, 1, &heads5)?.unwrap().0 == Value::int(50)); + assert!(doc.length_at(&list, &heads5) == 2); + assert!(doc.value_at(&list, 0, &heads5)?.unwrap().0 == Value::int(30)); + assert!(doc.value_at(&list, 1, &heads5)?.unwrap().0 == Value::int(50)); - assert!(doc.length_at(list, &heads6) == 1); - assert!(doc.value_at(list, 0, &heads6)?.unwrap().0 == Value::int(50)); + assert!(doc.length_at(&list, &heads6) == 1); + assert!(doc.value_at(&list, 0, &heads6)?.unwrap().0 == Value::int(50)); Ok(()) } diff --git a/automerge/src/op_set.rs b/automerge/src/op_set.rs index 537cb80f..2603bca1 100644 --- a/automerge/src/op_set.rs +++ b/automerge/src/op_set.rs @@ -1,8 +1,7 @@ use crate::op_tree::OpTreeInternal; use crate::query::TreeQuery; -use crate::{ActorId, IndexedCache, Key, ObjId, Op, OpId}; +use crate::{ObjId, Op}; use fxhash::FxBuildHasher; -use std::cmp::Ordering; use std::collections::HashMap; pub(crate) type OpSet = OpSetInternal<16>; @@ -12,7 +11,6 @@ pub(crate) struct OpSetInternal { trees: HashMap, FxBuildHasher>, objs: Vec, length: usize, - pub m: OpSetMetadata, } impl OpSetInternal { @@ -21,10 +19,6 @@ impl OpSetInternal { trees: Default::default(), objs: Default::default(), length: 0, - m: OpSetMetadata { - actors: IndexedCache::new(), - props: IndexedCache::new(), - }, } } @@ -36,34 +30,34 @@ impl OpSetInternal { } } - pub fn search(&self, obj: ObjId, query: Q) -> Q + pub fn search(&self, obj: &ObjId, query: Q) -> Q where Q: TreeQuery, { - if let Some(tree) = self.trees.get(&obj) { - tree.search(query, &self.m) + if let Some(tree) = self.trees.get(obj) { + tree.search(query) } else { query } } - pub fn replace(&mut self, obj: ObjId, index: usize, f: F) -> Option + pub fn replace(&mut self, obj: &ObjId, index: usize, f: F) -> Option where F: FnMut(&mut Op), { - if let Some(tree) = self.trees.get_mut(&obj) { + if let Some(tree) = self.trees.get_mut(obj) { tree.replace(index, f) } else { None } } - pub fn remove(&mut self, obj: ObjId, index: usize) -> Op { - let tree = self.trees.get_mut(&obj).unwrap(); + pub fn remove(&mut self, obj: &ObjId, index: usize) -> Op { + let tree = self.trees.get_mut(obj).unwrap(); self.length -= 1; let op = tree.remove(index); if tree.is_empty() { - self.trees.remove(&obj); + self.trees.remove(obj); } op } @@ -76,16 +70,15 @@ impl OpSetInternal { let Self { ref mut trees, ref mut objs, - ref mut m, .. } = self; trees - .entry(element.obj) + .entry(element.obj.clone()) .or_insert_with(|| { let pos = objs - .binary_search_by(|probe| m.lamport_cmp(probe.0, element.obj.0)) + .binary_search_by(|probe| probe.cmp(&element.obj)) .unwrap_err(); - objs.insert(pos, element.obj); + objs.insert(pos, element.obj.clone()); Default::default() }) .insert(index, element); @@ -147,29 +140,3 @@ impl<'a, const B: usize> Iterator for Iter<'a, B> { } } } - -#[derive(Clone, Debug)] -pub(crate) struct OpSetMetadata { - pub actors: IndexedCache, - pub props: IndexedCache, -} - -impl OpSetMetadata { - pub fn key_cmp(&self, left: &Key, right: &Key) -> Ordering { - match (left, right) { - (Key::Map(a), Key::Map(b)) => self.props[*a].cmp(&self.props[*b]), - _ => panic!("can only compare map keys"), - } - } - - pub fn lamport_cmp(&self, left: OpId, right: OpId) -> Ordering { - match (left, right) { - (OpId(0, _), OpId(0, _)) => Ordering::Equal, - (OpId(0, _), OpId(_, _)) => Ordering::Less, - (OpId(_, _), OpId(0, _)) => Ordering::Greater, - // FIXME - this one seems backwards to me - why - is values() returning in the wrong order? - (OpId(a, x), OpId(b, y)) if a == b => self.actors[y].cmp(&self.actors[x]), - (OpId(a, _), OpId(b, _)) => a.cmp(&b), - } - } -} diff --git a/automerge/src/op_tree.rs b/automerge/src/op_tree.rs index 6142a7bf..67ff8363 100644 --- a/automerge/src/op_tree.rs +++ b/automerge/src/op_tree.rs @@ -4,7 +4,6 @@ use std::{ mem, }; -pub(crate) use crate::op_set::OpSetMetadata; use crate::query::{Index, QueryResult, TreeQuery}; use crate::{Op, OpId}; use std::collections::HashSet; @@ -36,14 +35,14 @@ impl OpTreeInternal { self.root_node.as_ref().map_or(0, |n| n.len()) } - pub fn search(&self, mut query: Q, m: &OpSetMetadata) -> Q + pub fn search(&self, mut query: Q) -> Q where Q: TreeQuery, { self.root_node .as_ref() - .map(|root| match query.query_node_with_metadata(root, m) { - QueryResult::Decend => root.search(&mut query, m), + .map(|root| match query.query_node(root) { + QueryResult::Decend => root.search(&mut query), _ => true, }); query @@ -177,22 +176,22 @@ impl OpTreeNode { } } - pub fn search(&self, query: &mut Q, m: &OpSetMetadata) -> bool + pub fn search(&self, query: &mut Q) -> bool where Q: TreeQuery, { if self.is_leaf() { for e in &self.elements { - if query.query_element_with_metadata(e, m) == QueryResult::Finish { + if query.query_element(e) == QueryResult::Finish { return true; } } false } else { for (child_index, child) in self.children.iter().enumerate() { - match query.query_node_with_metadata(child, m) { + match query.query_node(child) { QueryResult::Decend => { - if child.search(query, m) { + if child.search(query) { return true; } } @@ -200,7 +199,7 @@ impl OpTreeNode { QueryResult::Next => (), } if let Some(e) = self.elements.get(child_index) { - if query.query_element_with_metadata(e, m) == QueryResult::Finish { + if query.query_element(e) == QueryResult::Finish { return true; } } @@ -627,13 +626,14 @@ struct CounterData { #[cfg(test)] mod tests { + /* FIXME use crate::legacy as amp; use crate::{Op, OpId}; use super::*; fn op(n: usize) -> Op { - let zero = OpId(0, 0); + let zero = OpId(0,0); Op { change: n, id: zero, @@ -680,4 +680,5 @@ mod tests { assert_eq!(v, t.iter().cloned().collect::>()) } } + */ } diff --git a/automerge/src/query.rs b/automerge/src/query.rs index 15ac6fd6..cc9f048d 100644 --- a/automerge/src/query.rs +++ b/automerge/src/query.rs @@ -1,4 +1,4 @@ -use crate::op_tree::{OpSetMetadata, OpTreeNode}; +use crate::op_tree::OpTreeNode; use crate::{Clock, ElemId, Op, OpId, OpType, ScalarValue}; use fxhash::FxBuildHasher; use std::cmp::Ordering; @@ -40,24 +40,10 @@ pub(crate) struct CounterData { } pub(crate) trait TreeQuery { - #[inline(always)] - fn query_node_with_metadata( - &mut self, - child: &OpTreeNode, - _m: &OpSetMetadata, - ) -> QueryResult { - self.query_node(child) - } - fn query_node(&mut self, _child: &OpTreeNode) -> QueryResult { QueryResult::Decend } - #[inline(always)] - fn query_element_with_metadata(&mut self, element: &Op, _m: &OpSetMetadata) -> QueryResult { - self.query_element(element) - } - fn query_element(&mut self, _element: &Op) -> QueryResult { panic!("invalid element query") } @@ -97,7 +83,7 @@ impl Index { pub fn replace(&mut self, old: &Op, new: &Op) { if old.id != new.id { self.ops.remove(&old.id); - self.ops.insert(new.id); + self.ops.insert(new.id.clone()); } assert!(new.key == old.key); @@ -127,7 +113,7 @@ impl Index { } pub fn insert(&mut self, op: &Op) { - self.ops.insert(op.id); + self.ops.insert(op.id.clone()); if op.succ.is_empty() { if let Some(elem) = op.elemid() { match self.visible.get(&elem).copied() { @@ -163,16 +149,16 @@ impl Index { pub fn merge(&mut self, other: &Index) { for id in &other.ops { - self.ops.insert(*id); + self.ops.insert(id.clone()); } for (elem, n) in other.visible.iter() { match self.visible.get(elem).cloned() { None => { - self.visible.insert(*elem, 1); + self.visible.insert(elem.clone(), 1); self.len += 1; } Some(m) => { - self.visible.insert(*elem, m + n); + self.visible.insert(elem.clone(), m + n); } } } @@ -196,7 +182,7 @@ impl VisWindow { match op.action { OpType::Set(ScalarValue::Counter(val)) => { self.counters.insert( - op.id, + op.id.clone(), CounterData { pos, val, @@ -238,7 +224,7 @@ impl VisWindow { match op.action { OpType::Set(ScalarValue::Counter(val)) => { self.counters.insert( - op.id, + op.id.clone(), CounterData { pos, val, @@ -292,7 +278,7 @@ pub(crate) fn is_visible(op: &Op, pos: usize, counters: &mut HashMap { counters.insert( - op.id, + op.id.clone(), CounterData { pos, val, diff --git a/automerge/src/query/insert.rs b/automerge/src/query/insert.rs index 745af80e..eb53498a 100644 --- a/automerge/src/query/insert.rs +++ b/automerge/src/query/insert.rs @@ -1,6 +1,6 @@ use crate::op_tree::OpTreeNode; use crate::query::{QueryResult, TreeQuery, VisWindow}; -use crate::{AutomergeError, ElemId, Key, Op, HEAD}; +use crate::{AutomergeError, ElemId, Key, Op}; use std::fmt::Debug; #[derive(Debug, Clone, PartialEq)] @@ -27,9 +27,9 @@ impl InsertNth { pub fn key(&self) -> Result { if self.target == 0 { - Ok(HEAD.into()) + Ok(Key::Seq(ElemId::Head)) } else if self.seen == self.target && self.last_insert.is_some() { - Ok(Key::Seq(self.last_insert.unwrap())) + Ok(Key::Seq(self.last_insert.clone().unwrap())) } else { Err(AutomergeError::InvalidIndex(self.target)) } diff --git a/automerge/src/query/keys.rs b/automerge/src/query/keys.rs index 12cfaaa6..3fd9389c 100644 --- a/automerge/src/query/keys.rs +++ b/automerge/src/query/keys.rs @@ -24,9 +24,10 @@ impl TreeQuery for Keys { for i in 0..child.len() { let op = child.get(i).unwrap(); let visible = self.window.visible(op, i); - if Some(op.key) != last && visible { - self.keys.push(op.key); - last = Some(op.key); + // FIXME - clone? + if Some(op.key.clone()) != last && visible { + self.keys.push(op.key.clone()); + last = Some(op.key.clone()); } } QueryResult::Finish diff --git a/automerge/src/query/keys_at.rs b/automerge/src/query/keys_at.rs index cd66b29e..eeb5c780 100644 --- a/automerge/src/query/keys_at.rs +++ b/automerge/src/query/keys_at.rs @@ -26,9 +26,9 @@ impl KeysAt { impl TreeQuery for KeysAt { fn query_element(&mut self, op: &Op) -> QueryResult { let visible = self.window.visible_at(op, self.pos, &self.clock); - if Some(op.key) != self.last && visible { - self.keys.push(op.key); - self.last = Some(op.key); + if Some(&op.key) != self.last.as_ref() && visible { + self.keys.push(op.key.clone()); + self.last = Some(op.key.clone()); } self.pos += 1; QueryResult::Next diff --git a/automerge/src/query/len.rs b/automerge/src/query/len.rs index 494b3515..f92b8096 100644 --- a/automerge/src/query/len.rs +++ b/automerge/src/query/len.rs @@ -1,17 +1,15 @@ use crate::op_tree::OpTreeNode; use crate::query::{QueryResult, TreeQuery}; -use crate::ObjId; use std::fmt::Debug; #[derive(Debug, Clone, PartialEq)] pub(crate) struct Len { - obj: ObjId, pub len: usize, } impl Len { - pub fn new(obj: ObjId) -> Self { - Len { obj, len: 0 } + pub fn new() -> Self { + Len { len: 0 } } } diff --git a/automerge/src/query/list_vals.rs b/automerge/src/query/list_vals.rs index c19ac4ad..d8381c43 100644 --- a/automerge/src/query/list_vals.rs +++ b/automerge/src/query/list_vals.rs @@ -1,19 +1,17 @@ -use crate::op_tree::{OpSetMetadata, OpTreeNode}; -use crate::query::{binary_search_by, is_visible, visible_op, QueryResult, TreeQuery}; -use crate::{ElemId, ObjId, Op}; +use crate::op_tree::OpTreeNode; +use crate::query::{is_visible, visible_op, QueryResult, TreeQuery}; +use crate::{ElemId, Op}; use std::fmt::Debug; #[derive(Debug, Clone, PartialEq)] pub(crate) struct ListVals { - obj: ObjId, last_elem: Option, pub ops: Vec, } impl ListVals { - pub fn new(obj: ObjId) -> Self { + pub fn new() -> Self { ListVals { - obj, last_elem: None, ops: vec![], } @@ -21,18 +19,10 @@ impl ListVals { } impl TreeQuery for ListVals { - fn query_node_with_metadata( - &mut self, - child: &OpTreeNode, - m: &OpSetMetadata, - ) -> QueryResult { - let start = binary_search_by(child, |op| m.lamport_cmp(op.obj.0, self.obj.0)); + fn query_node(&mut self, child: &OpTreeNode) -> QueryResult { let mut counters = Default::default(); - for pos in start..child.len() { + for pos in 0..child.len() { let op = child.get(pos).unwrap(); - if op.obj != self.obj { - break; - } if op.insert { self.last_elem = None; } diff --git a/automerge/src/query/nth.rs b/automerge/src/query/nth.rs index e76bc385..ba5c27b2 100644 --- a/automerge/src/query/nth.rs +++ b/automerge/src/query/nth.rs @@ -30,8 +30,8 @@ impl Nth { } pub fn key(&self) -> Result { - if let Some(e) = self.last_elem { - Ok(Key::Seq(e)) + if let Some(e) = &self.last_elem { + Ok(Key::Seq(e.clone())) } else { Err(AutomergeError::InvalidIndex(self.target)) } diff --git a/automerge/src/query/prop.rs b/automerge/src/query/prop.rs index ac4b2bca..492310ed 100644 --- a/automerge/src/query/prop.rs +++ b/automerge/src/query/prop.rs @@ -1,11 +1,10 @@ -use crate::op_tree::{OpSetMetadata, OpTreeNode}; +use crate::op_tree::OpTreeNode; use crate::query::{binary_search_by, is_visible, visible_op, QueryResult, TreeQuery}; -use crate::{Key, ObjId, Op}; +use crate::{Key, Op}; use std::fmt::Debug; #[derive(Debug, Clone, PartialEq)] pub(crate) struct Prop { - obj: ObjId, key: Key, pub ops: Vec, pub ops_pos: Vec, @@ -13,9 +12,8 @@ pub(crate) struct Prop { } impl Prop { - pub fn new(obj: ObjId, prop: usize) -> Self { + pub fn new(prop: String) -> Self { Prop { - obj, key: Key::Map(prop), ops: vec![], ops_pos: vec![], @@ -25,20 +23,13 @@ impl Prop { } impl TreeQuery for Prop { - fn query_node_with_metadata( - &mut self, - child: &OpTreeNode, - m: &OpSetMetadata, - ) -> QueryResult { - let start = binary_search_by(child, |op| { - m.lamport_cmp(op.obj.0, self.obj.0) - .then_with(|| m.key_cmp(&op.key, &self.key)) - }); + fn query_node(&mut self, child: &OpTreeNode) -> QueryResult { + let start = binary_search_by(child, |op| op.key.cmp(&self.key)); let mut counters = Default::default(); self.pos = start; for pos in start..child.len() { let op = child.get(pos).unwrap(); - if !(op.obj == self.obj && op.key == self.key) { + if op.key != self.key { break; } if is_visible(op, pos, &mut counters) { diff --git a/automerge/src/query/prop_at.rs b/automerge/src/query/prop_at.rs index 3fcb2c19..25596004 100644 --- a/automerge/src/query/prop_at.rs +++ b/automerge/src/query/prop_at.rs @@ -1,4 +1,4 @@ -use crate::op_tree::{OpSetMetadata, OpTreeNode}; +use crate::op_tree::OpTreeNode; use crate::query::{binary_search_by, QueryResult, TreeQuery, VisWindow}; use crate::{Clock, Key, Op}; use std::fmt::Debug; @@ -13,7 +13,7 @@ pub(crate) struct PropAt { } impl PropAt { - pub fn new(prop: usize, clock: Clock) -> Self { + pub fn new(prop: String, clock: Clock) -> Self { PropAt { clock, key: Key::Map(prop), @@ -25,12 +25,8 @@ impl PropAt { } impl TreeQuery for PropAt { - fn query_node_with_metadata( - &mut self, - child: &OpTreeNode, - m: &OpSetMetadata, - ) -> QueryResult { - let start = binary_search_by(child, |op| m.key_cmp(&op.key, &self.key)); + fn query_node(&mut self, child: &OpTreeNode) -> QueryResult { + let start = binary_search_by(child, |op| op.key.cmp(&self.key)); let mut window: VisWindow = Default::default(); self.pos = start; for pos in start..child.len() { diff --git a/automerge/src/query/seek_op.rs b/automerge/src/query/seek_op.rs index 5a6b3e24..caacb10c 100644 --- a/automerge/src/query/seek_op.rs +++ b/automerge/src/query/seek_op.rs @@ -1,6 +1,6 @@ -use crate::op_tree::{OpSetMetadata, OpTreeNode}; +use crate::op_tree::OpTreeNode; use crate::query::{binary_search_by, QueryResult, TreeQuery}; -use crate::{Key, Op, HEAD}; +use crate::{ElemId, Key, Op}; use std::cmp::Ordering; use std::fmt::Debug; @@ -26,12 +26,12 @@ impl SeekOp { op.obj != self.op.obj } - fn lesser_insert(&self, op: &Op, m: &OpSetMetadata) -> bool { - op.insert && m.lamport_cmp(op.id, self.op.id) == Ordering::Less + fn lesser_insert(&self, op: &Op) -> bool { + op.insert && op.id.cmp(&self.op.id) == Ordering::Less } - fn greater_opid(&self, op: &Op, m: &OpSetMetadata) -> bool { - m.lamport_cmp(op.id, self.op.id) == Ordering::Greater + fn greater_opid(&self, op: &Op) -> bool { + op.id.cmp(&self.op.id) == Ordering::Greater } fn is_target_insert(&self, op: &Op) -> bool { @@ -47,30 +47,26 @@ impl SeekOp { } impl TreeQuery for SeekOp { - fn query_node_with_metadata( - &mut self, - child: &OpTreeNode, - m: &OpSetMetadata, - ) -> QueryResult { + fn query_node(&mut self, child: &OpTreeNode) -> QueryResult { if self.found { return QueryResult::Decend; } - match self.op.key { - Key::Seq(e) if e == HEAD => { + match &self.op.key { + Key::Seq(ElemId::Head) => { while self.pos < child.len() { let op = child.get(self.pos).unwrap(); if self.op.overwrites(op) { self.succ.push(self.pos); } - if op.insert && m.lamport_cmp(op.id, self.op.id) == Ordering::Less { + if op.insert && op.id.cmp(&self.op.id) == Ordering::Less { break; } self.pos += 1; } QueryResult::Finish } - Key::Seq(e) => { - if self.found || child.index.ops.contains(&e.0) { + Key::Seq(ElemId::Id(id)) => { + if self.found || child.index.ops.contains(id) { QueryResult::Decend } else { self.pos += child.len(); @@ -78,7 +74,7 @@ impl TreeQuery for SeekOp { } } Key::Map(_) => { - self.pos = binary_search_by(child, |op| m.key_cmp(&op.key, &self.op.key)); + self.pos = binary_search_by(child, |op| op.key.cmp(&self.op.key)); while self.pos < child.len() { let op = child.get(self.pos).unwrap(); if op.key != self.op.key { @@ -87,7 +83,7 @@ impl TreeQuery for SeekOp { if self.op.overwrites(op) { self.succ.push(self.pos); } - if m.lamport_cmp(op.id, self.op.id) == Ordering::Greater { + if op.id.cmp(&self.op.id) == Ordering::Greater { break; } self.pos += 1; @@ -97,7 +93,7 @@ impl TreeQuery for SeekOp { } } - fn query_element_with_metadata(&mut self, e: &Op, m: &OpSetMetadata) -> QueryResult { + fn query_element(&mut self, e: &Op) -> QueryResult { if !self.found { if self.is_target_insert(e) { self.found = true; @@ -112,13 +108,13 @@ impl TreeQuery for SeekOp { self.succ.push(self.pos); } if self.op.insert { - if self.different_obj(e) || self.lesser_insert(e, m) { + if self.different_obj(e) || self.lesser_insert(e) { QueryResult::Finish } else { self.pos += 1; QueryResult::Next } - } else if e.insert || self.different_obj(e) || self.greater_opid(e, m) { + } else if e.insert || self.different_obj(e) || self.greater_opid(e) { QueryResult::Finish } else { self.pos += 1; diff --git a/automerge/src/types.rs b/automerge/src/types.rs index f00beed3..a78dbe35 100644 --- a/automerge/src/types.rs +++ b/automerge/src/types.rs @@ -3,18 +3,14 @@ use crate::legacy as amp; use crate::ScalarValue; use serde::{Deserialize, Serialize}; use std::cmp::Eq; +use std::cmp::Ordering; use std::convert::TryFrom; use std::convert::TryInto; use std::fmt; +use std::rc::Rc; use std::str::FromStr; use tinyvec::{ArrayVec, TinyVec}; -pub(crate) const HEAD: ElemId = ElemId(OpId(0, 0)); -pub const ROOT: OpId = OpId(0, 0); - -const ROOT_STR: &str = "_root"; -const HEAD_STR: &str = "_head"; - /// An actor id is a sequence of bytes. By default we use a uuid which can be nicely stack /// allocated. /// @@ -107,6 +103,39 @@ impl fmt::Display for ActorId { } } +impl fmt::Display for OpId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}@{}", &self.counter, &self.actor) + } +} + +impl fmt::Display for ObjId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ObjId::Root => write!(f, "_root"), + ObjId::Id(id) => write!(f, "{}", id), + } + } +} + +impl fmt::Display for ElemId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ElemId::Head => write!(f, "_head"), + ElemId::Id(id) => write!(f, "{}", id), + } + } +} + +impl fmt::Display for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Key::Map(s) => write!(f, "{}", s), + Key::Seq(id) => write!(f, "{}", id), + } + } +} + #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Copy, Hash)] #[serde(rename_all = "camelCase", untagged)] pub enum ObjType { @@ -160,128 +189,103 @@ pub enum OpType { Set(ScalarValue), } -#[derive(Debug)] -pub enum Export { - Id(OpId), - Special(String), - Prop(usize), -} - -pub trait Exportable { - fn export(&self) -> Export; -} - -pub trait Importable { - fn wrap(id: OpId) -> Self; - fn from(s: &str) -> Option - where - Self: std::marker::Sized; -} - -impl OpId { - #[inline] - pub fn counter(&self) -> u64 { - self.0 - } - #[inline] - pub fn actor(&self) -> usize { - self.1 - } -} - -impl Exportable for ObjId { - fn export(&self) -> Export { - if self.0 == ROOT { - Export::Special(ROOT_STR.to_owned()) - } else { - Export::Id(self.0) - } - } -} - -impl Exportable for &ObjId { - fn export(&self) -> Export { - if self.0 == ROOT { - Export::Special(ROOT_STR.to_owned()) - } else { - Export::Id(self.0) - } - } -} - -impl Exportable for ElemId { - fn export(&self) -> Export { - if self == &HEAD { - Export::Special(HEAD_STR.to_owned()) - } else { - Export::Id(self.0) - } - } -} - -impl Exportable for OpId { - fn export(&self) -> Export { - Export::Id(*self) - } -} - -impl Exportable for Key { - fn export(&self) -> Export { - match self { - Key::Map(p) => Export::Prop(*p), - Key::Seq(e) => e.export(), - } - } -} - -impl Importable for ObjId { - fn wrap(id: OpId) -> Self { - ObjId(id) - } - fn from(s: &str) -> Option { - if s == ROOT_STR { - Some(ROOT.into()) - } else { - None - } - } -} - -impl Importable for OpId { - fn wrap(id: OpId) -> Self { - id - } - fn from(s: &str) -> Option { - if s == ROOT_STR { - Some(ROOT) - } else { - None - } - } -} - -impl Importable for ElemId { - fn wrap(id: OpId) -> Self { - ElemId(id) - } - fn from(s: &str) -> Option { - if s == HEAD_STR { - Some(HEAD) - } else { - None - } - } -} - impl From for ObjId { fn from(o: OpId) -> Self { - ObjId(o) + ObjId::Id(o) + } +} + +impl From<&OpId> for ObjId { + fn from(o: &OpId) -> Self { + ObjId::Id(o.clone()) + } +} + +impl From for amp::OpId { + fn from(o: OpId) -> Self { + amp::OpId(o.counter, o.actor.as_ref().clone()) + } +} + +impl From<&OpId> for amp::OpId { + fn from(o: &OpId) -> Self { + amp::OpId(o.counter, o.actor.as_ref().clone()) + } +} + +impl From for amp::Key { + fn from(k: Key) -> Self { + match k { + Key::Map(s) => amp::Key::Map(s.into()), + Key::Seq(e) => amp::Key::Seq(e.into()), + } + } +} + +impl From<&Key> for amp::Key { + fn from(k: &Key) -> Self { + match k { + Key::Map(s) => amp::Key::Map(s.into()), + Key::Seq(e) => amp::Key::Seq(e.into()), + } + } +} + +impl From for amp::ObjectId { + fn from(o: ObjId) -> Self { + match o { + ObjId::Root => amp::ObjectId::Root, + ObjId::Id(id) => amp::ObjectId::Id(id.into()), + } + } +} + +impl From<&ObjId> for amp::ObjectId { + fn from(o: &ObjId) -> Self { + match o { + ObjId::Root => amp::ObjectId::Root, + ObjId::Id(id) => amp::ObjectId::Id(id.into()), + } + } +} + +impl From<&ElemId> for amp::ElementId { + fn from(o: &ElemId) -> Self { + match o { + ElemId::Head => amp::ElementId::Head, + ElemId::Id(id) => amp::ElementId::Id(id.into()), + } + } +} + +impl From for amp::ElementId { + fn from(o: ElemId) -> Self { + match o { + ElemId::Head => amp::ElementId::Head, + ElemId::Id(id) => amp::ElementId::Id(id.into()), + } + } +} + +impl From<&Op> for amp::Op { + fn from(op: &Op) -> Self { + let action = op.action.clone(); + let key = (&op.key).into(); + let obj = (&op.obj).into(); + let pred = op.pred.iter().map(|id| id.into()).collect(); + amp::Op { + action, + obj, + insert: op.insert, + pred, + key, + } } } impl From for ElemId { fn from(o: OpId) -> Self { - ElemId(o) + ElemId::Id(o) } } @@ -317,7 +321,7 @@ impl From for Prop { impl From for Key { fn from(id: OpId) -> Self { - Key::Seq(ElemId(id)) + Key::Seq(ElemId::Id(id)) } } @@ -327,9 +331,9 @@ impl From for Key { } } -#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub(crate) enum Key { - Map(usize), + Map(String), Seq(ElemId), } @@ -346,19 +350,38 @@ impl Key { pub fn elemid(&self) -> Option { match self { Key::Map(_) => None, - Key::Seq(id) => Some(*id), + Key::Seq(id) => Some(id.clone()), } } } -#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq, Copy, Hash, Default)] -pub struct OpId(pub u64, pub usize); +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct OpId { + pub counter: u64, + pub actor: Rc, +} -#[derive(Debug, Clone, Copy, PartialOrd, Eq, PartialEq, Ord, Hash, Default)] -pub(crate) struct ObjId(pub OpId); +impl OpId { + #[cfg(test)] + pub(crate) fn at(counter: u64, actor: &ActorId) -> OpId { + OpId { + counter, + actor: Rc::new(actor.clone()), + } + } +} -#[derive(Debug, Clone, Copy, PartialOrd, Eq, PartialEq, Ord, Hash, Default)] -pub(crate) struct ElemId(pub OpId); +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum ObjId { + Root, + Id(OpId), +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub(crate) enum ElemId { + Head, + Id(OpId), +} #[derive(Debug, Clone, PartialEq)] pub(crate) struct Op { @@ -383,7 +406,7 @@ impl Op { pub fn elemid(&self) -> Option { if self.insert { - Some(ElemId(self.id)) + Some(ElemId::Id(self.id.clone())) } else { self.key.elemid() } @@ -451,3 +474,68 @@ impl TryFrom<&[u8]> for ChangeHash { } } } + +impl Ord for OpId { + fn cmp(&self, other: &Self) -> Ordering { + match self.counter.cmp(&other.counter) { + Ordering::Equal => other.actor.cmp(&self.actor), + order => order, + } + } +} + +impl PartialOrd for OpId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialOrd for Key { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialOrd for ElemId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialOrd for ObjId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ElemId { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (ElemId::Head, ElemId::Head) => Ordering::Equal, + (ElemId::Head, _) => Ordering::Less, + (_, ElemId::Head) => Ordering::Greater, + (ElemId::Id(a), ElemId::Id(b)) => a.cmp(b), + } + } +} + +impl Ord for ObjId { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (ObjId::Root, ObjId::Root) => Ordering::Equal, + (ObjId::Root, _) => Ordering::Less, + (_, ObjId::Root) => Ordering::Greater, + (ObjId::Id(a), ObjId::Id(b)) => a.cmp(b), + } + } +} + +impl Ord for Key { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Key::Map(a), Key::Map(b)) => a.cmp(b), + (Key::Seq(a), Key::Seq(b)) => a.cmp(b), + (_, _) => panic!("comparing seq key to map key"), + } + } +} diff --git a/automerge/src/value.rs b/automerge/src/value.rs index 333c1f53..e7572347 100644 --- a/automerge/src/value.rs +++ b/automerge/src/value.rs @@ -1,4 +1,4 @@ -use crate::{error, ObjType, Op, OpId, OpType}; +use crate::{error, ObjId, ObjType, Op, OpType}; use serde::{Deserialize, Serialize}; use smol_str::SmolStr; use std::convert::TryFrom; @@ -110,21 +110,21 @@ impl From for Value { } } -impl From<&Op> for (Value, OpId) { +impl From<&Op> for (Value, ObjId) { fn from(op: &Op) -> Self { match &op.action { - OpType::Make(obj_type) => (Value::Object(*obj_type), op.id), - OpType::Set(scalar) => (Value::Scalar(scalar.clone()), op.id), + OpType::Make(obj_type) => (Value::Object(*obj_type), op.id.clone().into()), + OpType::Set(scalar) => (Value::Scalar(scalar.clone()), op.id.clone().into()), _ => panic!("cant convert op into a value - {:?}", op), } } } -impl From for (Value, OpId) { +impl From for (Value, ObjId) { fn from(op: Op) -> Self { match &op.action { - OpType::Make(obj_type) => (Value::Object(*obj_type), op.id), - OpType::Set(scalar) => (Value::Scalar(scalar.clone()), op.id), + OpType::Make(obj_type) => (Value::Object(*obj_type), op.id.clone().into()), + OpType::Set(scalar) => (Value::Scalar(scalar.clone()), op.id.clone().into()), _ => panic!("cant convert op into a value - {:?}", op), } } diff --git a/automerge/tests/test.rs b/automerge/tests/test.rs deleted file mode 100644 index 8dcc51df..00000000 --- a/automerge/tests/test.rs +++ /dev/null @@ -1,943 +0,0 @@ -use automerge::Automerge; - -mod helpers; -#[allow(unused_imports)] -use helpers::{ - mk_counter, new_doc, new_doc_with_actor, pretty_print, realize, realize_obj, sorted_actors, - translate_obj_id, OpIdExt, RealizedObject, -}; -#[test] -fn no_conflict_on_repeated_assignment() { - let mut doc = Automerge::new(); - doc.set(automerge::ROOT, "foo", 1).unwrap(); - let op = doc.set(automerge::ROOT, "foo", 2).unwrap().unwrap(); - assert_doc!( - &doc, - map! { - "foo" => { op => 2}, - } - ); -} - -#[test] -fn no_change_on_repeated_map_set() { - let mut doc = new_doc(); - doc.set(automerge::ROOT, "foo", 1).unwrap(); - assert!(doc.set(automerge::ROOT, "foo", 1).unwrap().is_none()); -} - -#[test] -fn no_change_on_repeated_list_set() { - let mut doc = new_doc(); - let list_id = doc - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - doc.insert(list_id, 0, 1).unwrap(); - doc.set(list_id, 0, 1).unwrap(); - assert!(doc.set(list_id, 0, 1).unwrap().is_none()); -} - -#[test] -fn no_change_on_list_insert_followed_by_set_of_same_value() { - let mut doc = new_doc(); - let list_id = doc - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - doc.insert(list_id, 0, 1).unwrap(); - assert!(doc.set(list_id, 0, 1).unwrap().is_none()); -} - -#[test] -fn repeated_map_assignment_which_resolves_conflict_not_ignored() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - doc1.set(automerge::ROOT, "field", 123).unwrap(); - doc2.merge(&mut doc1); - doc2.set(automerge::ROOT, "field", 456).unwrap(); - doc1.set(automerge::ROOT, "field", 789).unwrap(); - doc1.merge(&mut doc2); - assert_eq!(doc1.values(automerge::ROOT, "field").unwrap().len(), 2); - - let op = doc1.set(automerge::ROOT, "field", 123).unwrap().unwrap(); - assert_doc!( - &doc1, - map! { - "field" => { - op => 123 - } - } - ); -} - -#[test] -fn repeated_list_assignment_which_resolves_conflict_not_ignored() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let list_id = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - doc1.insert(list_id, 0, 123).unwrap(); - doc2.merge(&mut doc1); - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - doc2.set(list_id_in_doc2, 0, 456).unwrap().unwrap(); - doc1.merge(&mut doc2); - let doc1_op = doc1.set(list_id, 0, 789).unwrap().unwrap(); - - assert_doc!( - &doc1, - map! { - "list" => { - list_id => list![ - { doc1_op => 789 }, - ] - } - } - ); -} - -#[test] -fn list_deletion() { - let mut doc = new_doc(); - let list_id = doc - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - let op1 = doc.insert(list_id, 0, 123).unwrap(); - doc.insert(list_id, 1, 456).unwrap(); - let op3 = doc.insert(list_id, 2, 789).unwrap(); - doc.del(list_id, 1).unwrap(); - assert_doc!( - &doc, - map! { - "list" => {list_id => list![ - { op1 => 123 }, - { op3 => 789 }, - ]} - } - ) -} - -#[test] -fn merge_concurrent_map_prop_updates() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let op1 = doc1.set(automerge::ROOT, "foo", "bar").unwrap().unwrap(); - let hello = doc2 - .set(automerge::ROOT, "hello", "world") - .unwrap() - .unwrap(); - doc1.merge(&mut doc2); - assert_eq!( - doc1.value(automerge::ROOT, "foo").unwrap().unwrap().0, - "bar".into() - ); - assert_doc!( - &doc1, - map! { - "foo" => { op1 => "bar" }, - "hello" => { hello.translate(&doc2) => "world" }, - } - ); - doc2.merge(&mut doc1); - assert_doc!( - &doc2, - map! { - "foo" => { op1.translate(&doc1) => "bar" }, - "hello" => { hello => "world" }, - } - ); - assert_eq!(realize(&doc1), realize(&doc2)); -} - -#[test] -fn add_concurrent_increments_of_same_property() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let counter_id = doc1 - .set(automerge::ROOT, "counter", mk_counter(0)) - .unwrap() - .unwrap(); - doc2.merge(&mut doc1); - doc1.inc(automerge::ROOT, "counter", 1).unwrap(); - doc2.inc(automerge::ROOT, "counter", 2).unwrap(); - doc1.merge(&mut doc2); - assert_doc!( - &doc1, - map! { - "counter" => { - counter_id => mk_counter(3) - } - } - ); -} - -#[test] -fn add_increments_only_to_preceeded_values() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - - // create a counter in doc1 - let doc1_counter_id = doc1 - .set(automerge::ROOT, "counter", mk_counter(0)) - .unwrap() - .unwrap(); - doc1.inc(automerge::ROOT, "counter", 1).unwrap(); - - // create a counter in doc2 - let doc2_counter_id = doc2 - .set(automerge::ROOT, "counter", mk_counter(0)) - .unwrap() - .unwrap(); - doc2.inc(automerge::ROOT, "counter", 3).unwrap(); - - // The two values should be conflicting rather than added - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "counter" => { - doc1_counter_id.native() => mk_counter(1), - doc2_counter_id.translate(&doc2) => mk_counter(3), - } - } - ); -} - -#[test] -fn concurrent_updates_of_same_field() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let set_one_opid = doc1.set(automerge::ROOT, "field", "one").unwrap().unwrap(); - let set_two_opid = doc2.set(automerge::ROOT, "field", "two").unwrap().unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "field" => { - set_one_opid.native() => "one", - set_two_opid.translate(&doc2) => "two", - } - } - ); -} - -#[test] -fn concurrent_updates_of_same_list_element() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let list_id = doc1 - .set(automerge::ROOT, "birds", automerge::Value::list()) - .unwrap() - .unwrap(); - doc1.insert(list_id, 0, "finch").unwrap(); - doc2.merge(&mut doc1); - let set_one_op = doc1.set(list_id, 0, "greenfinch").unwrap().unwrap(); - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - let set_op_two = doc2.set(list_id_in_doc2, 0, "goldfinch").unwrap().unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => { - list_id => list![{ - set_one_op.native() => "greenfinch", - set_op_two.translate(&doc2) => "goldfinch", - }] - } - } - ); -} - -#[test] -fn assignment_conflicts_of_different_types() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let mut doc3 = new_doc(); - let op_one = doc1 - .set(automerge::ROOT, "field", "string") - .unwrap() - .unwrap(); - let op_two = doc2 - .set(automerge::ROOT, "field", automerge::Value::list()) - .unwrap() - .unwrap(); - let op_three = doc3 - .set(automerge::ROOT, "field", automerge::Value::map()) - .unwrap() - .unwrap(); - - doc1.merge(&mut doc2); - doc1.merge(&mut doc3); - - assert_doc!( - &doc1, - map! { - "field" => { - op_one.native() => "string", - op_two.translate(&doc2) => list!{}, - op_three.translate(&doc3) => map!{}, - } - } - ); -} - -#[test] -fn changes_within_conflicting_map_field() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let op_one = doc1 - .set(automerge::ROOT, "field", "string") - .unwrap() - .unwrap(); - let map_id = doc2 - .set(automerge::ROOT, "field", automerge::Value::map()) - .unwrap() - .unwrap(); - let set_in_doc2 = doc2.set(map_id, "innerKey", 42).unwrap().unwrap(); - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "field" => { - op_one.native() => "string", - map_id.translate(&doc2) => map!{ - "innerKey" => { - set_in_doc2.translate(&doc2) => 42, - } - } - } - } - ); -} - -#[test] -fn changes_within_conflicting_list_element() { - let (actor1, actor2) = sorted_actors(); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - let list_id = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - doc1.insert(list_id, 0, "hello").unwrap(); - doc2.merge(&mut doc1); - - let map_in_doc1 = doc1 - .set(list_id, 0, automerge::Value::map()) - .unwrap() - .unwrap(); - let set_map1 = doc1.set(map_in_doc1, "map1", true).unwrap().unwrap(); - let set_key1 = doc1.set(map_in_doc1, "key", 1).unwrap().unwrap(); - - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - let map_in_doc2 = doc2 - .set(list_id_in_doc2, 0, automerge::Value::map()) - .unwrap() - .unwrap(); - doc1.merge(&mut doc2); - let set_map2 = doc2.set(map_in_doc2, "map2", true).unwrap().unwrap(); - let set_key2 = doc2.set(map_in_doc2, "key", 2).unwrap().unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "list" => { - list_id => list![ - { - map_in_doc2.translate(&doc2) => map!{ - "map2" => { set_map2.translate(&doc2) => true }, - "key" => { set_key2.translate(&doc2) => 2 }, - }, - map_in_doc1.native() => map!{ - "key" => { set_key1.native() => 1 }, - "map1" => { set_map1.native() => true }, - } - } - ] - } - } - ); -} - -#[test] -fn concurrently_assigned_nested_maps_should_not_merge() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - - let doc1_map_id = doc1 - .set(automerge::ROOT, "config", automerge::Value::map()) - .unwrap() - .unwrap(); - let doc1_field = doc1 - .set(doc1_map_id, "background", "blue") - .unwrap() - .unwrap(); - - let doc2_map_id = doc2 - .set(automerge::ROOT, "config", automerge::Value::map()) - .unwrap() - .unwrap(); - let doc2_field = doc2 - .set(doc2_map_id, "logo_url", "logo.png") - .unwrap() - .unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "config" => { - doc1_map_id.native() => map!{ - "background" => {doc1_field.native() => "blue"} - }, - doc2_map_id.translate(&doc2) => map!{ - "logo_url" => {doc2_field.translate(&doc2) => "logo.png"} - } - } - } - ); -} - -#[test] -fn concurrent_insertions_at_different_list_positions() { - let (actor1, actor2) = sorted_actors(); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - assert!(doc1.maybe_get_actor().unwrap() < doc2.maybe_get_actor().unwrap()); - - let list_id = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - - let one = doc1.insert(list_id, 0, "one").unwrap(); - let three = doc1.insert(list_id, 1, "three").unwrap(); - doc2.merge(&mut doc1); - let two = doc1.splice(list_id, 1, 0, vec!["two".into()]).unwrap()[0]; - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - let four = doc2.insert(list_id_in_doc2, 2, "four").unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "list" => { - list_id => list![ - {one.native() => "one"}, - {two.native() => "two"}, - {three.native() => "three"}, - {four.translate(&doc2) => "four"}, - ] - } - } - ); -} - -#[test] -fn concurrent_insertions_at_same_list_position() { - let (actor1, actor2) = sorted_actors(); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - assert!(doc1.maybe_get_actor().unwrap() < doc2.maybe_get_actor().unwrap()); - - let list_id = doc1 - .set(automerge::ROOT, "birds", automerge::Value::list()) - .unwrap() - .unwrap(); - let parakeet = doc1.insert(list_id, 0, "parakeet").unwrap(); - - doc2.merge(&mut doc1); - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - let starling = doc1.insert(list_id, 1, "starling").unwrap(); - let chaffinch = doc2.insert(list_id_in_doc2, 1, "chaffinch").unwrap(); - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => { - list_id => list![ - { - parakeet.native() => "parakeet", - }, - { - starling.native() => "starling", - }, - { - chaffinch.translate(&doc2) => "chaffinch", - }, - ] - }, - } - ); -} - -#[test] -fn concurrent_assignment_and_deletion_of_a_map_entry() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - doc1.set(automerge::ROOT, "bestBird", "robin").unwrap(); - doc2.merge(&mut doc1); - doc1.del(automerge::ROOT, "bestBird").unwrap(); - let set_two = doc2 - .set(automerge::ROOT, "bestBird", "magpie") - .unwrap() - .unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "bestBird" => { - set_two.translate(&doc2) => "magpie", - } - } - ); -} - -#[test] -fn concurrent_assignment_and_deletion_of_list_entry() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let list_id = doc1 - .set(automerge::ROOT, "birds", automerge::Value::list()) - .unwrap() - .unwrap(); - let blackbird = doc1.insert(list_id, 0, "blackbird").unwrap(); - doc1.insert(list_id, 1, "thrush").unwrap(); - let goldfinch = doc1.insert(list_id, 2, "goldfinch").unwrap(); - doc2.merge(&mut doc1); - - let starling = doc1.set(list_id, 1, "starling").unwrap().unwrap(); - - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - doc2.del(list_id_in_doc2, 1).unwrap(); - - assert_doc!( - &doc2, - map! { - "birds" => {list_id.translate(&doc1) => list![ - { blackbird.translate(&doc1) => "blackbird"}, - { goldfinch.translate(&doc1) => "goldfinch"}, - ]} - } - ); - - assert_doc!( - &doc1, - map! { - "birds" => {list_id => list![ - { blackbird => "blackbird" }, - { starling => "starling" }, - { goldfinch => "goldfinch" }, - ]} - } - ); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => {list_id => list![ - { blackbird => "blackbird" }, - { starling => "starling" }, - { goldfinch => "goldfinch" }, - ]} - } - ); -} - -#[test] -fn insertion_after_a_deleted_list_element() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let list_id = doc1 - .set(automerge::ROOT, "birds", automerge::Value::list()) - .unwrap() - .unwrap(); - - let blackbird = doc1.insert(list_id, 0, "blackbird").unwrap(); - doc1.insert(list_id, 1, "thrush").unwrap(); - doc1.insert(list_id, 2, "goldfinch").unwrap(); - - doc2.merge(&mut doc1); - - doc1.splice(list_id, 1, 2, Vec::new()).unwrap(); - - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - let starling = doc2 - .splice(list_id_in_doc2, 2, 0, vec!["starling".into()]) - .unwrap()[0]; - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => {list_id => list![ - { blackbird.native() => "blackbird" }, - { starling.translate(&doc2) => "starling" } - ]} - } - ); - - doc2.merge(&mut doc1); - assert_doc!( - &doc2, - map! { - "birds" => {list_id.translate(&doc1) => list![ - { blackbird.translate(&doc1) => "blackbird" }, - { starling.native() => "starling" } - ]} - } - ); -} - -#[test] -fn concurrent_deletion_of_same_list_element() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - let list_id = doc1 - .set(automerge::ROOT, "birds", automerge::Value::list()) - .unwrap() - .unwrap(); - - let albatross = doc1.insert(list_id, 0, "albatross").unwrap(); - doc1.insert(list_id, 1, "buzzard").unwrap(); - let cormorant = doc1.insert(list_id, 2, "cormorant").unwrap(); - - doc2.merge(&mut doc1); - - doc1.del(list_id, 1).unwrap(); - - let list_id_in_doc2 = translate_obj_id(&doc1, &doc2, list_id); - doc2.del(list_id_in_doc2, 1).unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => {list_id => list![ - { albatross => "albatross" }, - { cormorant => "cormorant" } - ]} - } - ); - - doc2.merge(&mut doc1); - assert_doc!( - &doc2, - map! { - "birds" => {list_id.translate(&doc1) => list![ - { albatross.translate(&doc1) => "albatross" }, - { cormorant.translate(&doc1) => "cormorant" } - ]} - } - ); -} - -#[test] -fn concurrent_updates_at_different_levels() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - - let animals = doc1 - .set(automerge::ROOT, "animals", automerge::Value::map()) - .unwrap() - .unwrap(); - let birds = doc1 - .set(animals, "birds", automerge::Value::map()) - .unwrap() - .unwrap(); - doc1.set(birds, "pink", "flamingo").unwrap().unwrap(); - doc1.set(birds, "black", "starling").unwrap().unwrap(); - - let mammals = doc1 - .set(animals, "mammals", automerge::Value::list()) - .unwrap() - .unwrap(); - let badger = doc1.insert(mammals, 0, "badger").unwrap(); - - doc2.merge(&mut doc1); - - doc1.set(birds, "brown", "sparrow").unwrap().unwrap(); - - let animals_in_doc2 = translate_obj_id(&doc1, &doc2, animals); - doc2.del(animals_in_doc2, "birds").unwrap(); - doc1.merge(&mut doc2); - - assert_obj!( - &doc1, - automerge::ROOT, - "animals", - map! { - "mammals" => { - mammals => list![{ badger => "badger" }], - } - } - ); - - assert_obj!( - &doc2, - automerge::ROOT, - "animals", - map! { - "mammals" => { - mammals.translate(&doc1) => list![{ badger.translate(&doc1) => "badger" }], - } - } - ); -} - -#[test] -fn concurrent_updates_of_concurrently_deleted_objects() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - - let birds = doc1 - .set(automerge::ROOT, "birds", automerge::Value::map()) - .unwrap() - .unwrap(); - let blackbird = doc1 - .set(birds, "blackbird", automerge::Value::map()) - .unwrap() - .unwrap(); - doc1.set(blackbird, "feathers", "black").unwrap().unwrap(); - - doc2.merge(&mut doc1); - - doc1.del(birds, "blackbird").unwrap(); - - translate_obj_id(&doc1, &doc2, blackbird); - doc2.set(blackbird, "beak", "orange").unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "birds" => { - birds => map!{}, - } - } - ); -} - -#[test] -fn does_not_interleave_sequence_insertions_at_same_position() { - let (actor1, actor2) = sorted_actors(); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - - let wisdom = doc1 - .set(automerge::ROOT, "wisdom", automerge::Value::list()) - .unwrap() - .unwrap(); - doc2.merge(&mut doc1); - - let doc1elems = doc1 - .splice( - wisdom, - 0, - 0, - vec![ - "to".into(), - "be".into(), - "is".into(), - "to".into(), - "do".into(), - ], - ) - .unwrap(); - - let wisdom_in_doc2 = translate_obj_id(&doc1, &doc2, wisdom); - let doc2elems = doc2 - .splice( - wisdom_in_doc2, - 0, - 0, - vec![ - "to".into(), - "do".into(), - "is".into(), - "to".into(), - "be".into(), - ], - ) - .unwrap(); - - doc1.merge(&mut doc2); - - assert_doc!( - &doc1, - map! { - "wisdom" => {wisdom => list![ - {doc1elems[0].native() => "to"}, - {doc1elems[1].native() => "be"}, - {doc1elems[2].native() => "is"}, - {doc1elems[3].native() => "to"}, - {doc1elems[4].native() => "do"}, - {doc2elems[0].translate(&doc2) => "to"}, - {doc2elems[1].translate(&doc2) => "do"}, - {doc2elems[2].translate(&doc2) => "is"}, - {doc2elems[3].translate(&doc2) => "to"}, - {doc2elems[4].translate(&doc2) => "be"}, - ]} - } - ); -} - -#[test] -fn mutliple_insertions_at_same_list_position_with_insertion_by_greater_actor_id() { - let (actor1, actor2) = sorted_actors(); - assert!(actor2 > actor1); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - - let list = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - let two = doc1.insert(list, 0, "two").unwrap(); - doc2.merge(&mut doc1); - - let list_in_doc2 = translate_obj_id(&doc1, &doc2, list); - let one = doc2.insert(list_in_doc2, 0, "one").unwrap(); - assert_doc!( - &doc2, - map! { - "list" => { list.translate(&doc1) => list![ - { one.native() => "one" }, - { two.translate(&doc1) => "two" }, - ]} - } - ); -} - -#[test] -fn mutliple_insertions_at_same_list_position_with_insertion_by_lesser_actor_id() { - let (actor2, actor1) = sorted_actors(); - assert!(actor2 < actor1); - let mut doc1 = new_doc_with_actor(actor1); - let mut doc2 = new_doc_with_actor(actor2); - - let list = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - let two = doc1.insert(list, 0, "two").unwrap(); - doc2.merge(&mut doc1); - - let list_in_doc2 = translate_obj_id(&doc1, &doc2, list); - let one = doc2.insert(list_in_doc2, 0, "one").unwrap(); - assert_doc!( - &doc2, - map! { - "list" => { list.translate(&doc1) => list![ - { one.native() => "one" }, - { two.translate(&doc1) => "two" }, - ]} - } - ); -} - -#[test] -fn insertion_consistent_with_causality() { - let mut doc1 = new_doc(); - let mut doc2 = new_doc(); - - let list = doc1 - .set(automerge::ROOT, "list", automerge::Value::list()) - .unwrap() - .unwrap(); - let four = doc1.insert(list, 0, "four").unwrap(); - doc2.merge(&mut doc1); - let list_in_doc2 = translate_obj_id(&doc1, &doc2, list); - let three = doc2.insert(list_in_doc2, 0, "three").unwrap(); - doc1.merge(&mut doc2); - let two = doc1.insert(list, 0, "two").unwrap(); - doc2.merge(&mut doc1); - let one = doc2.insert(list_in_doc2, 0, "one").unwrap(); - - assert_doc!( - &doc2, - map! { - "list" => {list.translate(&doc1) => list![ - {one.native() => "one"}, - {two.translate(&doc1) => "two"}, - {three.native() => "three" }, - {four.translate(&doc1) => "four"}, - ]} - } - ); -} - -#[test] -fn save_and_restore_empty() { - let mut doc = new_doc(); - let loaded = Automerge::load(&doc.save().unwrap()).unwrap(); - - assert_doc!(&loaded, map! {}); -} - -#[test] -fn save_restore_complex() { - let mut doc1 = new_doc(); - let todos = doc1 - .set(automerge::ROOT, "todos", automerge::Value::list()) - .unwrap() - .unwrap(); - - let first_todo = doc1.insert(todos, 0, automerge::Value::map()).unwrap(); - doc1.set(first_todo, "title", "water plants") - .unwrap() - .unwrap(); - let first_done = doc1.set(first_todo, "done", false).unwrap().unwrap(); - - let mut doc2 = new_doc(); - doc2.merge(&mut doc1); - let first_todo_in_doc2 = translate_obj_id(&doc1, &doc2, first_todo); - let weed_title = doc2 - .set(first_todo_in_doc2, "title", "weed plants") - .unwrap() - .unwrap(); - - let kill_title = doc1 - .set(first_todo, "title", "kill plants") - .unwrap() - .unwrap(); - doc1.merge(&mut doc2); - - let reloaded = Automerge::load(&doc1.save().unwrap()).unwrap(); - - assert_doc!( - &reloaded, - map! { - "todos" => {todos.translate(&doc1) => list![ - {first_todo.translate(&doc1) => map!{ - "title" => { - weed_title.translate(&doc2) => "weed plants", - kill_title.translate(&doc1) => "kill plants", - }, - "done" => {first_done.translate(&doc1) => false}, - }} - ]} - } - ); -} diff --git a/edit-trace/Cargo.toml b/edit-trace/Cargo.toml index 68d47433..ac2643c4 100644 --- a/edit-trace/Cargo.toml +++ b/edit-trace/Cargo.toml @@ -6,6 +6,10 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "edit-trace" +bench = false + [dependencies] automerge = { path = "../automerge" } criterion = "0.3.5" diff --git a/edit-trace/benches/main.rs b/edit-trace/benches/main.rs index fed72f1e..197614f6 100644 --- a/edit-trace/benches/main.rs +++ b/edit-trace/benches/main.rs @@ -5,9 +5,9 @@ use std::fs; fn replay_trace(commands: Vec<(usize, usize, Vec)>) -> Automerge { let mut doc = Automerge::new(); - let text = doc.set(ROOT, "text", Value::text()).unwrap().unwrap(); + let text = doc.set(&ROOT, "text", Value::text()).unwrap().unwrap(); for (pos, del, vals) in commands { - doc.splice(text, pos, del, vals).unwrap(); + doc.splice(&text, pos, del, vals).unwrap(); } doc.commit(None, None); doc diff --git a/edit-trace/src/main.rs b/edit-trace/src/main.rs index 94fde72c..6c54bcad 100644 --- a/edit-trace/src/main.rs +++ b/edit-trace/src/main.rs @@ -19,12 +19,12 @@ fn main() -> Result<(), AutomergeError> { let mut doc = Automerge::new(); let now = Instant::now(); - let text = doc.set(ROOT, "text", Value::text()).unwrap().unwrap(); + let text = doc.set(&ROOT, "text", Value::text()).unwrap().unwrap(); for (i, (pos, del, vals)) in commands.into_iter().enumerate() { if i % 1000 == 0 { println!("Processed {} edits in {} ms", i, now.elapsed().as_millis()); } - doc.splice(text, pos, del, vals)?; + doc.splice(&text, pos, del, vals)?; } let _ = doc.save(); println!("Done in {} ms", now.elapsed().as_millis());