refactoring the tarnsition functions
This commit is contained in:
		
							parent
							
								
									d6e1cd42a2
								
							
						
					
					
						commit
						541a93d152
					
				@ -18,7 +18,7 @@ g.g = g
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
g.YConcurrency_TestingMode = true
 | 
					g.YConcurrency_TestingMode = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000
 | 
					jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
g.describeManyTimes = function describeManyTimes (times, name, f) {
 | 
					g.describeManyTimes = function describeManyTimes (times, name, f) {
 | 
				
			||||||
  for (var i = 0; i < times; i++) {
 | 
					  for (var i = 0; i < times; i++) {
 | 
				
			||||||
@ -36,7 +36,7 @@ function wait (t) {
 | 
				
			|||||||
  return new Promise(function (resolve) {
 | 
					  return new Promise(function (resolve) {
 | 
				
			||||||
    setTimeout(function () {
 | 
					    setTimeout(function () {
 | 
				
			||||||
      resolve()
 | 
					      resolve()
 | 
				
			||||||
    }, t * 2)
 | 
					    }, t)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
g.wait = wait
 | 
					g.wait = wait
 | 
				
			||||||
@ -141,7 +141,7 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
 | 
				
			|||||||
    s1 = yield* this.getStateSet()
 | 
					    s1 = yield* this.getStateSet()
 | 
				
			||||||
    ds1 = yield* this.getDeleteSet()
 | 
					    ds1 = yield* this.getDeleteSet()
 | 
				
			||||||
    allDels1 = []
 | 
					    allDels1 = []
 | 
				
			||||||
    this.ds.iterate(null, null, function (d) {
 | 
					    yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
      allDels1.push(d)
 | 
					      allDels1.push(d)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -149,7 +149,7 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
 | 
				
			|||||||
    s2 = yield* this.getStateSet()
 | 
					    s2 = yield* this.getStateSet()
 | 
				
			||||||
    ds2 = yield* this.getDeleteSet()
 | 
					    ds2 = yield* this.getDeleteSet()
 | 
				
			||||||
    allDels2 = []
 | 
					    allDels2 = []
 | 
				
			||||||
    this.ds.iterate(null, null, function (d) {
 | 
					    yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
      allDels2.push(d)
 | 
					      allDels2.push(d)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -158,16 +158,16 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  for (var uid = 0; uid < users.length; uid++) {
 | 
					  for (var uid = 0; uid < users.length; uid++) {
 | 
				
			||||||
    var u = users[uid]
 | 
					    var u = users[uid]
 | 
				
			||||||
 | 
					    u.db.requestTransaction(function * () {
 | 
				
			||||||
      // compare deleted ops against deleteStore
 | 
					      // compare deleted ops against deleteStore
 | 
				
			||||||
    u.db.os.iterate(null, null, function (o) {
 | 
					      yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
        if (o.deleted === true) {
 | 
					        if (o.deleted === true) {
 | 
				
			||||||
        expect(u.db.ds.isDeleted(o.id)).toBeTruthy()
 | 
					          expect(yield* this.isDeleted(o.id)).toBeTruthy()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      // compare deleteStore against deleted ops
 | 
					      // compare deleteStore against deleted ops
 | 
				
			||||||
    u.db.requestTransaction(function * () {
 | 
					 | 
				
			||||||
      var ds = []
 | 
					      var ds = []
 | 
				
			||||||
      u.db.ds.iterate(null, null, function (d) {
 | 
					      yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
        ds.push(d)
 | 
					        ds.push(d)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      for (var j in ds) {
 | 
					      for (var j in ds) {
 | 
				
			||||||
@ -186,25 +186,30 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
 | 
				
			|||||||
    // compare allDels tree
 | 
					    // compare allDels tree
 | 
				
			||||||
    yield wait()
 | 
					    yield wait()
 | 
				
			||||||
    if (s1 == null) {
 | 
					    if (s1 == null) {
 | 
				
			||||||
      u.db.requestTransaction(t1)
 | 
					      u.db.requestTransaction(function * () {
 | 
				
			||||||
      yield wait()
 | 
					        yield* t1.call(this)
 | 
				
			||||||
      u.db.os.iterate(null, null, function (o) {
 | 
					        yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
          o = Y.utils.copyObject(o)
 | 
					          o = Y.utils.copyObject(o)
 | 
				
			||||||
          delete o.origin
 | 
					          delete o.origin
 | 
				
			||||||
          db1.push(o)
 | 
					          db1.push(o)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    } else {
 | 
					      })
 | 
				
			||||||
      u.db.requestTransaction(t2)
 | 
					 | 
				
			||||||
      yield wait()
 | 
					      yield wait()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // TODO: make requestTransaction return a promise..
 | 
				
			||||||
 | 
					      u.db.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* t2.call(this)
 | 
				
			||||||
        expect(s1).toEqual(s2)
 | 
					        expect(s1).toEqual(s2)
 | 
				
			||||||
        expect(allDels1).toEqual(allDels2) // inner structure
 | 
					        expect(allDels1).toEqual(allDels2) // inner structure
 | 
				
			||||||
        expect(ds1).toEqual(ds2) // exported structure
 | 
					        expect(ds1).toEqual(ds2) // exported structure
 | 
				
			||||||
        var count = 0
 | 
					        var count = 0
 | 
				
			||||||
      u.db.os.iterate(null, null, function (o) {
 | 
					        yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
          o = Y.utils.copyObject(o)
 | 
					          o = Y.utils.copyObject(o)
 | 
				
			||||||
          delete o.origin
 | 
					          delete o.origin
 | 
				
			||||||
          expect(db1[count++]).toEqual(o)
 | 
					          expect(db1[count++]).toEqual(o)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      yield wait()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
@ -263,15 +268,16 @@ function async (makeGenerator) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
g.async = async
 | 
					g.async = async
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function logUsers (self) {
 | 
					var logUsers = async(function * logUsers (self) {
 | 
				
			||||||
  if (self.constructor === Array) {
 | 
					  if (self.constructor === Array) {
 | 
				
			||||||
    self = {users: self}
 | 
					    self = {users: self}
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  console.log('User 1: ', self.users[0].connector.userId, "=============================================") // eslint-disable-line
 | 
					  console.log('User 1: ', self.users[0].connector.userId, "=============================================") // eslint-disable-line
 | 
				
			||||||
  self.users[0].db.logTable() // eslint-disable-line
 | 
					  yield self.users[0].db.logTable() // eslint-disable-line
 | 
				
			||||||
  console.log('User 2: ', self.users[1].connector.userId, "=============================================") // eslint-disable-line
 | 
					  console.log('User 2: ', self.users[1].connector.userId, "=============================================") // eslint-disable-line
 | 
				
			||||||
  self.users[1].db.logTable() // eslint-disable-line
 | 
					  yield self.users[1].db.logTable() // eslint-disable-line
 | 
				
			||||||
  console.log('User 3: ', self.users[2].connector.userId, "=============================================") // eslint-disable-line
 | 
					  console.log('User 3: ', self.users[2].connector.userId, "=============================================") // eslint-disable-line
 | 
				
			||||||
  self.users[2].db.logTable() // eslint-disable-line
 | 
					  yield self.users[2].db.logTable() // eslint-disable-line
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
g.logUsers = logUsers
 | 
					g.logUsers = logUsers
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,6 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "userY": ...
 | 
					        "userY": ...
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
  * isDeleted(id)
 | 
					 | 
				
			||||||
  * getOpsFromDeleteSet(ds) -- TODO: just call this.deleteOperation(id) here
 | 
					  * getOpsFromDeleteSet(ds) -- TODO: just call this.deleteOperation(id) here
 | 
				
			||||||
    - get a set of deletions that need to be applied in order to get to
 | 
					    - get a set of deletions that need to be applied in order to get to
 | 
				
			||||||
      achieve the state of the supplied ds
 | 
					      achieve the state of the supplied ds
 | 
				
			||||||
@ -78,6 +77,9 @@
 | 
				
			|||||||
class AbstractTransaction {
 | 
					class AbstractTransaction {
 | 
				
			||||||
  constructor (store) {
 | 
					  constructor (store) {
 | 
				
			||||||
    this.store = store
 | 
					    this.store = store
 | 
				
			||||||
 | 
					    this.ss = store.ss
 | 
				
			||||||
 | 
					    this.os = store.os
 | 
				
			||||||
 | 
					    this.ds = store.ds
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
    Get a type based on the id of its model.
 | 
					    Get a type based on the id of its model.
 | 
				
			||||||
@ -138,7 +140,7 @@ class AbstractTransaction {
 | 
				
			|||||||
    var target = yield* this.getOperation(targetId)
 | 
					    var target = yield* this.getOperation(targetId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (target == null || !target.deleted) {
 | 
					    if (target == null || !target.deleted) {
 | 
				
			||||||
      this.ds.markDeleted(targetId)
 | 
					      yield* this.markDeleted(targetId)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (target != null && target.gc == null) {
 | 
					    if (target != null && target.gc == null) {
 | 
				
			||||||
@ -193,6 +195,87 @@ class AbstractTransaction {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    Mark an operation as deleted&gc'd
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  * markGarbageCollected (id) {
 | 
				
			||||||
 | 
					    // this.mem.push(["gc", id]);
 | 
				
			||||||
 | 
					    var n = yield* this.markDeleted(id)
 | 
				
			||||||
 | 
					    if (!n.val.gc) {
 | 
				
			||||||
 | 
					      if (n.val.id[1] < id[1]) {
 | 
				
			||||||
 | 
					        // un-extend left
 | 
				
			||||||
 | 
					        var newlen = n.val.len - (id[1] - n.val.id[1])
 | 
				
			||||||
 | 
					        n.val.len -= newlen
 | 
				
			||||||
 | 
					        n = yield this.ds.add({id: id, len: newlen, gc: false})
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // get prev&next before adding a new operation
 | 
				
			||||||
 | 
					      var prev = n.prev()
 | 
				
			||||||
 | 
					      var next = n.next()
 | 
				
			||||||
 | 
					      if (id[1] < n.val.id[1] + n.val.len - 1) {
 | 
				
			||||||
 | 
					        // un-extend right
 | 
				
			||||||
 | 
					        yield this.ds.add({id: [id[0], id[1] + 1], len: n.val.len - 1, gc: false})
 | 
				
			||||||
 | 
					        n.val.len = 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // set gc'd
 | 
				
			||||||
 | 
					      n.val.gc = true
 | 
				
			||||||
 | 
					      // can extend left?
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        prev != null &&
 | 
				
			||||||
 | 
					        prev.val.gc &&
 | 
				
			||||||
 | 
					        Y.utils.compareIds([prev.val.id[0], prev.val.id[1] + prev.val.len], n.val.id)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        prev.val.len += n.val.len
 | 
				
			||||||
 | 
					        yield this.ds.delete(n.val.id)
 | 
				
			||||||
 | 
					        n = prev
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // can extend right?
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        next != null &&
 | 
				
			||||||
 | 
					        next.val.gc &&
 | 
				
			||||||
 | 
					        Y.utils.compareIds([n.val.id[0], n.val.id[1] + n.val.len], next.val.id)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        n.val.len += next.val.len
 | 
				
			||||||
 | 
					        yield this.ds.delete(next.val.id)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    Mark an operation as deleted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    returns the delete node
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  * markDeleted (id) {
 | 
				
			||||||
 | 
					    // this.mem.push(["del", id]);
 | 
				
			||||||
 | 
					    var n = yield this.ds.findNodeWithUpperBound(id)
 | 
				
			||||||
 | 
					    if (n != null && n.val.id[0] === id[0]) {
 | 
				
			||||||
 | 
					      if (n.val.id[1] <= id[1] && id[1] < n.val.id[1] + n.val.len) {
 | 
				
			||||||
 | 
					        // already deleted
 | 
				
			||||||
 | 
					        return n
 | 
				
			||||||
 | 
					      } else if (n.val.id[1] + n.val.len === id[1] && !n.val.gc) {
 | 
				
			||||||
 | 
					        // can extend existing deletion
 | 
				
			||||||
 | 
					        n.val.len++
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // cannot extend left
 | 
				
			||||||
 | 
					        n = yield this.ds.add({id: id, len: 1, gc: false})
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // cannot extend left
 | 
				
			||||||
 | 
					      n = yield this.ds.add({id: id, len: 1, gc: false})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // can extend right?
 | 
				
			||||||
 | 
					    var next = n.next()
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      next !== null &&
 | 
				
			||||||
 | 
					      Y.utils.compareIds([n.val.id[0], n.val.id[1] + n.val.len], next.val.id) &&
 | 
				
			||||||
 | 
					      !next.val.gc
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      n.val.len = n.val.len + next.val.len
 | 
				
			||||||
 | 
					      yield this.ds.delete(next.val.id)
 | 
				
			||||||
 | 
					      return yield this.ds.findNode(n.val.id)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return n
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
    Really remove an op and all its effects.
 | 
					    Really remove an op and all its effects.
 | 
				
			||||||
    The complicated case here is the Insert operation:
 | 
					    The complicated case here is the Insert operation:
 | 
				
			||||||
@ -212,7 +295,7 @@ class AbstractTransaction {
 | 
				
			|||||||
      // then set the state
 | 
					      // then set the state
 | 
				
			||||||
      yield* this.setState(state)
 | 
					      yield* this.setState(state)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.ds.markGarbageCollected(id)
 | 
					    yield* this.markGarbageCollected(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // if op exists, then clean that mess up..
 | 
					    // if op exists, then clean that mess up..
 | 
				
			||||||
    var o = yield* this.getOperation(id)
 | 
					    var o = yield* this.getOperation(id)
 | 
				
			||||||
@ -292,6 +375,248 @@ class AbstractTransaction {
 | 
				
			|||||||
      yield* this.removeOperation(o.id)
 | 
					      yield* this.removeOperation(o.id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  * checkDeleteStoreForState (state) {
 | 
				
			||||||
 | 
					    var n = yield this.ds.findNodeWithUpperBound([state.user, state.clock])
 | 
				
			||||||
 | 
					    if (n !== null && n.val.id[0] === state.user && n.val.gc) {
 | 
				
			||||||
 | 
					      state.clock = Math.max(state.clock, n.val.id[1] + n.val.len)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    apply a delete set in order to get
 | 
				
			||||||
 | 
					    the state of the supplied ds
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  * applyDeleteSet (ds) {
 | 
				
			||||||
 | 
					    var deletions = []
 | 
				
			||||||
 | 
					    function createDeletions (user, start, len, gc) {
 | 
				
			||||||
 | 
					      for (var c = start; c < start + len; c++) {
 | 
				
			||||||
 | 
					        deletions.push([user, c, gc])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var user in ds) {
 | 
				
			||||||
 | 
					      var dv = ds[user]
 | 
				
			||||||
 | 
					      var pos = 0
 | 
				
			||||||
 | 
					      var d = dv[pos]
 | 
				
			||||||
 | 
					      yield* this.ds.iterate(this, [user, 0], [user, Number.MAX_VALUE], function * (n) {
 | 
				
			||||||
 | 
					        // cases:
 | 
				
			||||||
 | 
					        // 1. d deletes something to the right of n
 | 
				
			||||||
 | 
					        //  => go to next n (break)
 | 
				
			||||||
 | 
					        // 2. d deletes something to the left of n
 | 
				
			||||||
 | 
					        //  => create deletions
 | 
				
			||||||
 | 
					        //  => reset d accordingly
 | 
				
			||||||
 | 
					        //  *)=> if d doesn't delete anything anymore, go to next d (continue)
 | 
				
			||||||
 | 
					        // 3. not 2) and d deletes something that also n deletes
 | 
				
			||||||
 | 
					        //  => reset d so that it doesn't contain n's deletion
 | 
				
			||||||
 | 
					        //  *)=> if d does not delete anything anymore, go to next d (continue)
 | 
				
			||||||
 | 
					        while (d != null) {
 | 
				
			||||||
 | 
					          var diff = 0 // describe the diff of length in 1) and 2)
 | 
				
			||||||
 | 
					          if (n.id[1] + n.len <= d[0]) {
 | 
				
			||||||
 | 
					            // 1)
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					          } else if (d[0] < n.id[1]) {
 | 
				
			||||||
 | 
					            // 2)
 | 
				
			||||||
 | 
					            // delete maximum the len of d
 | 
				
			||||||
 | 
					            // else delete as much as possible
 | 
				
			||||||
 | 
					            diff = Math.min(n.id[1] - d[0], d[1])
 | 
				
			||||||
 | 
					            createDeletions(user, d[0], diff, d[2])
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            // 3)
 | 
				
			||||||
 | 
					            diff = n.id[1] + n.len - d[0] // never null (see 1)
 | 
				
			||||||
 | 
					            if (d[2] && !n.gc) {
 | 
				
			||||||
 | 
					              // d marks as gc'd but n does not
 | 
				
			||||||
 | 
					              // then delete either way
 | 
				
			||||||
 | 
					              createDeletions(user, d[0], Math.min(diff, d[1]), d[2])
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (d[1] <= diff) {
 | 
				
			||||||
 | 
					            // d doesn't delete anything anymore
 | 
				
			||||||
 | 
					            d = dv[++pos]
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            d[0] = d[0] + diff // reset pos
 | 
				
			||||||
 | 
					            d[1] = d[1] - diff // reset length
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      // for the rest.. just apply it
 | 
				
			||||||
 | 
					      for (; pos < dv.length; pos++) {
 | 
				
			||||||
 | 
					        d = dv[pos]
 | 
				
			||||||
 | 
					        createDeletions(user, d[0], d[1], d[2])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (var i in deletions) {
 | 
				
			||||||
 | 
					      var del = deletions[i]
 | 
				
			||||||
 | 
					      var id = [del[0], del[1]]
 | 
				
			||||||
 | 
					      // always try to delete..
 | 
				
			||||||
 | 
					      yield* this.deleteOperation(id)
 | 
				
			||||||
 | 
					      if (del[2]) {
 | 
				
			||||||
 | 
					        // gc
 | 
				
			||||||
 | 
					        yield* this.garbageCollectOperation(id)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * isGarbageCollected (id) {
 | 
				
			||||||
 | 
					    var n = yield this.ds.findNodeWithUpperBound(id)
 | 
				
			||||||
 | 
					    return n !== null && n.val.id[0] === id[0] && id[1] < n.val.id[1] + n.val.len && n.val.gc
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    A DeleteSet (ds) describes all the deleted ops in the OS
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  * getDeleteSet () {
 | 
				
			||||||
 | 
					    var ds = {}
 | 
				
			||||||
 | 
					    yield* this.ds.iterate(this, null, null, function * (n) {
 | 
				
			||||||
 | 
					      var user = n.id[0]
 | 
				
			||||||
 | 
					      var counter = n.id[1]
 | 
				
			||||||
 | 
					      var len = n.len
 | 
				
			||||||
 | 
					      var gc = n.gc
 | 
				
			||||||
 | 
					      var dv = ds[user]
 | 
				
			||||||
 | 
					      if (dv === void 0) {
 | 
				
			||||||
 | 
					        dv = []
 | 
				
			||||||
 | 
					        ds[user] = dv
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      dv.push([counter, len, gc])
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    return ds
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * isDeleted (id) {
 | 
				
			||||||
 | 
					    var n = yield this.ds.findNodeWithUpperBound(id)
 | 
				
			||||||
 | 
					    return n !== null && n.val.id[0] === id[0] && id[1] < n.val.id[1] + n.val.len
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * setOperation (op) {
 | 
				
			||||||
 | 
					    // TODO: you can remove this step! probs..
 | 
				
			||||||
 | 
					    var n = yield this.os.findNode(op.id)
 | 
				
			||||||
 | 
					    n.val = op
 | 
				
			||||||
 | 
					    return op
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * addOperation (op) {
 | 
				
			||||||
 | 
					    var n = yield this.os.add(op)
 | 
				
			||||||
 | 
					    return function () {
 | 
				
			||||||
 | 
					      if (n != null) {
 | 
				
			||||||
 | 
					        n = n.next()
 | 
				
			||||||
 | 
					        return n != null ? n.val : null
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * getOperation (id) {
 | 
				
			||||||
 | 
					    return yield this.os.find(id)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * removeOperation (id) {
 | 
				
			||||||
 | 
					    yield this.os.delete(id)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * setState (state) {
 | 
				
			||||||
 | 
					    this.ss[state.user] = state.clock
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * getState (user) {
 | 
				
			||||||
 | 
					    var clock = this.ss[user]
 | 
				
			||||||
 | 
					    if (clock == null) {
 | 
				
			||||||
 | 
					      clock = 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      user: user,
 | 
				
			||||||
 | 
					      clock: clock
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * getStateVector () {
 | 
				
			||||||
 | 
					    var stateVector = []
 | 
				
			||||||
 | 
					    for (var user in this.ss) {
 | 
				
			||||||
 | 
					      var clock = this.ss[user]
 | 
				
			||||||
 | 
					      stateVector.push({
 | 
				
			||||||
 | 
					        user: user,
 | 
				
			||||||
 | 
					        clock: clock
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return stateVector
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * getStateSet () {
 | 
				
			||||||
 | 
					    return Y.utils.copyObject(this.ss)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  * getOperations (startSS) {
 | 
				
			||||||
 | 
					    // TODO: use bounds here!
 | 
				
			||||||
 | 
					    if (startSS == null) {
 | 
				
			||||||
 | 
					      startSS = {}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var ops = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var endSV = yield* this.getStateVector()
 | 
				
			||||||
 | 
					    for (var endState of endSV) {
 | 
				
			||||||
 | 
					      var user = endState.user
 | 
				
			||||||
 | 
					      if (user === '_') {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      var startPos = startSS[user] || 0
 | 
				
			||||||
 | 
					      var endPos = endState.clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      yield* this.os.iterate(this, [user, startPos], [user, endPos], function * (op) {
 | 
				
			||||||
 | 
					        ops.push(op)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var res = []
 | 
				
			||||||
 | 
					    for (var op of ops) {
 | 
				
			||||||
 | 
					      res.push(yield* this.makeOperationReady(startSS, op))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    Here, we make op executable for the receiving user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Notes:
 | 
				
			||||||
 | 
					      startSS: denotes to the SV that the remote user sent
 | 
				
			||||||
 | 
					      currSS:  denotes to the state vector that the user should have if he
 | 
				
			||||||
 | 
					               applies all already sent operations (increases is each step)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    We face several problems:
 | 
				
			||||||
 | 
					    * Execute op as is won't work because ops depend on each other
 | 
				
			||||||
 | 
					     -> find a way so that they do not anymore
 | 
				
			||||||
 | 
					    * When changing left, must not go more to the left than the origin
 | 
				
			||||||
 | 
					    * When changing right, you have to consider that other ops may have op
 | 
				
			||||||
 | 
					      as their origin, this means that you must not set one of these ops
 | 
				
			||||||
 | 
					      as the new right (interdependencies of ops)
 | 
				
			||||||
 | 
					    * can't just go to the right until you find the first known operation,
 | 
				
			||||||
 | 
					      With currSS
 | 
				
			||||||
 | 
					        -> interdependency of ops is a problem
 | 
				
			||||||
 | 
					      With startSS
 | 
				
			||||||
 | 
					        -> leads to inconsistencies when two users join at the same time.
 | 
				
			||||||
 | 
					           Then the position depends on the order of execution -> error!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Solution:
 | 
				
			||||||
 | 
					      -> re-create originial situation
 | 
				
			||||||
 | 
					        -> set op.left = op.origin (which never changes)
 | 
				
			||||||
 | 
					        -> set op.right
 | 
				
			||||||
 | 
					             to the first operation that is known (according to startSS)
 | 
				
			||||||
 | 
					             or to the first operation that has an origin that is not to the
 | 
				
			||||||
 | 
					             right of op.
 | 
				
			||||||
 | 
					        -> Enforces unique execution order -> happy user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Improvements: TODO
 | 
				
			||||||
 | 
					        * Could set left to origin, or the first known operation
 | 
				
			||||||
 | 
					          (startSS or currSS.. ?)
 | 
				
			||||||
 | 
					          -> Could be necessary when I turn GC again.
 | 
				
			||||||
 | 
					          -> Is a bad(ish) idea because it requires more computation
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  * makeOperationReady (startSS, op) {
 | 
				
			||||||
 | 
					    op = Y.Struct[op.struct].encode(op)
 | 
				
			||||||
 | 
					    op = Y.utils.copyObject(op)
 | 
				
			||||||
 | 
					    var o = op
 | 
				
			||||||
 | 
					    var ids = [op.id]
 | 
				
			||||||
 | 
					    // search for the new op.right
 | 
				
			||||||
 | 
					    // it is either the first known op (according to startSS)
 | 
				
			||||||
 | 
					    // or the o that has no origin to the right of op
 | 
				
			||||||
 | 
					    // (this is why we use the ids array)
 | 
				
			||||||
 | 
					    while (o.right != null) {
 | 
				
			||||||
 | 
					      var right = yield* this.getOperation(o.right)
 | 
				
			||||||
 | 
					      if (o.right[1] < (startSS[o.right[0]] || 0) || !ids.some(function (id) {
 | 
				
			||||||
 | 
					        return Y.utils.compareIds(id, right.origin)
 | 
				
			||||||
 | 
					      })) {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ids.push(o.right)
 | 
				
			||||||
 | 
					      o = right
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    op.right = o.right
 | 
				
			||||||
 | 
					    op.left = op.origin
 | 
				
			||||||
 | 
					    return op
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Y.AbstractTransaction = AbstractTransaction
 | 
					Y.AbstractTransaction = AbstractTransaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -374,15 +699,15 @@ class AbstractOperationStore {
 | 
				
			|||||||
      })
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  garbageCollectAfterSync () {
 | 
					  * garbageCollectAfterSync () {
 | 
				
			||||||
    var os = this.os
 | 
					    this.requestTransaction(function * () {
 | 
				
			||||||
    var self = this
 | 
					      yield* this.os.iterate(this, null, null, function * (op) {
 | 
				
			||||||
    os.iterate(null, null, function (op) {
 | 
					 | 
				
			||||||
        if (op.deleted && op.left != null) {
 | 
					        if (op.deleted && op.left != null) {
 | 
				
			||||||
        var left = os.find(op.left)
 | 
					          var left = yield this.os.find(op.left)
 | 
				
			||||||
        self.addToGarbageCollector(op, left)
 | 
					          this.store.addToGarbageCollector(op, left)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
    Try to add to GC.
 | 
					    Try to add to GC.
 | 
				
			||||||
@ -530,13 +855,13 @@ class AbstractOperationStore {
 | 
				
			|||||||
  * tryExecute (op) {
 | 
					  * tryExecute (op) {
 | 
				
			||||||
    if (op.struct === 'Delete') {
 | 
					    if (op.struct === 'Delete') {
 | 
				
			||||||
      yield* Y.Struct.Delete.execute.call(this, op)
 | 
					      yield* Y.Struct.Delete.execute.call(this, op)
 | 
				
			||||||
    } else if ((yield* this.getOperation(op.id)) == null && !this.store.ds.isGarbageCollected(op.id)) {
 | 
					    } else if ((yield* this.getOperation(op.id)) == null && !(yield* this.isGarbageCollected(op.id))) {
 | 
				
			||||||
      yield* Y.Struct[op.struct].execute.call(this, op)
 | 
					      yield* Y.Struct[op.struct].execute.call(this, op)
 | 
				
			||||||
      var next = yield* this.addOperation(op)
 | 
					      var next = yield* this.addOperation(op)
 | 
				
			||||||
      yield* this.store.operationAdded(this, op, next)
 | 
					      yield* this.store.operationAdded(this, op, next)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Delete if DS says this is actually deleted
 | 
					      // Delete if DS says this is actually deleted
 | 
				
			||||||
      if (this.store.ds.isDeleted(op.id)) {
 | 
					      if (yield* this.isDeleted(op.id)) {
 | 
				
			||||||
        yield* Y.Struct['Delete'].execute.call(this, {struct: 'Delete', target: op.id})
 | 
					        yield* Y.Struct['Delete'].execute.call(this, {struct: 'Delete', target: op.id})
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,350 +6,12 @@ class DeleteStore extends Y.utils.RBTree {
 | 
				
			|||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.mem = []
 | 
					    this.mem = []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  isDeleted (id) {
 | 
					 | 
				
			||||||
    var n = this.findNodeWithUpperBound(id)
 | 
					 | 
				
			||||||
    return n !== null && n.val.id[0] === id[0] && id[1] < n.val.id[1] + n.val.len
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  isGarbageCollected (id) {
 | 
					 | 
				
			||||||
    var n = this.findNodeWithUpperBound(id)
 | 
					 | 
				
			||||||
    return n !== null && n.val.id[0] === id[0] && id[1] < n.val.id[1] + n.val.len && n.val.gc
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
    Mark an operation as deleted&gc'd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    returns the delete node
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
  markGarbageCollected (id) {
 | 
					 | 
				
			||||||
    // this.mem.push(["gc", id]);
 | 
					 | 
				
			||||||
    var n = this.markDeleted(id)
 | 
					 | 
				
			||||||
    if (!n.val.gc) {
 | 
					 | 
				
			||||||
      if (n.val.id[1] < id[1]) {
 | 
					 | 
				
			||||||
        // un-extend left
 | 
					 | 
				
			||||||
        var newlen = n.val.len - (id[1] - n.val.id[1])
 | 
					 | 
				
			||||||
        n.val.len -= newlen
 | 
					 | 
				
			||||||
        n = this.add({id: id, len: newlen, gc: false})
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // get prev&next before adding a new operation
 | 
					 | 
				
			||||||
      var prev = n.prev()
 | 
					 | 
				
			||||||
      var next = n.next()
 | 
					 | 
				
			||||||
      if (id[1] < n.val.id[1] + n.val.len - 1) {
 | 
					 | 
				
			||||||
        // un-extend right
 | 
					 | 
				
			||||||
        this.add({id: [id[0], id[1] + 1], len: n.val.len - 1, gc: false})
 | 
					 | 
				
			||||||
        n.val.len = 1
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // set gc'd
 | 
					 | 
				
			||||||
      n.val.gc = true
 | 
					 | 
				
			||||||
      // can extend left?
 | 
					 | 
				
			||||||
      if (
 | 
					 | 
				
			||||||
        prev != null &&
 | 
					 | 
				
			||||||
        prev.val.gc &&
 | 
					 | 
				
			||||||
        Y.utils.compareIds([prev.val.id[0], prev.val.id[1] + prev.val.len], n.val.id)
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        prev.val.len += n.val.len
 | 
					 | 
				
			||||||
        super.delete(n.val.id)
 | 
					 | 
				
			||||||
        n = prev
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // can extend right?
 | 
					 | 
				
			||||||
      if (
 | 
					 | 
				
			||||||
        next != null &&
 | 
					 | 
				
			||||||
        next.val.gc &&
 | 
					 | 
				
			||||||
        Y.utils.compareIds([n.val.id[0], n.val.id[1] + n.val.len], next.val.id)
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        n.val.len += next.val.len
 | 
					 | 
				
			||||||
        super.delete(next.val.id)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
    Mark an operation as deleted.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    returns the delete node
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
  markDeleted (id) {
 | 
					 | 
				
			||||||
    // this.mem.push(["del", id]);
 | 
					 | 
				
			||||||
    var n = this.findNodeWithUpperBound(id)
 | 
					 | 
				
			||||||
    if (n != null && n.val.id[0] === id[0]) {
 | 
					 | 
				
			||||||
      if (n.val.id[1] <= id[1] && id[1] < n.val.id[1] + n.val.len) {
 | 
					 | 
				
			||||||
        // already deleted
 | 
					 | 
				
			||||||
        return n
 | 
					 | 
				
			||||||
      } else if (n.val.id[1] + n.val.len === id[1] && !n.val.gc) {
 | 
					 | 
				
			||||||
        // can extend existing deletion
 | 
					 | 
				
			||||||
        n.val.len++
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        // cannot extend left
 | 
					 | 
				
			||||||
        n = this.add({id: id, len: 1, gc: false})
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      // cannot extend left
 | 
					 | 
				
			||||||
      n = this.add({id: id, len: 1, gc: false})
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // can extend right?
 | 
					 | 
				
			||||||
    var next = n.next()
 | 
					 | 
				
			||||||
    if (
 | 
					 | 
				
			||||||
      next !== null &&
 | 
					 | 
				
			||||||
      Y.utils.compareIds([n.val.id[0], n.val.id[1] + n.val.len], next.val.id) &&
 | 
					 | 
				
			||||||
      !next.val.gc
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
      n.val.len = n.val.len + next.val.len
 | 
					 | 
				
			||||||
      super.delete(next.val.id)
 | 
					 | 
				
			||||||
      return this.findNode(n.val.id)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      return n
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
    A DeleteSet (ds) describes all the deleted ops in the OS
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
  toDeleteSet () {
 | 
					 | 
				
			||||||
    var ds = {}
 | 
					 | 
				
			||||||
    this.iterate(null, null, function (n) {
 | 
					 | 
				
			||||||
      var user = n.id[0]
 | 
					 | 
				
			||||||
      var counter = n.id[1]
 | 
					 | 
				
			||||||
      var len = n.len
 | 
					 | 
				
			||||||
      var gc = n.gc
 | 
					 | 
				
			||||||
      var dv = ds[user]
 | 
					 | 
				
			||||||
      if (dv === void 0) {
 | 
					 | 
				
			||||||
        dv = []
 | 
					 | 
				
			||||||
        ds[user] = dv
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      dv.push([counter, len, gc])
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    return ds
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Y.utils.DeleteStore = DeleteStore
 | 
					Y.utils.DeleteStore = DeleteStore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Y.Memory = (function () {
 | 
					Y.Memory = (function () {
 | 
				
			||||||
  class Transaction extends Y.AbstractTransaction {
 | 
					  class Transaction extends Y.AbstractTransaction {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor (store) {
 | 
					 | 
				
			||||||
      super(store)
 | 
					 | 
				
			||||||
      this.ss = store.ss
 | 
					 | 
				
			||||||
      this.os = store.os
 | 
					 | 
				
			||||||
      this.ds = store.ds
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * checkDeleteStoreForState (state) {
 | 
					 | 
				
			||||||
      var n = this.ds.findNodeWithUpperBound([state.user, state.clock])
 | 
					 | 
				
			||||||
      if (n !== null && n.val.id[0] === state.user && n.val.gc) {
 | 
					 | 
				
			||||||
        state.clock = Math.max(state.clock, n.val.id[1] + n.val.len)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getDeleteSet (id) {
 | 
					 | 
				
			||||||
      return this.ds.toDeleteSet(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /*
 | 
					 | 
				
			||||||
      apply a delete set in order to get
 | 
					 | 
				
			||||||
      the state of the supplied ds
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
    * applyDeleteSet (ds) {
 | 
					 | 
				
			||||||
      var deletions = []
 | 
					 | 
				
			||||||
      function createDeletions (user, start, len, gc) {
 | 
					 | 
				
			||||||
        for (var c = start; c < start + len; c++) {
 | 
					 | 
				
			||||||
          deletions.push([user, c, gc])
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      for (var user in ds) {
 | 
					 | 
				
			||||||
        var dv = ds[user]
 | 
					 | 
				
			||||||
        var pos = 0
 | 
					 | 
				
			||||||
        var d = dv[pos]
 | 
					 | 
				
			||||||
        this.ds.iterate([user, 0], [user, Number.MAX_VALUE], function (n) {
 | 
					 | 
				
			||||||
          // cases:
 | 
					 | 
				
			||||||
          // 1. d deletes something to the right of n
 | 
					 | 
				
			||||||
          //  => go to next n (break)
 | 
					 | 
				
			||||||
          // 2. d deletes something to the left of n
 | 
					 | 
				
			||||||
          //  => create deletions
 | 
					 | 
				
			||||||
          //  => reset d accordingly
 | 
					 | 
				
			||||||
          //  *)=> if d doesn't delete anything anymore, go to next d (continue)
 | 
					 | 
				
			||||||
          // 3. not 2) and d deletes something that also n deletes
 | 
					 | 
				
			||||||
          //  => reset d so that it doesn't contain n's deletion
 | 
					 | 
				
			||||||
          //  *)=> if d does not delete anything anymore, go to next d (continue)
 | 
					 | 
				
			||||||
          while (d != null) {
 | 
					 | 
				
			||||||
            var diff = 0 // describe the diff of length in 1) and 2)
 | 
					 | 
				
			||||||
            if (n.id[1] + n.len <= d[0]) {
 | 
					 | 
				
			||||||
              // 1)
 | 
					 | 
				
			||||||
              break
 | 
					 | 
				
			||||||
            } else if (d[0] < n.id[1]) {
 | 
					 | 
				
			||||||
              // 2)
 | 
					 | 
				
			||||||
              // delete maximum the len of d
 | 
					 | 
				
			||||||
              // else delete as much as possible
 | 
					 | 
				
			||||||
              diff = Math.min(n.id[1] - d[0], d[1])
 | 
					 | 
				
			||||||
              createDeletions(user, d[0], diff, d[2])
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              // 3)
 | 
					 | 
				
			||||||
              diff = n.id[1] + n.len - d[0] // never null (see 1)
 | 
					 | 
				
			||||||
              if (d[2] && !n.gc) {
 | 
					 | 
				
			||||||
                // d marks as gc'd but n does not
 | 
					 | 
				
			||||||
                // then delete either way
 | 
					 | 
				
			||||||
                createDeletions(user, d[0], Math.min(diff, d[1]), d[2])
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (d[1] <= diff) {
 | 
					 | 
				
			||||||
              // d doesn't delete anything anymore
 | 
					 | 
				
			||||||
              d = dv[++pos]
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              d[0] = d[0] + diff // reset pos
 | 
					 | 
				
			||||||
              d[1] = d[1] - diff // reset length
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        // for the rest.. just apply it
 | 
					 | 
				
			||||||
        for (; pos < dv.length; pos++) {
 | 
					 | 
				
			||||||
          d = dv[pos]
 | 
					 | 
				
			||||||
          createDeletions(user, d[0], d[1], d[2])
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      for (var i in deletions) {
 | 
					 | 
				
			||||||
        var del = deletions[i]
 | 
					 | 
				
			||||||
        var id = [del[0], del[1]]
 | 
					 | 
				
			||||||
        // always try to delete..
 | 
					 | 
				
			||||||
        yield* this.deleteOperation(id)
 | 
					 | 
				
			||||||
        if (del[2]) {
 | 
					 | 
				
			||||||
          // gc
 | 
					 | 
				
			||||||
          yield* this.garbageCollectOperation(id)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * isDeleted (id) {
 | 
					 | 
				
			||||||
      return this.ds.isDeleted(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * setOperation (op) {
 | 
					 | 
				
			||||||
      // TODO: you can remove this step! probs..
 | 
					 | 
				
			||||||
      var n = this.os.findNode(op.id)
 | 
					 | 
				
			||||||
      n.val = op
 | 
					 | 
				
			||||||
      return op
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * addOperation (op) {
 | 
					 | 
				
			||||||
      var n = this.os.add(op)
 | 
					 | 
				
			||||||
      return function () {
 | 
					 | 
				
			||||||
        if (n != null) {
 | 
					 | 
				
			||||||
          n = n.next()
 | 
					 | 
				
			||||||
          return n != null ? n.val : null
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          return null
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getOperation (id) {
 | 
					 | 
				
			||||||
      return this.os.find(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * removeOperation (id) {
 | 
					 | 
				
			||||||
      this.os.delete(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * setState (state) {
 | 
					 | 
				
			||||||
      this.ss[state.user] = state.clock
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getState (user) {
 | 
					 | 
				
			||||||
      var clock = this.ss[user]
 | 
					 | 
				
			||||||
      if (clock == null) {
 | 
					 | 
				
			||||||
        clock = 0
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        user: user,
 | 
					 | 
				
			||||||
        clock: clock
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getStateVector () {
 | 
					 | 
				
			||||||
      var stateVector = []
 | 
					 | 
				
			||||||
      for (var user in this.ss) {
 | 
					 | 
				
			||||||
        var clock = this.ss[user]
 | 
					 | 
				
			||||||
        stateVector.push({
 | 
					 | 
				
			||||||
          user: user,
 | 
					 | 
				
			||||||
          clock: clock
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return stateVector
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getStateSet () {
 | 
					 | 
				
			||||||
      return Y.utils.copyObject(this.ss)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    * getOperations (startSS) {
 | 
					 | 
				
			||||||
      // TODO: use bounds here!
 | 
					 | 
				
			||||||
      if (startSS == null) {
 | 
					 | 
				
			||||||
        startSS = {}
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var ops = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      var endSV = yield* this.getStateVector()
 | 
					 | 
				
			||||||
      for (var endState of endSV) {
 | 
					 | 
				
			||||||
        var user = endState.user
 | 
					 | 
				
			||||||
        if (user === '_') {
 | 
					 | 
				
			||||||
          continue
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        var startPos = startSS[user] || 0
 | 
					 | 
				
			||||||
        var endPos = endState.clock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.os.iterate([user, startPos], [user, endPos], function (op) {
 | 
					 | 
				
			||||||
          ops.push(op)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var res = []
 | 
					 | 
				
			||||||
      for (var op of ops) {
 | 
					 | 
				
			||||||
        res.push(yield* this.makeOperationReady(startSS, op))
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return res
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /*
 | 
					 | 
				
			||||||
      Here, we make op executable for the receiving user.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Notes:
 | 
					 | 
				
			||||||
        startSS: denotes to the SV that the remote user sent
 | 
					 | 
				
			||||||
        currSS:  denotes to the state vector that the user should have if he
 | 
					 | 
				
			||||||
                 applies all already sent operations (increases is each step)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      We face several problems:
 | 
					 | 
				
			||||||
      * Execute op as is won't work because ops depend on each other
 | 
					 | 
				
			||||||
       -> find a way so that they do not anymore
 | 
					 | 
				
			||||||
      * When changing left, must not go more to the left than the origin
 | 
					 | 
				
			||||||
      * When changing right, you have to consider that other ops may have op
 | 
					 | 
				
			||||||
        as their origin, this means that you must not set one of these ops
 | 
					 | 
				
			||||||
        as the new right (interdependencies of ops)
 | 
					 | 
				
			||||||
      * can't just go to the right until you find the first known operation,
 | 
					 | 
				
			||||||
        With currSS
 | 
					 | 
				
			||||||
          -> interdependency of ops is a problem
 | 
					 | 
				
			||||||
        With startSS
 | 
					 | 
				
			||||||
          -> leads to inconsistencies when two users join at the same time.
 | 
					 | 
				
			||||||
             Then the position depends on the order of execution -> error!
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Solution:
 | 
					 | 
				
			||||||
        -> re-create originial situation
 | 
					 | 
				
			||||||
          -> set op.left = op.origin (which never changes)
 | 
					 | 
				
			||||||
          -> set op.right
 | 
					 | 
				
			||||||
               to the first operation that is known (according to startSS)
 | 
					 | 
				
			||||||
               or to the first operation that has an origin that is not to the
 | 
					 | 
				
			||||||
               right of op.
 | 
					 | 
				
			||||||
          -> Enforces unique execution order -> happy user
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Improvements: TODO
 | 
					 | 
				
			||||||
          * Could set left to origin, or the first known operation
 | 
					 | 
				
			||||||
            (startSS or currSS.. ?)
 | 
					 | 
				
			||||||
            -> Could be necessary when I turn GC again.
 | 
					 | 
				
			||||||
            -> Is a bad(ish) idea because it requires more computation
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
    * makeOperationReady (startSS, op) {
 | 
					 | 
				
			||||||
      op = Y.Struct[op.struct].encode(op)
 | 
					 | 
				
			||||||
      op = Y.utils.copyObject(op)
 | 
					 | 
				
			||||||
      var o = op
 | 
					 | 
				
			||||||
      var ids = [op.id]
 | 
					 | 
				
			||||||
      // search for the new op.right
 | 
					 | 
				
			||||||
      // it is either the first known op (according to startSS)
 | 
					 | 
				
			||||||
      // or the o that has no origin to the right of op
 | 
					 | 
				
			||||||
      // (this is why we use the ids array)
 | 
					 | 
				
			||||||
      while (o.right != null) {
 | 
					 | 
				
			||||||
        var right = yield* this.getOperation(o.right)
 | 
					 | 
				
			||||||
        if (o.right[1] < (startSS[o.right[0]] || 0) || !ids.some(function (id) {
 | 
					 | 
				
			||||||
          return Y.utils.compareIds(id, right.origin)
 | 
					 | 
				
			||||||
        })) {
 | 
					 | 
				
			||||||
          break
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ids.push(o.right)
 | 
					 | 
				
			||||||
        o = right
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      op.right = o.right
 | 
					 | 
				
			||||||
      op.left = op.origin
 | 
					 | 
				
			||||||
      return op
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  class OperationStore extends Y.AbstractOperationStore {
 | 
					  class OperationStore extends Y.AbstractOperationStore {
 | 
				
			||||||
    constructor (y, opts) {
 | 
					    constructor (y, opts) {
 | 
				
			||||||
@ -361,44 +23,45 @@ Y.Memory = (function () {
 | 
				
			|||||||
      this.ds = new DeleteStore()
 | 
					      this.ds = new DeleteStore()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    logTable () {
 | 
					    logTable () {
 | 
				
			||||||
      console.log('User: ', this.y.connector.userId, "=============================================") // eslint-disable-line
 | 
					      var self = this
 | 
				
			||||||
 | 
					      return new Promise(function (resolve) {
 | 
				
			||||||
 | 
					        self.requestTransaction(function * () {
 | 
				
			||||||
 | 
					          console.log('User: ', this.store.y.connector.userId, "==============================") // eslint-disable-line
 | 
				
			||||||
          console.log("State Set (SS):", this.ss) // eslint-disable-line
 | 
					          console.log("State Set (SS):", this.ss) // eslint-disable-line
 | 
				
			||||||
          console.log("Operation Store (OS):") // eslint-disable-line
 | 
					          console.log("Operation Store (OS):") // eslint-disable-line
 | 
				
			||||||
      this.os.logTable() // eslint-disable-line
 | 
					          yield* this.os.logTable() // eslint-disable-line
 | 
				
			||||||
          console.log("Deletion Store (DS):") //eslint-disable-line
 | 
					          console.log("Deletion Store (DS):") //eslint-disable-line
 | 
				
			||||||
      this.ds.logTable() // eslint-disable-line
 | 
					          yield* this.ds.logTable() // eslint-disable-line
 | 
				
			||||||
 | 
					          resolve()
 | 
				
			||||||
 | 
					        }, true)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    requestTransaction (_makeGen, requestNow) {
 | 
					    requestTransaction (_makeGen, callImmediately) {
 | 
				
			||||||
      if (requestNow == null) { requestNow = false }
 | 
					 | 
				
			||||||
      if (!this.transactionInProgress) {
 | 
					      if (!this.transactionInProgress) {
 | 
				
			||||||
        this.transactionInProgress = true
 | 
					        this.transactionInProgress = true
 | 
				
			||||||
        var transact = (xxxx) => {
 | 
					        var transact = () => {
 | 
				
			||||||
          var makeGen = _makeGen
 | 
					          var makeGen = _makeGen
 | 
				
			||||||
          while (makeGen != null) {
 | 
					          while (makeGen != null) {
 | 
				
			||||||
            var t = new Transaction(this)
 | 
					            var t = new Transaction(this)
 | 
				
			||||||
            var gen = makeGen.call(t)
 | 
					            var gen = makeGen.call(t)
 | 
				
			||||||
            var res = gen.next()
 | 
					            var res = gen.next()
 | 
				
			||||||
            while (!res.done) {
 | 
					            while (!res.done) {
 | 
				
			||||||
              if (res.value === 'transaction') {
 | 
					              res = gen.next(res.value)
 | 
				
			||||||
                res = gen.next(t)
 | 
					 | 
				
			||||||
              } else {
 | 
					 | 
				
			||||||
                throw new Error("You must not yield this type. (Maybe you meant to use 'yield*'?)")
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            makeGen = this.waitingTransactions.shift()
 | 
					            makeGen = this.waitingTransactions.shift()
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.transactionInProgress = false
 | 
					          this.transactionInProgress = false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!requestNow) {
 | 
					        if (callImmediately) {
 | 
				
			||||||
          setTimeout(transact, 0)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          transact()
 | 
					          transact()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          setTimeout(transact, 0)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.waitingTransactions.push(_makeGen)
 | 
					        this.waitingTransactions.push(_makeGen)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    * destroy () { // eslint-disable-line
 | 
					    * destroy () {
 | 
				
			||||||
      super.destroy()
 | 
					      super.destroy()
 | 
				
			||||||
      delete this.os
 | 
					      delete this.os
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,127 +3,146 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
describe('Memory', function () {
 | 
					describe('Memory', function () {
 | 
				
			||||||
  describe('DeleteStore', function () {
 | 
					  describe('DeleteStore', function () {
 | 
				
			||||||
    var ds
 | 
					    var store
 | 
				
			||||||
    beforeEach(function () {
 | 
					    beforeEach(function () {
 | 
				
			||||||
      ds = new Y.utils.DeleteStore()
 | 
					      store = new Y.Memory(null, {
 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Deleted operation is deleted', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['u1', 10])
 | 
					 | 
				
			||||||
      expect(ds.isDeleted(['u1', 10])).toBeTruthy()
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'u1': [[10, 1, false]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Deleted operation extends other deleted operation', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['u1', 10])
 | 
					 | 
				
			||||||
      ds.markDeleted(['u1', 11])
 | 
					 | 
				
			||||||
      expect(ds.isDeleted(['u1', 10])).toBeTruthy()
 | 
					 | 
				
			||||||
      expect(ds.isDeleted(['u1', 11])).toBeTruthy()
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'u1': [[10, 2, false]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Deleted operation extends other deleted operation', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['0', 3])
 | 
					 | 
				
			||||||
      ds.markDeleted(['0', 4])
 | 
					 | 
				
			||||||
      ds.markDeleted(['0', 2])
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'0': [[2, 3, false]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Debug #1', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['166', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 3])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['166', 3])
 | 
					 | 
				
			||||||
      ds.markDeleted(['166', 0])
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Debug #2', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['293', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['291', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['291', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['293', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['293', 1])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['291', 2])
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Debug #3', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['581', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['581', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 0])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['581', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['581', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['580', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['581', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['581', 1])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['580', 1])
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Debug #4', function () {
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['543', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['543', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['544', 0])
 | 
					 | 
				
			||||||
      ds.markDeleted(['545', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['543', 4])
 | 
					 | 
				
			||||||
      ds.markDeleted(['543', 3])
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 2])
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 1])
 | 
					 | 
				
			||||||
      ds.markDeleted(['544', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['543', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['543', 4])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['544', 2])
 | 
					 | 
				
			||||||
      ds.markGarbageCollected(['543', 3])
 | 
					 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]})
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    it('Debug #5', async(function * (done) {
 | 
					 | 
				
			||||||
      var store = new Y.Memory(null, {
 | 
					 | 
				
			||||||
        name: 'Memory',
 | 
					        name: 'Memory',
 | 
				
			||||||
        gcTimeout: -1
 | 
					        gcTimeout: -1
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    it('Deleted operation is deleted', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['u1', 10])
 | 
				
			||||||
 | 
					        expect(yield* this.isDeleted(['u1', 10])).toBeTruthy()
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'u1': [[10, 1, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Deleted operation extends other deleted operation', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['u1', 10])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['u1', 11])
 | 
				
			||||||
 | 
					        expect(yield* this.isDeleted(['u1', 10])).toBeTruthy()
 | 
				
			||||||
 | 
					        expect(yield* this.isDeleted(['u1', 11])).toBeTruthy()
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'u1': [[10, 2, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Deleted operation extends other deleted operation', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['0', 3])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['0', 4])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['0', 2])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'0': [[2, 3, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Debug #1', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['166', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 3])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['166', 3])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['166', 0])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Debug #2', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['293', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['291', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['291', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['293', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['293', 1])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['291', 2])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Debug #3', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['581', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['581', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 0])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['581', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['581', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['580', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['581', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['581', 1])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['580', 1])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Debug #4', async(function * (done) {
 | 
				
			||||||
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['543', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['543', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['544', 0])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['545', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['543', 4])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['543', 3])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 2])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 1])
 | 
				
			||||||
 | 
					        yield* this.markDeleted(['544', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['543', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['543', 4])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['544', 2])
 | 
				
			||||||
 | 
					        yield* this.markGarbageCollected(['543', 3])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					    it('Debug #5', async(function * (done) {
 | 
				
			||||||
      store.requestTransaction(function * () {
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
        yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
 | 
					        yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
 | 
				
			||||||
        expect(this.ds.toDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
 | 
				
			||||||
        yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 4, true]]})
 | 
					        yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 4, true]]})
 | 
				
			||||||
        expect(this.ds.toDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 4, true]]})
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 4, true]]})
 | 
				
			||||||
        done()
 | 
					        done()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }))
 | 
					    }))
 | 
				
			||||||
    it('Debug #6', async(function * (done) {
 | 
					    it('Debug #6', async(function * (done) {
 | 
				
			||||||
      var store = new Y.Memory(null, {
 | 
					 | 
				
			||||||
        name: 'Memory',
 | 
					 | 
				
			||||||
        gcTimeout: -1
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      store.requestTransaction(function * () {
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
        yield* this.applyDeleteSet({'40': [[0, 3, false]]})
 | 
					        yield* this.applyDeleteSet({'40': [[0, 3, false]]})
 | 
				
			||||||
        expect(this.ds.toDeleteSet()).toEqual({'40': [[0, 3, false]]})
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'40': [[0, 3, false]]})
 | 
				
			||||||
        yield* this.applyDeleteSet({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
 | 
					        yield* this.applyDeleteSet({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
 | 
				
			||||||
        expect(this.ds.toDeleteSet()).toEqual({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
 | 
				
			||||||
        done()
 | 
					        done()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }))
 | 
					    }))
 | 
				
			||||||
    it('Debug #7', function () {
 | 
					    it('Debug #7', async(function * (done) {
 | 
				
			||||||
      ds.markDeleted(['9', 2])
 | 
					      store.requestTransaction(function * () {
 | 
				
			||||||
      ds.markDeleted(['11', 2])
 | 
					        yield* this.markDeleted(['9', 2])
 | 
				
			||||||
      ds.markDeleted(['11', 4])
 | 
					        yield* this.markDeleted(['11', 2])
 | 
				
			||||||
      ds.markDeleted(['11', 1])
 | 
					        yield* this.markDeleted(['11', 4])
 | 
				
			||||||
      ds.markDeleted(['9', 4])
 | 
					        yield* this.markDeleted(['11', 1])
 | 
				
			||||||
      ds.markDeleted(['10', 0])
 | 
					        yield* this.markDeleted(['9', 4])
 | 
				
			||||||
      ds.markGarbageCollected(['11', 2])
 | 
					        yield* this.markDeleted(['10', 0])
 | 
				
			||||||
      ds.markDeleted(['11', 2])
 | 
					        yield* this.markGarbageCollected(['11', 2])
 | 
				
			||||||
      ds.markGarbageCollected(['11', 3])
 | 
					        yield* this.markDeleted(['11', 2])
 | 
				
			||||||
      ds.markDeleted(['11', 3])
 | 
					        yield* this.markGarbageCollected(['11', 3])
 | 
				
			||||||
      ds.markDeleted(['11', 3])
 | 
					        yield* this.markDeleted(['11', 3])
 | 
				
			||||||
      ds.markDeleted(['9', 4])
 | 
					        yield* this.markDeleted(['11', 3])
 | 
				
			||||||
      ds.markDeleted(['10', 0])
 | 
					        yield* this.markDeleted(['9', 4])
 | 
				
			||||||
      ds.markGarbageCollected(['11', 1])
 | 
					        yield* this.markDeleted(['10', 0])
 | 
				
			||||||
      ds.markDeleted(['11', 1])
 | 
					        yield* this.markGarbageCollected(['11', 1])
 | 
				
			||||||
      expect(ds.toDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]})
 | 
					        yield* this.markDeleted(['11', 1])
 | 
				
			||||||
 | 
					        expect(yield* this.getDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]})
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -186,15 +186,15 @@ class RBTree {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  iterate (from, to, f) {
 | 
					  * iterate (t, from, to, f) {
 | 
				
			||||||
    var o = this.findNodeWithLowerBound(from)
 | 
					    var o = this.findNodeWithLowerBound(from)
 | 
				
			||||||
    while (o !== null && (to === null || Y.utils.smaller(o.val.id, to) || Y.utils.compareIds(o.val.id, to))) {
 | 
					    while (o !== null && (to === null || Y.utils.smaller(o.val.id, to) || Y.utils.compareIds(o.val.id, to))) {
 | 
				
			||||||
      f(o.val)
 | 
					      yield* f.call(t, o.val)
 | 
				
			||||||
      o = o.next()
 | 
					      o = o.next()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  logTable (from, to, filter) {
 | 
					  * logTable (from, to, filter) {
 | 
				
			||||||
    if (filter == null) {
 | 
					    if (filter == null) {
 | 
				
			||||||
      filter = function () {
 | 
					      filter = function () {
 | 
				
			||||||
        return true
 | 
					        return true
 | 
				
			||||||
@ -203,7 +203,7 @@ class RBTree {
 | 
				
			|||||||
    if (from == null) { from = null }
 | 
					    if (from == null) { from = null }
 | 
				
			||||||
    if (to == null) { to = null }
 | 
					    if (to == null) { to = null }
 | 
				
			||||||
    var os = []
 | 
					    var os = []
 | 
				
			||||||
    this.iterate(from, to, function (o) {
 | 
					    yield* this.iterate(this, from, to, function * (o) {
 | 
				
			||||||
      if (filter(o)) {
 | 
					      if (filter(o)) {
 | 
				
			||||||
        var o_ = {}
 | 
					        var o_ = {}
 | 
				
			||||||
        for (var key in o) {
 | 
					        for (var key in o) {
 | 
				
			||||||
 | 
				
			|||||||
@ -51,7 +51,11 @@ function itRootNodeIsBlack (tree) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
describe('RedBlack Tree', function () {
 | 
					describe('RedBlack Tree', function () {
 | 
				
			||||||
  beforeEach(function () {
 | 
					  beforeEach(function () {
 | 
				
			||||||
    this.tree = new Y.utils.RBTree()
 | 
					    this.memory = new Y.Memory(null, {
 | 
				
			||||||
 | 
					      name: 'Memory',
 | 
				
			||||||
 | 
					      gcTimeout: -1
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    this.tree = this.memory.os
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  it('can add&retrieve 5 elements', function () {
 | 
					  it('can add&retrieve 5 elements', function () {
 | 
				
			||||||
    this.tree.add({val: 'four', id: [4]})
 | 
					    this.tree.add({val: 'four', id: [4]})
 | 
				
			||||||
@ -144,48 +148,57 @@ describe('RedBlack Tree', function () {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    itBlackHeightOfSubTreesAreEqual(tree)
 | 
					    itBlackHeightOfSubTreesAreEqual(tree)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('iterating over a tree with lower bound yields the right amount of results', function () {
 | 
					    it('iterating over a tree with lower bound yields the right amount of results', function (done) {
 | 
				
			||||||
      var lowerBound = elements[Math.floor(Math.random() * elements.length)]
 | 
					      var lowerBound = elements[Math.floor(Math.random() * elements.length)]
 | 
				
			||||||
      var expectedResults = elements.filter(function (e, pos) {
 | 
					      var expectedResults = elements.filter(function (e, pos) {
 | 
				
			||||||
        return (Y.utils.smaller(lowerBound, e) || Y.utils.compareIds(e, lowerBound)) && elements.indexOf(e) === pos
 | 
					        return (Y.utils.smaller(lowerBound, e) || Y.utils.compareIds(e, lowerBound)) && elements.indexOf(e) === pos
 | 
				
			||||||
      }).length
 | 
					      }).length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var actualResults = 0
 | 
					      var actualResults = 0
 | 
				
			||||||
      tree.iterate(lowerBound, null, function (val) {
 | 
					      this.memory.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* tree.iterate(this, lowerBound, null, function * (val) {
 | 
				
			||||||
          expect(val).not.toBeUndefined()
 | 
					          expect(val).not.toBeUndefined()
 | 
				
			||||||
          actualResults++
 | 
					          actualResults++
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        expect(expectedResults).toEqual(actualResults)
 | 
					        expect(expectedResults).toEqual(actualResults)
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('iterating over a tree without bounds yield the right amount of results', function () {
 | 
					    it('iterating over a tree without bounds yield the right amount of results', function (done) {
 | 
				
			||||||
      var lowerBound = null
 | 
					      var lowerBound = null
 | 
				
			||||||
      var expectedResults = elements.filter(function (e, pos) {
 | 
					      var expectedResults = elements.filter(function (e, pos) {
 | 
				
			||||||
        return elements.indexOf(e) === pos
 | 
					        return elements.indexOf(e) === pos
 | 
				
			||||||
      }).length
 | 
					      }).length
 | 
				
			||||||
      var actualResults = 0
 | 
					      var actualResults = 0
 | 
				
			||||||
      tree.iterate(lowerBound, null, function (val) {
 | 
					      this.memory.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* tree.iterate(this, lowerBound, null, function * (val) {
 | 
				
			||||||
          expect(val).not.toBeUndefined()
 | 
					          expect(val).not.toBeUndefined()
 | 
				
			||||||
          actualResults++
 | 
					          actualResults++
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        expect(expectedResults).toEqual(actualResults)
 | 
					        expect(expectedResults).toEqual(actualResults)
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('iterating over a tree with upper bound yields the right amount of results', function () {
 | 
					    it('iterating over a tree with upper bound yields the right amount of results', function (done) {
 | 
				
			||||||
      var upperBound = elements[Math.floor(Math.random() * elements.length)]
 | 
					      var upperBound = elements[Math.floor(Math.random() * elements.length)]
 | 
				
			||||||
      var expectedResults = elements.filter(function (e, pos) {
 | 
					      var expectedResults = elements.filter(function (e, pos) {
 | 
				
			||||||
        return (Y.utils.smaller(e, upperBound) || Y.utils.compareIds(e, upperBound)) && elements.indexOf(e) === pos
 | 
					        return (Y.utils.smaller(e, upperBound) || Y.utils.compareIds(e, upperBound)) && elements.indexOf(e) === pos
 | 
				
			||||||
      }).length
 | 
					      }).length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var actualResults = 0
 | 
					      var actualResults = 0
 | 
				
			||||||
      tree.iterate(null, upperBound, function (val) {
 | 
					      this.memory.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* tree.iterate(this, null, upperBound, function * (val) {
 | 
				
			||||||
          expect(val).not.toBeUndefined()
 | 
					          expect(val).not.toBeUndefined()
 | 
				
			||||||
          actualResults++
 | 
					          actualResults++
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        expect(expectedResults).toEqual(actualResults)
 | 
					        expect(expectedResults).toEqual(actualResults)
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('iterating over a tree with upper and lower bounds yield the right amount of results', function () {
 | 
					    it('iterating over a tree with upper and lower bounds yield the right amount of results', function (done) {
 | 
				
			||||||
      var b1 = elements[Math.floor(Math.random() * elements.length)]
 | 
					      var b1 = elements[Math.floor(Math.random() * elements.length)]
 | 
				
			||||||
      var b2 = elements[Math.floor(Math.random() * elements.length)]
 | 
					      var b2 = elements[Math.floor(Math.random() * elements.length)]
 | 
				
			||||||
      var upperBound, lowerBound
 | 
					      var upperBound, lowerBound
 | 
				
			||||||
@ -201,11 +214,14 @@ describe('RedBlack Tree', function () {
 | 
				
			|||||||
          (Y.utils.smaller(e, upperBound) || Y.utils.compareIds(e, upperBound)) && elements.indexOf(e) === pos
 | 
					          (Y.utils.smaller(e, upperBound) || Y.utils.compareIds(e, upperBound)) && elements.indexOf(e) === pos
 | 
				
			||||||
      }).length
 | 
					      }).length
 | 
				
			||||||
      var actualResults = 0
 | 
					      var actualResults = 0
 | 
				
			||||||
      tree.iterate(lowerBound, upperBound, function (val) {
 | 
					      this.memory.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* tree.iterate(this, lowerBound, upperBound, function * (val) {
 | 
				
			||||||
          expect(val).not.toBeUndefined()
 | 
					          expect(val).not.toBeUndefined()
 | 
				
			||||||
          actualResults++
 | 
					          actualResults++
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        expect(expectedResults).toEqual(actualResults)
 | 
					        expect(expectedResults).toEqual(actualResults)
 | 
				
			||||||
 | 
					        done()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
/* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
 | 
					/* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
 | 
				
			||||||
/* eslint-env browser,jasmine */
 | 
					/* eslint-env browser,jasmine */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var numberOfYArrayTests = 100
 | 
					var numberOfYArrayTests = 10
 | 
				
			||||||
var repeatArrayTests = 1
 | 
					var repeatArrayTests = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Array Type', function () {
 | 
					describe('Array Type', function () {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
/* global createUsers, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */
 | 
					/* global createUsers, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */
 | 
				
			||||||
/* eslint-env browser,jasmine */
 | 
					/* eslint-env browser,jasmine */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var numberOfYMapTests = 100
 | 
					var numberOfYMapTests = 10
 | 
				
			||||||
var repeatMapTeasts = 1
 | 
					var repeatMapTeasts = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Map Type', function () {
 | 
					describe('Map Type', function () {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user