add flowy ot

This commit is contained in:
appflowy 2021-07-31 20:53:45 +08:00
parent 55de7f69a4
commit b449707021
9 changed files with 880 additions and 0 deletions

View File

@ -15,6 +15,7 @@ members = [
"flowy-observable",
"flowy-document",
"flowy-editor",
"flowy-ot",
]
[profile.dev]

View File

@ -0,0 +1,13 @@
[package]
name = "flowy-ot"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytecount = "0.6.0"
[dev-dependencies]
criterion = "0.3"
rand = "0.7.3"

View File

@ -0,0 +1,97 @@
use std::collections::{hash_map::RandomState, HashMap};
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Attributes {
inner: HashMap<String, String>,
}
impl Attributes {
pub fn new() -> Self {
Attributes {
inner: HashMap::new(),
}
}
pub fn remove_empty_value(&mut self) { self.inner.retain((|_, v| v.is_empty())); }
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
}
impl std::convert::From<HashMap<String, String>> for Attributes {
fn from(attributes: HashMap<String, String, RandomState>) -> Self {
Attributes { inner: attributes }
}
}
impl std::ops::Deref for Attributes {
type Target = HashMap<String, String>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for Attributes {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
pub fn compose_attributes(
mut a: Attributes,
b: Attributes,
keep_empty: bool,
) -> Option<Attributes> {
a.extend(b);
let mut result = a;
if !keep_empty {
result.remove_empty_value()
}
return if result.is_empty() {
None
} else {
Some(result)
};
}
pub fn transform_attributes(a: Attributes, b: Attributes, priority: bool) -> Option<Attributes> {
if a.is_empty() {
return Some(b);
}
if b.is_empty() {
return None;
}
if !priority {
return Some(b);
}
let result = b.iter().fold(Attributes::new(), |mut attributes, (k, v)| {
if a.contains_key(k) == false {
attributes.insert(k.clone(), v.clone());
}
attributes
});
Some(result)
}
pub fn invert_attributes(attr: Option<Attributes>, base: Option<Attributes>) -> Attributes {
let attr = attr.unwrap_or(Attributes::new());
let base = base.unwrap_or(Attributes::new());
let base_inverted = base
.iter()
.fold(Attributes::new(), |mut attributes, (k, v)| {
if base.get(k) != attr.get(k) && attr.contains_key(k) {
attributes.insert(k.clone(), v.clone());
}
attributes
});
let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, v)| {
if base.get(k) != attr.get(k) && !base.contains_key(k) {
attributes.insert(k.clone(), "".to_owned());
}
attributes
});
return inverted;
}

View File

@ -0,0 +1,443 @@
use crate::{errors::OTError, operation::*};
use bytecount::num_chars;
use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator};
#[derive(Clone, Debug, PartialEq)]
pub struct Delta {
pub ops: Vec<Operation>,
pub base_len: usize,
pub target_len: usize,
}
impl Default for Delta {
fn default() -> Self {
Self {
ops: Vec::new(),
base_len: 0,
target_len: 0,
}
}
}
impl FromIterator<OpType> for Delta {
fn from_iter<T: IntoIterator<Item = OpType>>(ops: T) -> Self {
let mut operations = Delta::default();
for op in ops {
operations.add(op);
}
operations
}
}
impl Delta {
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
ops: Vec::with_capacity(capacity),
base_len: 0,
target_len: 0,
}
}
fn add(&mut self, op: OpType) {
match op {
OpType::Delete(i) => self.delete(i),
OpType::Insert(s) => self.insert(&s),
OpType::Retain(i) => self.retain(i),
}
}
pub fn delete(&mut self, n: u64) {
if n == 0 {
return;
}
self.base_len += n as usize;
if let Some(operation) = self.ops.last_mut() {
if operation.ty.is_delete() {
operation.delete(n);
return;
}
}
self.ops.push(OperationBuilder::delete(n).build());
}
pub fn insert(&mut self, s: &str) {
if s.is_empty() {
return;
}
self.target_len += num_chars(s.as_bytes());
let new_last = match self
.ops
.iter_mut()
.map(|op| &mut op.ty)
.collect::<Vec<&mut OpType>>()
.as_mut_slice()
{
[.., OpType::Insert(s_last)] => {
*s_last += &s;
return;
},
[.., OpType::Insert(s_pre_last), OpType::Delete(_)] => {
*s_pre_last += s;
return;
},
[.., op_last @ OpType::Delete(_)] => {
let new_last = op_last.clone();
*(*op_last) = OpType::Insert(s.to_owned());
new_last
},
_ => OpType::Insert(s.to_owned()),
};
self.ops.push(OperationBuilder::new(new_last).build());
}
pub fn retain(&mut self, n: u64) {
if n == 0 {
return;
}
self.base_len += n as usize;
self.target_len += n as usize;
if let Some(operation) = self.ops.last_mut() {
if operation.ty.is_retain() {
operation.retain(n);
return;
}
}
self.ops.push(OperationBuilder::retain(n).build());
}
/// Merges the operation with `other` into one operation while preserving
/// the changes of both. Or, in other words, for each input string S and a
/// pair of consecutive operations A and B.
/// `apply(apply(S, A), B) = apply(S, compose(A, B))`
/// must hold.
///
/// # Error
///
/// Returns an `OTError` if the operations are not composable due to length
/// conflicts.
pub fn compose(&self, other: &Self) -> Result<Self, OTError> {
if self.target_len != other.base_len {
return Err(OTError);
}
let mut new_delta = Delta::default();
let mut ops1 = self.ops.iter().cloned();
let mut ops2 = other.ops.iter().cloned();
let mut maybe_op1 = ops1.next();
let mut maybe_op2 = ops2.next();
loop {
match (
&maybe_op1.as_ref().map(|o| &o.ty),
&maybe_op2.as_ref().map(|o| &o.ty),
) {
(None, None) => break,
(Some(OpType::Delete(i)), _) => {
new_delta.delete(*i);
maybe_op1 = ops1.next();
},
(_, Some(OpType::Insert(s))) => {
new_delta.insert(s);
maybe_op2 = ops2.next();
},
(None, _) | (_, None) => {
return Err(OTError);
},
(Some(OpType::Retain(i)), Some(OpType::Retain(j))) => match i.cmp(&j) {
Ordering::Less => {
new_delta.retain(*i);
maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
maybe_op1 = ops1.next();
},
std::cmp::Ordering::Equal => {
new_delta.retain(*i);
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
std::cmp::Ordering::Greater => {
new_delta.retain(*j);
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
maybe_op2 = ops2.next();
},
},
(Some(OpType::Insert(s)), Some(OpType::Delete(j))) => {
match (num_chars(s.as_bytes()) as u64).cmp(j) {
Ordering::Less => {
maybe_op2 = Some(
OperationBuilder::delete(*j - num_chars(s.as_bytes()) as u64)
.build(),
);
maybe_op1 = ops1.next();
},
Ordering::Equal => {
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
maybe_op1 = Some(
OperationBuilder::insert(s.chars().skip(*j as usize).collect())
.build(),
);
maybe_op2 = ops2.next();
},
}
},
(Some(OpType::Insert(s)), Some(OpType::Retain(j))) => {
match (num_chars(s.as_bytes()) as u64).cmp(j) {
Ordering::Less => {
new_delta.insert(s);
maybe_op2 = Some(
OperationBuilder::retain(*j - num_chars(s.as_bytes()) as u64)
.build(),
);
maybe_op1 = ops1.next();
},
Ordering::Equal => {
new_delta.insert(s);
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
let chars = &mut s.chars();
new_delta.insert(&chars.take(*j as usize).collect::<String>());
maybe_op1 = Some(OperationBuilder::insert(chars.collect()).build());
maybe_op2 = ops2.next();
},
}
},
(Some(OpType::Retain(i)), Some(OpType::Delete(j))) => match i.cmp(&j) {
Ordering::Less => {
new_delta.delete(*i);
maybe_op2 = Some(OperationBuilder::delete(*j - *i).build());
maybe_op1 = ops1.next();
},
Ordering::Equal => {
new_delta.delete(*j);
maybe_op2 = ops2.next();
maybe_op1 = ops1.next();
},
Ordering::Greater => {
new_delta.delete(*j);
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
maybe_op2 = ops2.next();
},
},
};
}
Ok(new_delta)
}
/// Transforms two operations A and B that happened concurrently and
/// produces two operations A' and B' (in an array) such that
/// `apply(apply(S, A), B') = apply(apply(S, B), A')`.
/// This function is the heart of OT.
///
/// # Error
///
/// Returns an `OTError` if the operations cannot be transformed due to
/// length conflicts.
pub fn transform(&self, other: &Self) -> Result<(Self, Self), OTError> {
if self.base_len != other.base_len {
return Err(OTError);
}
let mut a_prime = Delta::default();
let mut b_prime = Delta::default();
let mut ops1 = self.ops.iter().cloned();
let mut ops2 = other.ops.iter().cloned();
let mut maybe_op1 = ops1.next();
let mut maybe_op2 = ops2.next();
loop {
match (
&maybe_op1.as_ref().map(|o| &o.ty),
&maybe_op2.as_ref().map(|o| &o.ty),
) {
(None, None) => break,
(Some(OpType::Insert(s)), _) => {
a_prime.insert(s);
b_prime.retain(num_chars(s.as_bytes()) as _);
maybe_op1 = ops1.next();
},
(_, Some(OpType::Insert(s))) => {
a_prime.retain(num_chars(s.as_bytes()) as _);
b_prime.insert(s);
maybe_op2 = ops2.next();
},
(None, _) => {
return Err(OTError);
},
(_, None) => {
return Err(OTError);
},
(Some(OpType::Retain(i)), Some(OpType::Retain(j))) => {
match i.cmp(&j) {
Ordering::Less => {
a_prime.retain(*i);
b_prime.retain(*i);
maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
maybe_op1 = ops1.next();
},
Ordering::Equal => {
a_prime.retain(*i);
b_prime.retain(*i);
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
a_prime.retain(*j);
b_prime.retain(*j);
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
maybe_op2 = ops2.next();
},
};
},
(Some(OpType::Delete(i)), Some(OpType::Delete(j))) => match i.cmp(&j) {
Ordering::Less => {
maybe_op2 = Some(OperationBuilder::delete(*j - *i).build());
maybe_op1 = ops1.next();
},
Ordering::Equal => {
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
maybe_op1 = Some(OperationBuilder::delete(*i - *j).build());
maybe_op2 = ops2.next();
},
},
(Some(OpType::Delete(i)), Some(OpType::Retain(j))) => {
match i.cmp(&j) {
Ordering::Less => {
a_prime.delete(*i);
maybe_op2 = Some(OperationBuilder::retain(*j - *i).build());
maybe_op1 = ops1.next();
},
Ordering::Equal => {
a_prime.delete(*i);
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
a_prime.delete(*j);
maybe_op1 = Some(OperationBuilder::delete(*i - *j).build());
maybe_op2 = ops2.next();
},
};
},
(Some(OpType::Retain(i)), Some(OpType::Delete(j))) => {
match i.cmp(&j) {
Ordering::Less => {
b_prime.delete(*i);
maybe_op2 = Some(OperationBuilder::delete(*j - *i).build());
maybe_op1 = ops1.next();
},
Ordering::Equal => {
b_prime.delete(*i);
maybe_op1 = ops1.next();
maybe_op2 = ops2.next();
},
Ordering::Greater => {
b_prime.delete(*j);
maybe_op1 = Some(OperationBuilder::retain(*i - *j).build());
maybe_op2 = ops2.next();
},
};
},
}
}
Ok((a_prime, b_prime))
}
/// Applies an operation to a string, returning a new string.
///
/// # Error
///
/// Returns an error if the operation cannot be applied due to length
/// conflicts.
pub fn apply(&self, s: &str) -> Result<String, OTError> {
if num_chars(s.as_bytes()) != self.base_len {
return Err(OTError);
}
let mut new_s = String::new();
let chars = &mut s.chars();
for op in &self.ops {
match &op.ty {
OpType::Retain(retain) => {
for c in chars.take(*retain as usize) {
new_s.push(c);
}
},
OpType::Delete(delete) => {
for _ in 0..*delete {
chars.next();
}
},
OpType::Insert(insert) => {
new_s += insert;
},
}
}
Ok(new_s)
}
/// Computes the inverse of an operation. The inverse of an operation is the
/// operation that reverts the effects of the operation
pub fn invert(&self, s: &str) -> Self {
let mut inverse = Delta::default();
let chars = &mut s.chars();
for op in &self.ops {
match &op.ty {
OpType::Retain(retain) => {
inverse.retain(*retain);
for _ in 0..*retain {
chars.next();
}
},
OpType::Insert(insert) => {
inverse.delete(num_chars(insert.as_bytes()) as u64);
},
OpType::Delete(delete) => {
inverse.insert(&chars.take(*delete as usize).collect::<String>());
},
}
}
inverse
}
/// Checks if this operation has no effect.
#[inline]
pub fn is_noop(&self) -> bool {
match self
.ops
.iter()
.map(|op| &op.ty)
.collect::<Vec<&OpType>>()
.as_slice()
{
[] => true,
[OpType::Retain(_)] => true,
_ => false,
}
}
/// Returns the length of a string these operations can be applied to
#[inline]
pub fn base_len(&self) -> usize { self.base_len }
/// Returns the length of the resulting string after the operations have
/// been applied.
#[inline]
pub fn target_len(&self) -> usize { self.target_len }
/// Returns the wrapped sequence of operations.
#[inline]
pub fn ops(&self) -> &[Operation] { &self.ops }
}

View File

@ -0,0 +1,12 @@
use std::{error::Error, fmt};
#[derive(Clone, Debug)]
pub struct OTError;
impl fmt::Display for OTError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "incompatible lengths") }
}
impl Error for OTError {
fn source(&self) -> Option<&(dyn Error + 'static)> { None }
}

View File

@ -0,0 +1,4 @@
mod attributes;
pub mod delta;
pub mod errors;
pub mod operation;

View File

@ -0,0 +1,103 @@
use crate::attributes::Attributes;
use std::{
cmp::Ordering,
collections::{hash_map::RandomState, HashMap},
ops::Deref,
};
#[derive(Clone, Debug, PartialEq)]
pub struct Operation {
pub ty: OpType,
pub attrs: Attributes,
}
impl Operation {
pub fn delete(&mut self, n: u64) { self.ty.delete(n); }
pub fn retain(&mut self, n: u64) { self.ty.retain(n); }
pub fn is_plain(&self) -> bool { self.attrs.is_empty() }
pub fn is_noop(&self) -> bool {
match self.ty {
OpType::Retain(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Delete(u64),
Retain(u64),
Insert(String),
}
impl OpType {
pub fn is_delete(&self) -> bool {
match self {
OpType::Delete(_) => true,
_ => false,
}
}
pub fn is_retain(&self) -> bool {
match self {
OpType::Retain(_) => true,
_ => false,
}
}
pub fn is_insert(&self) -> bool {
match self {
OpType::Insert(_) => true,
_ => false,
}
}
pub fn delete(&mut self, n: u64) {
debug_assert_eq!(self.is_delete(), true);
if let OpType::Delete(n_last) = self {
*n_last += n;
}
}
pub fn retain(&mut self, n: u64) {
debug_assert_eq!(self.is_retain(), true);
if let OpType::Retain(i_last) = self {
*i_last += n;
}
}
}
pub struct OperationBuilder {
ty: OpType,
attrs: Attributes,
}
impl OperationBuilder {
pub fn new(ty: OpType) -> OperationBuilder {
OperationBuilder {
ty,
attrs: Attributes::default(),
}
}
pub fn retain(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Retain(n)) }
pub fn delete(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Delete(n)) }
pub fn insert(s: String) -> OperationBuilder { OperationBuilder::new(OpType::Insert(s)) }
pub fn with_attrs(mut self, attrs: Attributes) -> OperationBuilder {
self.attrs = attrs;
self
}
pub fn build(self) -> Operation {
Operation {
ty: self.ty,
attrs: self.attrs,
}
}
}

View File

@ -0,0 +1,46 @@
use flowy_ot::delta::Delta;
use rand::{prelude::*, Rng as WrappedRng};
pub struct Rng(StdRng);
impl Default for Rng {
fn default() -> Self { Rng(StdRng::from_rng(thread_rng()).unwrap()) }
}
impl Rng {
pub fn from_seed(seed: [u8; 32]) -> Self { Rng(StdRng::from_seed(seed)) }
pub fn gen_string(&mut self, len: usize) -> String {
(0..len).map(|_| self.0.gen::<char>()).collect()
}
pub fn gen_delta(&mut self, s: &str) -> Delta {
let mut op = Delta::default();
loop {
let left = s.chars().count() - op.base_len();
if left == 0 {
break;
}
let i = if left == 1 {
1
} else {
1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))
};
match self.0.gen_range(0.0, 1.0) {
f if f < 0.2 => {
op.insert(&self.gen_string(i));
},
f if f < 0.4 => {
op.delete(i as u64);
},
_ => {
op.retain(i as u64);
},
}
}
if self.0.gen_range(0.0, 1.0) < 0.3 {
op.insert(&("1".to_owned() + &self.gen_string(10)));
}
op
}
}

View File

@ -0,0 +1,161 @@
mod helper;
use crate::helper::Rng;
use bytecount::num_chars;
use flowy_ot::{
delta::Delta,
operation::{OpType, OperationBuilder},
};
#[test]
fn lengths() {
let mut delta = Delta::default();
assert_eq!(delta.base_len, 0);
assert_eq!(delta.target_len, 0);
delta.retain(5);
assert_eq!(delta.base_len, 5);
assert_eq!(delta.target_len, 5);
delta.insert("abc");
assert_eq!(delta.base_len, 5);
assert_eq!(delta.target_len, 8);
delta.retain(2);
assert_eq!(delta.base_len, 7);
assert_eq!(delta.target_len, 10);
delta.delete(2);
assert_eq!(delta.base_len, 9);
assert_eq!(delta.target_len, 10);
}
#[test]
fn sequence() {
let mut delta = Delta::default();
delta.retain(5);
delta.retain(0);
delta.insert("lorem");
delta.insert("");
delta.delete(3);
delta.delete(0);
assert_eq!(delta.ops.len(), 3);
}
#[test]
fn apply() {
for _ in 0..1000 {
let mut rng = Rng::default();
let s = rng.gen_string(50);
let delta = rng.gen_delta(&s);
assert_eq!(num_chars(s.as_bytes()), delta.base_len);
assert_eq!(delta.apply(&s).unwrap().chars().count(), delta.target_len);
}
}
#[test]
fn invert() {
for _ in 0..1000 {
let mut rng = Rng::default();
let s = rng.gen_string(50);
let delta_a = rng.gen_delta(&s);
let delta_b = delta_a.invert(&s);
assert_eq!(delta_a.base_len, delta_b.target_len);
assert_eq!(delta_a.target_len, delta_b.base_len);
assert_eq!(delta_b.apply(&delta_a.apply(&s).unwrap()).unwrap(), s);
}
}
#[test]
fn empty_ops() {
let mut delta = Delta::default();
delta.retain(0);
delta.insert("");
delta.delete(0);
assert_eq!(delta.ops.len(), 0);
}
#[test]
fn eq() {
let mut delta_a = Delta::default();
delta_a.delete(1);
delta_a.insert("lo");
delta_a.retain(2);
delta_a.retain(3);
let mut delta_b = Delta::default();
delta_b.delete(1);
delta_b.insert("l");
delta_b.insert("o");
delta_b.retain(5);
assert_eq!(delta_a, delta_b);
delta_a.delete(1);
delta_b.retain(1);
assert_ne!(delta_a, delta_b);
}
#[test]
fn ops_merging() {
let mut delta = Delta::default();
assert_eq!(delta.ops.len(), 0);
delta.retain(2);
assert_eq!(delta.ops.len(), 1);
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
delta.retain(3);
assert_eq!(delta.ops.len(), 1);
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
delta.insert("abc");
assert_eq!(delta.ops.len(), 2);
assert_eq!(
delta.ops.last(),
Some(&OperationBuilder::insert("abc".to_owned()).build())
);
delta.insert("xyz");
assert_eq!(delta.ops.len(), 2);
assert_eq!(
delta.ops.last(),
Some(&OperationBuilder::insert("abcxyz".to_owned()).build())
);
delta.delete(1);
assert_eq!(delta.ops.len(), 3);
assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(1).build()));
delta.delete(1);
assert_eq!(delta.ops.len(), 3);
assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(2).build()));
}
#[test]
fn is_noop() {
let mut delta = Delta::default();
assert!(delta.is_noop());
delta.retain(5);
assert!(delta.is_noop());
delta.retain(3);
assert!(delta.is_noop());
delta.insert("lorem");
assert!(!delta.is_noop());
}
#[test]
fn compose() {
for _ in 0..1000 {
let mut rng = Rng::default();
let s = rng.gen_string(20);
let a = rng.gen_delta(&s);
let after_a = a.apply(&s).unwrap();
assert_eq!(a.target_len, num_chars(after_a.as_bytes()));
let b = rng.gen_delta(&after_a);
let after_b = b.apply(&after_a).unwrap();
assert_eq!(b.target_len, num_chars(after_b.as_bytes()));
let ab = a.compose(&b).unwrap();
assert_eq!(ab.target_len, b.target_len);
let after_ab = ab.apply(&s).unwrap();
assert_eq!(after_b, after_ab);
}
}
#[test]
fn transform() {
for _ in 0..1000 {
let mut rng = Rng::default();
let s = rng.gen_string(20);
let a = rng.gen_delta(&s);
let b = rng.gen_delta(&s);
let (a_prime, b_prime) = a.transform(&b).unwrap();
let ab_prime = a.compose(&b_prime).unwrap();
let ba_prime = b.compose(&a_prime).unwrap();
let after_ab_prime = ab_prime.apply(&s).unwrap();
let after_ba_prime = ba_prime.apply(&s).unwrap();
assert_eq!(ab_prime, ba_prime);
assert_eq!(after_ab_prime, after_ba_prime);
}
}