//! [Change]s are merged ops. //! //! Every [Change] has deps on other [Change]s. All [Change]s in the document thus form a DAG. //! Note, `dep` may point to the middle of the other [Change]. //! //! In future, we may also use [Change] to represent a transaction. But this decision is postponed. use crate::{ dag::DagNode, id::{Counter, ID}, op::Op, span::{HasId, HasLamport}, version::Frontiers, }; use num::traits::AsPrimitive; use rle::{HasIndex, HasLength, Mergable, RleVec, Sliceable}; pub type Timestamp = i64; pub type Lamport = u32; /// A `Change` contains a list of [Op]s. /// /// When undo/redo we should always undo/redo a whole [Change]. #[derive(Debug, Clone)] pub struct Change { pub(crate) ops: RleVec<[O; 1]>, pub(crate) deps: Frontiers, /// id of the first op in the change pub(crate) id: ID, /// Lamport timestamp of the change. It can be calculated from deps pub(crate) lamport: Lamport, /// [Unix time](https://en.wikipedia.org/wiki/Unix_time) /// It is the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970. pub(crate) timestamp: Timestamp, } impl Change { pub fn new( ops: RleVec<[O; 1]>, deps: Frontiers, id: ID, lamport: Lamport, timestamp: Timestamp, ) -> Self { Change { ops, deps, id, lamport, timestamp, } } } impl HasIndex for Change { type Int = Counter; fn get_start_index(&self) -> Self::Int { self.id.counter } } impl HasId for Change { fn id_start(&self) -> ID { self.id } } impl HasLamport for Change { fn lamport(&self) -> Lamport { self.lamport } } impl Mergable for Change { fn is_mergable(&self, _other: &Self, _conf: &()) -> bool where Self: Sized, { false } fn merge(&mut self, _other: &Self, _conf: &()) where Self: Sized, { unreachable!() } } use std::fmt::Debug; impl HasLength for Change { fn content_len(&self) -> usize { self.ops.span().as_() } } impl Sliceable for Change { // TODO: feels slow, need to confirm whether this affects performance fn slice(&self, from: usize, to: usize) -> Self { Self { ops: self.ops.slice(from, to), deps: if from > 0 { Frontiers::from_id(self.id.inc(from as Counter - 1)) } else { self.deps.clone() }, id: self.id.inc(from as Counter), lamport: self.lamport + from as Lamport, timestamp: self.timestamp, } } } impl DagNode for Change { fn deps(&self) -> &[ID] { &self.deps } } impl Change { pub fn can_merge_right(&self, other: &Self) -> bool { other.id.peer == self.id.peer && other.id.counter == self.id.counter + self.content_len() as Counter && other.deps.len() == 1 && other.deps[0].peer == self.id.peer } }