From bc9f0583bede67a4af50f7d4b83c0e29f68b1fa9 Mon Sep 17 00:00:00 2001 From: Orion Henry Date: Thu, 3 Nov 2022 12:10:29 -0500 Subject: [PATCH] swap out vec for seq_tree in observer for big patch --- rust/automerge-wasm/src/interop.rs | 8 +++--- rust/automerge-wasm/src/observer.rs | 8 +++--- rust/automerge/src/lib.rs | 2 ++ rust/automerge/src/sequence_tree.rs | 38 +++++++++++++++-------------- rust/edit-trace/automerge-js.ts | 30 +++++++++++++++++++++++ rust/edit-trace/automerge-wasm.js | 2 +- rust/edit-trace/package.json | 9 +++++++ rust/edit-trace/tsconfig.json | 22 +++++++++++++++++ rust/edit-trace/webpack.config.js | 37 ++++++++++++++++++++++++++++ 9 files changed, 130 insertions(+), 26 deletions(-) create mode 100644 rust/edit-trace/automerge-js.ts create mode 100644 rust/edit-trace/tsconfig.json create mode 100644 rust/edit-trace/webpack.config.js diff --git a/rust/automerge-wasm/src/interop.rs b/rust/automerge-wasm/src/interop.rs index ed76f3a7..3c18f9ca 100644 --- a/rust/automerge-wasm/src/interop.rs +++ b/rust/automerge-wasm/src/interop.rs @@ -557,7 +557,7 @@ impl Automerge { Reflect::set(&result, &(*index as f64).into(), &sub_val)?; Ok(result.into()) } - Patch::DeleteSeq { index, .. } => self.sub_splice(result, *index, 1, &[], meta), + Patch::DeleteSeq { index, .. } => self.sub_splice(result, *index, 1, vec![], meta), Patch::Insert { index, values, .. } => self.sub_splice(result, *index, 0, values, meta), Patch::Increment { prop, value, .. } => { if let Prop::Seq(index) = prop { @@ -650,16 +650,16 @@ impl Automerge { self.wrap_object(result, datatype, &id, meta) } - fn sub_splice( + fn sub_splice<'a, I: IntoIterator, ObjId)>>( &self, o: Array, index: usize, num_del: usize, - values: &[(Value<'_>, ObjId)], + values: I, meta: &JsValue, ) -> Result { let args: Array = values - .iter() + .into_iter() .map(|v| self.maybe_wrap_object(alloc(&v.0), &v.1, meta)) .collect::>()?; args.unshift(&(num_del as u32).into()); diff --git a/rust/automerge-wasm/src/observer.rs b/rust/automerge-wasm/src/observer.rs index ab59abf4..b72167bf 100644 --- a/rust/automerge-wasm/src/observer.rs +++ b/rust/automerge-wasm/src/observer.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use crate::interop::{alloc, js_set}; -use automerge::{ObjId, OpObserver, Parents, Prop, Value}; +use automerge::{ObjId, OpObserver, Parents, Prop, Value, SequenceTree}; use js_sys::{Array, Object}; use wasm_bindgen::prelude::*; @@ -45,7 +45,7 @@ pub(crate) enum Patch { obj: ObjId, path: Vec<(ObjId, Prop)>, index: usize, - values: Vec<(Value<'static>, ObjId)>, + values: SequenceTree<(Value<'static>, ObjId)>, }, Increment { obj: ObjId, @@ -91,11 +91,13 @@ impl OpObserver for Observer { } } let path = parents.path(); + let mut values = SequenceTree::new(); + values.push(value); let patch = Patch::Insert { path, obj, index, - values: vec![value], + values, }; self.patches.push(patch); } diff --git a/rust/automerge/src/lib.rs b/rust/automerge/src/lib.rs index df33e096..15cee2a7 100644 --- a/rust/automerge/src/lib.rs +++ b/rust/automerge/src/lib.rs @@ -77,6 +77,7 @@ mod op_set; mod op_tree; mod parents; mod query; +mod sequence_tree; mod storage; pub mod sync; pub mod transaction; @@ -105,6 +106,7 @@ pub use op_observer::OpObserver; pub use op_observer::Patch; pub use op_observer::VecOpObserver; pub use parents::Parents; +pub use sequence_tree::SequenceTree; pub use types::{ActorId, ChangeHash, ObjType, OpType, Prop}; pub use value::{ScalarValue, Value}; pub use values::Values; diff --git a/rust/automerge/src/sequence_tree.rs b/rust/automerge/src/sequence_tree.rs index ba5c7ff6..eab2d114 100644 --- a/rust/automerge/src/sequence_tree.rs +++ b/rust/automerge/src/sequence_tree.rs @@ -4,21 +4,22 @@ use std::{ mem, }; -pub type SequenceTree = SequenceTreeInternal; +pub(crate) const B: usize = 500; +pub type SequenceTree = SequenceTreeInternal; #[derive(Clone, Debug)] pub struct SequenceTreeInternal { - root_node: Option>, + root_node: Option>, } #[derive(Clone, Debug, PartialEq)] struct SequenceTreeNode { elements: Vec, - children: Vec>, + children: Vec>, length: usize, } -impl SequenceTreeInternal +impl SequenceTreeInternal where T: Clone + Debug, { @@ -38,7 +39,7 @@ where } /// Create an iterator through the sequence. - pub fn iter(&self) -> Iter<'_, T, B> { + pub fn iter(&self) -> Iter<'_, T> { Iter { inner: self, index: 0, @@ -145,7 +146,7 @@ where } } -impl SequenceTreeNode +impl SequenceTreeNode where T: Clone + Debug, { @@ -157,7 +158,7 @@ where } } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.length } @@ -380,7 +381,7 @@ where l } - pub fn remove(&mut self, index: usize) -> T { + pub(crate) fn remove(&mut self, index: usize) -> T { let original_len = self.len(); if self.is_leaf() { let v = self.remove_from_leaf(index); @@ -423,7 +424,7 @@ where } } - fn merge(&mut self, middle: T, successor_sibling: SequenceTreeNode) { + fn merge(&mut self, middle: T, successor_sibling: SequenceTreeNode) { self.elements.push(middle); self.elements.extend(successor_sibling.elements); self.children.extend(successor_sibling.children); @@ -431,7 +432,7 @@ where assert!(self.is_full()); } - pub fn set(&mut self, index: usize, element: T) -> T { + pub(crate) fn set(&mut self, index: usize, element: T) -> T { if self.is_leaf() { let old_element = self.elements.get_mut(index).unwrap(); mem::replace(old_element, element) @@ -455,7 +456,7 @@ where } } - pub fn get(&self, index: usize) -> Option<&T> { + pub(crate) fn get(&self, index: usize) -> Option<&T> { if self.is_leaf() { return self.elements.get(index); } else { @@ -475,7 +476,7 @@ where None } - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { + pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut T> { if self.is_leaf() { return self.elements.get_mut(index); } else { @@ -496,7 +497,7 @@ where } } -impl Default for SequenceTreeInternal +impl Default for SequenceTreeInternal where T: Clone + Debug, { @@ -505,7 +506,7 @@ where } } -impl PartialEq for SequenceTreeInternal +impl PartialEq for SequenceTreeInternal where T: Clone + Debug + PartialEq, { @@ -514,13 +515,13 @@ where } } -impl<'a, T> IntoIterator for &'a SequenceTreeInternal +impl<'a, T> IntoIterator for &'a SequenceTreeInternal where T: Clone + Debug, { type Item = &'a T; - type IntoIter = Iter<'a, T, B>; + type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { Iter { @@ -530,12 +531,13 @@ where } } +#[derive(Debug)] pub struct Iter<'a, T> { - inner: &'a SequenceTreeInternal, + inner: &'a SequenceTreeInternal, index: usize, } -impl<'a, T> Iterator for Iter<'a, T, B> +impl<'a, T> Iterator for Iter<'a, T> where T: Clone + Debug, { diff --git a/rust/edit-trace/automerge-js.ts b/rust/edit-trace/automerge-js.ts new file mode 100644 index 00000000..2db30169 --- /dev/null +++ b/rust/edit-trace/automerge-js.ts @@ -0,0 +1,30 @@ +//import * as Automerge from "@automerge/automerge" +import * as Automerge from "@automerge/automerge" +import * as fs from "fs" + +const start = new Date() +let state = Automerge.from({text: new Automerge.Text()}) + +type WithInsert = [number, number, string] +type WithoutInsert = [number, number] +const edits: Array = JSON.parse(fs.readFileSync("./edits.json", {encoding: "utf8"})) + +state = Automerge.change(state, doc => { + const start2 = new Date() + for (let i = 0; i < edits.length; i++) { + if (i % 1000 === 0) { + const elapsed2 = (new Date() as any) - (start2 as any) + console.log(`processed 1000 edits in ${elapsed2}`) + } + let edit = edits[i] + let [start, del, values] = edit + doc.text.deleteAt!(start, del) + if (values != null) { + doc.text.insertAt!(start, ...values) + } + } +}) + +let elapsed = (new Date() as any) - (start as any) +console.log(`Done in ${elapsed} ms`) + diff --git a/rust/edit-trace/automerge-wasm.js b/rust/edit-trace/automerge-wasm.js index e0f1454d..6c7dfb4f 100644 --- a/rust/edit-trace/automerge-wasm.js +++ b/rust/edit-trace/automerge-wasm.js @@ -1,5 +1,5 @@ const { edits, finalText } = require('./editing-trace') -const Automerge = require('../automerge-wasm') +const Automerge = require('@automerge/automerge-wasm') const start = new Date() diff --git a/rust/edit-trace/package.json b/rust/edit-trace/package.json index a9d1e0e0..4579baae 100644 --- a/rust/edit-trace/package.json +++ b/rust/edit-trace/package.json @@ -4,9 +4,18 @@ "main": "wasm-text.js", "license": "MIT", "scripts": { + "build": "webpack", "wasm": "0x -D prof wasm-text.js" }, "devDependencies": { "0x": "^4.11.0" + }, + "dependencies": { + "@automerge/automerge": "^2.0.0-beta.4", + "ts-loader": "^9.4.1", + "typescript": "^4.8.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0", + "webpack-node-externals": "^3.0.0" } } diff --git a/rust/edit-trace/tsconfig.json b/rust/edit-trace/tsconfig.json new file mode 100644 index 00000000..6630521c --- /dev/null +++ b/rust/edit-trace/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es2016", + "sourceMap": false, + "declaration": true, + "resolveJsonModule": true, + "module": "commonjs", + "moduleResolution": "node", + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "outDir": "./dist" + }, + "include": [ "./automerge-js.ts" ], + "exclude": [ + "./dist/**/*", + "./node_modules" + ] +} diff --git a/rust/edit-trace/webpack.config.js b/rust/edit-trace/webpack.config.js new file mode 100644 index 00000000..1a76e6b1 --- /dev/null +++ b/rust/edit-trace/webpack.config.js @@ -0,0 +1,37 @@ +const path = require('path'); +const nodeExternals = require('webpack-node-externals'); + +// the most basic webpack config for node or web targets for automerge-wasm + +const serverConfig = { + // basic setup for bundling a node package + target: 'node', + externals: [nodeExternals()], + externalsPresets: { node: true }, + entry: './automerge-js.ts', + module: { rules: [ { use: 'ts-loader' } ] }, + output: { + filename: 'node.js', + path: path.resolve(__dirname, 'dist'), + }, + mode: "development", // or production +}; + +const clientConfig = { + experiments: { asyncWebAssembly: true }, + target: 'web', + entry: './automerge-js.js', + output: { + filename: 'main.js', + path: path.resolve(__dirname, 'public'), + }, + mode: "development", // or production + performance: { // we dont want the wasm blob to generate warnings + hints: false, + maxEntrypointSize: 512000, + maxAssetSize: 512000 + } +}; + +//module.exports = [serverConfig, clientConfig]; +module.exports = [serverConfig];