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)?;
|
Reflect::set(&result, &(*index as f64).into(), &sub_val)?;
|
||||||
Ok(result.into())
|
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::Insert { index, values, .. } => self.sub_splice(result, *index, 0, values, meta),
|
||||||
Patch::Increment { prop, value, .. } => {
|
Patch::Increment { prop, value, .. } => {
|
||||||
if let Prop::Seq(index) = prop {
|
if let Prop::Seq(index) = prop {
|
||||||
|
@ -650,16 +650,16 @@ impl Automerge {
|
||||||
self.wrap_object(result, datatype, &id, meta)
|
self.wrap_object(result, datatype, &id, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sub_splice(
|
fn sub_splice<'a, I: IntoIterator<Item = &'a (Value<'a>, ObjId)>>(
|
||||||
&self,
|
&self,
|
||||||
o: Array,
|
o: Array,
|
||||||
index: usize,
|
index: usize,
|
||||||
num_del: usize,
|
num_del: usize,
|
||||||
values: &[(Value<'_>, ObjId)],
|
values: I,
|
||||||
meta: &JsValue,
|
meta: &JsValue,
|
||||||
) -> Result<Object, JsValue> {
|
) -> Result<Object, JsValue> {
|
||||||
let args: Array = values
|
let args: Array = values
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|v| self.maybe_wrap_object(alloc(&v.0), &v.1, meta))
|
.map(|v| self.maybe_wrap_object(alloc(&v.0), &v.1, meta))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
args.unshift(&(num_del as u32).into());
|
args.unshift(&(num_del as u32).into());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use crate::interop::{alloc, js_set};
|
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 js_sys::{Array, Object};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) enum Patch {
|
||||||
obj: ObjId,
|
obj: ObjId,
|
||||||
path: Vec<(ObjId, Prop)>,
|
path: Vec<(ObjId, Prop)>,
|
||||||
index: usize,
|
index: usize,
|
||||||
values: Vec<(Value<'static>, ObjId)>,
|
values: SequenceTree<(Value<'static>, ObjId)>,
|
||||||
},
|
},
|
||||||
Increment {
|
Increment {
|
||||||
obj: ObjId,
|
obj: ObjId,
|
||||||
|
@ -91,11 +91,13 @@ impl OpObserver for Observer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let path = parents.path();
|
let path = parents.path();
|
||||||
|
let mut values = SequenceTree::new();
|
||||||
|
values.push(value);
|
||||||
let patch = Patch::Insert {
|
let patch = Patch::Insert {
|
||||||
path,
|
path,
|
||||||
obj,
|
obj,
|
||||||
index,
|
index,
|
||||||
values: vec![value],
|
values,
|
||||||
};
|
};
|
||||||
self.patches.push(patch);
|
self.patches.push(patch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ mod op_set;
|
||||||
mod op_tree;
|
mod op_tree;
|
||||||
mod parents;
|
mod parents;
|
||||||
mod query;
|
mod query;
|
||||||
|
mod sequence_tree;
|
||||||
mod storage;
|
mod storage;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
@ -105,6 +106,7 @@ pub use op_observer::OpObserver;
|
||||||
pub use op_observer::Patch;
|
pub use op_observer::Patch;
|
||||||
pub use op_observer::VecOpObserver;
|
pub use op_observer::VecOpObserver;
|
||||||
pub use parents::Parents;
|
pub use parents::Parents;
|
||||||
|
pub use sequence_tree::SequenceTree;
|
||||||
pub use types::{ActorId, ChangeHash, ObjType, OpType, Prop};
|
pub use types::{ActorId, ChangeHash, ObjType, OpType, Prop};
|
||||||
pub use value::{ScalarValue, Value};
|
pub use value::{ScalarValue, Value};
|
||||||
pub use values::Values;
|
pub use values::Values;
|
||||||
|
|
|
@ -4,21 +4,22 @@ use std::{
|
||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type SequenceTree<T> = SequenceTreeInternal<T, 25>;
|
pub(crate) const B: usize = 500;
|
||||||
|
pub type SequenceTree<T> = SequenceTreeInternal<T>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SequenceTreeInternal<T> {
|
pub struct SequenceTreeInternal<T> {
|
||||||
root_node: Option<SequenceTreeNode<T, B>>,
|
root_node: Option<SequenceTreeNode<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct SequenceTreeNode<T> {
|
struct SequenceTreeNode<T> {
|
||||||
elements: Vec<T>,
|
elements: Vec<T>,
|
||||||
children: Vec<SequenceTreeNode<T, B>>,
|
children: Vec<SequenceTreeNode<T>>,
|
||||||
length: usize,
|
length: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SequenceTreeInternal<T, B>
|
impl<T> SequenceTreeInternal<T>
|
||||||
where
|
where
|
||||||
T: Clone + Debug,
|
T: Clone + Debug,
|
||||||
{
|
{
|
||||||
|
@ -38,7 +39,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an iterator through the sequence.
|
/// Create an iterator through the sequence.
|
||||||
pub fn iter(&self) -> Iter<'_, T, B> {
|
pub fn iter(&self) -> Iter<'_, T> {
|
||||||
Iter {
|
Iter {
|
||||||
inner: self,
|
inner: self,
|
||||||
index: 0,
|
index: 0,
|
||||||
|
@ -145,7 +146,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SequenceTreeNode<T, B>
|
impl<T> SequenceTreeNode<T>
|
||||||
where
|
where
|
||||||
T: Clone + Debug,
|
T: Clone + Debug,
|
||||||
{
|
{
|
||||||
|
@ -157,7 +158,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub(crate) fn len(&self) -> usize {
|
||||||
self.length
|
self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +381,7 @@ where
|
||||||
l
|
l
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, index: usize) -> T {
|
pub(crate) fn remove(&mut self, index: usize) -> T {
|
||||||
let original_len = self.len();
|
let original_len = self.len();
|
||||||
if self.is_leaf() {
|
if self.is_leaf() {
|
||||||
let v = self.remove_from_leaf(index);
|
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.push(middle);
|
||||||
self.elements.extend(successor_sibling.elements);
|
self.elements.extend(successor_sibling.elements);
|
||||||
self.children.extend(successor_sibling.children);
|
self.children.extend(successor_sibling.children);
|
||||||
|
@ -431,7 +432,7 @@ where
|
||||||
assert!(self.is_full());
|
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() {
|
if self.is_leaf() {
|
||||||
let old_element = self.elements.get_mut(index).unwrap();
|
let old_element = self.elements.get_mut(index).unwrap();
|
||||||
mem::replace(old_element, element)
|
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() {
|
if self.is_leaf() {
|
||||||
return self.elements.get(index);
|
return self.elements.get(index);
|
||||||
} else {
|
} else {
|
||||||
|
@ -475,7 +476,7 @@ where
|
||||||
None
|
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() {
|
if self.is_leaf() {
|
||||||
return self.elements.get_mut(index);
|
return self.elements.get_mut(index);
|
||||||
} else {
|
} else {
|
||||||
|
@ -496,7 +497,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for SequenceTreeInternal<T, B>
|
impl<T> Default for SequenceTreeInternal<T>
|
||||||
where
|
where
|
||||||
T: Clone + Debug,
|
T: Clone + Debug,
|
||||||
{
|
{
|
||||||
|
@ -505,7 +506,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PartialEq for SequenceTreeInternal<T, B>
|
impl<T> PartialEq for SequenceTreeInternal<T>
|
||||||
where
|
where
|
||||||
T: Clone + Debug + PartialEq,
|
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
|
where
|
||||||
T: Clone + Debug,
|
T: Clone + Debug,
|
||||||
{
|
{
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
|
|
||||||
type IntoIter = Iter<'a, T, B>;
|
type IntoIter = Iter<'a, T>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Iter {
|
Iter {
|
||||||
|
@ -530,12 +531,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Iter<'a, T> {
|
pub struct Iter<'a, T> {
|
||||||
inner: &'a SequenceTreeInternal<T, B>,
|
inner: &'a SequenceTreeInternal<T>,
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Iterator for Iter<'a, T, B>
|
impl<'a, T> Iterator for Iter<'a, T>
|
||||||
where
|
where
|
||||||
T: Clone + Debug,
|
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 { edits, finalText } = require('./editing-trace')
|
||||||
const Automerge = require('../automerge-wasm')
|
const Automerge = require('@automerge/automerge-wasm')
|
||||||
|
|
||||||
const start = new Date()
|
const start = new Date()
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,18 @@
|
||||||
"main": "wasm-text.js",
|
"main": "wasm-text.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build": "webpack",
|
||||||
"wasm": "0x -D prof wasm-text.js"
|
"wasm": "0x -D prof wasm-text.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"0x": "^4.11.0"
|
"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