diff --git a/src/lib.rs b/src/lib.rs index ccc9682dd334a549d074f51925b414aed39a127a..44daf96cc79de47ea3efd0ea499965537c6b3e3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ mod utils; mod crdt; use crdt::{EventListener, IntervalUnion, Point, StrokeDelta, StrokeID, CRDT}; +mod packing; + #[wasm_bindgen] extern "C" { #[derive(Clone)] @@ -43,13 +45,13 @@ impl EventListener for WasmEventListener { } fn on_deltas(&self, deltas: HashMap<StrokeID, StrokeDelta>) { - self.on_deltas(bincode::serialize(&deltas).unwrap().into_boxed_slice()) + self.on_deltas(packing::pack(&bincode::serialize(&deltas).unwrap()).into_boxed_slice()) } fn on_deltas_from_state(&self, user: String, deltas: HashMap<StrokeID, StrokeDelta>) { self.on_deltas_from_state( user, - bincode::serialize(&deltas).unwrap().into_boxed_slice(), + packing::pack(&bincode::serialize(&deltas).unwrap()).into_boxed_slice(), ) } } @@ -115,7 +117,7 @@ impl WasmCRDT { } pub fn apply_deltas(&mut self, deltas: Box<[u8]>) -> Result<(), JsValue> { - let deltas = match bincode::deserialize(&deltas) { + let deltas = match bincode::deserialize(&packing::unpack(&deltas)) { Ok(deltas) => deltas, Err(error) => return Err(Error::new(&format!("{:?}", error)).into()), }; @@ -124,9 +126,7 @@ impl WasmCRDT { } pub fn get_state_vector(&self) -> Box<[u8]> { - bincode::serialize(&self.0.get_state_vector()) - .unwrap() - .into_boxed_slice() + packing::pack(&bincode::serialize(&self.0.get_state_vector()).unwrap()).into_boxed_slice() } pub fn fetch_deltas_from_state_vector( @@ -134,7 +134,7 @@ impl WasmCRDT { user: String, remote_state: Box<[u8]>, ) -> Result<(), JsValue> { - let remote_state = match bincode::deserialize(&remote_state) { + let remote_state = match bincode::deserialize(&packing::unpack(&remote_state)) { Ok(remote_state) => remote_state, Err(error) => return Err(Error::new(&format!("{:?}", error)).into()), }; diff --git a/src/packing.rs b/src/packing.rs new file mode 100644 index 0000000000000000000000000000000000000000..0168b2f48705592ce4edfe3f93c2b9949b4dbfe7 --- /dev/null +++ b/src/packing.rs @@ -0,0 +1,69 @@ +pub fn pack(bytes: &[u8]) -> Vec<u8> { + let mut packed = Vec::with_capacity((((bytes.len() as f64) * 1.125f64).ceil() as usize) + 1); + + let mut buffer = Vec::with_capacity(8); + + for slice in bytes.chunks(8) { + let mut mask = 0x00u8; + let mut marker = 0x80u8; + + for byte in slice { + if *byte != 0x00u8 { + mask |= marker; + buffer.push(*byte); + } + + marker >>= 1; + } + + packed.push(mask); + packed.append(&mut buffer); + } + + match bytes.len() % 8 { + 0 => (), + l => packed.push(0xFFu8.wrapping_shl(8u32 - (l as u32))), + }; + + packed.shrink_to_fit(); + + packed +} + +pub fn unpack(bytes: &[u8]) -> Vec<u8> { + let mut unpacked = Vec::with_capacity(bytes.len() * 8); + + let mut bytes_iter = bytes.into_iter().peekable(); + + while let Some(byte) = bytes_iter.next() { + let mask = *byte; + + if mask != 0x00u8 && bytes_iter.peek().is_none() { + unpacked.truncate(unpacked.len() - (mask.count_zeros() as usize)); + + break; + }; + + if mask == 0x00u8 { + unpacked.resize(unpacked.len() + 8, 0x00u8); + + continue; + }; + + let mut marker = 0x80u8; + + for _ in 0..8 { + if (mask & marker) != 0x00u8 { + unpacked.push(*bytes_iter.next().unwrap()) + } else { + unpacked.push(0x00u8) + }; + + marker >>= 1; + } + } + + unpacked.shrink_to_fit(); + + unpacked +} diff --git a/www/index.js b/www/index.js index 61ae56850bd74ed89edba0e0d128c522e93d0332..3d286bdff6cd1f6485b42b620a2507c8dd323fba 100644 --- a/www/index.js +++ b/www/index.js @@ -18,6 +18,8 @@ const crdt = new wasm.WasmCRDT({ }, }) +console.log("CRDT original StateVec", crdt.get_state_vector()) + crdt.set_user("36577c51-a80b-47d6-b3c3-cfb11f705b87") let stroke_id = crdt.add_stroke(4, 2, 3.14, "ffff00") @@ -72,8 +74,3 @@ console.log("CRDT2 StateVec", state2) console.log("pre fetch deltas from 1 for 2") crdt.fetch_deltas_from_state_vector("moritz", state2) console.log("post fetch deltas from 1 for 2") - -/*console.log("pre apply 1st deltas + fetch events") -crdt2.apply_deltas(broadcasts[0]) -crdt2.fetch_events() -console.log("post apply 1st deltas + fetch events")*/ \ No newline at end of file