From 0b23d5aeeb72d42fcb2e61d7fcc82672a7c8d948 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 16 Dec 2020 22:53:11 +0100 Subject: [PATCH] First working version of differential updates - #263 --- src/utils/updates.js | 65 +++++++++++++++++++++++++----------------- tests/updates.tests.js | 5 +++- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/utils/updates.js b/src/utils/updates.js index 9fa49f8a..90441b0b 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -216,40 +216,53 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U // write from currDecoder until the next operation is from another client or if filler-struct // then we need to reorder the decoders and find the next operation to write const firstClient = /** @type {Item | GC} */ (currDecoder.curr).id.client + if (currWrite !== null) { - let curr = /** @type {Item | GC} */ (currDecoder.curr) + let curr = /** @type {Item | GC | null} */ (currDecoder.curr) + + // iterate until we find something that we haven't written already + // remember: first the high client-ids are written + while (curr !== null && curr.id.clock + curr.length <= currWrite.struct.id.clock + currWrite.struct.length && curr.id.client >= currWrite.struct.id.client) { + curr = currDecoder.next() + } + if (curr === null || curr.id.client !== firstClient) { + continue + } + if (firstClient !== currWrite.struct.id.client) { writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset) currWrite = { struct: curr, offset: 0 } currDecoder.next() - } else if (currWrite.struct.id.clock + currWrite.struct.length < curr.id.clock) { - // @todo write currStruct & set currStruct = Skip(clock = currStruct.id.clock + currStruct.length, length = curr.id.clock - self.clock) - if (currWrite.struct.constructor === Skip) { - // extend existing skip - currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock - } else { - writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset) - const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length - /** - * @type {Skip} - */ - const struct = new Skip(createID(firstClient, currWrite.struct.id.clock + currWrite.struct.length), diff) - currWrite = { struct, offset: 0 } - } - } else if (currWrite.struct.id.clock + currWrite.struct.length >= curr.id.clock) { - const diff = currWrite.struct.id.clock + currWrite.struct.length - curr.id.clock - if (diff > 0) { + } else { + if (currWrite.struct.id.clock + currWrite.struct.length < curr.id.clock) { + // @todo write currStruct & set currStruct = Skip(clock = currStruct.id.clock + currStruct.length, length = curr.id.clock - self.clock) if (currWrite.struct.constructor === Skip) { - // prefer to slice Skip because the other struct might contain more information - currWrite.struct.length -= diff + // extend existing skip + currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock } else { - curr = sliceStruct(curr, diff) + writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset) + const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length + /** + * @type {Skip} + */ + const struct = new Skip(createID(firstClient, currWrite.struct.id.clock + currWrite.struct.length), diff) + currWrite = { struct, offset: 0 } + } + } else { // if (currWrite.struct.id.clock + currWrite.struct.length >= curr.id.clock) { + const diff = currWrite.struct.id.clock + currWrite.struct.length - curr.id.clock + if (diff > 0) { + if (currWrite.struct.constructor === Skip) { + // prefer to slice Skip because the other struct might contain more information + currWrite.struct.length -= diff + } else { + curr = sliceStruct(curr, diff) + } + } + if (!currWrite.struct.mergeWith(/** @type {any} */ (curr))) { + writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset) + currWrite = { struct: curr, offset: 0 } + currDecoder.next() } - } - if (!currWrite.struct.mergeWith(/** @type {any} */ (curr))) { - writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset) - currWrite = { struct: curr, offset: 0 } - currDecoder.next() } } } else { diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 25fec654..d52388c8 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -59,7 +59,10 @@ export const testMergeUpdatesWrongOrder = tc => { const targetState = Y.encodeStateAsUpdate(ydoc) t.info('Target State: ') Y.logUpdate(targetState) - ;[wrongOrder, overlapping, separated].forEach((updates, i) => { + const allcases = [wrongOrder, overlapping, separated] + // case 4: merging all cases above + allcases.push(Y.mergeUpdates(allcases)) + allcases.forEach((updates, i) => { t.info('State $' + i + ':') Y.logUpdate(updates) const merged = new Y.Doc()