diff --git a/src/Database.js b/src/Database.js index 650a8f4f..2beb4e25 100644 --- a/src/Database.js +++ b/src/Database.js @@ -216,11 +216,15 @@ module.exports = function (Y /* :any */) { whenUserIdSet (f) { this.userIdPromise.then(f) } - getNextOpId () { - if (this.userId == null) { + getNextOpId (numberOfIds) { + if (numberOfIds == null) { + throw new Error('getNextOpId expects the number of created ids to create!') + } else if (this.userId == null) { throw new Error('OperationStore not yet initialized!') } else { - return [this.userId, this.opClock++] + var id = [this.userId, this.opClock] + this.opClock += numberOfIds + return id } } /* @@ -294,7 +298,7 @@ module.exports = function (Y /* :any */) { for (var sid in ls) { var l = ls[sid] var id = JSON.parse(sid) - var op = yield* this.getOperation(id) + var op = yield* this.getInsertion(id) if (op == null) { store.listenersById[sid] = l } else { diff --git a/src/SpecHelper.js b/src/SpecHelper.js index e2afb847..303153c1 100644 --- a/src/SpecHelper.js +++ b/src/SpecHelper.js @@ -25,7 +25,7 @@ g.g = g g.YConcurrency_TestingMode = true -jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000 +jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000 g.describeManyTimes = function describeManyTimes (times, name, f) { for (var i = 0; i < times; i++) { diff --git a/src/Struct.js b/src/Struct.js index 32a1d30c..2ca4d6a3 100644 --- a/src/Struct.js +++ b/src/Struct.js @@ -93,21 +93,17 @@ module.exports = function (Y/* :any */) { return ids }, getDistanceToOrigin: function * (op) { - if (op.left == null) { - return 0 - } else { - var d = 0 - var o = yield* this.getInsertion(op.left) - while (!Y.utils.compareIds(op.origin, (o ? o.id : null))) { - d++ - if (o.left == null) { - break - } else { - o = yield* this.getInsertion(o.left) - } + var d = 0 + var o = op + while (!Y.utils.matchesId(o, op.origin)) { + d++ + if (o.left == null) { + break + } else { + o = yield* this.getInsertion(o.left) } - return d } + return d }, /* # $this has to find a unique position between origin and the next known character @@ -162,14 +158,14 @@ module.exports = function (Y/* :any */) { if (oOriginDistance === i) { // case 1 if (o.id[0] < op.id[0]) { - op.left = o.id - distanceToOrigin = i + 1 + op.left = Y.utils.getLastId(o) + distanceToOrigin = i + 1 // just ignore o.content.length, doesn't make a difference } } else if (oOriginDistance < i) { // case 2 if (i - distanceToOrigin <= oOriginDistance) { - op.left = o.id - distanceToOrigin = i + 1 + op.left = Y.utils.getLastId(o) + distanceToOrigin = i + 1 // just ignore o.content.length, doesn't make a difference } } else { break @@ -194,7 +190,8 @@ module.exports = function (Y/* :any */) { // reconnect left and set right of op if (op.left != null) { - left = yield* this.getOperation(op.left) + left = yield* this.getInsertion(op.left) + // TODO: remove false if (false && op.content != null && left.content != null && left.id[0] === op.id[0] && left.id[1] + left.content.length === op.id[1] && left.originOf == null && left.deleted !== true && left.gc !== true) { // extend left left.content = left.content.concat(op.content) @@ -214,7 +211,7 @@ module.exports = function (Y/* :any */) { if (op.right != null) { // TODO: wanna connect right too? right = yield* this.getOperation(op.right) - right.left = op.id + right.left = Y.utils.getLastId(op) // if right exists, and it is supposed to be gc'd. Remove it from the gc if (right.gc != null) { @@ -257,7 +254,7 @@ module.exports = function (Y/* :any */) { end: null, struct: "List", type: "", - id: this.os.getNextOpId() + id: this.os.getNextOpId(1) } */ create: function (id) { @@ -338,7 +335,7 @@ module.exports = function (Y/* :any */) { map: {}, struct: "Map", type: "", - id: this.os.getNextOpId() + id: this.os.getNextOpId(1) } */ create: function (id) { diff --git a/src/Transaction.js b/src/Transaction.js index b0a54eb8..99c37702 100644 --- a/src/Transaction.js +++ b/src/Transaction.js @@ -101,7 +101,7 @@ module.exports = function (Y/* :any */) { } * createType (typedefinition, id) { var structname = typedefinition[0].struct - id = id || this.store.getNextOpId() + id = id || this.store.getNextOpId(1) var op if (id[0] === '_') { op = yield* this.getOperation(id) @@ -121,7 +121,7 @@ module.exports = function (Y/* :any */) { } /* createType (typedefinition, id) { var structname = typedefinition[0].struct - id = id || this.store.getNextOpId() + id = id || this.store.getNextOpId(1) var op = Y.Struct[structname].create(id) op.type = typedefinition[0].name if (typedefinition[0].appendAdditionalInfo != null) { @@ -224,7 +224,7 @@ module.exports = function (Y/* :any */) { } var left if (target.left != null) { - left = yield* this.getOperation(target.left) + left = yield* this.getInsertion(target.left) } else { left = null } @@ -417,7 +417,7 @@ module.exports = function (Y/* :any */) { } } if (op.deleted && op.left != null) { - var left = yield* this.getOperation(op.left) + var left = yield* this.getInsertion(op.left) this.store.addToGarbageCollector(op, left) } } @@ -434,7 +434,7 @@ module.exports = function (Y/* :any */) { */ * garbageCollectOperation (id) { this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')') - var o = yield* this.getOperation(id) + var o = yield* this.getInsertionCleanStartEnd(id) yield* this.markGarbageCollected(id, 1) // always mark gc'd // if op exists, then clean that mess up.. if (o != null) { @@ -470,7 +470,7 @@ module.exports = function (Y/* :any */) { // remove gc'd op from the left op, if it exists if (o.left != null) { - var left = yield* this.getOperation(o.left) + var left = yield* this.getInsertion(o.left) left.right = o.right yield* this.setOperation(left) } @@ -486,7 +486,7 @@ module.exports = function (Y/* :any */) { var neworigin = o.left var neworigin_ = null while (neworigin != null) { - neworigin_ = yield* this.getOperation(neworigin) + neworigin_ = yield* this.getInsertion(neworigin) if (neworigin_.deleted) { break } @@ -554,7 +554,7 @@ module.exports = function (Y/* :any */) { // o may originate in another operation. // Since o is deleted, we have to reset o.origin's `originOf` property if (o.origin != null) { - var origin = yield* this.getOperation(o.origin) + var origin = yield* this.getInsertion(o.origin) origin.originOf = origin.originOf.filter(function (_id) { return !Y.utils.compareIds(id, _id) }) @@ -604,7 +604,7 @@ module.exports = function (Y/* :any */) { var o = yield* this.getOperation([user, state.clock]) while (o != null && o.id[1] === state.clock && user === o.id[0]) { // either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS - state.clock++ + state.clock += o.content == null ? 1 : o.content.length yield* this.checkDeleteStoreForState(state) o = yield* this.os.findNext(o.id) } @@ -762,7 +762,7 @@ module.exports = function (Y/* :any */) { } * getInsertionCleanStartEnd (id) { yield* this.getInsertionCleanStart(id) - yield* this.getInsertionCleanEnd(id) + return yield* this.getInsertionCleanEnd(id) } // Return an insertion such that id is the first element of content // This function manipulates an operation, if necessary @@ -775,11 +775,12 @@ module.exports = function (Y/* :any */) { var left = Y.utils.copyObject(ins) ins.content = left.content.splice(ins.id[1] - id[1]) ins.id = id - ins.origin = left.id + var leftLid = Y.utils.getLastId(left) + ins.origin = leftLid left.originOf = [ins.id] left.right = ins.id - ins.left = left.id - debugger // check + ins.left = leftLid + // debugger // check yield* this.setOperation(left) yield* this.setOperation(ins) return ins @@ -799,11 +800,12 @@ module.exports = function (Y/* :any */) { var right = Y.utils.copyObject(ins) right.content = ins.content.splice(-(ins.id[1] + ins.content.length - 1 - id[1])) // cut off remainder right.id = [id[0], id[1] + 1] - right.origin = ins.id + var insLid = Y.utils.getLastId(ins) + right.origin = insLid ins.originOf = [right.id] ins.right = right.id - right.left = ins.id - debugger // check + right.left = insLid + // debugger // check yield* this.setOperation(right) yield* this.setOperation(ins) return ins @@ -961,17 +963,17 @@ module.exports = function (Y/* :any */) { } break } - o = yield* this.getOperation(o.left) + o = yield* this.getInsertion(o.left) // we set another o, check if we can reduce $missing_origins while (missing_origins.length > 0 && Y.utils.compareIds(missing_origins[missing_origins.length - 1].origin, o.id)) { missing_origins.pop() } if (o.id[1] < (startSS[o.id[0]] || 0)) { // case 2. o is known - op.left = o.id + op.left = Y.utils.getLastId(o) send.push(op) break - } else if (Y.utils.compareIds(o.id, op.origin)) { + } else if (Y.utils.matchesId(o, op.origin)) { // case 3. o is op.origin op.left = op.origin send.push(op) diff --git a/src/Utils.js b/src/Utils.js index 2f87c4de..f15ca718 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -244,22 +244,37 @@ module.exports = function (Y /* : any*/) { function compareIds (id1, id2) { if (id1 == null || id2 == null) { - if (id1 == null && id2 == null) { - return true - } - return false - } - if (id1[0] === id2[0]) { - var add1 = id1.length > 2 ? id1[2] : 0 - var add2 = id2.length > 2 ? id2[2] : 0 - if (id1[1] + add1 === id2[1] + add2) { - return true - } - } - return false + return id1 === id2 + } else { + return id1[0] === id2[0] && id1[1] === id2[1] + } } Y.utils.compareIds = compareIds + function matchesId (op, id) { + if (id == null || op == null) { + return id === op + } else { + if (id[0] === op.id[0]) { + if (op.content == null) { + return id[1] === op.id[1] + } else { + return id[1] >= op.id[1] && id[1] < op.id[1] + op.content.length + } + } + } + } + Y.utils.matchesId = matchesId + + function getLastId (op) { + if (op.content == null || op.content.length === 1) { + return op.id + } else { + return [op.id[0], op.id[1] + op.content.length - 1] + } + } + Y.utils.getLastId = getLastId + function createEmptyOpsArray (n) { var a = new Array(n) for (var i = 0; i < a.length; i++) { @@ -288,7 +303,7 @@ module.exports = function (Y /* : any*/) { this.writeBuffer = createEmptyOpsArray(5) this.readBuffer = createEmptyOpsArray(10) } - * find (id) { + * find (id, noSuperCall) { var i, r for (i = this.readBuffer.length - 1; i >= 0; i--) { r = this.readBuffer[i] @@ -311,7 +326,7 @@ module.exports = function (Y /* : any*/) { break } } - if (i < 0) { + if (i < 0 && noSuperCall === undefined) { // did not reach break in last loop // read id and put it to the end of readBuffer o = yield* super.find(id) @@ -378,7 +393,7 @@ module.exports = function (Y /* : any*/) { yield* super.delete(id) } * findWithLowerBound (id) { - var o = yield* this.find(id) + var o = yield* this.find(id, true) if (o != null) { return o } else { @@ -387,7 +402,7 @@ module.exports = function (Y /* : any*/) { } } * findWithUpperBound (id) { - var o = yield* this.find(id) + var o = yield* this.find(id, true) if (o != null) { return o } else {