diff --git a/src/structs/AbstractItem.js b/src/structs/AbstractItem.js index ba8c02b5..f61a5484 100644 --- a/src/structs/AbstractItem.js +++ b/src/structs/AbstractItem.js @@ -221,7 +221,7 @@ export class AbstractItem extends AbstractStruct { } } // adjust the length of parent - if (parentSub === null && this.countable) { + if (parentSub === null && this.countable && !this.deleted) { parent._length += length } addStruct(store, this) @@ -393,8 +393,8 @@ export class AbstractItem extends AbstractStruct { mergeWith (right) { if (compareIDs(right.origin, this.lastId) && this.right === right && compareIDs(this.rightOrigin, right.rightOrigin)) { this.right = right.right - if (right.right !== null) { - right.right = this + if (this.right !== null) { + this.right.left = this } return true } @@ -472,28 +472,27 @@ export class AbstractItem extends AbstractStruct { * @private */ write (encoder, offset, encodingRef) { + const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin + const rightOrigin = this.rightOrigin + const parentSub = this.parentSub 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 + (origin === null ? 0 : binary.BIT8) | // origin is defined + (rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined + (parentSub === null ? 0 : binary.BIT6) // parentSub is non-null encoding.writeUint8(encoder, info) - if (this.origin !== null) { - if (offset === 0) { - writeID(encoder, this.origin) - } else { - writeID(encoder, createID(this.id.client, this.id.clock + offset - 1)) - } + if (origin !== null) { + writeID(encoder, origin) } - if (this.rightOrigin !== null) { - writeID(encoder, this.rightOrigin) + if (rightOrigin !== null) { + writeID(encoder, rightOrigin) } - if (this.origin === null && this.rightOrigin === null) { + if (origin === null && rightOrigin === null) { const parent = this.parent if (parent._item === null) { // parent type on y._map // find the correct key // @ts-ignore we know that y exists - const ykey = findRootTypeKey(this.parent) + const ykey = findRootTypeKey(parent) encoding.writeVarUint(encoder, 1) // write parentYKey encoding.writeVarString(encoder, ykey) } else { @@ -501,8 +500,8 @@ export class AbstractItem extends AbstractStruct { // @ts-ignore _item is defined because parent is integrated writeID(encoder, parent._item.id) } - if (this.parentSub !== null) { - encoding.writeVarString(encoder, this.parentSub) + if (parentSub !== null) { + encoding.writeVarString(encoder, parentSub) } } } diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index ee4c5c81..3fe77940 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -403,6 +403,7 @@ export const typeArrayInsertGenerics = (transaction, parent, index, content) => * @param {number} length */ export const typeArrayDelete = (transaction, parent, index, length) => { + if (length === 0) { return } let n = parent._start // compute the first item to be deleted for (; n !== null; n = n.right) { diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index c02440e1..b1f437af 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -189,13 +189,11 @@ export const readDeleteSet = (decoder, transaction, store) => { * @type {AbstractItem} */ // @ts-ignore - let struct = structs[index++] - if (!struct.deleted) { - if (struct.id.clock < clock) { - struct = struct.splitAt(store, clock - struct.id.clock) - structs.splice(index, 0, struct) - } - struct.delete(transaction) + let struct = structs[index] + // split the first item if necessary + if (!struct.deleted && struct.id.clock < clock) { + structs.splice(index + 1, 0, struct.splitAt(store, clock - struct.id.clock)) + index++ // increase we now want to use the next struct } while (index < structs.length) { // @ts-ignore diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 44fb523f..bb42a6f0 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -98,7 +98,7 @@ export const addStruct = (store, struct) => { */ export const findIndexSS = (structs, clock) => { let left = 0 - let right = structs.length + let right = structs.length - 1 while (left <= right) { const midindex = math.floor((left + right) / 2) const mid = structs[midindex] @@ -112,6 +112,8 @@ export const findIndexSS = (structs, clock) => { right = midindex - 1 } } + // Always check state before looking for a struct in StructStore + // Therefore the case of not finding a struct is unexpected throw error.unexpectedCase() } diff --git a/src/utils/Y.js b/src/utils/Y.js index 2bad4c12..37c724d7 100644 --- a/src/utils/Y.js +++ b/src/utils/Y.js @@ -62,6 +62,8 @@ export class Y extends Observable { * other peers. * * @param {function(Transaction):void} f The function that should be executed as a transaction + * + * @todo separate this into a separate function */ transact (f) { let initialCall = false diff --git a/src/utils/structEncoding.js b/src/utils/structEncoding.js index 65ae50e5..2cc89006 100644 --- a/src/utils/structEncoding.js +++ b/src/utils/structEncoding.js @@ -226,6 +226,9 @@ const execStructReaders = (transaction, store, localState, structReaders, stack) } } } + if (stack.length > 0) { + store.pendingStructReaders.add({ stack, structReaders, missing: stack[stack.length - 1].id }) + } } /** diff --git a/tests/testHelper.js b/tests/testHelper.js index a73e11e1..19fea109 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -322,14 +322,19 @@ export const compareStructStores = (ss1, ss2) => { if (s1 instanceof AbstractItem) { if ( !(s2 instanceof AbstractItem) || - !compareItemIDs(s1.left, s2.left) || + !((s1.left === null && s2.left === null) || (s1.left !== null && s2.left !== null && Y.compareIDs(s1.left.lastId, s2.left.lastId))) || !compareItemIDs(s1.right, s2.right) || !Y.compareIDs(s1.origin, s2.origin) || !Y.compareIDs(s1.rightOrigin, s2.rightOrigin) || s1.parentSub !== s2.parentSub ) { - t.fail('Items dont match') + return t.fail('Items dont match') } + // make sure that items are connected correctly + t.assert(s1.left === null || s1.left.right === s1) + t.assert(s1.right === null || s1.right.left === s1) + t.assert(s2.left === null || s2.left.right === s2) + t.assert(s2.right === null || s2.right.left === s2) } } } diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 7b51e271..aa50bdef 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -310,7 +310,7 @@ const arrayTransactions = [ * @param {t.TestCase} tc */ export const testRepeatGeneratingYarrayTests20 = tc => { - applyRandomTests(tc, arrayTransactions, 2) + applyRandomTests(tc, arrayTransactions, 3) } /**