after refactor - some tests are working again

This commit is contained in:
Kevin Jahns 2019-04-05 00:37:09 +02:00
parent 30bf3742c9
commit e56899a02c
23 changed files with 234 additions and 152 deletions

View File

@ -85,7 +85,8 @@ export default [{
}),
commonjs()
]
}, {
}
/* {
input: ['./examples/codemirror.js', './examples/textarea.js', './examples/quill.js', './examples/dom.js', './examples/prosemirror.js'], // fs.readdirSync('./examples').filter(file => /(?<!\.(test|config))\.js$/.test(file)).map(file => './examples/' + file),
output: {
dir: 'examples/build',
@ -102,4 +103,4 @@ export default [{
commonjs(),
...minificationPlugins
]
}]
} */]

View File

@ -11,6 +11,7 @@ import {
addStruct,
addToDeleteSet,
ItemDeleted,
findRootTypeKey,
ID, AbstractType, Y, Transaction // eslint-disable-line
} from '../internals.js'
@ -229,7 +230,7 @@ export class AbstractItem extends AbstractStruct {
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
}
// @ts-ignore
if (parent._item.deleted || (left !== null && parentSub !== null)) {
if ((parent._item !== null && parent._item.deleted) || (left !== null && parentSub !== null)) {
// delete if parent is deleted or if this is not the current attribute value of parent
this.delete(transaction)
} else if (parentSub !== null && left === null && right !== null) {
@ -443,16 +444,12 @@ export class AbstractItem extends AbstractStruct {
const info = (encodingRef & binary.BITS5) |
((this.origin === null) ? 0 : binary.BIT8) | // origin is defined
((this.rightOrigin === null) ? 0 : binary.BIT7) | // right origin is defined
((this.parentSub !== null) ? 0 : binary.BIT6) // parentSub is non-null
((this.parentSub === null) ? 0 : binary.BIT6) // parentSub is non-null
encoding.writeUint8(encoder, info)
if (offset === 0) {
writeID(encoder, this.id)
if (this.origin !== null) {
if (this.origin !== null) {
if (offset === 0) {
writeID(encoder, this.origin.lastId)
}
} else {
writeID(encoder, createID(this.id.client, this.id.clock + offset))
if (this.origin !== null) {
} else {
writeID(encoder, createID(this.id.client, this.id.clock + offset - 1))
}
}
@ -465,17 +462,7 @@ export class AbstractItem extends AbstractStruct {
// parent type on y._map
// find the correct key
// @ts-ignore we know that y exists
const map = parent._y.share
let ykey = null
for (const [key, type] of map) {
if (type === parent) {
ykey = key
break
}
}
if (ykey === null) {
throw error.unexpectedCase()
}
const ykey = findRootTypeKey(this.parent)
encoding.writeVarUint(encoder, 1) // write parentYKey
encoding.writeVarString(encoder, ykey)
} else {
@ -509,7 +496,7 @@ export class AbstractItemRef extends AbstractRef {
*/
this.right = (info & binary.BIT7) === binary.BIT7 ? readID(decoder) : null
const canCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
const hasParentYKey = decoding.readVarUint(decoder) === 1
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.
@ -541,16 +528,4 @@ export class AbstractItemRef extends AbstractRef {
missing.push(this.parent)
}
}
/**
* @param {Transaction} transaction
* @return {Array<ID|null>}
*/
getMissing (transaction) {
return [
createID(this.id.client, this.id.clock - 1),
this.left,
this.right,
this.parent
]
}
}

View File

@ -18,6 +18,7 @@ export class AbstractStruct {
* @readonly
*/
this.id = id
this.deleted = false
}
/**
* Merge this struct with the item to the right.
@ -35,12 +36,6 @@ export class AbstractStruct {
get length () {
throw error.methodUnimplemented()
}
/**
* @type {boolean}
*/
get deleted () {
throw error.methodUnimplemented()
}
/**
* @param {encoding.Encoder} encoder The encoder to write data to.
* @param {number} offset
@ -87,4 +82,10 @@ export class AbstractRef {
toStruct (transaction) {
throw error.methodUnimplemented()
}
/**
* @type {number}
*/
get length () {
return 1
}
}

View File

@ -25,11 +25,12 @@ export class GC extends AbstractStruct {
/**
* @type {number}
*/
this.length = length
this._len = length
this.deleted = true
}
get deleted () {
return true
get length () {
return this._len
}
/**
@ -37,7 +38,7 @@ export class GC extends AbstractStruct {
* @return {boolean}
*/
mergeWith (right) {
this.length += right.length
this._len += right.length
return true
}
@ -52,7 +53,7 @@ export class GC extends AbstractStruct {
} else {
writeID(encoder, createID(this.id.client, this.id.clock + offset))
}
encoding.writeVarUint(encoder, this.length)
encoding.writeVarUint(encoder, this._len)
}
}
@ -71,7 +72,10 @@ export class GCRef extends AbstractRef {
/**
* @type {number}
*/
this.length = decoding.readVarUint(decoder)
this._len = decoding.readVarUint(decoder)
}
get length () {
return this._len
}
missing () {
return [
@ -84,7 +88,7 @@ export class GCRef extends AbstractRef {
toStruct () {
return new GC(
this.id,
this.length
this._len
)
}
}

View File

@ -29,7 +29,11 @@ export class ItemDeleted extends AbstractItem {
*/
constructor (id, left, right, parent, parentSub, length) {
super(id, left, right, parent, parentSub)
this.length = length
this._len = length
this.deleted = true
}
get length () {
return this._len
}
/**
* @param {ID} id
@ -47,7 +51,7 @@ export class ItemDeleted extends AbstractItem {
*/
mergeWith (right) {
if (right.origin === this && this.right === right) {
this.length += right.length
this._len += right.length
return true
}
return false
@ -73,7 +77,10 @@ export class ItemDeletedRef extends AbstractItemRef {
/**
* @type {number}
*/
this.length = decoding.readVarUint(decoder)
this.len = decoding.readVarUint(decoder)
}
get length () {
return this.len
}
/**
* @param {Transaction} transaction
@ -89,7 +96,7 @@ export class ItemDeletedRef extends AbstractItemRef {
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.length
this.len
)
}
}

View File

@ -74,7 +74,7 @@ export class ItemEmbedRef extends AbstractItemRef {
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.embed
)

View File

@ -81,7 +81,7 @@ export class ItemFormatRef extends AbstractItemRef {
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.key,
this.value

View File

@ -108,6 +108,9 @@ export class ItemJSONRef extends AbstractItemRef {
}
this.content = cs
}
get length () {
return this.content.length
}
/**
* @param {Transaction} transaction
* @return {ItemJSON}
@ -120,7 +123,7 @@ export class ItemJSONRef extends AbstractItemRef {
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.content
)

View File

@ -97,6 +97,9 @@ export class ItemStringRef extends AbstractItemRef {
*/
this.string = decoding.readVarString(decoder)
}
get length () {
return this.string.length
}
/**
* @param {Transaction} transaction
* @return {ItemString}
@ -109,7 +112,7 @@ export class ItemStringRef extends AbstractItemRef {
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.string
)

View File

@ -49,6 +49,14 @@ export const typeRefs = [
readYXmlText
]
export const YArrayRefID = 0
export const YMapRefID = 1
export const YTextRefID = 2
export const YXmlElementRefID = 3
export const YXmlFragmentRefID = 4
export const YXmlHookRefID = 5
export const YXmlTextRefID = 6
export class ItemType extends AbstractItem {
/**
* @param {ID} id
@ -62,6 +70,7 @@ export class ItemType extends AbstractItem {
super(id, left, right, parent, parentSub)
this.type = type
}
getContent () {
return [this.type]
}
@ -80,7 +89,8 @@ export class ItemType extends AbstractItem {
* @param {Transaction} transaction
*/
integrate (transaction) {
this.type._integrate(transaction, this)
this.type._integrate(transaction.y, this)
super.integrate(transaction)
}
/**
* @param {encoding.Encoder} encoder
@ -168,7 +178,7 @@ export class ItemTypeRef extends AbstractItemRef {
this.left === null ? null : getItemCleanEnd(store, transaction, this.left),
this.right === null ? null : getItemCleanStart(store, transaction, this.right),
// @ts-ignore
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent),
this.parent === null ? y.get(this.parentYKey) : getItemType(store, this.parent).type,
this.parentSub,
this.type
)

View File

@ -68,12 +68,12 @@ export class AbstractType {
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Transaction} transaction The Yjs instance
* @param {ItemType} item
* @param {Y} y The Yjs instance
* @param {ItemType|null} item
* @private
*/
_integrate (transaction, item) {
this._y = transaction.y
_integrate (y, item) {
this._y = y
this._item = item
}
@ -87,9 +87,7 @@ export class AbstractType {
/**
* @param {encoding.Encoder} encoder
*/
_write (encoder) {
throw new Error('unimplemented')
}
_write (encoder) { }
/**
* The first non-deleted item
@ -329,12 +327,19 @@ export const typeArrayGet = (type, index) => {
* @param {Array<Object<string,any>|Array<any>|number|string|ArrayBuffer>} content
*/
export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem, content) => {
let left = referenceItem
const left = referenceItem
const right = referenceItem === null ? parent._start : referenceItem.right
/**
* @type {Array<Object|Array|number>}
*/
let jsonContent = []
const packJsonContent = () => {
if (jsonContent.length > 0) {
const item = new ItemJSON(nextID(transaction), left, right, parent, null, jsonContent)
item.integrate(transaction)
jsonContent = []
}
}
content.forEach(c => {
switch (c.constructor) {
case Number:
@ -344,11 +349,7 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
jsonContent.push(c)
break
default:
if (jsonContent.length > 0) {
const item = new ItemJSON(nextID(transaction), left, right, parent, null, jsonContent)
item.integrate(transaction)
jsonContent = []
}
packJsonContent()
switch (c.constructor) {
case ArrayBuffer:
// @ts-ignore c is definitely an ArrayBuffer
@ -363,6 +364,7 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
}
}
})
packJsonContent()
}
/**
@ -373,20 +375,22 @@ export const typeArrayInsertGenericsAfter = (transaction, parent, referenceItem,
*/
export const typeArrayInsertGenerics = (transaction, parent, index, content) => {
if (index === 0) {
typeArrayInsertGenericsAfter(transaction, parent, null, content)
return typeArrayInsertGenericsAfter(transaction, parent, null, content)
}
for (let n = parent._start; n !== null; n = n.right) {
let n = parent._start
for (; n !== null; n = n.right) {
if (!n.deleted && n.countable) {
if (index <= n.length) {
if (index < n.length) {
// insert in-between
getItemCleanStart(transaction.y.store, transaction, createID(n.id.client, n.id.clock + index))
}
return typeArrayInsertGenericsAfter(transaction, parent, n, content)
break
}
index -= n.length
}
}
throw new Error('Index exceeds array range')
return typeArrayInsertGenericsAfter(transaction, parent, n, content)
}
/**
@ -440,6 +444,10 @@ export const typeMapDelete = (transaction, parent, key) => {
*/
export const typeMapSet = (transaction, parent, key, value) => {
const right = parent._map.get(key) || null
if (value == null) {
new ItemJSON(nextID(transaction), null, right, parent, key, [value]).integrate(transaction)
return
}
switch (value.constructor) {
case Number:
case Object:

View File

@ -12,10 +12,12 @@ import {
typeArrayInsertGenerics,
typeArrayDelete,
typeArrayMap,
Transaction, ItemType, // eslint-disable-line
YArrayRefID,
Y, Transaction, ItemType, // eslint-disable-line
} 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
@ -52,12 +54,12 @@ export class YArray extends AbstractType {
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Transaction} transaction The Yjs instance
* @param {Y} y The Yjs instance
* @param {ItemType} item
* @private
*/
_integrate (transaction, item) {
super._integrate(transaction, item)
_integrate (y, item) {
super._integrate(y, item)
// @ts-ignore
this.insert(0, this._prelimContent)
this._prelimContent = null
@ -183,6 +185,13 @@ export class YArray extends AbstractType {
push (content) {
this.insert(this.length, content)
}
/**
* @param {encoding.Encoder} encoder
*/
_write (encoder) {
encoding.writeVarUint(encoder, YArrayRefID)
}
}
/**

View File

@ -10,9 +10,11 @@ import {
typeMapGet,
typeMapHas,
createMapIterator,
Transaction, ItemType, // eslint-disable-line
YMapRefID,
Y, Transaction, ItemType, // eslint-disable-line
} 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'
@ -53,12 +55,12 @@ export class YMap extends AbstractType {
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Transaction} transaction The Yjs instance
* @param {Y} y The Yjs instance
* @param {ItemType} item
* @private
*/
_integrate (transaction, item) {
super._integrate(transaction, item)
_integrate (y, item) {
super._integrate(y, item)
// @ts-ignore
for (let [key, value] of this._prelimContent) {
this.set(key, value)
@ -88,7 +90,8 @@ export class YMap extends AbstractType {
const map = {}
for (let [key, item] of this._map) {
if (!item.deleted) {
map[key] = item.getContent()[0]
const v = item.getContent()[0]
map[key] = v instanceof AbstractType ? v.toJSON() : v
}
}
return map
@ -169,6 +172,13 @@ export class YMap extends AbstractType {
has (key) {
return typeMapHas(this, key)
}
/**
* @param {encoding.Encoder} encoder
*/
_write (encoder) {
encoding.writeVarUint(encoder, YMapRefID)
}
}
/**

View File

@ -12,10 +12,12 @@ import {
createID,
getItemCleanStart,
isVisible,
ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
YTextRefID,
Y, ItemType, AbstractItem, Snapshot, StructStore, Transaction // eslint-disable-line
} from '../internals.js'
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
import * as encoding from 'lib0/encoding.js'
/**
* @private
@ -566,11 +568,11 @@ export class YText extends AbstractType {
}
/**
* @param {Transaction} transaction
* @param {Y} y
* @param {ItemType} item
*/
_integrate (transaction, item) {
super._integrate(transaction, item)
_integrate (y, item) {
super._integrate(y, item)
// @ts-ignore this._prelimContent is still defined
this.insert(0, this._prelimContent.join(''))
this._prelimContent = null
@ -839,6 +841,13 @@ export class YText extends AbstractType {
})
}
}
/**
* @param {encoding.Encoder} encoder
*/
_write (encoder) {
encoding.writeVarUint(encoder, YTextRefID)
}
}
/**

View File

@ -13,7 +13,8 @@ import {
typeArrayDelete,
typeMapSet,
typeMapDelete,
Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
YXmlElementRefID,
Y, Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
} from '../internals.js'
import * as encoding from 'lib0/encoding.js'
@ -259,12 +260,12 @@ export class YXmlElement extends YXmlFragment {
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Transaction} transaction The Yjs instance
* @param {Y} y The Yjs instance
* @param {ItemType} item
* @private
*/
_integrate (transaction, item) {
super._integrate(transaction, item)
_integrate (y, item) {
super._integrate(y, item)
// @ts-ignore
this.insert(0, this._prelimContent)
this._prelimContent = null
@ -285,19 +286,6 @@ export class YXmlElement extends YXmlFragment {
return new YXmlElement(this.nodeName)
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @private
* @param {encoding.Encoder} encoder The encoder to write data to.
*/
_write (encoder) {
encoding.writeVarString(encoder, this.nodeName)
}
toString () {
return this.toDomString()
}
@ -460,6 +448,20 @@ export class YXmlElement extends YXmlFragment {
}
return dom
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @private
* @param {encoding.Encoder} encoder The encoder to write data to.
*/
_write (encoder) {
encoding.writeVarUint(encoder, YXmlElementRefID)
encoding.writeVarString(encoder, this.nodeName)
}
}
/**

View File

@ -2,7 +2,10 @@
* @module types
*/
import { YMap } from '../internals.js'
import {
YMap,
YXmlHookRefID
} from '../internals.js'
import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js'
@ -71,6 +74,7 @@ export class YXmlHook extends YMap {
*/
_write (encoder) {
super._write(encoder)
encoding.writeVarUint(encoder, YXmlHookRefID)
encoding.writeVarString(encoder, this.hookName)
}
}

View File

@ -2,8 +2,9 @@
* @module types
*/
import { YText } from '../internals.js'
import { YText, YXmlTextRefID } from '../internals.js'
import * as encoding from 'lib0/encoding.js'
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
/**
@ -33,6 +34,12 @@ export class YXmlText extends YText {
}
return dom
}
/**
* @param {encoding.Encoder} encoder
*/
_write (encoder) {
encoding.writeVarUint(encoder, YXmlTextRefID)
}
}
/**

View File

@ -88,6 +88,9 @@ export const findIndexSS = (structs, clock) => {
if (clock < midclock + mid.length) {
return midindex
}
if (left === midindex) {
throw error.unexpectedCase()
}
left = midindex
} else {
right = midindex

View File

@ -9,6 +9,7 @@ import {
writeDeleteSet,
DeleteSet,
sortAndMergeDeleteSet,
getStates,
AbstractType, AbstractItem, YEvent, ItemType, Y // eslint-disable-line
} from '../internals.js'
@ -52,12 +53,12 @@ export class Transaction {
*/
this.deleteSet = new DeleteSet()
/**
* If a state was modified, the original value is saved here.
* Use `stateUpdates` to compute the original state before the transaction,
* or to compute the set of inserted operations.
* Holds the state before the transaction started.
* @type {Map<Number,Number>}
*/
this.stateUpdates = new Map()
this.beforeState = new Map()
getStates(y.store).forEach(({client, clock}) => { this.beforeState.set(client, clock) })
this.afterState = new Map()
/**
* All types that were directly modified (property added or child
* inserted/deleted). New types are not included in this Set.

View File

@ -1,4 +1,4 @@
import { } from './StructStore.js'
import { getStates } from './StructStore.js'
import {
callEventHandlerListeners,
@ -75,26 +75,29 @@ export class Y extends Observable {
if (initialCall) {
const transaction = this._transaction
this._transaction = null
// only call event listeners / observers if anything changed
this.emit('beforeObserverCalls', [this, this._transaction])
// emit change events on changed types
transaction.changed.forEach((subs, itemtype) => {
itemtype._callObserver(transaction, subs)
})
transaction.changedParentTypes.forEach((events, type) => {
events = events
.filter(event =>
event.target._item === null || !event.target._item.deleted
)
events
.forEach(event => {
event.currentTarget = type
})
// we don't need to check for events.length
// because we know it has at least one element
callEventHandlerListeners(type._dEH, [events, transaction])
})
// only call afterTransaction listeners if anything changed
const transactionChangedContent = transaction.changedParentTypes.size !== 0
if (transactionChangedContent) {
this.emit('beforeObserverCalls', [this, this._transaction])
// emit change events on changed types
transaction.changed.forEach((subs, itemtype) => {
itemtype._callObserver(transaction, subs)
})
transaction.changedParentTypes.forEach((events, type) => {
events = events
.filter(event =>
event.target._item === null || !event.target._item.deleted
)
events
.forEach(event => {
event.currentTarget = type
})
// we don't need to check for events.length
// because we know it has at least one element
callEventHandlerListeners(type._dEH, [events, transaction])
getStates(transaction.y.store).forEach(({client, clock}) => {
transaction.afterState.set(client, clock)
})
// when all changes & events are processed, emit afterTransaction event
this.emit('afterTransaction', [this, transaction])
@ -141,15 +144,17 @@ export class Y extends Observable {
}
}
// on all affected store.clients props, try to merge
for (const [client, clock] of transaction.stateUpdates) {
/**
* @type {Array<AbstractStruct>}
*/
// @ts-ignore
const structs = store.clients.get(client)
// we iterate from right to left so we can safely remove entries
for (let i = structs.length - 1; i >= math.max(findIndexSS(structs, clock), 1); i--) {
tryToMergeWithLeft(structs, i)
for (const [client, clock] of transaction.beforeState) {
if (transaction.afterState.get(client) !== clock) {
/**
* @type {Array<AbstractStruct>}
*/
// @ts-ignore
const structs = store.clients.get(client)
// we iterate from right to left so we can safely remove entries
for (let i = structs.length - 1; i >= math.max(findIndexSS(structs, clock), 1); i--) {
tryToMergeWithLeft(structs, i)
}
}
}
// try to merge replacedItems
@ -200,16 +205,21 @@ export class Y extends Observable {
* @return {AbstractType<any>} The created type. Constructed with TypeConstructor
*/
get (name, TypeConstructor = AbstractType) {
// @ts-ignore
const type = map.setIfUndefined(this.share, name, () => new TypeConstructor())
const type = map.setIfUndefined(this.share, name, () => {
// @ts-ignore
const t = new TypeConstructor()
t._integrate(this, null)
return t
})
const Constr = type.constructor
if (Constr !== TypeConstructor) {
if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
if (Constr === AbstractType) {
const t = new Constr()
t._map = type._map
t._start = type._start
t._length = type._length
this.share.set(name, t)
t._integrate(this, null)
return t
} else {
throw new Error(`Type with the name ${name} has already been defined with a different constructor`)

View File

@ -67,7 +67,7 @@ export class YEvent {
* @return {boolean}
*/
adds (struct) {
return struct.id.clock > (this.transaction.stateUpdates.get(struct.id.client) || 0)
return struct.id.clock > (this.transaction.beforeState.get(struct.id.client) || 0)
}
}

View File

@ -13,6 +13,8 @@ import {
writeID,
createID,
readID,
getState,
getStates,
Transaction, AbstractStruct, AbstractRef, StructStore, ID // eslint-disable-line
} from '../internals.js'
@ -51,7 +53,8 @@ const createStructReaderIterator = (decoder, structsLen, nextID) => iterator.cre
} else {
const info = decoding.readUint8(decoder)
value = new structRefs[binary.BITS5 & info](decoder, nextID, info)
nextID = createID(nextID.client, nextID.clock)
nextID = createID(nextID.client, nextID.clock + value.length)
structsLen--
}
return { done, value }
})
@ -60,18 +63,30 @@ const createStructReaderIterator = (decoder, structsLen, nextID) => iterator.cre
* @param {encoding.Encoder} encoder
* @param {Transaction} transaction
*/
export const writeStructsFromTransaction = (encoder, transaction) => writeStructs(encoder, transaction.y.store, transaction.stateUpdates)
export const writeStructsFromTransaction = (encoder, transaction) => writeStructs(encoder, transaction.y.store, transaction.beforeState)
/**
* @param {encoding.Encoder} encoder
* @param {StructStore} store
* @param {StateMap} sm
* @param {StateMap} _sm
*/
export const writeStructs = (encoder, store, sm) => {
export const writeStructs = (encoder, store, _sm) => {
// we filter all valid _sm entries into sm
const sm = new Map()
_sm.forEach((clock, client) => {
if (getState(store, client) > clock) {
sm.set(client, clock)
}
})
getStates(store).forEach(({client}) => {
if (!_sm.has(client)) {
sm.set(client, 0)
}
})
const encoderUserPosMap = map.create()
// write # states that were updated
encoding.writeVarUint(encoder, sm.size)
sm.forEach((client, clock) => {
sm.forEach((clock, client) => {
// write first id
writeID(encoder, createID(client, clock))
encoderUserPosMap.set(client, encoding.length(encoder))
@ -79,7 +94,7 @@ export const writeStructs = (encoder, store, sm) => {
// We will fill out this value later *)
encoding.writeUint32(encoder, 0)
})
sm.forEach((client, clock) => {
sm.forEach((clock, client) => {
const decPos = encoderUserPosMap.get(client)
encoding.setUint32(encoder, decPos, encoding.length(encoder) - decPos)
/**
@ -115,8 +130,8 @@ export const readStructs = (decoder, transaction, store) => {
* @type {Map<number,Iterator<AbstractRef>>}
*/
const structReaders = new Map()
const clientStateUpdates = decoding.readVarUint(decoder)
for (let i = 0; i < clientStateUpdates; i++) {
const clientbeforeState = decoding.readVarUint(decoder)
for (let i = 0; i < clientbeforeState; i++) {
const nextID = readID(decoder)
const decoderPos = decoder.pos + decoding.readUint32(decoder)
const structReaderDecoder = decoding.clone(decoder, decoderPos)

View File

@ -268,7 +268,7 @@ export const compare = users => {
while (users[0].tc.flushAllMessages()) {}
const userArrayValues = users.map(u => u.getArray('array').toJSON().map(val => JSON.stringify(val)))
const userMapValues = users.map(u => u.getMap('map').toJSON())
const userXmlValues = users.map(u => u.getXmlFragment('xml').toString())
const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString())
const userTextValues = users.map(u => u.getText('text').toDelta())
for (var i = 0; i < users.length - 1; i++) {
t.describe(`Comparing user${i} with user${i + 1}`)