Implement experimental new encoder 🚀
This commit is contained in:
parent
e31e968f0d
commit
6c2cf0f769
12
.jsdoc.json
12
.jsdoc.json
@ -17,10 +17,13 @@
|
|||||||
"useCollapsibles": true,
|
"useCollapsibles": true,
|
||||||
"collapse": true,
|
"collapse": true,
|
||||||
"resources": {
|
"resources": {
|
||||||
"yjs.dev": "Yjs website"
|
"yjs.dev": "Website",
|
||||||
|
"docs.yjs.dev": "Docs",
|
||||||
|
"discuss.yjs.dev": "Forum",
|
||||||
|
"https://gitter.im/Yjs/community": "Chat"
|
||||||
},
|
},
|
||||||
"logo": {
|
"logo": {
|
||||||
"url": "https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png",
|
"url": "https://yjs.dev/images/logo/yjs-512x512.png",
|
||||||
"width": "162px",
|
"width": "162px",
|
||||||
"height": "162px",
|
"height": "162px",
|
||||||
"link": "/"
|
"link": "/"
|
||||||
@ -35,7 +38,7 @@
|
|||||||
],
|
],
|
||||||
"default": {
|
"default": {
|
||||||
"staticFiles": {
|
"staticFiles": {
|
||||||
"include": ["examples/"]
|
"include": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -44,7 +47,6 @@
|
|||||||
"encoding": "utf8",
|
"encoding": "utf8",
|
||||||
"private": false,
|
"private": false,
|
||||||
"recurse": true,
|
"recurse": true,
|
||||||
"template": "./node_modules/tui-jsdoc-template",
|
"template": "./node_modules/tui-jsdoc-template"
|
||||||
"tutorials": "./examples"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,5 +958,6 @@ Yjs and all related projects are [**MIT licensed**](./LICENSE).
|
|||||||
Yjs is based on my research as a student at the [RWTH
|
Yjs is based on my research as a student at the [RWTH
|
||||||
i5](http://dbis.rwth-aachen.de/). Now I am working on Yjs in my spare time.
|
i5](http://dbis.rwth-aachen.de/). Now I am working on Yjs in my spare time.
|
||||||
|
|
||||||
Fund this project by donating on [Patreon](https://www.patreon.com/dmonad) or
|
Fund this project by donating on [GitHub Sponsors](https://github.com/sponsors/dmonad)
|
||||||
hiring [me](https://github.com/dmonad) for professional support.
|
or hiring [me](https://github.com/dmonad) as a contractor for your collaborative
|
||||||
|
app.
|
||||||
|
40
package-lock.json
generated
40
package-lock.json
generated
@ -1429,9 +1429,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"isomorphic.js": {
|
"isomorphic.js": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.1.4.tgz",
|
||||||
"integrity": "sha512-pabBRLDwYefSsNS+qCazJ97o7P5xDTrNoxSYFTM09JlZTxPrOEPGKekwqUy3/Np4C4PHnVUXHYsZPOix0jELsA=="
|
"integrity": "sha512-t9zbgkjE7f9f2M6OSW49YEq0lUrSdAllBbWFUZoeck/rnnFae6UlhmDtXWs48VJY3ZpryCoZsRiAiKD44hPIGQ=="
|
||||||
},
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -1548,9 +1548,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib0": {
|
"lib0": {
|
||||||
"version": "0.2.29",
|
"version": "0.2.32",
|
||||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.29.tgz",
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.32.tgz",
|
||||||
"integrity": "sha512-bcQqmh3bUDVXwZrAJnekTpNk0uaShRg1bHMK7uzBIDFAWWdMXXCUtQXO/d/XsIxCiskgHTqF4jiQmDdoPCMVIw==",
|
"integrity": "sha512-cHHKhHTojtvFSsthTk+CKuD17jMHIxuZxYpTzXj9TeQLPNoGNDPl6ax+J6eFETVe3ZvPMh3V0nGfJgGo6QgSvA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"isomorphic.js": "^0.1.3"
|
"isomorphic.js": "^0.1.3"
|
||||||
}
|
}
|
||||||
@ -1716,18 +1716,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"markdownlint": {
|
"markdownlint": {
|
||||||
"version": "0.20.3",
|
"version": "0.20.4",
|
||||||
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.20.4.tgz",
|
||||||
"integrity": "sha512-J93s59tGvSFvAPWVUtEgxqPI0CHayTx1Z8poj1/4UJAquHGPIruWRMurkRldiNbgBiaQ4OOt15rHZbFfU6u05A==",
|
"integrity": "sha512-jpfaPgjT0OpeBbemjYNZbzGG3hCLcAIvrm/pEY3+q/szDScG6ZonDacqySVRJAv9glbo8y4wBPJ0wgW17+9GGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"markdown-it": "10.0.0"
|
"markdown-it": "10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"markdownlint-cli": {
|
"markdownlint-cli": {
|
||||||
"version": "0.23.1",
|
"version": "0.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.23.2.tgz",
|
||||||
"integrity": "sha512-UARWuPILksAcVLTosUv1F1tLognNYQ/qjLRIgWwQAYqdl3QQrTPurU/X9Z2jrdAJYlOim868QsufxjYJpH0K7Q==",
|
"integrity": "sha512-OSl5OZ8xzGN6z355cqRkiq67zPi3reJimklaF72p0554q85Dng5ToOjjSB9tDKZebSt85jX8cp+ruoQlPqOsPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"commander": "~2.9.0",
|
"commander": "~2.9.0",
|
||||||
@ -1739,8 +1739,8 @@
|
|||||||
"jsonc-parser": "~2.2.0",
|
"jsonc-parser": "~2.2.0",
|
||||||
"lodash.differencewith": "~4.5.0",
|
"lodash.differencewith": "~4.5.0",
|
||||||
"lodash.flatten": "~4.4.0",
|
"lodash.flatten": "~4.4.0",
|
||||||
"markdownlint": "~0.20.3",
|
"markdownlint": "~0.20.4",
|
||||||
"markdownlint-rule-helpers": "~0.10.0",
|
"markdownlint-rule-helpers": "~0.11.0",
|
||||||
"minimatch": "~3.0.4",
|
"minimatch": "~3.0.4",
|
||||||
"minimist": "~1.2.5",
|
"minimist": "~1.2.5",
|
||||||
"rc": "~1.2.7"
|
"rc": "~1.2.7"
|
||||||
@ -1770,9 +1770,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"markdownlint-rule-helpers": {
|
"markdownlint-rule-helpers": {
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.11.0.tgz",
|
||||||
"integrity": "sha512-0e8VUTjNdQwS7hTyNan9oOLsy4a7KEsXo3fxKMDRFRk6Jn+pLB3iKZ3mj/m6ECrlOUCxPYYmgOmmyk3bSdbIvw==",
|
"integrity": "sha512-PhGii9dOiDJDXxiRMpK8N0FM9powprvRPsXALgkjlSPTwLh6ymH+iF3iUe3nq8KGu26tclFBlLL5xAGy/zb7FA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"marked": {
|
"marked": {
|
||||||
@ -2786,9 +2786,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.9.3",
|
"version": "3.9.6",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz",
|
||||||
"integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
|
"integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uc.micro": {
|
"uc.micro": {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"lint": "markdownlint README.md && standard && tsc",
|
"lint": "markdownlint README.md && standard && tsc",
|
||||||
"docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.md --package ./package.json || true",
|
"docs": "rm -rf docs; jsdoc --configure ./.jsdoc.json --verbose --readme ./README.md --package ./package.json || true",
|
||||||
"serve-docs": "npm run docs && http-server ./docs/",
|
"serve-docs": "npm run docs && http-server ./docs/",
|
||||||
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.cjs --repitition-time 1000",
|
"preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && node ./dist/tests.cjs --repitition-time 1000 && test -e dist/src/index.d.ts && test -e dist/yjs.cjs && test -e dist/yjs.cjs",
|
||||||
"debug": "concurrently 'http-server -o test.html' 'npm run watch'",
|
"debug": "concurrently 'http-server -o test.html' 'npm run watch'",
|
||||||
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.cjs",
|
"trace-deopt": "clear && rollup -c && node --trace-deopt dist/test.cjs",
|
||||||
"trace-opt": "clear && rollup -c && node --trace-opt dist/test.cjs"
|
"trace-opt": "clear && rollup -c && node --trace-opt dist/test.cjs"
|
||||||
@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://yjs.dev",
|
"homepage": "https://yjs.dev",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.29"
|
"lib0": "^0.2.32"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^11.1.0",
|
"@rollup/plugin-commonjs": "^11.1.0",
|
||||||
@ -68,12 +68,12 @@
|
|||||||
"concurrently": "^3.6.1",
|
"concurrently": "^3.6.1",
|
||||||
"http-server": "^0.12.3",
|
"http-server": "^0.12.3",
|
||||||
"jsdoc": "^3.6.4",
|
"jsdoc": "^3.6.4",
|
||||||
"markdownlint-cli": "^0.23.1",
|
"markdownlint-cli": "^0.23.2",
|
||||||
"rollup": "^1.32.1",
|
"rollup": "^1.32.1",
|
||||||
"rollup-cli": "^1.0.9",
|
"rollup-cli": "^1.0.9",
|
||||||
"standard": "^14.3.4",
|
"standard": "^14.3.4",
|
||||||
"tui-jsdoc-template": "^1.2.2",
|
"tui-jsdoc-template": "^1.2.2",
|
||||||
"typescript": "^3.9.3",
|
"typescript": "^3.9.6",
|
||||||
"y-protocols": "^0.2.3"
|
"y-protocols": "^0.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,12 +48,18 @@ export {
|
|||||||
typeMapGetSnapshot,
|
typeMapGetSnapshot,
|
||||||
iterateDeletedStructs,
|
iterateDeletedStructs,
|
||||||
applyUpdate,
|
applyUpdate,
|
||||||
|
applyUpdateV2,
|
||||||
readUpdate,
|
readUpdate,
|
||||||
|
readUpdateV2,
|
||||||
encodeStateAsUpdate,
|
encodeStateAsUpdate,
|
||||||
|
encodeStateAsUpdateV2,
|
||||||
encodeStateVector,
|
encodeStateVector,
|
||||||
|
encodeStateVectorV2,
|
||||||
UndoManager,
|
UndoManager,
|
||||||
decodeSnapshot,
|
decodeSnapshot,
|
||||||
encodeSnapshot,
|
encodeSnapshot,
|
||||||
|
decodeSnapshotV2,
|
||||||
|
encodeSnapshotV2,
|
||||||
isDeleted,
|
isDeleted,
|
||||||
isParentOf,
|
isParentOf,
|
||||||
equalSnapshots,
|
equalSnapshots,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
export * from './utils/DeleteSet.js'
|
export * from './utils/DeleteSet.js'
|
||||||
export * from './utils/Doc.js'
|
export * from './utils/Doc.js'
|
||||||
|
export * from './utils/UpdateDecoder.js'
|
||||||
|
export * from './utils/UpdateEncoder.js'
|
||||||
export * from './utils/encoding.js'
|
export * from './utils/encoding.js'
|
||||||
export * from './utils/EventHandler.js'
|
export * from './utils/EventHandler.js'
|
||||||
export * from './utils/ID.js'
|
export * from './utils/ID.js'
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
StructStore, ID, Transaction // eslint-disable-line
|
AbstractUpdateEncoder, ID, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
export class AbstractStruct {
|
export class AbstractStruct {
|
||||||
@ -35,7 +34,7 @@ export class AbstractStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {AbstractUpdateEncoder} encoder The encoder to write data to.
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
* @param {number} encodingRef
|
* @param {number} encodingRef
|
||||||
*/
|
*/
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Transaction, Item, StructStore // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Transaction, Item, StructStore // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
export class ContentAny {
|
export class ContentAny {
|
||||||
/**
|
/**
|
||||||
* @param {Array<any>} arr
|
* @param {Array<any>} arr
|
||||||
@ -77,15 +74,15 @@ export class ContentAny {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
const len = this.arr.length
|
const len = this.arr.length
|
||||||
encoding.writeVarUint(encoder, len - offset)
|
encoder.writeLen(len - offset)
|
||||||
for (let i = offset; i < len; i++) {
|
for (let i = offset; i < len; i++) {
|
||||||
const c = this.arr[i]
|
const c = this.arr[i]
|
||||||
encoding.writeAny(encoder, c)
|
encoder.writeAny(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,14 +95,14 @@ export class ContentAny {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentAny}
|
* @return {ContentAny}
|
||||||
*/
|
*/
|
||||||
export const readContentAny = decoder => {
|
export const readContentAny = decoder => {
|
||||||
const len = decoding.readVarUint(decoder)
|
const len = decoder.readLen()
|
||||||
const cs = []
|
const cs = []
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
cs.push(decoding.readAny(decoder))
|
cs.push(decoder.readAny())
|
||||||
}
|
}
|
||||||
return new ContentAny(cs)
|
return new ContentAny(cs)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
StructStore, Item, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, StructStore, Item, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as buffer from 'lib0/buffer.js'
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
export class ContentBinary {
|
export class ContentBinary {
|
||||||
@ -73,11 +70,11 @@ export class ContentBinary {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeVarUint8Array(encoder, this.content)
|
encoder.writeBuf(this.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +86,7 @@ export class ContentBinary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentBinary}
|
* @return {ContentBinary}
|
||||||
*/
|
*/
|
||||||
export const readContentBinary = decoder => new ContentBinary(buffer.copyUint8Array(decoding.readVarUint8Array(decoder)))
|
export const readContentBinary = decoder => new ContentBinary(decoder.readBuf())
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
addToDeleteSet,
|
addToDeleteSet,
|
||||||
StructStore, Item, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, StructStore, Item, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
export class ContentDeleted {
|
export class ContentDeleted {
|
||||||
/**
|
/**
|
||||||
* @param {number} len
|
* @param {number} len
|
||||||
@ -67,7 +64,7 @@ export class ContentDeleted {
|
|||||||
* @param {Item} item
|
* @param {Item} item
|
||||||
*/
|
*/
|
||||||
integrate (transaction, item) {
|
integrate (transaction, item) {
|
||||||
addToDeleteSet(transaction.deleteSet, item.id, this.len)
|
addToDeleteSet(transaction.deleteSet, item.id.client, item.id.clock, this.len)
|
||||||
item.markDeleted()
|
item.markDeleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,11 +77,11 @@ export class ContentDeleted {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeVarUint(encoder, this.len - offset)
|
encoder.writeLen(this.len - offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +95,7 @@ export class ContentDeleted {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentDeleted}
|
* @return {ContentDeleted}
|
||||||
*/
|
*/
|
||||||
export const readContentDeleted = decoder => new ContentDeleted(decoding.readVarUint(decoder))
|
export const readContentDeleted = decoder => new ContentDeleted(decoder.readLen())
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
StructStore, Item, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, StructStore, Item, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,11 +74,11 @@ export class ContentEmbed {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeVarString(encoder, JSON.stringify(this.embed))
|
encoder.writeJSON(this.embed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,7 +92,7 @@ export class ContentEmbed {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentEmbed}
|
* @return {ContentEmbed}
|
||||||
*/
|
*/
|
||||||
export const readContentEmbed = decoder => new ContentEmbed(JSON.parse(decoding.readVarString(decoder)))
|
export const readContentEmbed = decoder => new ContentEmbed(decoder.readJSON())
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Item, StructStore, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Item, StructStore, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,12 +76,12 @@ export class ContentFormat {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeVarString(encoder, this.key)
|
encoder.writeKey(this.key)
|
||||||
encoding.writeVarString(encoder, JSON.stringify(this.value))
|
encoder.writeJSON(this.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +93,7 @@ export class ContentFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentFormat}
|
* @return {ContentFormat}
|
||||||
*/
|
*/
|
||||||
export const readContentFormat = decoder => new ContentFormat(decoding.readVarString(decoder), JSON.parse(decoding.readVarString(decoder)))
|
export const readContentFormat = decoder => new ContentFormat(decoder.readString(), decoder.readJSON())
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Transaction, Item, StructStore // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Transaction, Item, StructStore // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -80,15 +77,15 @@ export class ContentJSON {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
const len = this.arr.length
|
const len = this.arr.length
|
||||||
encoding.writeVarUint(encoder, len - offset)
|
encoder.writeLen(len - offset)
|
||||||
for (let i = offset; i < len; i++) {
|
for (let i = offset; i < len; i++) {
|
||||||
const c = this.arr[i]
|
const c = this.arr[i]
|
||||||
encoding.writeVarString(encoder, c === undefined ? 'undefined' : JSON.stringify(c))
|
encoder.writeString(c === undefined ? 'undefined' : JSON.stringify(c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,14 +100,14 @@ export class ContentJSON {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentJSON}
|
* @return {ContentJSON}
|
||||||
*/
|
*/
|
||||||
export const readContentJSON = decoder => {
|
export const readContentJSON = decoder => {
|
||||||
const len = decoding.readVarUint(decoder)
|
const len = decoder.readLen()
|
||||||
const cs = []
|
const cs = []
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const c = decoding.readVarString(decoder)
|
const c = decoder.readString()
|
||||||
if (c === 'undefined') {
|
if (c === 'undefined') {
|
||||||
cs.push(undefined)
|
cs.push(undefined)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Transaction, Item, StructStore // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Transaction, Item, StructStore // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -80,11 +77,11 @@ export class ContentString {
|
|||||||
*/
|
*/
|
||||||
gc (store) {}
|
gc (store) {}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeVarString(encoder, offset === 0 ? this.str : this.str.slice(offset))
|
encoder.writeString(offset === 0 ? this.str : this.str.slice(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +95,7 @@ export class ContentString {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentString}
|
* @return {ContentString}
|
||||||
*/
|
*/
|
||||||
export const readContentString = decoder => new ContentString(decoding.readVarString(decoder))
|
export const readContentString = decoder => new ContentString(decoder.readString())
|
||||||
|
@ -7,15 +7,13 @@ import {
|
|||||||
readYXmlFragment,
|
readYXmlFragment,
|
||||||
readYXmlHook,
|
readYXmlHook,
|
||||||
readYXmlText,
|
readYXmlText,
|
||||||
ID, StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<function(decoding.Decoder):AbstractType<any>>}
|
* @type {Array<function(AbstractUpdateDecoder):AbstractType<any>>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export const typeRefs = [
|
export const typeRefs = [
|
||||||
@ -150,7 +148,7 @@ export class ContentType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
@ -168,7 +166,7 @@ export class ContentType {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {ContentType}
|
* @return {ContentType}
|
||||||
*/
|
*/
|
||||||
export const readContentType = decoder => new ContentType(typeRefs[decoding.readVarUint(decoder)](decoder))
|
export const readContentType = decoder => new ContentType(typeRefs[decoder.readTypeRef()](decoder))
|
||||||
|
@ -2,11 +2,9 @@
|
|||||||
import {
|
import {
|
||||||
AbstractStruct,
|
AbstractStruct,
|
||||||
addStruct,
|
addStruct,
|
||||||
StructStore, Transaction, ID // eslint-disable-line
|
AbstractUpdateEncoder, StructStore, Transaction, ID // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
|
|
||||||
export const structGCRefNumber = 0
|
export const structGCRefNumber = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,12 +39,12 @@ export class GC extends AbstractStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
encoding.writeUint8(encoder, structGCRefNumber)
|
encoder.writeInfo(structGCRefNumber)
|
||||||
encoding.writeVarUint(encoder, this.length - offset)
|
encoder.writeLen(this.length - offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
readID,
|
|
||||||
writeID,
|
|
||||||
GC,
|
GC,
|
||||||
getState,
|
getState,
|
||||||
AbstractStruct,
|
AbstractStruct,
|
||||||
@ -23,12 +21,10 @@ import {
|
|||||||
readContentFormat,
|
readContentFormat,
|
||||||
readContentType,
|
readContentType,
|
||||||
addChangedTypeToTransaction,
|
addChangedTypeToTransaction,
|
||||||
Doc, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
import * as maplib from 'lib0/map.js'
|
import * as maplib from 'lib0/map.js'
|
||||||
import * as set from 'lib0/set.js'
|
import * as set from 'lib0/set.js'
|
||||||
import * as binary from 'lib0/binary.js'
|
import * as binary from 'lib0/binary.js'
|
||||||
@ -574,7 +570,7 @@ export class Item extends AbstractStruct {
|
|||||||
parent._length -= this.length
|
parent._length -= this.length
|
||||||
}
|
}
|
||||||
this.markDeleted()
|
this.markDeleted()
|
||||||
addToDeleteSet(transaction.deleteSet, this.id, this.length)
|
addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
|
||||||
maplib.setIfUndefined(transaction.changed, parent, set.create).add(this.parentSub)
|
maplib.setIfUndefined(transaction.changed, parent, set.create).add(this.parentSub)
|
||||||
this.content.delete(transaction)
|
this.content.delete(transaction)
|
||||||
}
|
}
|
||||||
@ -602,7 +598,7 @@ export class Item extends AbstractStruct {
|
|||||||
*
|
*
|
||||||
* This is called when this Item is sent to a remote peer.
|
* This is called when this Item is sent to a remote peer.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {AbstractUpdateEncoder} encoder The encoder to write data to.
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
@ -613,12 +609,12 @@ export class Item extends AbstractStruct {
|
|||||||
(origin === null ? 0 : binary.BIT8) | // origin is defined
|
(origin === null ? 0 : binary.BIT8) | // origin is defined
|
||||||
(rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
|
(rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
|
||||||
(parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
|
(parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
|
||||||
encoding.writeUint8(encoder, info)
|
encoder.writeInfo(info)
|
||||||
if (origin !== null) {
|
if (origin !== null) {
|
||||||
writeID(encoder, origin)
|
encoder.writeLeftID(origin)
|
||||||
}
|
}
|
||||||
if (rightOrigin !== null) {
|
if (rightOrigin !== null) {
|
||||||
writeID(encoder, rightOrigin)
|
encoder.writeRightID(rightOrigin)
|
||||||
}
|
}
|
||||||
if (origin === null && rightOrigin === null) {
|
if (origin === null && rightOrigin === null) {
|
||||||
const parent = /** @type {AbstractType<any>} */ (this.parent)
|
const parent = /** @type {AbstractType<any>} */ (this.parent)
|
||||||
@ -627,14 +623,14 @@ export class Item extends AbstractStruct {
|
|||||||
// parent type on y._map
|
// parent type on y._map
|
||||||
// find the correct key
|
// find the correct key
|
||||||
const ykey = findRootTypeKey(parent)
|
const ykey = findRootTypeKey(parent)
|
||||||
encoding.writeVarUint(encoder, 1) // write parentYKey
|
encoder.writeParentInfo(true) // write parentYKey
|
||||||
encoding.writeVarString(encoder, ykey)
|
encoder.writeString(ykey)
|
||||||
} else {
|
} else {
|
||||||
encoding.writeVarUint(encoder, 0) // write parent id
|
encoder.writeParentInfo(false) // write parent id
|
||||||
writeID(encoder, parentItem.id)
|
encoder.writeLeftID(parentItem.id)
|
||||||
}
|
}
|
||||||
if (parentSub !== null) {
|
if (parentSub !== null) {
|
||||||
encoding.writeVarString(encoder, parentSub)
|
encoder.writeString(parentSub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.content.write(encoder, offset)
|
this.content.write(encoder, offset)
|
||||||
@ -642,15 +638,15 @@ export class Item extends AbstractStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @param {number} info
|
* @param {number} info
|
||||||
*/
|
*/
|
||||||
const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
|
export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lookup map for reading Item content.
|
* A lookup map for reading Item content.
|
||||||
*
|
*
|
||||||
* @type {Array<function(decoding.Decoder):AbstractContent>}
|
* @type {Array<function(AbstractUpdateDecoder):AbstractContent>}
|
||||||
*/
|
*/
|
||||||
export const contentRefs = [
|
export const contentRefs = [
|
||||||
() => { throw error.unexpectedCase() }, // GC is not ItemContent
|
() => { throw error.unexpectedCase() }, // GC is not ItemContent
|
||||||
@ -741,7 +737,7 @@ export class AbstractContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
write (encoder, offset) {
|
write (encoder, offset) {
|
||||||
@ -755,38 +751,3 @@ export class AbstractContent {
|
|||||||
throw error.methodUnimplemented()
|
throw error.methodUnimplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {decoding.Decoder} decoder
|
|
||||||
* @param {ID} id
|
|
||||||
* @param {number} info
|
|
||||||
* @param {Doc} doc
|
|
||||||
*/
|
|
||||||
export const readItem = (decoder, id, info, doc) => {
|
|
||||||
/**
|
|
||||||
* The item that was originally to the left of this item.
|
|
||||||
* @type {ID | null}
|
|
||||||
*/
|
|
||||||
const origin = (info & binary.BIT8) === binary.BIT8 ? readID(decoder) : null
|
|
||||||
/**
|
|
||||||
* The item that was originally to the right of this item.
|
|
||||||
* @type {ID | null}
|
|
||||||
*/
|
|
||||||
const rightOrigin = (info & binary.BIT7) === binary.BIT7 ? readID(decoder) : null
|
|
||||||
const canCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
|
|
||||||
const hasParentYKey = canCopyParentInfo ? decoding.readVarUint(decoder) === 1 : false
|
|
||||||
/**
|
|
||||||
* If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
|
|
||||||
* and we read the next string as parentYKey.
|
|
||||||
* It indicates how we store/retrieve parent from `y.share`
|
|
||||||
* @type {string|null}
|
|
||||||
*/
|
|
||||||
const parentYKey = canCopyParentInfo && hasParentYKey ? decoding.readVarString(decoder) : null
|
|
||||||
|
|
||||||
return new Item(
|
|
||||||
id, null, origin, null, rightOrigin,
|
|
||||||
canCopyParentInfo && !hasParentYKey ? readID(decoder) : (parentYKey ? doc.get(parentYKey) : null), // parent
|
|
||||||
canCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoding.readVarString(decoder) : null, // parentSub
|
|
||||||
/** @type {AbstractContent} */ (readItemContent(decoder, info)) // item content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -11,13 +11,12 @@ import {
|
|||||||
ContentAny,
|
ContentAny,
|
||||||
ContentBinary,
|
ContentBinary,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
ID, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
AbstractUpdateEncoder, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as map from 'lib0/map.js'
|
import * as map from 'lib0/map.js'
|
||||||
import * as iterator from 'lib0/iterator.js'
|
import * as iterator from 'lib0/iterator.js'
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accumulate all (list) children of a type and return them as an Array.
|
* Accumulate all (list) children of a type and return them as an Array.
|
||||||
@ -116,7 +115,7 @@ export class AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
*/
|
*/
|
||||||
_write (encoder) { }
|
_write (encoder) { }
|
||||||
|
|
||||||
|
@ -15,12 +15,9 @@ import {
|
|||||||
YArrayRefID,
|
YArrayRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, Transaction, Item // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event that describes the changes on a YArray
|
* Event that describes the changes on a YArray
|
||||||
* @template T
|
* @template T
|
||||||
@ -204,15 +201,15 @@ export class YArray extends AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YArrayRefID)
|
encoder.writeTypeRef(YArrayRefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
|
@ -14,11 +14,9 @@ import {
|
|||||||
YMapRefID,
|
YMapRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, Transaction, Item // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
|
||||||
import * as iterator from 'lib0/iterator.js'
|
import * as iterator from 'lib0/iterator.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,15 +227,15 @@ export class YMap extends AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YMapRefID)
|
encoder.writeTypeRef(YMapRefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
|
@ -20,11 +20,9 @@ import {
|
|||||||
splitSnapshotAffectedStructs,
|
splitSnapshotAffectedStructs,
|
||||||
iterateDeletedStructs,
|
iterateDeletedStructs,
|
||||||
iterateStructs,
|
iterateStructs,
|
||||||
ID, Doc, Item, Snapshot, Transaction // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as object from 'lib0/object.js'
|
import * as object from 'lib0/object.js'
|
||||||
import * as map from 'lib0/map.js'
|
import * as map from 'lib0/map.js'
|
||||||
|
|
||||||
@ -1096,15 +1094,15 @@ export class YText extends AbstractType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YTextRefID)
|
encoder.writeTypeRef(YTextRefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {YText}
|
* @return {YText}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
@ -8,12 +8,9 @@ import {
|
|||||||
typeMapGetAll,
|
typeMapGetAll,
|
||||||
typeListForEach,
|
typeListForEach,
|
||||||
YXmlElementRefID,
|
YXmlElementRefID,
|
||||||
Snapshot, Doc, Item // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Snapshot, Doc, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An YXmlElement imitates the behavior of a
|
* An YXmlElement imitates the behavior of a
|
||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}.
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}.
|
||||||
@ -181,18 +178,18 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
*
|
*
|
||||||
* This is called when this Item is sent to a remote peer.
|
* This is called when this Item is sent to a remote peer.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {AbstractUpdateEncoder} encoder The encoder to write data to.
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YXmlElementRefID)
|
encoder.writeTypeRef(YXmlElementRefID)
|
||||||
encoding.writeVarString(encoder, this.nodeName)
|
encoder.writeKey(this.nodeName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {YXmlElement}
|
* @return {YXmlElement}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readYXmlElement = decoder => new YXmlElement(decoding.readVarString(decoder))
|
export const readYXmlElement = decoder => new YXmlElement(decoder.readKey())
|
||||||
|
@ -14,12 +14,9 @@ import {
|
|||||||
YXmlFragmentRefID,
|
YXmlFragmentRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the elements to which a set of CSS queries apply.
|
* Define the elements to which a set of CSS queries apply.
|
||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
|
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
|
||||||
@ -325,15 +322,15 @@ export class YXmlFragment extends AbstractType {
|
|||||||
*
|
*
|
||||||
* This is called when this Item is sent to a remote peer.
|
* This is called when this Item is sent to a remote peer.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {AbstractUpdateEncoder} encoder The encoder to write data to.
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YXmlFragmentRefID)
|
encoder.writeTypeRef(YXmlFragmentRefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {YXmlFragment}
|
* @return {YXmlFragment}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YMap,
|
YMap,
|
||||||
YXmlHookRefID
|
YXmlHookRefID,
|
||||||
|
AbstractUpdateDecoder, AbstractUpdateEncoder // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You can manage binding to a custom type with YXmlHook.
|
* You can manage binding to a custom type with YXmlHook.
|
||||||
@ -66,21 +65,20 @@ export class YXmlHook extends YMap {
|
|||||||
*
|
*
|
||||||
* This is called when this Item is sent to a remote peer.
|
* This is called when this Item is sent to a remote peer.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
* @param {AbstractUpdateEncoder} encoder The encoder to write data to.
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
super._write(encoder)
|
encoder.writeTypeRef(YXmlHookRefID)
|
||||||
encoding.writeVarUint(encoder, YXmlHookRefID)
|
encoder.writeKey(this.hookName)
|
||||||
encoding.writeVarString(encoder, this.hookName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {YXmlHook}
|
* @return {YXmlHook}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readYXmlHook = decoder =>
|
export const readYXmlHook = decoder =>
|
||||||
new YXmlHook(decoding.readVarString(decoder))
|
new YXmlHook(decoder.readKey())
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
import { YText, YXmlTextRefID } from '../internals.js'
|
import {
|
||||||
|
YText,
|
||||||
import * as encoding from 'lib0/encoding.js'
|
YXmlTextRefID,
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
AbstractUpdateDecoder, AbstractUpdateEncoder // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents text in a Dom Element. In the future this type will also handle
|
* Represents text in a Dom Element. In the future this type will also handle
|
||||||
@ -78,15 +79,15 @@ export class YXmlText extends YText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
*/
|
*/
|
||||||
_write (encoder) {
|
_write (encoder) {
|
||||||
encoding.writeVarUint(encoder, YXmlTextRefID)
|
encoder.writeTypeRef(YXmlTextRefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractUpdateDecoder} decoder
|
||||||
* @return {YXmlText}
|
* @return {YXmlText}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
@ -3,9 +3,8 @@ import {
|
|||||||
findIndexSS,
|
findIndexSS,
|
||||||
getState,
|
getState,
|
||||||
splitItem,
|
splitItem,
|
||||||
createID,
|
|
||||||
iterateStructs,
|
iterateStructs,
|
||||||
Item, AbstractStruct, GC, StructStore, Transaction, ID // eslint-disable-line
|
AbstractUpdateDecoder, AbstractDSDecoder, AbstractDSEncoder, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as array from 'lib0/array.js'
|
import * as array from 'lib0/array.js'
|
||||||
@ -163,14 +162,15 @@ export const mergeDeleteSets = dss => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DeleteSet} ds
|
* @param {DeleteSet} ds
|
||||||
* @param {ID} id
|
* @param {number} client
|
||||||
|
* @param {number} clock
|
||||||
* @param {number} length
|
* @param {number} length
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const addToDeleteSet = (ds, id, length) => {
|
export const addToDeleteSet = (ds, client, clock, length) => {
|
||||||
map.setIfUndefined(ds.clients, id.client, () => []).push(new DeleteItem(id.clock, length))
|
map.setIfUndefined(ds.clients, client, () => []).push(new DeleteItem(clock, length))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createDeleteSet = () => new DeleteSet()
|
export const createDeleteSet = () => new DeleteSet()
|
||||||
@ -210,28 +210,29 @@ export const createDeleteSetFromStructStore = ss => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractDSEncoder} encoder
|
||||||
* @param {DeleteSet} ds
|
* @param {DeleteSet} ds
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const writeDeleteSet = (encoder, ds) => {
|
export const writeDeleteSet = (encoder, ds) => {
|
||||||
encoding.writeVarUint(encoder, ds.clients.size)
|
encoding.writeVarUint(encoder.restEncoder, ds.clients.size)
|
||||||
ds.clients.forEach((dsitems, client) => {
|
ds.clients.forEach((dsitems, client) => {
|
||||||
encoding.writeVarUint(encoder, client)
|
encoder.resetDsCurVal()
|
||||||
|
encoding.writeVarUint(encoder.restEncoder, client)
|
||||||
const len = dsitems.length
|
const len = dsitems.length
|
||||||
encoding.writeVarUint(encoder, len)
|
encoding.writeVarUint(encoder.restEncoder, len)
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const item = dsitems[i]
|
const item = dsitems[i]
|
||||||
encoding.writeVarUint(encoder, item.clock)
|
encoder.writeDsClock(item.clock)
|
||||||
encoding.writeVarUint(encoder, item.len)
|
encoder.writeDsLen(item.len)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractDSDecoder} decoder
|
||||||
* @return {DeleteSet}
|
* @return {DeleteSet}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@ -239,19 +240,27 @@ export const writeDeleteSet = (encoder, ds) => {
|
|||||||
*/
|
*/
|
||||||
export const readDeleteSet = decoder => {
|
export const readDeleteSet = decoder => {
|
||||||
const ds = new DeleteSet()
|
const ds = new DeleteSet()
|
||||||
const numClients = decoding.readVarUint(decoder)
|
const numClients = decoding.readVarUint(decoder.restDecoder)
|
||||||
for (let i = 0; i < numClients; i++) {
|
for (let i = 0; i < numClients; i++) {
|
||||||
const client = decoding.readVarUint(decoder)
|
decoder.resetDsCurVal()
|
||||||
const numberOfDeletes = decoding.readVarUint(decoder)
|
const client = decoding.readVarUint(decoder.restDecoder)
|
||||||
for (let i = 0; i < numberOfDeletes; i++) {
|
const numberOfDeletes = decoding.readVarUint(decoder.restDecoder)
|
||||||
addToDeleteSet(ds, createID(client, decoding.readVarUint(decoder)), decoding.readVarUint(decoder))
|
if (numberOfDeletes > 0) {
|
||||||
|
const dsField = map.setIfUndefined(ds.clients, client, () => [])
|
||||||
|
for (let i = 0; i < numberOfDeletes; i++) {
|
||||||
|
dsField.push(new DeleteItem(decoder.readDsClock(), decoder.readDsLen()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder
|
* @todo YDecoder also contains references to String and other Decoders. Would make sense to exchange YDecoder.toUint8Array for YDecoder.DsToUint8Array()..
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractDSDecoder} decoder
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
*
|
*
|
||||||
@ -260,18 +269,19 @@ export const readDeleteSet = decoder => {
|
|||||||
*/
|
*/
|
||||||
export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
||||||
const unappliedDS = new DeleteSet()
|
const unappliedDS = new DeleteSet()
|
||||||
const numClients = decoding.readVarUint(decoder)
|
const numClients = decoding.readVarUint(decoder.restDecoder)
|
||||||
for (let i = 0; i < numClients; i++) {
|
for (let i = 0; i < numClients; i++) {
|
||||||
const client = decoding.readVarUint(decoder)
|
decoder.resetDsCurVal()
|
||||||
const numberOfDeletes = decoding.readVarUint(decoder)
|
const client = decoding.readVarUint(decoder.restDecoder)
|
||||||
|
const numberOfDeletes = decoding.readVarUint(decoder.restDecoder)
|
||||||
const structs = store.clients.get(client) || []
|
const structs = store.clients.get(client) || []
|
||||||
const state = getState(store, client)
|
const state = getState(store, client)
|
||||||
for (let i = 0; i < numberOfDeletes; i++) {
|
for (let i = 0; i < numberOfDeletes; i++) {
|
||||||
const clock = decoding.readVarUint(decoder)
|
const clock = decoder.readDsClock()
|
||||||
const len = decoding.readVarUint(decoder)
|
const clockEnd = clock + decoder.readDsLen()
|
||||||
if (clock < state) {
|
if (clock < state) {
|
||||||
if (state < clock + len) {
|
if (state < clockEnd) {
|
||||||
addToDeleteSet(unappliedDS, createID(client, state), clock + len - state)
|
addToDeleteSet(unappliedDS, client, state, clockEnd - state)
|
||||||
}
|
}
|
||||||
let index = findIndexSS(structs, clock)
|
let index = findIndexSS(structs, clock)
|
||||||
/**
|
/**
|
||||||
@ -288,10 +298,10 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
|||||||
while (index < structs.length) {
|
while (index < structs.length) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
struct = structs[index++]
|
struct = structs[index++]
|
||||||
if (struct.id.clock < clock + len) {
|
if (struct.id.clock < clockEnd) {
|
||||||
if (!struct.deleted) {
|
if (!struct.deleted) {
|
||||||
if (clock + len < struct.id.clock + struct.length) {
|
if (clockEnd < struct.id.clock + struct.length) {
|
||||||
structs.splice(index, 0, splitItem(transaction, struct, clock + len - struct.id.clock))
|
structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock))
|
||||||
}
|
}
|
||||||
struct.delete(transaction)
|
struct.delete(transaction)
|
||||||
}
|
}
|
||||||
@ -300,14 +310,14 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addToDeleteSet(unappliedDS, createID(client, clock), len)
|
addToDeleteSet(unappliedDS, client, clock, clockEnd - clock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (unappliedDS.clients.size > 0) {
|
if (unappliedDS.clients.size > 0) {
|
||||||
// TODO: no need for encoding+decoding ds anymore
|
// TODO: no need for encoding+decoding ds anymore
|
||||||
const unappliedDSEncoder = encoding.createEncoder()
|
const unappliedDSEncoder = new DSEncoderV2()
|
||||||
writeDeleteSet(unappliedDSEncoder, unappliedDS)
|
writeDeleteSet(unappliedDSEncoder, unappliedDS)
|
||||||
store.pendingDeleteReaders.push(decoding.createDecoder(encoding.toUint8Array(unappliedDSEncoder)))
|
store.pendingDeleteReaders.push(new DSDecoderV2(decoding.createDecoder((unappliedDSEncoder.toUint8Array()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@ import {
|
|||||||
readDeleteSet,
|
readDeleteSet,
|
||||||
writeDeleteSet,
|
writeDeleteSet,
|
||||||
createDeleteSet,
|
createDeleteSet,
|
||||||
ID, DeleteSet, YArrayEvent, Transaction, Doc // eslint-disable-line
|
DSEncoderV1, DSDecoderV1, ID, DeleteSet, YArrayEvent, Transaction, Doc // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import { mergeDeleteSets, isDeleted } from './DeleteSet.js'
|
import { mergeDeleteSets, isDeleted } from './DeleteSet.js'
|
||||||
|
|
||||||
export class PermanentUserData {
|
export class PermanentUserData {
|
||||||
@ -46,12 +46,12 @@ export class PermanentUserData {
|
|||||||
event.changes.added.forEach(item => {
|
event.changes.added.forEach(item => {
|
||||||
item.content.getContent().forEach(encodedDs => {
|
item.content.getContent().forEach(encodedDs => {
|
||||||
if (encodedDs instanceof Uint8Array) {
|
if (encodedDs instanceof Uint8Array) {
|
||||||
this.dss.set(userDescription, mergeDeleteSets([this.dss.get(userDescription) || createDeleteSet(), readDeleteSet(decoding.createDecoder(encodedDs))]))
|
this.dss.set(userDescription, mergeDeleteSets([this.dss.get(userDescription) || createDeleteSet(), readDeleteSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.dss.set(userDescription, mergeDeleteSets(ds.map(encodedDs => readDeleteSet(decoding.createDecoder(encodedDs)))))
|
this.dss.set(userDescription, mergeDeleteSets(ds.map(encodedDs => readDeleteSet(new DSDecoderV1(encodedDs)))))
|
||||||
ids.observe(/** @param {YArrayEvent<any>} event */ event =>
|
ids.observe(/** @param {YArrayEvent<any>} event */ event =>
|
||||||
event.changes.added.forEach(item => item.content.getContent().forEach(addClientId))
|
event.changes.added.forEach(item => item.content.getContent().forEach(addClientId))
|
||||||
)
|
)
|
||||||
@ -97,11 +97,11 @@ export class PermanentUserData {
|
|||||||
user.get('ids').push([clientid])
|
user.get('ids').push([clientid])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = new DSEncoderV1()
|
||||||
const ds = this.dss.get(userDescription)
|
const ds = this.dss.get(userDescription)
|
||||||
if (ds) {
|
if (ds) {
|
||||||
writeDeleteSet(encoder, ds)
|
writeDeleteSet(encoder, ds)
|
||||||
user.get('ds').push([encoding.toUint8Array(encoder)])
|
user.get('ds').push([encoder.toUint8Array()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
@ -111,9 +111,9 @@ export class PermanentUserData {
|
|||||||
const yds = user.get('ds')
|
const yds = user.get('ds')
|
||||||
const ds = transaction.deleteSet
|
const ds = transaction.deleteSet
|
||||||
if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) {
|
if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) {
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = new DSEncoderV1()
|
||||||
writeDeleteSet(encoder, ds)
|
writeDeleteSet(encoder, ds)
|
||||||
yds.push([encoding.toUint8Array(encoder)])
|
yds.push([encoder.toUint8Array()])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -12,13 +12,13 @@ import {
|
|||||||
createDeleteSet,
|
createDeleteSet,
|
||||||
createID,
|
createID,
|
||||||
getState,
|
getState,
|
||||||
Transaction, Doc, DeleteSet, Item // eslint-disable-line
|
AbstractDSDecoder, AbstractDSEncoder, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as map from 'lib0/map.js'
|
import * as map from 'lib0/map.js'
|
||||||
import * as set from 'lib0/set.js'
|
import * as set from 'lib0/set.js'
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import { DefaultDSEncoder } from './encoding.js'
|
||||||
|
|
||||||
export class Snapshot {
|
export class Snapshot {
|
||||||
/**
|
/**
|
||||||
@ -74,23 +74,35 @@ export const equalSnapshots = (snap1, snap2) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Snapshot} snapshot
|
* @param {Snapshot} snapshot
|
||||||
|
* @param {AbstractDSEncoder} [encoder]
|
||||||
* @return {Uint8Array}
|
* @return {Uint8Array}
|
||||||
*/
|
*/
|
||||||
export const encodeSnapshot = snapshot => {
|
export const encodeSnapshotV2 = (snapshot, encoder = new DSEncoderV2()) => {
|
||||||
const encoder = encoding.createEncoder()
|
|
||||||
writeDeleteSet(encoder, snapshot.ds)
|
writeDeleteSet(encoder, snapshot.ds)
|
||||||
writeStateVector(encoder, snapshot.sv)
|
writeStateVector(encoder, snapshot.sv)
|
||||||
return encoding.toUint8Array(encoder)
|
return encoder.toUint8Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Snapshot} snapshot
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new DefaultDSEncoder())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} buf
|
||||||
|
* @param {AbstractDSDecoder} [decoder]
|
||||||
|
* @return {Snapshot}
|
||||||
|
*/
|
||||||
|
export const decodeSnapshotV2 = (buf, decoder = new DSDecoderV2(decoding.createDecoder(buf))) => {
|
||||||
|
return new Snapshot(readDeleteSet(decoder), readStateVector(decoder))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} buf
|
* @param {Uint8Array} buf
|
||||||
* @return {Snapshot}
|
* @return {Snapshot}
|
||||||
*/
|
*/
|
||||||
export const decodeSnapshot = buf => {
|
export const decodeSnapshot = buf => decodeSnapshotV2(buf, new DSDecoderV1(decoding.createDecoder(buf)))
|
||||||
const decoder = decoding.createDecoder(buf)
|
|
||||||
return new Snapshot(readDeleteSet(decoder), readStateVector(decoder))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DeleteSet} ds
|
* @param {DeleteSet} ds
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
import {
|
import {
|
||||||
GC,
|
GC,
|
||||||
splitItem,
|
splitItem,
|
||||||
AbstractStruct, Transaction, ID, Item // eslint-disable-line
|
Transaction, ID, Item, DSDecoderV2 // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as math from 'lib0/math.js'
|
import * as math from 'lib0/math.js'
|
||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
|
||||||
|
|
||||||
export class StructStore {
|
export class StructStore {
|
||||||
constructor () {
|
constructor () {
|
||||||
@ -31,7 +30,7 @@ export class StructStore {
|
|||||||
*/
|
*/
|
||||||
this.pendingStack = []
|
this.pendingStack = []
|
||||||
/**
|
/**
|
||||||
* @type {Array<decoding.Decoder>}
|
* @type {Array<DSDecoderV2>}
|
||||||
*/
|
*/
|
||||||
this.pendingDeleteReaders = []
|
this.pendingDeleteReaders = []
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,16 @@ import {
|
|||||||
Item,
|
Item,
|
||||||
generateNewClientId,
|
generateNewClientId,
|
||||||
createID,
|
createID,
|
||||||
GC, StructStore, ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
AbstractUpdateEncoder, GC, StructStore, UpdateEncoderV1, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
|
||||||
import * as map from 'lib0/map.js'
|
import * as map from 'lib0/map.js'
|
||||||
import * as math from 'lib0/math.js'
|
import * as math from 'lib0/math.js'
|
||||||
import * as set from 'lib0/set.js'
|
import * as set from 'lib0/set.js'
|
||||||
import * as logging from 'lib0/logging.js'
|
import * as logging from 'lib0/logging.js'
|
||||||
import { callAll } from 'lib0/function.js'
|
import { callAll } from 'lib0/function.js'
|
||||||
|
import { DefaultUpdateEncoder } from './encoding.js'
|
||||||
|
import { UpdateEncoderV2 } from './UpdateEncoder.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction is created for every change on the Yjs model. It is possible
|
* A transaction is created for every change on the Yjs model. It is possible
|
||||||
@ -107,17 +108,18 @@ export class Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
|
* @return {boolean} Whether data was written.
|
||||||
*/
|
*/
|
||||||
export const computeUpdateMessageFromTransaction = transaction => {
|
export const writeUpdateMessageFromTransaction = (encoder, transaction) => {
|
||||||
if (transaction.deleteSet.clients.size === 0 && !map.any(transaction.afterState, (clock, client) => transaction.beforeState.get(client) !== clock)) {
|
if (transaction.deleteSet.clients.size === 0 && !map.any(transaction.afterState, (clock, client) => transaction.beforeState.get(client) !== clock)) {
|
||||||
return null
|
return false
|
||||||
}
|
}
|
||||||
const encoder = encoding.createEncoder()
|
|
||||||
sortAndMergeDeleteSet(transaction.deleteSet)
|
sortAndMergeDeleteSet(transaction.deleteSet)
|
||||||
writeStructsFromTransaction(encoder, transaction)
|
writeStructsFromTransaction(encoder, transaction)
|
||||||
writeDeleteSet(encoder, transaction.deleteSet)
|
writeDeleteSet(encoder, transaction.deleteSet)
|
||||||
return encoder
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -322,9 +324,17 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
// @todo Merge all the transactions into one and provide send the data as a single update message
|
// @todo Merge all the transactions into one and provide send the data as a single update message
|
||||||
doc.emit('afterTransactionCleanup', [transaction, doc])
|
doc.emit('afterTransactionCleanup', [transaction, doc])
|
||||||
if (doc._observers.has('update')) {
|
if (doc._observers.has('update')) {
|
||||||
const updateMessage = computeUpdateMessageFromTransaction(transaction)
|
const encoder = new DefaultUpdateEncoder()
|
||||||
if (updateMessage !== null) {
|
const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
|
||||||
doc.emit('update', [encoding.toUint8Array(updateMessage), transaction.origin, doc])
|
if (hasContent) {
|
||||||
|
doc.emit('update', [encoder.toUint8Array(), transaction.origin, doc])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doc._observers.has('updateV2')) {
|
||||||
|
const encoder = new UpdateEncoderV2()
|
||||||
|
const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
|
||||||
|
if (hasContent) {
|
||||||
|
doc.emit('updateV2', [encoder.toUint8Array(), transaction.origin, doc])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transactionCleanups.length <= i + 1) {
|
if (transactionCleanups.length <= i + 1) {
|
||||||
|
392
src/utils/UpdateDecoder.js
Normal file
392
src/utils/UpdateDecoder.js
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
import * as buffer from 'lib0/buffer.js'
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
import {
|
||||||
|
ID, createID
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
export class AbstractDSDecoder {
|
||||||
|
/**
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
*/
|
||||||
|
constructor (decoder) {
|
||||||
|
this.restDecoder = decoder
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDsCurVal () { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readDsClock () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readDsLen () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AbstractUpdateDecoder extends AbstractDSDecoder {
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readLeftID () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readRightID () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next client id.
|
||||||
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readClient () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readInfo () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readString () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} isKey
|
||||||
|
*/
|
||||||
|
readParentInfo () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readTypeRef () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @return {number} len
|
||||||
|
*/
|
||||||
|
readLen () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readAny () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
readBuf () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy implementation uses JSON parse. We use any-decoding in v2.
|
||||||
|
*
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readJSON () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readKey () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DSDecoderV1 {
|
||||||
|
/**
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
*/
|
||||||
|
constructor (decoder) {
|
||||||
|
this.restDecoder = decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDsCurVal () {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readDsClock () {
|
||||||
|
return decoding.readVarUint(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readDsLen () {
|
||||||
|
return decoding.readVarUint(this.restDecoder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateDecoderV1 extends DSDecoderV1 {
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readLeftID () {
|
||||||
|
return createID(decoding.readVarUint(this.restDecoder), decoding.readVarUint(this.restDecoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readRightID () {
|
||||||
|
return createID(decoding.readVarUint(this.restDecoder), decoding.readVarUint(this.restDecoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next client id.
|
||||||
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
||||||
|
*/
|
||||||
|
readClient () {
|
||||||
|
return decoding.readVarUint(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readInfo () {
|
||||||
|
return decoding.readUint8(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readString () {
|
||||||
|
return decoding.readVarString(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} isKey
|
||||||
|
*/
|
||||||
|
readParentInfo () {
|
||||||
|
return decoding.readVarUint(this.restDecoder) === 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readTypeRef () {
|
||||||
|
return decoding.readVarUint(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @return {number} len
|
||||||
|
*/
|
||||||
|
readLen () {
|
||||||
|
return decoding.readVarUint(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readAny () {
|
||||||
|
return decoding.readAny(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
readBuf () {
|
||||||
|
return buffer.copyUint8Array(decoding.readVarUint8Array(this.restDecoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy implementation uses JSON parse. We use any-decoding in v2.
|
||||||
|
*
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readJSON () {
|
||||||
|
return JSON.parse(decoding.readVarString(this.restDecoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readKey () {
|
||||||
|
return decoding.readVarString(this.restDecoder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DSDecoderV2 {
|
||||||
|
/**
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
*/
|
||||||
|
constructor (decoder) {
|
||||||
|
this.dsCurrVal = 0
|
||||||
|
this.restDecoder = decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDsCurVal () {
|
||||||
|
this.dsCurrVal = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readDsClock () {
|
||||||
|
this.dsCurrVal += decoding.readVarUint(this.restDecoder)
|
||||||
|
return this.dsCurrVal
|
||||||
|
}
|
||||||
|
|
||||||
|
readDsLen () {
|
||||||
|
const diff = decoding.readVarUint(this.restDecoder) + 1
|
||||||
|
this.dsCurrVal += diff
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateDecoderV2 extends DSDecoderV2 {
|
||||||
|
/**
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
*/
|
||||||
|
constructor (decoder) {
|
||||||
|
super(decoder)
|
||||||
|
/**
|
||||||
|
* List of cached keys. If the keys[id] does not exist, we read a new key
|
||||||
|
* from stringEncoder and push it to keys.
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
this.keys = []
|
||||||
|
decoding.readUint8(decoder) // read feature flag - currently unused
|
||||||
|
this.keyClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.clientDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.leftClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.rightClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.infoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
|
||||||
|
this.stringDecoder = new decoding.StringDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.parentInfoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
|
||||||
|
this.typeRefDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
this.lenDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readLeftID () {
|
||||||
|
return new ID(this.clientDecoder.read(), this.leftClockDecoder.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {ID}
|
||||||
|
*/
|
||||||
|
readRightID () {
|
||||||
|
return new ID(this.clientDecoder.read(), this.rightClockDecoder.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next client id.
|
||||||
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
||||||
|
*/
|
||||||
|
readClient () {
|
||||||
|
return this.clientDecoder.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readInfo () {
|
||||||
|
return /** @type {number} */ (this.infoDecoder.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readString () {
|
||||||
|
return this.stringDecoder.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
readParentInfo () {
|
||||||
|
return this.parentInfoDecoder.read() === 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
readTypeRef () {
|
||||||
|
return this.typeRefDecoder.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readLen () {
|
||||||
|
return this.lenDecoder.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readAny () {
|
||||||
|
return decoding.readAny(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
readBuf () {
|
||||||
|
return decoding.readVarUint8Array(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is mainly here for legacy purposes.
|
||||||
|
*
|
||||||
|
* Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
|
||||||
|
*
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
readJSON () {
|
||||||
|
return decoding.readAny(this.restDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
readKey () {
|
||||||
|
const keyClock = this.keyClockDecoder.read()
|
||||||
|
if (keyClock < this.keys.length) {
|
||||||
|
return this.keys[keyClock]
|
||||||
|
} else {
|
||||||
|
const key = this.stringDecoder.read()
|
||||||
|
this.keys.push(key)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
408
src/utils/UpdateEncoder.js
Normal file
408
src/utils/UpdateEncoder.js
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
|
||||||
|
import * as error from 'lib0/error.js'
|
||||||
|
import * as encoding from 'lib0/encoding.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ID // eslint-disable-line
|
||||||
|
} from '../internals.js'
|
||||||
|
|
||||||
|
export class AbstractDSEncoder {
|
||||||
|
constructor () {
|
||||||
|
this.restEncoder = encoding.createEncoder()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
toUint8Array () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the ds value to 0.
|
||||||
|
* The v2 encoder uses this information to reset the initial diff value.
|
||||||
|
*/
|
||||||
|
resetDsCurVal () { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} clock
|
||||||
|
*/
|
||||||
|
writeDsClock (clock) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeDsLen (len) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AbstractUpdateEncoder extends AbstractDSEncoder {
|
||||||
|
/**
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*/
|
||||||
|
toUint8Array () {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeLeftID (id) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeRightID (id) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use writeClient and writeClock instead of writeID if possible.
|
||||||
|
* @param {number} client
|
||||||
|
*/
|
||||||
|
writeClient (client) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeInfo (info) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
*/
|
||||||
|
writeString (s) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} isYKey
|
||||||
|
*/
|
||||||
|
writeParentInfo (isYKey) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeTypeRef (info) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeLen (len) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} any
|
||||||
|
*/
|
||||||
|
writeAny (any) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} buf
|
||||||
|
*/
|
||||||
|
writeBuf (buf) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} embed
|
||||||
|
*/
|
||||||
|
writeJSON (embed) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
writeKey (key) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DSEncoderV1 {
|
||||||
|
constructor () {
|
||||||
|
this.restEncoder = new encoding.Encoder()
|
||||||
|
}
|
||||||
|
|
||||||
|
toUint8Array () {
|
||||||
|
return encoding.toUint8Array(this.restEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDsCurVal () {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} clock
|
||||||
|
*/
|
||||||
|
writeDsClock (clock) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeDsLen (len) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateEncoderV1 extends DSEncoderV1 {
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeLeftID (id) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, id.client)
|
||||||
|
encoding.writeVarUint(this.restEncoder, id.clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeRightID (id) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, id.client)
|
||||||
|
encoding.writeVarUint(this.restEncoder, id.clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use writeClient and writeClock instead of writeID if possible.
|
||||||
|
* @param {number} client
|
||||||
|
*/
|
||||||
|
writeClient (client) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeInfo (info) {
|
||||||
|
encoding.writeUint8(this.restEncoder, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
*/
|
||||||
|
writeString (s) {
|
||||||
|
encoding.writeVarString(this.restEncoder, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} isYKey
|
||||||
|
*/
|
||||||
|
writeParentInfo (isYKey) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, isYKey ? 1 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeTypeRef (info) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeLen (len) {
|
||||||
|
encoding.writeVarUint(this.restEncoder, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} any
|
||||||
|
*/
|
||||||
|
writeAny (any) {
|
||||||
|
encoding.writeAny(this.restEncoder, any)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} buf
|
||||||
|
*/
|
||||||
|
writeBuf (buf) {
|
||||||
|
encoding.writeVarUint8Array(this.restEncoder, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} embed
|
||||||
|
*/
|
||||||
|
writeJSON (embed) {
|
||||||
|
encoding.writeVarString(this.restEncoder, JSON.stringify(embed))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
writeKey (key) {
|
||||||
|
encoding.writeVarString(this.restEncoder, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DSEncoderV2 {
|
||||||
|
constructor () {
|
||||||
|
this.restEncoder = new encoding.Encoder() // encodes all the rest / non-optimized
|
||||||
|
this.dsCurrVal = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
toUint8Array () {
|
||||||
|
return encoding.toUint8Array(this.restEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDsCurVal () {
|
||||||
|
this.dsCurrVal = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} clock
|
||||||
|
*/
|
||||||
|
writeDsClock (clock) {
|
||||||
|
const diff = clock - this.dsCurrVal
|
||||||
|
this.dsCurrVal = clock
|
||||||
|
encoding.writeVarUint(this.restEncoder, diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeDsLen (len) {
|
||||||
|
if (len === 0) {
|
||||||
|
error.unexpectedCase()
|
||||||
|
}
|
||||||
|
encoding.writeVarUint(this.restEncoder, len - 1)
|
||||||
|
this.dsCurrVal += len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateEncoderV2 extends DSEncoderV2 {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
/**
|
||||||
|
* @type {Map<string,number>}
|
||||||
|
*/
|
||||||
|
this.keyMap = new Map()
|
||||||
|
/**
|
||||||
|
* Refers to the next uniqe key-identifier to me used.
|
||||||
|
* See writeKey method for more information.
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.keyClock = 0
|
||||||
|
this.keyClockEncoder = new encoding.IntDiffOptRleEncoder()
|
||||||
|
this.clientEncoder = new encoding.UintOptRleEncoder()
|
||||||
|
this.leftClockEncoder = new encoding.IntDiffOptRleEncoder()
|
||||||
|
this.rightClockEncoder = new encoding.IntDiffOptRleEncoder()
|
||||||
|
this.infoEncoder = new encoding.RleEncoder(encoding.writeUint8)
|
||||||
|
this.stringEncoder = new encoding.StringEncoder()
|
||||||
|
this.parentInfoEncoder = new encoding.RleEncoder(encoding.writeUint8)
|
||||||
|
this.typeRefEncoder = new encoding.UintOptRleEncoder()
|
||||||
|
this.lenEncoder = new encoding.UintOptRleEncoder()
|
||||||
|
}
|
||||||
|
|
||||||
|
toUint8Array () {
|
||||||
|
const encoder = encoding.createEncoder()
|
||||||
|
encoding.writeUint8(encoder, 0) // this is a feature flag that we might use in the future
|
||||||
|
encoding.writeVarUint8Array(encoder, this.keyClockEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, this.clientEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, this.leftClockEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, this.rightClockEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.infoEncoder))
|
||||||
|
encoding.writeVarUint8Array(encoder, this.stringEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.parentInfoEncoder))
|
||||||
|
encoding.writeVarUint8Array(encoder, this.typeRefEncoder.toUint8Array())
|
||||||
|
encoding.writeVarUint8Array(encoder, this.lenEncoder.toUint8Array())
|
||||||
|
// @note The rest encoder is appended! (note the missing var)
|
||||||
|
encoding.writeUint8Array(encoder, encoding.toUint8Array(this.restEncoder))
|
||||||
|
return encoding.toUint8Array(encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeLeftID (id) {
|
||||||
|
this.clientEncoder.write(id.client)
|
||||||
|
this.leftClockEncoder.write(id.clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ID} id
|
||||||
|
*/
|
||||||
|
writeRightID (id) {
|
||||||
|
this.clientEncoder.write(id.client)
|
||||||
|
this.rightClockEncoder.write(id.clock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} client
|
||||||
|
*/
|
||||||
|
writeClient (client) {
|
||||||
|
this.clientEncoder.write(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeInfo (info) {
|
||||||
|
this.infoEncoder.write(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
*/
|
||||||
|
writeString (s) {
|
||||||
|
this.stringEncoder.write(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} isYKey
|
||||||
|
*/
|
||||||
|
writeParentInfo (isYKey) {
|
||||||
|
this.parentInfoEncoder.write(isYKey ? 1 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} info An unsigned 8-bit integer
|
||||||
|
*/
|
||||||
|
writeTypeRef (info) {
|
||||||
|
this.typeRefEncoder.write(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write len of a struct - well suited for Opt RLE encoder.
|
||||||
|
*
|
||||||
|
* @param {number} len
|
||||||
|
*/
|
||||||
|
writeLen (len) {
|
||||||
|
this.lenEncoder.write(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} any
|
||||||
|
*/
|
||||||
|
writeAny (any) {
|
||||||
|
encoding.writeAny(this.restEncoder, any)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} buf
|
||||||
|
*/
|
||||||
|
writeBuf (buf) {
|
||||||
|
encoding.writeVarUint8Array(this.restEncoder, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is mainly here for legacy purposes.
|
||||||
|
*
|
||||||
|
* Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
|
||||||
|
*
|
||||||
|
* @param {any} embed
|
||||||
|
*/
|
||||||
|
writeJSON (embed) {
|
||||||
|
encoding.writeAny(this.restEncoder, embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property keys are often reused. For example, in y-prosemirror the key `bold` might
|
||||||
|
* occur very often. For a 3d application, the key `position` might occur very often.
|
||||||
|
*
|
||||||
|
* We cache these keys in a Map and refer to them via a unique number.
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
writeKey (key) {
|
||||||
|
const clock = this.keyMap.get(key)
|
||||||
|
if (clock === undefined) {
|
||||||
|
this.keyClockEncoder.write(this.keyClock++)
|
||||||
|
this.stringEncoder.write(key)
|
||||||
|
} else {
|
||||||
|
this.keyClockEncoder.write(this.keyClock++)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @module encoding
|
* @module encoding
|
||||||
*
|
*/
|
||||||
|
/*
|
||||||
* We use the first five bits in the info flag for determining the type of the struct.
|
* We use the first five bits in the info flag for determining the type of the struct.
|
||||||
*
|
*
|
||||||
* 0: GC
|
* 0: GC
|
||||||
@ -16,8 +17,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
findIndexSS,
|
findIndexSS,
|
||||||
writeID,
|
|
||||||
readID,
|
|
||||||
getState,
|
getState,
|
||||||
createID,
|
createID,
|
||||||
getStateVector,
|
getStateVector,
|
||||||
@ -25,16 +24,36 @@ import {
|
|||||||
writeDeleteSet,
|
writeDeleteSet,
|
||||||
createDeleteSetFromStructStore,
|
createDeleteSetFromStructStore,
|
||||||
transact,
|
transact,
|
||||||
readItem,
|
readItemContent,
|
||||||
Doc, Transaction, GC, Item, StructStore, ID // eslint-disable-line
|
UpdateDecoderV1,
|
||||||
|
UpdateDecoderV2,
|
||||||
|
UpdateEncoderV1,
|
||||||
|
UpdateEncoderV2,
|
||||||
|
DSDecoderV2,
|
||||||
|
DSEncoderV2,
|
||||||
|
DSDecoderV1,
|
||||||
|
DSEncoderV1,
|
||||||
|
AbstractDSEncoder, AbstractDSDecoder, AbstractUpdateEncoder, AbstractUpdateDecoder, AbstractContent, Doc, Transaction, GC, Item, StructStore, ID // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as encoding from 'lib0/encoding.js'
|
import * as encoding from 'lib0/encoding.js'
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
import * as binary from 'lib0/binary.js'
|
import * as binary from 'lib0/binary.js'
|
||||||
|
|
||||||
|
export let DefaultDSEncoder = DSEncoderV1
|
||||||
|
export let DefaultDSDecoder = DSDecoderV1
|
||||||
|
export let DefaultUpdateEncoder = UpdateEncoderV1
|
||||||
|
export let DefaultUpdateDecoder = UpdateDecoderV1
|
||||||
|
|
||||||
|
export const useV2Encoding = () => {
|
||||||
|
DefaultDSEncoder = DSEncoderV2
|
||||||
|
DefaultDSDecoder = DSDecoderV2
|
||||||
|
DefaultUpdateEncoder = UpdateEncoderV2
|
||||||
|
DefaultUpdateDecoder = UpdateDecoderV2
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {Array<GC|Item>} structs All structs by `client`
|
* @param {Array<GC|Item>} structs All structs by `client`
|
||||||
* @param {number} client
|
* @param {number} client
|
||||||
* @param {number} clock write structs starting with `ID(client,clock)`
|
* @param {number} clock write structs starting with `ID(client,clock)`
|
||||||
@ -45,8 +64,9 @@ const writeStructs = (encoder, structs, client, clock) => {
|
|||||||
// write first id
|
// write first id
|
||||||
const startNewStructs = findIndexSS(structs, clock)
|
const startNewStructs = findIndexSS(structs, clock)
|
||||||
// write # encoded structs
|
// write # encoded structs
|
||||||
encoding.writeVarUint(encoder, structs.length - startNewStructs)
|
encoding.writeVarUint(encoder.restEncoder, structs.length - startNewStructs)
|
||||||
writeID(encoder, createID(client, clock))
|
encoder.writeClient(client)
|
||||||
|
encoding.writeVarUint(encoder.restEncoder, clock)
|
||||||
const firstStruct = structs[startNewStructs]
|
const firstStruct = structs[startNewStructs]
|
||||||
// write first struct with an offset
|
// write first struct with an offset
|
||||||
firstStruct.write(encoder, clock - firstStruct.id.clock)
|
firstStruct.write(encoder, clock - firstStruct.id.clock)
|
||||||
@ -56,7 +76,7 @@ const writeStructs = (encoder, structs, client, clock) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {Map<number,number>} _sm
|
* @param {Map<number,number>} _sm
|
||||||
*
|
*
|
||||||
@ -78,7 +98,7 @@ export const writeClientsStructs = (encoder, store, _sm) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// write # states that were updated
|
// write # states that were updated
|
||||||
encoding.writeVarUint(encoder, sm.size)
|
encoding.writeVarUint(encoder.restEncoder, sm.size)
|
||||||
// Write items with higher client ids first
|
// Write items with higher client ids first
|
||||||
// This heavily improves the conflict algorithm.
|
// This heavily improves the conflict algorithm.
|
||||||
Array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
Array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||||
@ -88,7 +108,7 @@ export const writeClientsStructs = (encoder, store, _sm) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {decoding.Decoder} decoder The decoder object to read data from.
|
* @param {AbstractUpdateDecoder} decoder The decoder object to read data from.
|
||||||
* @param {Map<number,Array<GC|Item>>} clientRefs
|
* @param {Map<number,Array<GC|Item>>} clientRefs
|
||||||
* @param {Doc} doc
|
* @param {Doc} doc
|
||||||
* @return {Map<number,Array<GC|Item>>}
|
* @return {Map<number,Array<GC|Item>>}
|
||||||
@ -97,21 +117,52 @@ export const writeClientsStructs = (encoder, store, _sm) => {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readClientsStructRefs = (decoder, clientRefs, doc) => {
|
export const readClientsStructRefs = (decoder, clientRefs, doc) => {
|
||||||
const numOfStateUpdates = decoding.readVarUint(decoder)
|
const numOfStateUpdates = decoding.readVarUint(decoder.restDecoder)
|
||||||
for (let i = 0; i < numOfStateUpdates; i++) {
|
for (let i = 0; i < numOfStateUpdates; i++) {
|
||||||
const numberOfStructs = decoding.readVarUint(decoder)
|
const numberOfStructs = decoding.readVarUint(decoder.restDecoder)
|
||||||
/**
|
/**
|
||||||
* @type {Array<GC|Item>}
|
* @type {Array<GC|Item>}
|
||||||
*/
|
*/
|
||||||
const refs = []
|
const refs = []
|
||||||
let { client, clock } = readID(decoder)
|
const client = decoder.readClient()
|
||||||
let info, struct
|
let clock = decoding.readVarUint(decoder.restDecoder)
|
||||||
clientRefs.set(client, refs)
|
clientRefs.set(client, refs)
|
||||||
for (let i = 0; i < numberOfStructs; i++) {
|
for (let i = 0; i < numberOfStructs; i++) {
|
||||||
info = decoding.readUint8(decoder)
|
const info = decoder.readInfo()
|
||||||
struct = (binary.BITS5 & info) === 0 ? new GC(createID(client, clock), decoding.readVarUint(decoder)) : readItem(decoder, createID(client, clock), info, doc)
|
if ((binary.BITS5 & info) !== 0) {
|
||||||
refs.push(struct)
|
/**
|
||||||
clock += struct.length
|
* The item that was originally to the left of this item.
|
||||||
|
* @type {ID | null}
|
||||||
|
*/
|
||||||
|
const origin = (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null
|
||||||
|
/**
|
||||||
|
* The item that was originally to the right of this item.
|
||||||
|
* @type {ID | null}
|
||||||
|
*/
|
||||||
|
const rightOrigin = (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null
|
||||||
|
const canCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
|
||||||
|
const hasParentYKey = canCopyParentInfo ? decoder.readParentInfo() : false
|
||||||
|
/**
|
||||||
|
* If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
|
||||||
|
* and we read the next string as parentYKey.
|
||||||
|
* It indicates how we store/retrieve parent from `y.share`
|
||||||
|
* @type {string|null}
|
||||||
|
*/
|
||||||
|
const parentYKey = canCopyParentInfo && hasParentYKey ? decoder.readString() : null
|
||||||
|
|
||||||
|
const struct = new Item(
|
||||||
|
createID(client, clock), null, origin, null, rightOrigin,
|
||||||
|
canCopyParentInfo && !hasParentYKey ? decoder.readLeftID() : (parentYKey ? doc.get(parentYKey) : null), // parent
|
||||||
|
canCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
|
||||||
|
/** @type {AbstractContent} */ (readItemContent(decoder, info)) // item content
|
||||||
|
)
|
||||||
|
refs.push(struct)
|
||||||
|
clock += struct.length
|
||||||
|
} else {
|
||||||
|
const len = decoder.readLen()
|
||||||
|
refs.push(new GC(createID(client, clock), len))
|
||||||
|
clock += len
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return clientRefs
|
return clientRefs
|
||||||
@ -222,7 +273,7 @@ export const tryResumePendingDeleteReaders = (transaction, store) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@ -275,7 +326,7 @@ const cleanupPendingStructs = pendingClientsStructRefs => {
|
|||||||
*
|
*
|
||||||
* This is called when data is received from a remote peer.
|
* This is called when data is received from a remote peer.
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder The decoder object to read data from.
|
* @param {AbstractUpdateDecoder} decoder The decoder object to read data from.
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
*
|
*
|
||||||
@ -299,15 +350,46 @@ export const readStructs = (decoder, transaction, store) => {
|
|||||||
* @param {decoding.Decoder} decoder
|
* @param {decoding.Decoder} decoder
|
||||||
* @param {Doc} ydoc
|
* @param {Doc} ydoc
|
||||||
* @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
|
* @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
|
||||||
|
* @param {AbstractUpdateDecoder} [structDecoder]
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readUpdate = (decoder, ydoc, transactionOrigin) =>
|
export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = new UpdateDecoderV2(decoder)) =>
|
||||||
transact(ydoc, transaction => {
|
transact(ydoc, transaction => {
|
||||||
readStructs(decoder, transaction, ydoc.store)
|
readStructs(structDecoder, transaction, ydoc.store)
|
||||||
readAndApplyDeleteSet(decoder, transaction, ydoc.store)
|
readAndApplyDeleteSet(structDecoder, transaction, ydoc.store)
|
||||||
}, transactionOrigin, false)
|
}, transactionOrigin, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and apply a document update.
|
||||||
|
*
|
||||||
|
* This function has the same effect as `applyUpdate` but accepts an decoder.
|
||||||
|
*
|
||||||
|
* @param {decoding.Decoder} decoder
|
||||||
|
* @param {Doc} ydoc
|
||||||
|
* @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
export const readUpdate = (decoder, ydoc, transactionOrigin) => readUpdateV2(decoder, ydoc, transactionOrigin, new DefaultUpdateDecoder(decoder))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
|
||||||
|
*
|
||||||
|
* This function has the same effect as `readUpdate` but accepts an Uint8Array instead of a Decoder.
|
||||||
|
*
|
||||||
|
* @param {Doc} ydoc
|
||||||
|
* @param {Uint8Array} update
|
||||||
|
* @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
|
||||||
|
* @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
export const applyUpdateV2 = (ydoc, update, transactionOrigin, YDecoder = UpdateDecoderV2) => {
|
||||||
|
const decoder = decoding.createDecoder(update)
|
||||||
|
readUpdateV2(decoder, ydoc, transactionOrigin, new YDecoder(decoder))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
|
* Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
|
||||||
*
|
*
|
||||||
@ -319,14 +401,13 @@ export const readUpdate = (decoder, ydoc, transactionOrigin) =>
|
|||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const applyUpdate = (ydoc, update, transactionOrigin) =>
|
export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, DefaultUpdateDecoder)
|
||||||
readUpdate(decoding.createDecoder(update), ydoc, transactionOrigin)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
|
* Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
|
||||||
* only write the operations that are missing.
|
* only write the operations that are missing.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {AbstractUpdateEncoder} encoder
|
||||||
* @param {Doc} doc
|
* @param {Doc} doc
|
||||||
* @param {Map<number,number>} [targetStateVector] The state of the target that receives the update. Leave empty to write all known structs
|
* @param {Map<number,number>} [targetStateVector] The state of the target that receives the update. Leave empty to write all known structs
|
||||||
*
|
*
|
||||||
@ -345,31 +426,45 @@ export const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map())
|
|||||||
*
|
*
|
||||||
* @param {Doc} doc
|
* @param {Doc} doc
|
||||||
* @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
|
* @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
|
||||||
|
* @param {AbstractUpdateEncoder} [encoder]
|
||||||
* @return {Uint8Array}
|
* @return {Uint8Array}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const encodeStateAsUpdate = (doc, encodedTargetStateVector) => {
|
export const encodeStateAsUpdateV2 = (doc, encodedTargetStateVector, encoder = new UpdateEncoderV2()) => {
|
||||||
const encoder = encoding.createEncoder()
|
|
||||||
const targetStateVector = encodedTargetStateVector == null ? new Map() : decodeStateVector(encodedTargetStateVector)
|
const targetStateVector = encodedTargetStateVector == null ? new Map() : decodeStateVector(encodedTargetStateVector)
|
||||||
writeStateAsUpdate(encoder, doc, targetStateVector)
|
writeStateAsUpdate(encoder, doc, targetStateVector)
|
||||||
return encoding.toUint8Array(encoder)
|
return encoder.toUint8Array()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write all the document as a single update message that can be applied on the remote document. If you specify the state of the remote client (`targetState`) it will
|
||||||
|
* only write the operations that are missing.
|
||||||
|
*
|
||||||
|
* Use `writeStateAsUpdate` instead if you are working with lib0/encoding.js#Encoder
|
||||||
|
*
|
||||||
|
* @param {Doc} doc
|
||||||
|
* @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
export const encodeStateAsUpdate = (doc, encodedTargetStateVector) => encodeStateAsUpdateV2(doc, encodedTargetStateVector, new DefaultUpdateEncoder())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read state vector from Decoder and return as Map
|
* Read state vector from Decoder and return as Map
|
||||||
*
|
*
|
||||||
* @param {decoding.Decoder} decoder
|
* @param {AbstractDSDecoder} decoder
|
||||||
* @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
|
* @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readStateVector = decoder => {
|
export const readStateVector = decoder => {
|
||||||
const ss = new Map()
|
const ss = new Map()
|
||||||
const ssLength = decoding.readVarUint(decoder)
|
const ssLength = decoding.readVarUint(decoder.restDecoder)
|
||||||
for (let i = 0; i < ssLength; i++) {
|
for (let i = 0; i < ssLength; i++) {
|
||||||
const client = decoding.readVarUint(decoder)
|
const client = decoding.readVarUint(decoder.restDecoder)
|
||||||
const clock = decoding.readVarUint(decoder)
|
const clock = decoding.readVarUint(decoder.restDecoder)
|
||||||
ss.set(client, clock)
|
ss.set(client, clock)
|
||||||
}
|
}
|
||||||
return ss
|
return ss
|
||||||
@ -383,28 +478,34 @@ export const readStateVector = decoder => {
|
|||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const decodeStateVector = decodedState => readStateVector(decoding.createDecoder(decodedState))
|
export const decodeStateVectorV2 = decodedState => readStateVector(new DSDecoderV2(decoding.createDecoder(decodedState)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write State Vector to `lib0/encoding.js#Encoder`.
|
* Read decodedState and return State as Map.
|
||||||
*
|
*
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {Uint8Array} decodedState
|
||||||
|
* @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
export const decodeStateVector = decodedState => readStateVector(new DefaultDSDecoder(decoding.createDecoder(decodedState)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractDSEncoder} encoder
|
||||||
* @param {Map<number,number>} sv
|
* @param {Map<number,number>} sv
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const writeStateVector = (encoder, sv) => {
|
export const writeStateVector = (encoder, sv) => {
|
||||||
encoding.writeVarUint(encoder, sv.size)
|
encoding.writeVarUint(encoder.restEncoder, sv.size)
|
||||||
sv.forEach((clock, client) => {
|
sv.forEach((clock, client) => {
|
||||||
encoding.writeVarUint(encoder, client)
|
encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping
|
||||||
encoding.writeVarUint(encoder, clock)
|
encoding.writeVarUint(encoder.restEncoder, clock)
|
||||||
})
|
})
|
||||||
return encoder
|
return encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write State Vector to `lib0/encoding.js#Encoder`.
|
* @param {AbstractDSEncoder} encoder
|
||||||
*
|
|
||||||
* @param {encoding.Encoder} encoder
|
|
||||||
* @param {Doc} doc
|
* @param {Doc} doc
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
@ -415,12 +516,22 @@ export const writeDocumentStateVector = (encoder, doc) => writeStateVector(encod
|
|||||||
* Encode State as Uint8Array.
|
* Encode State as Uint8Array.
|
||||||
*
|
*
|
||||||
* @param {Doc} doc
|
* @param {Doc} doc
|
||||||
|
* @param {AbstractDSEncoder} [encoder]
|
||||||
* @return {Uint8Array}
|
* @return {Uint8Array}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const encodeStateVector = doc => {
|
export const encodeStateVectorV2 = (doc, encoder = new DSEncoderV2()) => {
|
||||||
const encoder = encoding.createEncoder()
|
|
||||||
writeDocumentStateVector(encoder, doc)
|
writeDocumentStateVector(encoder, doc)
|
||||||
return encoding.toUint8Array(encoder)
|
return encoder.toUint8Array()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode State as Uint8Array.
|
||||||
|
*
|
||||||
|
* @param {Doc} doc
|
||||||
|
* @return {Uint8Array}
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
export const encodeStateVector = doc => encodeStateVectorV2(doc, new DefaultDSEncoder())
|
||||||
|
@ -394,21 +394,21 @@ export const applyRandomTests = (tc, mods, iterations, initTestObject) => {
|
|||||||
const result = init(tc, { users: 5 }, initTestObject)
|
const result = init(tc, { users: 5 }, initTestObject)
|
||||||
const { testConnector, users } = result
|
const { testConnector, users } = result
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
if (prng.int31(gen, 0, 100) <= 2) {
|
if (prng.int32(gen, 0, 100) <= 2) {
|
||||||
// 2% chance to disconnect/reconnect a random user
|
// 2% chance to disconnect/reconnect a random user
|
||||||
if (prng.bool(gen)) {
|
if (prng.bool(gen)) {
|
||||||
testConnector.disconnectRandom()
|
testConnector.disconnectRandom()
|
||||||
} else {
|
} else {
|
||||||
testConnector.reconnectRandom()
|
testConnector.reconnectRandom()
|
||||||
}
|
}
|
||||||
} else if (prng.int31(gen, 0, 100) <= 1) {
|
} else if (prng.int32(gen, 0, 100) <= 1) {
|
||||||
// 1% chance to flush all
|
// 1% chance to flush all
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
} else if (prng.int31(gen, 0, 100) <= 50) {
|
} else if (prng.int32(gen, 0, 100) <= 50) {
|
||||||
// 50% chance to flush a random message
|
// 50% chance to flush a random message
|
||||||
testConnector.flushRandomMessage()
|
testConnector.flushRandomMessage()
|
||||||
}
|
}
|
||||||
const user = prng.int31(gen, 0, users.length - 1)
|
const user = prng.int32(gen, 0, users.length - 1)
|
||||||
const test = prng.oneOf(gen, mods)
|
const test = prng.oneOf(gen, mods)
|
||||||
test(users[user], gen, result.testObjects[user])
|
test(users[user], gen, result.testObjects[user])
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,18 @@ import * as t from 'lib0/testing.js'
|
|||||||
import * as prng from 'lib0/prng.js'
|
import * as prng from 'lib0/prng.js'
|
||||||
import * as math from 'lib0/math.js'
|
import * as math from 'lib0/math.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testBasicUpdate = tc => {
|
||||||
|
const doc1 = new Y.Doc()
|
||||||
|
const doc2 = new Y.Doc()
|
||||||
|
doc1.getArray('array').insert(0, ['hi'])
|
||||||
|
const update = Y.encodeStateAsUpdate(doc1)
|
||||||
|
Y.applyUpdate(doc2, update)
|
||||||
|
t.compare(doc2.getArray('array').toArray(), ['hi'])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
@ -335,23 +347,23 @@ const arrayTransactions = [
|
|||||||
const yarray = user.getArray('array')
|
const yarray = user.getArray('array')
|
||||||
var uniqueNumber = getUniqueNumber()
|
var uniqueNumber = getUniqueNumber()
|
||||||
var content = []
|
var content = []
|
||||||
var len = prng.int31(gen, 1, 4)
|
var len = prng.int32(gen, 1, 4)
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
content.push(uniqueNumber)
|
content.push(uniqueNumber)
|
||||||
}
|
}
|
||||||
var pos = prng.int31(gen, 0, yarray.length)
|
var pos = prng.int32(gen, 0, yarray.length)
|
||||||
yarray.insert(pos, content)
|
yarray.insert(pos, content)
|
||||||
},
|
},
|
||||||
function insertTypeArray (user, gen) {
|
function insertTypeArray (user, gen) {
|
||||||
const yarray = user.getArray('array')
|
const yarray = user.getArray('array')
|
||||||
var pos = prng.int31(gen, 0, yarray.length)
|
var pos = prng.int32(gen, 0, yarray.length)
|
||||||
yarray.insert(pos, [new Y.Array()])
|
yarray.insert(pos, [new Y.Array()])
|
||||||
var array2 = yarray.get(pos)
|
var array2 = yarray.get(pos)
|
||||||
array2.insert(0, [1, 2, 3, 4])
|
array2.insert(0, [1, 2, 3, 4])
|
||||||
},
|
},
|
||||||
function insertTypeMap (user, gen) {
|
function insertTypeMap (user, gen) {
|
||||||
const yarray = user.getArray('array')
|
const yarray = user.getArray('array')
|
||||||
var pos = prng.int31(gen, 0, yarray.length)
|
var pos = prng.int32(gen, 0, yarray.length)
|
||||||
yarray.insert(pos, [new Y.Map()])
|
yarray.insert(pos, [new Y.Map()])
|
||||||
var map = yarray.get(pos)
|
var map = yarray.get(pos)
|
||||||
map.set('someprop', 42)
|
map.set('someprop', 42)
|
||||||
@ -362,13 +374,13 @@ const arrayTransactions = [
|
|||||||
const yarray = user.getArray('array')
|
const yarray = user.getArray('array')
|
||||||
var length = yarray.length
|
var length = yarray.length
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
var somePos = prng.int31(gen, 0, length - 1)
|
var somePos = prng.int32(gen, 0, length - 1)
|
||||||
var delLength = prng.int31(gen, 1, math.min(2, length - somePos))
|
var delLength = prng.int32(gen, 1, math.min(2, length - somePos))
|
||||||
if (prng.bool(gen)) {
|
if (prng.bool(gen)) {
|
||||||
var type = yarray.get(somePos)
|
var type = yarray.get(somePos)
|
||||||
if (type.length > 0) {
|
if (type.length > 0) {
|
||||||
somePos = prng.int31(gen, 0, type.length - 1)
|
somePos = prng.int32(gen, 0, type.length - 1)
|
||||||
delLength = prng.int31(gen, 0, math.min(2, type.length - somePos))
|
delLength = prng.int32(gen, 0, math.min(2, type.length - somePos))
|
||||||
type.delete(somePos, delLength)
|
type.delete(somePos, delLength)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,7 +215,7 @@ const tryGc = () => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testLargeFragmentedDocument = tc => {
|
export const testLargeFragmentedDocument = tc => {
|
||||||
const itemsToInsert = 2000000
|
const itemsToInsert = 1000000
|
||||||
let update = /** @type {any} */ (null)
|
let update = /** @type {any} */ (null)
|
||||||
;(() => {
|
;(() => {
|
||||||
const doc1 = new Y.Doc()
|
const doc1 = new Y.Doc()
|
||||||
@ -230,14 +230,15 @@ export const testLargeFragmentedDocument = tc => {
|
|||||||
})
|
})
|
||||||
tryGc()
|
tryGc()
|
||||||
t.measureTime('time to encode document', () => {
|
t.measureTime('time to encode document', () => {
|
||||||
update = Y.encodeStateAsUpdate(doc1)
|
update = Y.encodeStateAsUpdateV2(doc1)
|
||||||
})
|
})
|
||||||
|
t.describe('Document size:', update.byteLength)
|
||||||
})()
|
})()
|
||||||
;(() => {
|
;(() => {
|
||||||
const doc2 = new Y.Doc()
|
const doc2 = new Y.Doc()
|
||||||
tryGc()
|
tryGc()
|
||||||
t.measureTime(`time to apply ${itemsToInsert} updates`, () => {
|
t.measureTime(`time to apply ${itemsToInsert} updates`, () => {
|
||||||
Y.applyUpdate(doc2, update)
|
Y.applyUpdateV2(doc2, update)
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user