Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • sweng-group-15/drawing-crdt
1 result
Show changes
Commits on Source (2)
Showing
with 1987 additions and 7 deletions
[package]
name = "drawing-crdt"
version = "0.1.0"
version = "1.0.0"
authors = ["Moritz Langenstein <ml5717@ic.ac.uk>"]
edition = "2018"
......@@ -13,7 +13,7 @@ default = []
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3.33"
uuid = "0.8.1"
uuid = { version = "0.8.1", features = ["v4", "wasm-bindgen"] }
serde = "1.0.104"
serde_derive = "1.0.104"
serde-wasm-bindgen = "0.1.3"
......@@ -22,6 +22,7 @@ bincode = "1.2.1"
fixedbitset = "0.2.0"
ordered-float = { version = "1.0.2", features = ["serde"] }
twox-hash = "1.1.1"
paste = "0.1.6"
# web-sys = { version = "0.3.33", features = ["console"] }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
......
......@@ -38,6 +38,14 @@ export class WasmCRDT {
add_stroke(this.ptr: number, x: number, y: number, weight: string): string | undefined;
/**
* @param {number} this.ptr
* @param {number} x
* @param {number} y
* @param {string} weight
* @returns {string}
*/
add_stroke_unique(this.ptr: number, x: number, y: number, weight: string): string | undefined;
/**
* @param {number} this.ptr
* @param {string} stroke_id
* @param {number} x
* @param {number} y
......@@ -46,6 +54,15 @@ export class WasmCRDT {
*/
add_point(this.ptr: number, stroke_id: string, x: number, y: number, weight: string): number | undefined;
/**
* @param {number} this.ptr
* @param {string} stroke_id
* @param {number} x
* @param {number} y
* @param {string} weight
* @returns {number}
*/
add_point_unique(this.ptr: number, stroke_id: string, x: number, y: number, weight: string): number | undefined;
/**
* @param {string} stroke_id
* @returns {bool}
*/
......
......@@ -193,6 +193,10 @@ function passArray8ToWasm0(arg, malloc) {
WASM_VECTOR_LEN = arg.length;
return ptr;
}
function handleError(e) {
wasm.__wbindgen_exn_store(addHeapObject(e));
}
/**
*/
export class WasmCRDT {
......@@ -302,6 +306,26 @@ export class WasmCRDT {
}
/**
* @param {number} this.ptr
* @param {number} x
* @param {number} y
* @param {string} weight
* @returns {string}
*/
add_stroke_unique(x, y, weight, colour) {
var ptr0 = passStringToWasm0(colour, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
wasm.wasmcrdt_add_stroke_unique(8, this.ptr, x, y, weight, ptr0, len0);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
let v1;
if (r0 !== 0) {
v1 = getStringFromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
}
return v1;
}
/**
* @param {number} this.ptr
* @param {string} stroke_id
* @param {number} x
* @param {number} y
......@@ -319,6 +343,24 @@ export class WasmCRDT {
return r0 === 0 ? undefined : r1 >>> 0;
}
/**
* @param {number} this.ptr
* @param {string} stroke_id
* @param {number} x
* @param {number} y
* @param {string} weight
* @returns {number}
*/
add_point_unique(stroke_id, x, y, weight, colour) {
var ptr0 = passStringToWasm0(stroke_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
var ptr1 = passStringToWasm0(colour, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len1 = WASM_VECTOR_LEN;
wasm.wasmcrdt_add_point_unique(8, this.ptr, ptr0, len0, x, y, weight, ptr1, len1);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
return r0 === 0 ? undefined : r1 >>> 0;
}
/**
* @param {string} stroke_id
* @returns {bool}
*/
......@@ -414,7 +456,7 @@ export const __wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
export const __wbg_onstroke_a7ff14dcafc1fe51 = function(arg0, arg1, arg2, arg3) {
export const __wbg_onstroke_5f9b1da6899c8eb1 = function(arg0, arg1, arg2, arg3) {
try {
getObject(arg0).on_stroke(getStringFromWasm0(arg1, arg2), takeObject(arg3));
} finally {
......@@ -422,7 +464,7 @@ export const __wbg_onstroke_a7ff14dcafc1fe51 = function(arg0, arg1, arg2, arg3)
}
};
export const __wbg_oninterval_46888b73c9d9a955 = function(arg0, arg1, arg2, arg3) {
export const __wbg_oninterval_facb3bb86fd57772 = function(arg0, arg1, arg2, arg3) {
try {
getObject(arg0).on_interval(getStringFromWasm0(arg1, arg2), takeObject(arg3));
} finally {
......@@ -430,13 +472,13 @@ export const __wbg_oninterval_46888b73c9d9a955 = function(arg0, arg1, arg2, arg3
}
};
export const __wbg_ondeltas_5b4dc1394a6fe1b4 = function(arg0, arg1, arg2) {
export const __wbg_ondeltas_77cab6b3c01ff59b = function(arg0, arg1, arg2) {
var v0 = getArrayU8FromWasm0(arg1, arg2).slice();
wasm.__wbindgen_free(arg1, arg2 * 1);
getObject(arg0).on_deltas(v0);
};
export const __wbg_ondeltasfromstate_f665f5a618857e86 = function(arg0, arg1, arg2, arg3, arg4) {
export const __wbg_ondeltasfromstate_6effc852a5b4fd16 = function(arg0, arg1, arg2, arg3, arg4) {
try {
var v0 = getArrayU8FromWasm0(arg3, arg4).slice();
wasm.__wbindgen_free(arg3, arg4 * 1);
......@@ -451,6 +493,43 @@ export const __wbindgen_object_clone_ref = function(arg0) {
return addHeapObject(ret);
};
export const __wbg_randomFillSync_eabbc18af655bfbe = function(arg0, arg1, arg2) {
getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2));
};
export const __wbg_getRandomValues_40ceff860009fa55 = function(arg0, arg1, arg2) {
getObject(arg0).getRandomValues(getArrayU8FromWasm0(arg1, arg2));
};
export const __wbg_self_e70540c4956ad879 = function() {
try {
var ret = self.self;
return addHeapObject(ret);
} catch (e) {
handleError(e)
}
};
export const __wbg_require_9edeecb69c9dc31c = function(arg0, arg1) {
var ret = require(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
export const __wbg_crypto_58b0c631995fea92 = function(arg0) {
var ret = getObject(arg0).crypto;
return addHeapObject(ret);
};
export const __wbindgen_is_undefined = function(arg0) {
var ret = getObject(arg0) === undefined;
return ret;
};
export const __wbg_getRandomValues_532ec62d8e780edc = function(arg0) {
var ret = getObject(arg0).getRandomValues;
return addHeapObject(ret);
};
export const __wbindgen_number_new = function(arg0) {
var ret = arg0;
return addHeapObject(ret);
......
......@@ -8,7 +8,9 @@ export function wasmcrdt_get_user(a: number, b: number): void;
export function wasmcrdt_canonicalise_user(a: number, b: number, c: number): void;
export function wasmcrdt_canonicalise_stroke_author(a: number, b: number, c: number): void;
export function wasmcrdt_add_stroke(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
export function wasmcrdt_add_stroke_unique(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
export function wasmcrdt_add_point(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): void;
export function wasmcrdt_add_point_unique(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): void;
export function wasmcrdt_end_stroke(a: number, b: number, c: number): number;
export function wasmcrdt_erase_stroke(a: number, b: number, c: number, d: number, e: number): number;
export function wasmcrdt_get_stroke_ids(a: number): number;
......@@ -22,3 +24,4 @@ export function wasmcrdt_fetch_deltas_from_state_vector(a: number, b: number, c:
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number): number;
export function __wbindgen_free(a: number, b: number): void;
export function __wbindgen_exn_store(a: number): void;
No preview for this file type
......@@ -3,7 +3,7 @@
"collaborators": [
"Moritz Langenstein <ml5717@ic.ac.uk>"
],
"version": "0.1.0",
"version": "1.0.0",
"files": [
"drawing_crdt_bg.wasm",
"drawing_crdt.js",
......
use std::collections::HashMap;
use std::convert::{From, Into};
mod serde;
use super::{StrokeDelta, StrokeID};
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct DeltaVec(HashMap<StrokeID, StrokeDelta>);
impl From<HashMap<StrokeID, StrokeDelta>> for DeltaVec {
fn from(deltas: HashMap<StrokeID, StrokeDelta>) -> Self {
DeltaVec(deltas)
}
}
impl Into<HashMap<StrokeID, StrokeDelta>> for DeltaVec {
fn into(self) -> HashMap<StrokeID, StrokeDelta> {
self.0
}
}
use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, SerializeTuple, Serializer};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::fmt;
use super::{super::CRDT, DeltaVec, StrokeDelta, StrokeID};
impl Serialize for DeltaVec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
struct DeltaVecSerWrapper<'a>(Vec<((usize, u8, usize), &'a StrokeDelta)>);
impl<'a> Serialize for DeltaVecSerWrapper<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in &self.0 {
map.serialize_entry(k, v)?;
}
map.end()
}
}
let mut buffer: Vec<((usize, u8, usize), &StrokeDelta)> = Vec::with_capacity(self.0.len());
let mut lut_tmp: HashMap<u128, usize> = HashMap::new();
let mut lut_out: Vec<u128> = Vec::new();
for (k, v) in &self.0 {
let (user, active) = CRDT::canonicalise_uuid(k.user());
let id = match lut_tmp.entry(user) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let id = lut_out.len();
entry.insert(id);
lut_out.push(user);
id
}
};
buffer.push(((id, active, k.id()), v));
}
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&lut_out)?;
tuple.serialize_element(&DeltaVecSerWrapper(buffer))?;
tuple.end()
}
}
impl<'de> Deserialize<'de> for DeltaVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct DeltaVecDeWrapper<'a>(&'a Vec<u128>);
impl<'de, 'a> DeserializeSeed<'de> for DeltaVecDeWrapper<'a> {
type Value = HashMap<StrokeID, StrokeDelta>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct DeltaVecDeWrapperVisitor<'a>(&'a Vec<u128>);
impl<'de, 'a> Visitor<'de> for DeltaVecDeWrapperVisitor<'a> {
type Value = HashMap<StrokeID, StrokeDelta>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct DeltaVec")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
while let Some(((id, active, idx), value)) =
access.next_entry()?: Option<((usize, u8, usize), StrokeDelta)>
{
let user = match self.0.get(id) {
Some(user) => user,
None => {
return Err(de::Error::custom(
"invalid canonincal user lookup id",
))
}
};
map.insert(
StrokeID::new(CRDT::decanonicalise_uuid(*user, active), idx),
value,
);
}
Ok(map)
}
}
deserializer.deserialize_map(DeltaVecDeWrapperVisitor(self.0))
}
}
struct DeltaVecVisitor;
impl<'de> Visitor<'de> for DeltaVecVisitor {
type Value = DeltaVec;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct DeltaVec")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let lut_in: Vec<u128> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let deltas = seq
.next_element_seed(DeltaVecDeWrapper(&lut_in))?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(DeltaVec(deltas))
}
}
deserializer.deserialize_tuple(2, DeltaVecVisitor)
}
}
macro_rules! dirty {
[struct $s:ident($($a:ident),+)] => {
paste::item! {
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct $s<T> {
value: T,
$([<dirty_ $a>]: bool,)+
}
impl<T> Deref for $s<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> DerefMut for $s<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T> $s<T> {
pub fn new(value: T, dirty: bool) -> $s<T> {
$s {
value,
$([<dirty_ $a>]: dirty,)+
}
}
$(
pub fn [<has_dirty_ $a>](&self) -> bool {
self.[<dirty_ $a>]
}
pub fn [<clean_ $a>](&mut self) {
self.[<dirty_ $a>] = false;
}
)+
pub fn dirty(&mut self) {
$(
self.[<dirty_ $a>] = true;
)+
}
}
}
}
}
use ordered_float::NotNan;
use std::hash::Hash;
#[cfg(debug_assertions)]
use std::fmt::Debug;
#[cfg(debug_assertions)]
pub trait IntervalBound: Copy + Clone + Default + Ord + Hash + Debug {
fn up(self) -> Self;
fn down(self) -> Self;
fn sub(self, nom: u8, denom: u8) -> Self;
}
#[cfg(not(debug_assertions))]
pub trait IntervalBound: Copy + Clone + Default + Ord + Hash {
fn up(self) -> Self;
fn down(self) -> Self;
fn sub(self, nom: u8, denom: u8) -> Self;
}
impl IntervalBound for NotNan<f32> {
fn up(self) -> NotNan<f32> {
self
}
fn down(self) -> NotNan<f32> {
self
}
fn sub(self, nom: u8, denom: u8) -> NotNan<f32> {
self * unsafe { NotNan::unchecked_new(nom as f32) }
/ unsafe { NotNan::unchecked_new(denom as f32) }
}
}
impl IntervalBound for usize {
fn up(self) -> usize {
self + 1
}
fn down(self) -> usize {
self - 1
}
fn sub(self, nom: u8, denom: u8) -> usize {
self * (nom as usize) / (denom as usize)
}
}
pub mod bound;
use bound::IntervalBound;
mod serde;
pub mod union;
#[derive(Copy, Clone, PartialEq, Hash)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Interval<T>
where
T: IntervalBound,
{
pub from: T,
pub to: T,
}
impl<T> Interval<T>
where
T: IntervalBound,
{
pub fn new(from: T, to: T) -> Interval<T> {
Interval { from, to }
}
}
use serde::ser::{Serialize, SerializeTuple, Serializer};
use super::{Interval, IntervalBound};
impl<T> Serialize for Interval<T>
where
T: IntervalBound + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&self.from)?;
tuple.serialize_element(&self.to)?;
tuple.end()
}
}
use std::cmp;
use std::convert::TryFrom;
use super::{Interval, IntervalBound};
mod serde;
pub mod state;
#[derive(Clone, Hash)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct IntervalUnion<T>(Vec<Interval<T>>)
where
T: IntervalBound;
impl<T> IntervalUnion<T>
where
T: IntervalBound,
{
pub fn new() -> IntervalUnion<T> {
IntervalUnion(Vec::new())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_full(&self) -> bool {
match self.0.first() {
Some(interval) if self.0.len() == 1 => interval.from == T::default(),
_ => false,
}
}
pub fn max(&self) -> Option<T> {
self.0.last().map(|interval| interval.to)
}
pub fn union(&mut self, other: &IntervalUnion<T>) -> bool {
if other.is_empty() {
return false;
};
if self.is_empty() {
self.0.extend(&other.0);
return true;
}
let mut si = 0;
let mut oi = 0;
let mut intervals: Vec<Interval<T>> = Vec::new();
while si < self.0.len() || oi < other.0.len() {
let interval = if (oi >= other.0.len())
|| ((si < self.0.len()) && (self.0[si].from < other.0[oi].from))
{
let interval = self.0[si];
si += 1;
interval
} else {
let interval = other.0[oi];
oi += 1;
interval
};
match intervals.last_mut() {
Some(top) => {
if interval.from <= top.to.up() {
top.to = cmp::max(top.to, interval.to)
} else {
intervals.push(interval)
}
}
None => intervals.push(interval),
};
}
let changed = intervals != self.0;
self.0 = intervals;
changed
}
pub fn difference(&mut self, other: &IntervalUnion<T>) {
let mut si = 0;
let mut oi = 0;
let mut intervals: Vec<Interval<T>> = Vec::new();
while si < self.0.len() && oi < other.0.len() {
if other.0[oi].to < self.0[si].from {
oi += 1;
continue;
}
if other.0[oi].from <= self.0[si].from {
if other.0[oi].to >= self.0[si].to {
si += 1;
} else {
self.0[si].from = other.0[oi].to.up();
oi += 1;
}
continue;
}
if other.0[oi].from <= self.0[si].to {
intervals.push(Interval::new(self.0[si].from, other.0[oi].from.down()));
self.0[si].from = other.0[oi].from;
} else {
intervals.push(self.0[si]);
si += 1;
}
}
if si < self.0.len() {
intervals.extend_from_slice(&self.0[si..])
};
self.0 = intervals;
}
}
impl<T> IntoIterator for IntervalUnion<T>
where
T: IntervalBound,
{
type Item = Interval<T>;
type IntoIter = std::vec::IntoIter<Self::Item>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T> From<Interval<T>> for IntervalUnion<T>
where
T: IntervalBound,
{
fn from(interval: Interval<T>) -> Self {
IntervalUnion(vec![interval])
}
}
impl<T> TryFrom<Vec<Interval<T>>> for IntervalUnion<T>
where
T: IntervalBound,
{
type Error = &'static str;
fn try_from(intervals: Vec<Interval<T>>) -> Result<Self, Self::Error> {
let mut prev: Option<&Interval<T>> = None;
for interval in &intervals {
if interval.from > interval.to {
return Err("Invalid interval (from > to)");
};
if prev.map_or(false, |prev| prev.to.up() >= interval.from) {
return Err("Intervals have not been unioned correctly");
};
prev = Some(interval);
}
Ok(IntervalUnion(intervals))
}
}
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeSeq, Serializer};
use std::cmp;
use std::fmt;
use super::{Interval, IntervalBound, IntervalUnion};
impl<T> Serialize for IntervalUnion<T>
where
T: IntervalBound + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
self.0.serialize(serializer)
} else {
let mut seq = serializer.serialize_seq(Some(self.0.len() * 2))?;
for interval in &self.0 {
seq.serialize_element(&interval.from)?;
seq.serialize_element(&interval.to)?;
}
seq.end()
}
}
}
impl<'de, T> Deserialize<'de> for IntervalUnion<T>
where
T: IntervalBound + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use std::marker::PhantomData;
struct IntervalUnionVisitor<T>
where
T: IntervalBound,
{
marker: PhantomData<fn() -> IntervalUnion<T>>,
};
impl<'de, T> Visitor<'de> for IntervalUnionVisitor<T>
where
T: IntervalBound + Deserialize<'de>,
{
type Value = IntervalUnion<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct IntervalUnion<T: IntervalBound>")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let mut intervals =
Vec::with_capacity(cmp::max(seq.size_hint().unwrap_or(0) / 2, 4));
while let (Some(from), Some(to)) = (seq.next_element()?, seq.next_element()?) {
intervals.push(Interval::new(from, to));
}
Ok(IntervalUnion(intervals))
}
}
let visitor = IntervalUnionVisitor {
marker: PhantomData,
};
deserializer.deserialize_seq(visitor)
}
}
use std::hash::{Hash, Hasher};
use twox_hash::XxHash64 as XXHasher;
use super::{Interval, IntervalBound, IntervalUnion};
mod serde;
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct IntervalUnionState<T>
where
T: IntervalBound,
{
hash: u64,
footprint: u64,
range: T,
}
impl<T> IntervalUnionState<T>
where
T: IntervalBound,
{
const EMPTY_INTERVALS: u64 = std::u64::MIN;
const EMPTY_CHICKEN_INTERVALS: u64 = 0x3482b3a70bdbe3c0u64;
const FULL_INTERVALS: u64 = std::u64::MAX;
const FULL_CHICKEN_INTERVALS: u64 = 0x05b4d9adff1491aau64;
const FOOTPRINT_MARKER: u64 = 0x8000000000000000u64;
pub fn new(intervals: &IntervalUnion<T>) -> IntervalUnionState<T> {
IntervalUnionState {
hash: Self::hash(intervals),
footprint: Self::footprint(intervals),
range: Self::range(intervals),
}
}
pub fn hash(intervals: &IntervalUnion<T>) -> u64 {
if intervals.is_empty() {
Self::EMPTY_INTERVALS
} else if intervals.is_full() {
Self::FULL_INTERVALS
} else {
let mut hasher = XXHasher::default();
intervals.hash(&mut hasher);
match hasher.finish() {
Self::EMPTY_INTERVALS => Self::EMPTY_CHICKEN_INTERVALS,
Self::FULL_INTERVALS => Self::FULL_CHICKEN_INTERVALS,
hash => hash,
}
}
.wrapping_neg()
}
pub fn range(intervals: &IntervalUnion<T>) -> T {
intervals.max().unwrap_or(T::default())
}
pub fn footprint(intervals: &IntervalUnion<T>) -> u64 {
if intervals.is_empty() {
Self::EMPTY_INTERVALS
} else if intervals.is_full() {
Self::FULL_INTERVALS
} else {
let range = Self::range(intervals);
let mut si = 0u8;
let mut oi = 0;
let mut current = Interval::new(range.sub(si, 64u8), range.sub(si + 1u8, 64u8));
let mut footprint = Self::EMPTY_INTERVALS;
let mut marker = Self::FOOTPRINT_MARKER;
while si < 64u8 && oi < intervals.0.len() {
if intervals.0[oi].to < current.from {
oi += 1;
continue;
}
if intervals.0[oi].from <= current.from {
if intervals.0[oi].to >= current.to {
footprint |= marker;
} else {
oi += 1;
}
}
marker >>= 1;
si += 1u8;
current = Interval::new(range.sub(si, 64u8), range.sub(si + 1u8, 64u8));
}
footprint
}
.wrapping_neg()
}
}
impl<T> Into<IntervalUnion<T>> for IntervalUnionState<T>
where
T: IntervalBound,
{
fn into(self) -> IntervalUnion<T> {
let mut marker = IntervalUnionState::<T>::FOOTPRINT_MARKER;
let mut intervals = Vec::new();
let footprint = self.footprint.wrapping_neg();
if footprint == IntervalUnionState::<T>::FULL_INTERVALS {
intervals.push(Interval::new(T::default(), self.range));
} else if footprint != IntervalUnionState::<T>::EMPTY_INTERVALS {
for from in 0u8..64u8 {
if (footprint & marker) != IntervalUnionState::<T>::EMPTY_INTERVALS {
let interval =
Interval::new(self.range.sub(from, 64u8), self.range.sub(from + 1u8, 64u8));
match intervals.last_mut() {
Some(last) if last.to.up() == interval.from => last.to = interval.to,
_ => intervals.push(interval),
};
}
marker >>= 1;
}
}
IntervalUnion(intervals)
}
}
use serde::de::{self, Deserialize, Deserializer, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeTuple, Serializer};
use std::fmt;
use super::{IntervalBound, IntervalUnionState};
impl<T> Serialize for IntervalUnionState<T>
where
T: IntervalBound + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tuple = serializer.serialize_tuple(3)?;
tuple.serialize_element(&self.hash)?;
tuple.serialize_element(&self.footprint)?;
tuple.serialize_element(&self.range)?;
tuple.end()
}
}
impl<'de, T> Deserialize<'de> for IntervalUnionState<T>
where
T: IntervalBound + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use std::marker::PhantomData;
struct IntervalUnionStateVisitor<T>
where
T: IntervalBound,
{
marker: PhantomData<fn() -> IntervalUnionState<T>>,
};
impl<'de, T> Visitor<'de> for IntervalUnionStateVisitor<T>
where
T: IntervalBound + Deserialize<'de>,
{
type Value = IntervalUnionState<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct IntervalUnionState<T: IntervalBound>")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let hash = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let footprint = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let range = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
Ok(IntervalUnionState {
hash,
footprint,
range,
})
}
}
let visitor = IntervalUnionStateVisitor {
marker: PhantomData,
};
deserializer.deserialize_tuple(3, visitor)
}
}
This diff is collapsed.
use ordered_float::NotNan;
use std::collections::HashMap;
use super::{IntervalUnion, IntervalUnionState, StrokeID};
mod serde;
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct StateVec {
pub strokes: HashMap<u128, IntervalUnion<usize>>,
pub intervals: HashMap<StrokeID, IntervalUnionState<NotNan<f32>>>,
}
impl StateVec {
pub fn new() -> StateVec {
StateVec {
strokes: HashMap::new(),
intervals: HashMap::new(),
}
}
}
use ordered_float::NotNan;
use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, SerializeTuple, Serializer};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::fmt;
use super::{super::CRDT, IntervalUnion, IntervalUnionState, StateVec, StrokeID};
impl Serialize for StateVec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
struct StateVecStrokesSerWrapper<'a>(Vec<((usize, u8), &'a IntervalUnion<usize>)>);
impl<'a> Serialize for StateVecStrokesSerWrapper<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in &self.0 {
map.serialize_entry(k, v)?;
}
map.end()
}
}
struct StateVecIntervalsSerWrapper<'a>(
Vec<((usize, u8, usize), &'a IntervalUnionState<NotNan<f32>>)>,
);
impl<'a> Serialize for StateVecIntervalsSerWrapper<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in &self.0 {
map.serialize_entry(k, v)?;
}
map.end()
}
}
let mut strokes_buffer: Vec<((usize, u8), &IntervalUnion<usize>)> =
Vec::with_capacity(self.strokes.len());
let mut intervals_buffer: Vec<((usize, u8, usize), &IntervalUnionState<NotNan<f32>>)> =
Vec::with_capacity(self.intervals.len());
let mut lut_tmp: HashMap<u128, usize> = HashMap::new();
let mut lut_out: Vec<u128> = Vec::new();
for (k, v) in &self.strokes {
let (user, active) = CRDT::canonicalise_uuid(*k);
let id = match lut_tmp.entry(user) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let id = lut_out.len();
entry.insert(id);
lut_out.push(user);
id
}
};
strokes_buffer.push(((id, active), v));
}
for (k, v) in &self.intervals {
let (user, active) = CRDT::canonicalise_uuid(k.user());
let id = match lut_tmp.entry(user) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let id = lut_out.len();
entry.insert(id);
lut_out.push(user);
id
}
};
intervals_buffer.push(((id, active, k.id()), v));
}
let mut tuple = serializer.serialize_tuple(3)?;
tuple.serialize_element(&lut_out)?;
tuple.serialize_element(&StateVecStrokesSerWrapper(strokes_buffer))?;
tuple.serialize_element(&StateVecIntervalsSerWrapper(intervals_buffer))?;
tuple.end()
}
}
impl<'de> Deserialize<'de> for StateVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StateVecStrokesDeWrapper<'a>(&'a Vec<u128>);
impl<'de, 'a> DeserializeSeed<'de> for StateVecStrokesDeWrapper<'a> {
type Value = HashMap<u128, IntervalUnion<usize>>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct StateVecStrokesDeWrapperVisitor<'a>(&'a Vec<u128>);
impl<'de, 'a> Visitor<'de> for StateVecStrokesDeWrapperVisitor<'a> {
type Value = HashMap<u128, IntervalUnion<usize>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct StateVec")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
while let Some(((id, active), value)) =
access.next_entry()?: Option<((usize, u8), IntervalUnion<usize>)>
{
let user = match self.0.get(id) {
Some(user) => user,
None => {
return Err(de::Error::custom(
"invalid canonincal user lookup id",
))
}
};
map.insert(CRDT::decanonicalise_uuid(*user, active), value);
}
Ok(map)
}
}
deserializer.deserialize_map(StateVecStrokesDeWrapperVisitor(self.0))
}
}
struct StateVecIntervalsDeWrapper<'a>(&'a Vec<u128>);
impl<'de, 'a> DeserializeSeed<'de> for StateVecIntervalsDeWrapper<'a> {
type Value = HashMap<StrokeID, IntervalUnionState<NotNan<f32>>>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct StateVecIntervalsDeWrapperVisitor<'a>(&'a Vec<u128>);
impl<'de, 'a> Visitor<'de> for StateVecIntervalsDeWrapperVisitor<'a> {
type Value = HashMap<StrokeID, IntervalUnionState<NotNan<f32>>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct StateVec")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
while let Some(((id, active, idx), value)) = access.next_entry()?:
Option<((usize, u8, usize), IntervalUnionState<NotNan<f32>>)>
{
let user = match self.0.get(id) {
Some(user) => user,
None => {
return Err(de::Error::custom(
"invalid canonincal user lookup id",
))
}
};
map.insert(
StrokeID::new(CRDT::decanonicalise_uuid(*user, active), idx),
value,
);
}
Ok(map)
}
}
deserializer.deserialize_map(StateVecIntervalsDeWrapperVisitor(self.0))
}
}
struct StateVecVisitor;
impl<'de> Visitor<'de> for StateVecVisitor {
type Value = StateVec;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct StateVec")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let lut_in: Vec<u128> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let strokes = seq
.next_element_seed(StateVecStrokesDeWrapper(&lut_in))?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let intervals = seq
.next_element_seed(StateVecIntervalsDeWrapper(&lut_in))?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
Ok(StateVec { strokes, intervals })
}
}
deserializer.deserialize_tuple(3, StateVecVisitor)
}
}
use ordered_float::NotNan;
use std::collections::HashMap;
mod serde;
use super::{IntervalUnion, Point};
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct StrokeDelta {
pub points: HashMap<usize, Point>,
pub intervals: IntervalUnion<NotNan<f32>>,
}
impl StrokeDelta {
pub fn new() -> StrokeDelta {
StrokeDelta {
points: HashMap::new(),
intervals: IntervalUnion::new(),
}
}
}