after refactor - some tests are working again
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user