Compare commits
1 commit
main
...
big_observ
Author | SHA1 | Date | |
---|---|---|---|
|
bc9f0583be |
9 changed files with 130 additions and 26 deletions
|
@ -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<Item = &'a (Value<'a>, ObjId)>>(
|
||||
&self,
|
||||
o: Array,
|
||||
index: usize,
|
||||
num_del: usize,
|
||||
values: &[(Value<'_>, ObjId)],
|
||||
values: I,
|
||||
meta: &JsValue,
|
||||
) -> Result<Object, JsValue> {
|
||||
let args: Array = values
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|v| self.maybe_wrap_object(alloc(&v.0), &v.1, meta))
|
||||
.collect::<Result<_, _>>()?;
|
||||
args.unshift(&(num_del as u32).into());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,21 +4,22 @@ use std::{
|
|||
mem,
|
||||
};
|
||||
|
||||
pub type SequenceTree<T> = SequenceTreeInternal<T, 25>;
|
||||
pub(crate) const B: usize = 500;
|
||||
pub type SequenceTree<T> = SequenceTreeInternal<T>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SequenceTreeInternal<T> {
|
||||
root_node: Option<SequenceTreeNode<T, B>>,
|
||||
root_node: Option<SequenceTreeNode<T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct SequenceTreeNode<T> {
|
||||
elements: Vec<T>,
|
||||
children: Vec<SequenceTreeNode<T, B>>,
|
||||
children: Vec<SequenceTreeNode<T>>,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl<T> SequenceTreeInternal<T, B>
|
||||
impl<T> SequenceTreeInternal<T>
|
||||
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<T> SequenceTreeNode<T, B>
|
||||
impl<T> SequenceTreeNode<T>
|
||||
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<T, B>) {
|
||||
fn merge(&mut self, middle: T, successor_sibling: SequenceTreeNode<T>) {
|
||||
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<T> Default for SequenceTreeInternal<T, B>
|
||||
impl<T> Default for SequenceTreeInternal<T>
|
||||
where
|
||||
T: Clone + Debug,
|
||||
{
|
||||
|
@ -505,7 +506,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for SequenceTreeInternal<T, B>
|
||||
impl<T> PartialEq for SequenceTreeInternal<T>
|
||||
where
|
||||
T: Clone + Debug + PartialEq,
|
||||
{
|
||||
|
@ -514,13 +515,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a SequenceTreeInternal<T, B>
|
||||
impl<'a, T> IntoIterator for &'a SequenceTreeInternal<T>
|
||||
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<T, B>,
|
||||
inner: &'a SequenceTreeInternal<T>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T, B>
|
||||
impl<'a, T> Iterator for Iter<'a, T>
|
||||
where
|
||||
T: Clone + Debug,
|
||||
{
|
||||
|
|
30
rust/edit-trace/automerge-js.ts
Normal file
30
rust/edit-trace/automerge-js.ts
Normal file
|
@ -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<WithInsert | WithoutInsert> = 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`)
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
22
rust/edit-trace/tsconfig.json
Normal file
22
rust/edit-trace/tsconfig.json
Normal file
|
@ -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"
|
||||
]
|
||||
}
|
37
rust/edit-trace/webpack.config.js
Normal file
37
rust/edit-trace/webpack.config.js
Normal file
|
@ -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];
|
Loading…
Reference in a new issue