reduce number of variables and sanity checks 😵
This commit is contained in:
parent
6b0154f046
commit
6dd26d3b48
@ -52,7 +52,7 @@ export class GC extends AbstractStruct {
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @return {null | ID}
|
* @return {null | number}
|
||||||
*/
|
*/
|
||||||
getMissing (transaction, store) {
|
getMissing (transaction, store) {
|
||||||
return null
|
return null
|
||||||
|
@ -290,8 +290,6 @@ export class Item extends AbstractStruct {
|
|||||||
*/
|
*/
|
||||||
this.content = content
|
this.content = content
|
||||||
this.info = this.content.isCountable() ? binary.BIT2 : 0
|
this.info = this.content.isCountable() ? binary.BIT2 : 0
|
||||||
|
|
||||||
// this.keep = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -330,50 +328,34 @@ export class Item extends AbstractStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return missing ids, or define missing items and return null.
|
* Return the creator clientID of the missing op or define missing items and return null.
|
||||||
*
|
*
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @return {null | ID}
|
* @return {null | number}
|
||||||
*/
|
*/
|
||||||
getMissing (transaction, store) {
|
getMissing (transaction, store) {
|
||||||
const origin = this.origin
|
if (this.origin && this.origin.client !== this.id.client && this.origin.clock >= getState(store, this.origin.client)) {
|
||||||
const rightOrigin = this.rightOrigin
|
return this.origin.client
|
||||||
const parent = /** @type {ID} */ (this.parent)
|
|
||||||
|
|
||||||
if (origin && origin.clock >= getState(store, origin.client)) {
|
|
||||||
return this.origin
|
|
||||||
}
|
}
|
||||||
if (rightOrigin && rightOrigin.clock >= getState(store, rightOrigin.client)) {
|
if (this.rightOrigin && this.rightOrigin.client !== this.id.client && this.rightOrigin.clock >= getState(store, this.rightOrigin.client)) {
|
||||||
return this.rightOrigin
|
return this.rightOrigin.client
|
||||||
}
|
}
|
||||||
if (parent && parent.constructor === ID && parent.clock >= getState(store, parent.client)) {
|
if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) {
|
||||||
return parent
|
return this.parent.client
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have all missing ids, now find the items
|
// We have all missing ids, now find the items
|
||||||
|
|
||||||
if (origin) {
|
if (this.origin) {
|
||||||
this.left = getItemCleanEnd(transaction, store, origin)
|
this.left = getItemCleanEnd(transaction, store, this.origin)
|
||||||
this.origin = this.left.lastId
|
this.origin = this.left.lastId
|
||||||
}
|
}
|
||||||
if (rightOrigin) {
|
if (this.rightOrigin) {
|
||||||
this.right = getItemCleanStart(transaction, rightOrigin)
|
this.right = getItemCleanStart(transaction, this.rightOrigin)
|
||||||
this.rightOrigin = this.right.id
|
this.rightOrigin = this.right.id
|
||||||
}
|
}
|
||||||
if (parent && parent.constructor === ID) {
|
// only set parent if this shouldn't be garbage collected
|
||||||
if (parent.clock < getState(store, parent.client)) {
|
|
||||||
const parentItem = getItem(store, parent)
|
|
||||||
if (parentItem.constructor === GC) {
|
|
||||||
this.parent = null
|
|
||||||
} else {
|
|
||||||
this.parent = /** @type {ContentType} */ (parentItem.content).type
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// only set item if this shouldn't be garbage collected
|
|
||||||
if (!this.parent) {
|
if (!this.parent) {
|
||||||
if (this.left && this.left.constructor === Item) {
|
if (this.left && this.left.constructor === Item) {
|
||||||
this.parent = this.left.parent
|
this.parent = this.left.parent
|
||||||
@ -383,6 +365,13 @@ export class Item extends AbstractStruct {
|
|||||||
this.parent = this.right.parent
|
this.parent = this.right.parent
|
||||||
this.parentSub = this.right.parentSub
|
this.parentSub = this.right.parentSub
|
||||||
}
|
}
|
||||||
|
} else if (this.parent.constructor === ID) {
|
||||||
|
const parentItem = getItem(store, this.parent)
|
||||||
|
if (parentItem.constructor === GC) {
|
||||||
|
this.parent = null
|
||||||
|
} else {
|
||||||
|
this.parent = /** @type {ContentType} */ (parentItem.content).type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -786,24 +775,11 @@ export const readItem = (decoder, id, info, doc) => {
|
|||||||
* @type {string|null}
|
* @type {string|null}
|
||||||
*/
|
*/
|
||||||
const parentYKey = canCopyParentInfo && hasParentYKey ? decoding.readVarString(decoder) : null
|
const parentYKey = canCopyParentInfo && hasParentYKey ? decoding.readVarString(decoder) : null
|
||||||
/**
|
|
||||||
* The parent type.
|
|
||||||
* @type {ID | AbstractType<any> | null}
|
|
||||||
*/
|
|
||||||
const parent = canCopyParentInfo && !hasParentYKey ? readID(decoder) : (parentYKey ? doc.get(parentYKey) : null)
|
|
||||||
/**
|
|
||||||
* If the parent refers to this item with some kind of key (e.g. YMap, the
|
|
||||||
* key is specified here. The key is then used to refer to the list in which
|
|
||||||
* to insert this item. If `parentSub = null` type._start is the list in
|
|
||||||
* which to insert to. Otherwise it is `parent._map`.
|
|
||||||
* @type {String | null}
|
|
||||||
*/
|
|
||||||
const parentSub = canCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoding.readVarString(decoder) : null
|
|
||||||
|
|
||||||
/**
|
return new Item(
|
||||||
* @type {AbstractContent}
|
id, null, origin, null, rightOrigin,
|
||||||
*/
|
canCopyParentInfo && !hasParentYKey ? readID(decoder) : (parentYKey ? doc.get(parentYKey) : null), // parent
|
||||||
const content = readItemContent(decoder, info)
|
canCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoding.readVarString(decoder) : null, // parentSub
|
||||||
|
/** @type {AbstractContent} */ (readItemContent(decoder, info)) // item content
|
||||||
return new Item(id, null, origin, null, rightOrigin, parent, parentSub, content)
|
)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ export const findIndexSS = (structs, clock) => {
|
|||||||
let right = structs.length - 1
|
let right = structs.length - 1
|
||||||
let mid = structs[right]
|
let mid = structs[right]
|
||||||
let midclock = mid.id.clock
|
let midclock = mid.id.clock
|
||||||
if (mid.id.clock === clock) {
|
if (midclock === clock) {
|
||||||
return right
|
return right
|
||||||
}
|
}
|
||||||
// @todo does it even make sense to pivot the search?
|
// @todo does it even make sense to pivot the search?
|
||||||
|
@ -100,20 +100,18 @@ export const readClientsStructRefs = (decoder, clientRefs, doc) => {
|
|||||||
const numOfStateUpdates = decoding.readVarUint(decoder)
|
const numOfStateUpdates = decoding.readVarUint(decoder)
|
||||||
for (let i = 0; i < numOfStateUpdates; i++) {
|
for (let i = 0; i < numOfStateUpdates; i++) {
|
||||||
const numberOfStructs = decoding.readVarUint(decoder)
|
const numberOfStructs = decoding.readVarUint(decoder)
|
||||||
const nextID = readID(decoder)
|
|
||||||
const nextIdClient = nextID.client
|
|
||||||
let nextIdClock = nextID.clock
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<GC|Item>}
|
* @type {Array<GC|Item>}
|
||||||
*/
|
*/
|
||||||
const refs = []
|
const refs = []
|
||||||
clientRefs.set(nextIdClient, refs)
|
let { client, clock } = readID(decoder)
|
||||||
|
let info, struct
|
||||||
|
clientRefs.set(client, refs)
|
||||||
for (let i = 0; i < numberOfStructs; i++) {
|
for (let i = 0; i < numberOfStructs; i++) {
|
||||||
const info = decoding.readUint8(decoder)
|
info = decoding.readUint8(decoder)
|
||||||
const id = createID(nextIdClient, nextIdClock)
|
struct = (binary.BITS5 & info) === 0 ? new GC(createID(client, clock), decoding.readVarUint(decoder)) : readItem(decoder, createID(client, clock), info, doc)
|
||||||
const struct = (binary.BITS5 & info) === 0 ? new GC(id, decoding.readVarUint(decoder)) : readItem(decoder, id, info, doc)
|
|
||||||
refs.push(struct)
|
refs.push(struct)
|
||||||
nextIdClock += struct.length
|
clock += struct.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return clientRefs
|
return clientRefs
|
||||||
@ -147,14 +145,21 @@ export const readClientsStructRefs = (decoder, clientRefs, doc) => {
|
|||||||
const resumeStructIntegration = (transaction, store) => {
|
const resumeStructIntegration = (transaction, store) => {
|
||||||
const stack = store.pendingStack
|
const stack = store.pendingStack
|
||||||
const clientsStructRefs = store.pendingClientsStructRefs
|
const clientsStructRefs = store.pendingClientsStructRefs
|
||||||
|
// sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user.
|
||||||
|
const clientsStructRefsIds = Array.from(clientsStructRefs.keys()).sort((a, b) => a - b)
|
||||||
|
let curStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]))
|
||||||
// iterate over all struct readers until we are done
|
// iterate over all struct readers until we are done
|
||||||
while (stack.length !== 0 || clientsStructRefs.size !== 0) {
|
while (stack.length !== 0 || clientsStructRefsIds.length > 0) {
|
||||||
if (stack.length === 0) {
|
if (stack.length === 0) {
|
||||||
// take any first struct from clientsStructRefs and put it on the stack
|
// take any first struct from clientsStructRefs and put it on the stack
|
||||||
const [client, structRefs] = clientsStructRefs.entries().next().value
|
if (curStructsTarget.i < curStructsTarget.refs.length) {
|
||||||
stack.push(structRefs.refs[structRefs.i++])
|
stack.push(curStructsTarget.refs[curStructsTarget.i++])
|
||||||
if (structRefs.refs.length === structRefs.i) {
|
} else {
|
||||||
clientsStructRefs.delete(client)
|
clientsStructRefsIds.pop()
|
||||||
|
if (clientsStructRefsIds.length > 0) {
|
||||||
|
curStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]))
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const ref = stack[stack.length - 1]
|
const ref = stack[stack.length - 1]
|
||||||
@ -163,12 +168,11 @@ const resumeStructIntegration = (transaction, store) => {
|
|||||||
const refClock = refID.clock
|
const refClock = refID.clock
|
||||||
const localClock = getState(store, client)
|
const localClock = getState(store, client)
|
||||||
const offset = refClock < localClock ? localClock - refClock : 0
|
const offset = refClock < localClock ? localClock - refClock : 0
|
||||||
const missing = ref.getMissing(transaction, store)
|
|
||||||
if (refClock + offset !== localClock) {
|
if (refClock + offset !== localClock) {
|
||||||
// A previous message from this client is missing
|
// A previous message from this client is missing
|
||||||
// check if there is a pending structRef with a smaller clock and switch them
|
// check if there is a pending structRef with a smaller clock and switch them
|
||||||
const structRefs = clientsStructRefs.get(client)
|
const structRefs = clientsStructRefs.get(client) || { refs: [], i: 0 }
|
||||||
if (structRefs !== undefined) {
|
if (structRefs.refs.length !== structRefs.i) {
|
||||||
const r = structRefs.refs[structRefs.i]
|
const r = structRefs.refs[structRefs.i]
|
||||||
if (r.id.clock < refClock) {
|
if (r.id.clock < refClock) {
|
||||||
// put ref with smaller clock on stack instead and continue
|
// put ref with smaller clock on stack instead and continue
|
||||||
@ -183,18 +187,15 @@ const resumeStructIntegration = (transaction, store) => {
|
|||||||
// wait until missing struct is available
|
// wait until missing struct is available
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (missing) {
|
const missing = ref.getMissing(transaction, store)
|
||||||
const client = missing.client
|
if (missing !== null) {
|
||||||
// get the struct reader that has the missing struct
|
// get the struct reader that has the missing struct
|
||||||
const structRefs = clientsStructRefs.get(client)
|
const structRefs = clientsStructRefs.get(missing) || { refs: [], i: 0 }
|
||||||
if (structRefs === undefined) {
|
if (structRefs.refs.length === structRefs.i) {
|
||||||
// This update message causally depends on another update message.
|
// This update message causally depends on another update message.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stack.push(structRefs.refs[structRefs.i++])
|
stack.push(structRefs.refs[structRefs.i++])
|
||||||
if (structRefs.i === structRefs.refs.length) {
|
|
||||||
clientsStructRefs.delete(client)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (offset < ref.length) {
|
if (offset < ref.length) {
|
||||||
ref.integrate(transaction, offset)
|
ref.integrate(transaction, offset)
|
||||||
@ -202,6 +203,7 @@ const resumeStructIntegration = (transaction, store) => {
|
|||||||
stack.pop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
store.pendingClientsStructRefs.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,6 +255,21 @@ const mergeReadStructsIntoPendingReads = (store, clientsStructsRefs) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Map<number,{refs:Array<GC|Item>,i:number}>} pendingClientsStructRefs
|
||||||
|
*/
|
||||||
|
const cleanupPendingStructs = pendingClientsStructRefs => {
|
||||||
|
// cleanup pendingClientsStructs if not fully finished
|
||||||
|
for (const [client, refs] of pendingClientsStructRefs) {
|
||||||
|
if (refs.i === refs.refs.length) {
|
||||||
|
pendingClientsStructRefs.delete(client)
|
||||||
|
} else {
|
||||||
|
refs.refs.splice(0, refs.i)
|
||||||
|
refs.i = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the next Item in a Decoder and fill this Item with the read data.
|
* Read the next Item in a Decoder and fill this Item with the read data.
|
||||||
*
|
*
|
||||||
@ -270,6 +287,7 @@ export const readStructs = (decoder, transaction, store) => {
|
|||||||
readClientsStructRefs(decoder, clientsStructRefs, transaction.doc)
|
readClientsStructRefs(decoder, clientsStructRefs, transaction.doc)
|
||||||
mergeReadStructsIntoPendingReads(store, clientsStructRefs)
|
mergeReadStructsIntoPendingReads(store, clientsStructRefs)
|
||||||
resumeStructIntegration(transaction, store)
|
resumeStructIntegration(transaction, store)
|
||||||
|
cleanupPendingStructs(store.pendingClientsStructRefs)
|
||||||
tryResumePendingDeleteReaders(transaction, store)
|
tryResumePendingDeleteReaders(transaction, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user