Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub struct Json {
summary: Summary,
site_id: SiteId,
cached_ops: Vec<Op>,
outoforder_ops: Vec<Op>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -103,7 +104,7 @@ impl Json {
let mut summary = Summary::default();
let dot = summary.get_dot(site_id);
let inner = local_value.into_json(dot)?;
Ok(Json{inner, summary, site_id, cached_ops: vec![]})
Ok(Json{inner, summary, site_id, outoforder_ops: vec![], cached_ops: vec![]})
}

/// Constructs and returns a new `Json` CRDT with site 1 from an
Expand Down Expand Up @@ -179,7 +180,7 @@ impl Json {
JsonState,
Inner,
Op,
Option<LocalOp>,
LocalOp,
SJValue,
}
}
Expand Down Expand Up @@ -479,6 +480,14 @@ impl Op {
OpInner::String(ref op) => op.inserted_dots(),
}
}

fn removed_dots(&self) -> Vec<Dot> {
match self.op {
OpInner::Object(ref op) => op.removed_dots(),
OpInner::Array(ref op) => op.removed_dots(),
OpInner::String(ref op) => op.removed_dots(),
}
}
}

impl NestedOp for Op {
Expand Down
12 changes: 10 additions & 2 deletions src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct List<T: 'static> {
summary: Summary,
site_id: SiteId,
cached_ops: Vec<Op<T>>,
outoforder_ops: Vec<Op<T>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -92,7 +93,7 @@ impl<T: Clone> List<T> {
let inner = Inner::new();
let summary = Summary::default();
let site_id = 1;
List{inner, summary, site_id, cached_ops: vec![]}
List{inner, summary, site_id, outoforder_ops: vec![], cached_ops: vec![]}
}

/// Returns the number of elements in the list.
Expand Down Expand Up @@ -158,7 +159,7 @@ impl<T: Clone> List<T> {
ListState,
Inner<T>,
Op<T>,
Option<LocalOp<T>>,
LocalOp<T>,
Vec<T>,
}
}
Expand Down Expand Up @@ -423,6 +424,13 @@ impl<T> Op<T> {
vec![]
}
}

pub(crate) fn removed_dots(&self) -> Vec<Dot> {
match *self {
Op::Insert(_) => vec![],
Op::Remove(ref uid) => vec![uid.dot()],
}
}
}

impl<T: NestedInner> NestedOp for Op<T> {
Expand Down
5 changes: 3 additions & 2 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub struct Map<K: Key, V: Value> {
summary: Summary,
site_id: SiteId,
cached_ops: Vec<Op<K, V>>,
outoforder_ops: Vec<Op<K, V>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -109,7 +110,7 @@ impl<K: Key, V: Value> Map<K, V> {
let inner = Inner::new();
let summary = Summary::default();
let site_id = 1;
Map{inner, summary, site_id, cached_ops: vec![]}
Map{inner, summary, site_id, outoforder_ops: vec![], cached_ops: vec![]}
}

/// Returns true iff the map has the key.
Expand Down Expand Up @@ -378,7 +379,7 @@ impl<K: Key, V: Value> Op<K, V> {
pub fn inserted_element(&self) -> Option<&Element<V>> { self.inserted_element.as_ref() }

/// Returns a reference to the `Op`'s removed dots.
pub fn removed_dots(&self) -> &[Dot] { &self.removed_dots }
pub fn removed_dots(&self) -> Vec<Dot> { self.removed_dots.clone() }

/// Assigns a site id to any unassigned inserts and removes
pub fn add_site_id(&mut self, site_id: SiteId) {
Expand Down
5 changes: 3 additions & 2 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub struct Set<T: SetElement> {
summary: Summary,
site_id: SiteId,
cached_ops: Vec<Op<T>>,
outoforder_ops: Vec<Op<T>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -78,7 +79,7 @@ impl<T: SetElement> Set<T> {
let inner = Inner::new();
let summary = Summary::default();
let site_id = 1;
Set{inner, summary, site_id, cached_ops: vec![]}
Set{inner, summary, site_id, outoforder_ops: vec![], cached_ops: vec![]}
}

/// Returns true iff the set contains the value.
Expand Down Expand Up @@ -112,7 +113,7 @@ impl<T: SetElement> Set<T> {
SetState,
Inner<T>,
Op<T>,
Option<LocalOp<T>>,
LocalOp<T>,
HashSet<T>,
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/text/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub struct Text {
site_id: SiteId,
summary: Summary,
cached_ops: Vec<Op>,
outoforder_ops: Vec<Op>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -77,7 +78,7 @@ impl Text {
let inner = Inner::new();
let summary = Summary::default();
let site_id = 1;
Text{inner, summary, site_id, cached_ops: vec![]}
Text{inner, summary, site_id, outoforder_ops: vec![], cached_ops: vec![]}
}

/// Constructs and returns a new Text CRDT from a string.
Expand Down Expand Up @@ -117,7 +118,7 @@ impl Text {
TextState,
Inner,
Op,
Vec<LocalOp>,
LocalOp,
String,
}
}
Expand Down Expand Up @@ -358,6 +359,10 @@ impl Op {
self.inserted_elements.iter().map(|elt| elt.uid.dot()).collect()
}

pub fn removed_dots(&self) -> Vec<Dot> {
self.removed_uids.iter().map(|uid| uid.dot()).collect()
}

#[doc(hidden)]
pub fn inserted_elements(&self) -> &[Element] {
&self.inserted_elements
Expand Down
51 changes: 47 additions & 4 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ macro_rules! crdt_impl2 {
$local_op:ty,
$local_value:ty,
) => {

/// Returns the site id.
pub fn site_id(&self) -> SiteId {
self.site_id
Expand Down Expand Up @@ -64,6 +63,7 @@ macro_rules! crdt_impl2 {
site_id,
inner: state.inner.into_owned(),
summary: state.summary.into_owned(),
outoforder_ops: vec![],
cached_ops: vec![],
})
}
Expand All @@ -76,16 +76,30 @@ macro_rules! crdt_impl2 {
/// Executes an op and returns the equivalent local op.
/// This function assumes that the op always inserts values
/// from the correct site. For untrusted ops, used `validate_and_execute_op`.
pub fn execute_op(&mut self, op: $op) -> $local_op {
pub fn execute_op(&mut self, op: $op) -> Vec<$local_op> {
use traits::IntoVec;

for dot in op.inserted_dots() {
self.summary.insert(dot);
}
self.inner.execute_op(op)

if Self::is_outoforder(&op, &self.summary) {
self.outoforder_ops.push(op);
return vec![]
}

let mut local_ops: Vec<$local_op> = self.inner.execute_op(op).into_vec();

while let Some(op) = self.pop_outoforder_op() {
local_ops.append(&mut self.inner.execute_op(op).into_vec());
}

local_ops
}

/// Validates that an op only inserts elements from a given site id,
/// then executes the op and returns the equivalent local op.
pub fn validate_and_execute_op(&mut self, op: $op, site_id: SiteId) -> Result<$local_op, Error> {
pub fn validate_and_execute_op(&mut self, op: $op, site_id: SiteId) -> Result<Vec<$local_op>, Error> {
op.validate(site_id)?;
Ok(self.execute_op(op))
}
Expand Down Expand Up @@ -116,6 +130,15 @@ macro_rules! crdt_impl2 {
.collect())
}

fn is_outoforder(op: &$op, summary: &Summary) -> bool {
op.removed_dots().iter().any(|dot| !summary.contains(dot))
}

fn pop_outoforder_op(&mut self) -> Option<$op> {
let idx = self.outoforder_ops.iter().position(|op| Self::is_outoforder(&op, &self.summary))?;
Some(self.outoforder_ops.remove(idx))
}

fn after_op(&mut self, op: $op) -> Result<$op, Error> {
if self.site_id == 0 {
self.cached_ops.push(op);
Expand All @@ -127,6 +150,26 @@ macro_rules! crdt_impl2 {
}
}

pub(crate) trait IntoVec<T> {
fn into_vec(self) -> Vec<T>;
}

impl<T> IntoVec<T> for Option<T> {
fn into_vec(self) -> Vec<T> {
if let Some(value) = self { vec![value] } else { vec![] }
}
}

impl<T> IntoVec<T> for T {
fn into_vec(self) -> Vec<T> {
vec![self]
}
}

impl<T> IntoVec<T> for Vec<T> {
fn into_vec(self) -> Vec<T> { self }
}

pub(crate) trait NestedInner: Sized {
fn nested_add_site_id(&mut self, site_id: SiteId);

Expand Down