565 lines
16 KiB
JavaScript
565 lines
16 KiB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
/* global Y */
|
|
'use strict'
|
|
|
|
function extend (Y) {
|
|
require('./RedBlackTree.js')(Y)
|
|
class Transaction extends Y.Transaction {
|
|
constructor (store) {
|
|
super(store)
|
|
this.store = store
|
|
this.ss = store.ss
|
|
this.os = store.os
|
|
this.ds = store.ds
|
|
}
|
|
}
|
|
class Database extends Y.AbstractDatabase {
|
|
constructor (y, opts) {
|
|
super(y, opts)
|
|
this.os = new Y.utils.RBTree()
|
|
this.ds = new Y.utils.RBTree()
|
|
this.ss = new Y.utils.RBTree()
|
|
}
|
|
logTable () {
|
|
var self = this
|
|
self.requestTransaction(function * () {
|
|
console.log('User: ', this.store.y.connector.userId, "==============================") // eslint-disable-line
|
|
console.log("State Set (SS):", yield* this.getStateSet()) // eslint-disable-line
|
|
console.log("Operation Store (OS):") // eslint-disable-line
|
|
yield* this.os.logTable() // eslint-disable-line
|
|
console.log("Deletion Store (DS):") //eslint-disable-line
|
|
yield* this.ds.logTable() // eslint-disable-line
|
|
if (this.store.gc1.length > 0 || this.store.gc2.length > 0) {
|
|
console.warn('GC1|2 not empty!', this.store.gc1, this.store.gc2)
|
|
}
|
|
if (JSON.stringify(this.store.listenersById) !== '{}') {
|
|
console.warn('listenersById not empty!')
|
|
}
|
|
if (JSON.stringify(this.store.listenersByIdExecuteNow) !== '[]') {
|
|
console.warn('listenersByIdExecuteNow not empty!')
|
|
}
|
|
if (this.store.transactionInProgress) {
|
|
console.warn('Transaction still in progress!')
|
|
}
|
|
}, true)
|
|
}
|
|
transact (makeGen) {
|
|
var t = new Transaction(this)
|
|
while (makeGen !== null) {
|
|
var gen = makeGen.call(t)
|
|
var res = gen.next()
|
|
while (!res.done) {
|
|
res = gen.next(res.value)
|
|
}
|
|
makeGen = this.getNextRequest()
|
|
}
|
|
}
|
|
* destroy () {
|
|
super.destroy()
|
|
delete this.os
|
|
delete this.ss
|
|
delete this.ds
|
|
}
|
|
}
|
|
Y.extend('memory', Database)
|
|
}
|
|
|
|
module.exports = extend
|
|
if (typeof Y !== 'undefined') {
|
|
extend(Y)
|
|
}
|
|
|
|
},{"./RedBlackTree.js":2}],2:[function(require,module,exports){
|
|
'use strict'
|
|
|
|
/*
|
|
This file contains a not so fancy implemantion of a Red Black Tree.
|
|
*/
|
|
module.exports = function (Y) {
|
|
class N {
|
|
// A created node is always red!
|
|
constructor (val) {
|
|
this.val = val
|
|
this.color = true
|
|
this._left = null
|
|
this._right = null
|
|
this._parent = null
|
|
if (val.id === null) {
|
|
throw new Error('You must define id!')
|
|
}
|
|
}
|
|
isRed () { return this.color }
|
|
isBlack () { return !this.color }
|
|
redden () { this.color = true; return this }
|
|
blacken () { this.color = false; return this }
|
|
get grandparent () {
|
|
return this.parent.parent
|
|
}
|
|
get parent () {
|
|
return this._parent
|
|
}
|
|
get sibling () {
|
|
return (this === this.parent.left)
|
|
? this.parent.right : this.parent.left
|
|
}
|
|
get left () {
|
|
return this._left
|
|
}
|
|
get right () {
|
|
return this._right
|
|
}
|
|
set left (n) {
|
|
if (n !== null) {
|
|
n._parent = this
|
|
}
|
|
this._left = n
|
|
}
|
|
set right (n) {
|
|
if (n !== null) {
|
|
n._parent = this
|
|
}
|
|
this._right = n
|
|
}
|
|
rotateLeft (tree) {
|
|
var parent = this.parent
|
|
var newParent = this.right
|
|
var newRight = this.right.left
|
|
newParent.left = this
|
|
this.right = newRight
|
|
if (parent === null) {
|
|
tree.root = newParent
|
|
newParent._parent = null
|
|
} else if (parent.left === this) {
|
|
parent.left = newParent
|
|
} else if (parent.right === this) {
|
|
parent.right = newParent
|
|
} else {
|
|
throw new Error('The elements are wrongly connected!')
|
|
}
|
|
}
|
|
next () {
|
|
if (this.right !== null) {
|
|
// search the most left node in the right tree
|
|
var o = this.right
|
|
while (o.left !== null) {
|
|
o = o.left
|
|
}
|
|
return o
|
|
} else {
|
|
var p = this
|
|
while (p.parent !== null && p !== p.parent.left) {
|
|
p = p.parent
|
|
}
|
|
return p.parent
|
|
}
|
|
}
|
|
prev () {
|
|
if (this.left !== null) {
|
|
// search the most right node in the left tree
|
|
var o = this.left
|
|
while (o.right !== null) {
|
|
o = o.right
|
|
}
|
|
return o
|
|
} else {
|
|
var p = this
|
|
while (p.parent !== null && p !== p.parent.right) {
|
|
p = p.parent
|
|
}
|
|
return p.parent
|
|
}
|
|
}
|
|
rotateRight (tree) {
|
|
var parent = this.parent
|
|
var newParent = this.left
|
|
var newLeft = this.left.right
|
|
newParent.right = this
|
|
this.left = newLeft
|
|
if (parent === null) {
|
|
tree.root = newParent
|
|
newParent._parent = null
|
|
} else if (parent.left === this) {
|
|
parent.left = newParent
|
|
} else if (parent.right === this) {
|
|
parent.right = newParent
|
|
} else {
|
|
throw new Error('The elements are wrongly connected!')
|
|
}
|
|
}
|
|
getUncle () {
|
|
// we can assume that grandparent exists when this is called!
|
|
if (this.parent === this.parent.parent.left) {
|
|
return this.parent.parent.right
|
|
} else {
|
|
return this.parent.parent.left
|
|
}
|
|
}
|
|
}
|
|
|
|
class RBTree {
|
|
constructor () {
|
|
this.root = null
|
|
this.length = 0
|
|
}
|
|
* findNext (id) {
|
|
return yield* this.findWithLowerBound([id[0], id[1] + 1])
|
|
}
|
|
* findPrev (id) {
|
|
return yield* this.findWithUpperBound([id[0], id[1] - 1])
|
|
}
|
|
findNodeWithLowerBound (from) {
|
|
if (from === void 0) {
|
|
throw new Error('You must define from!')
|
|
}
|
|
var o = this.root
|
|
if (o === null) {
|
|
return null
|
|
} else {
|
|
while (true) {
|
|
if ((from === null || Y.utils.smaller(from, o.val.id)) && o.left !== null) {
|
|
// o is included in the bound
|
|
// try to find an element that is closer to the bound
|
|
o = o.left
|
|
} else if (from !== null && Y.utils.smaller(o.val.id, from)) {
|
|
// o is not within the bound, maybe one of the right elements is..
|
|
if (o.right !== null) {
|
|
o = o.right
|
|
} else {
|
|
// there is no right element. Search for the next bigger element,
|
|
// this should be within the bounds
|
|
return o.next()
|
|
}
|
|
} else {
|
|
return o
|
|
}
|
|
}
|
|
}
|
|
}
|
|
findNodeWithUpperBound (to) {
|
|
if (to === void 0) {
|
|
throw new Error('You must define from!')
|
|
}
|
|
var o = this.root
|
|
if (o === null) {
|
|
return null
|
|
} else {
|
|
while (true) {
|
|
if ((to === null || Y.utils.smaller(o.val.id, to)) && o.right !== null) {
|
|
// o is included in the bound
|
|
// try to find an element that is closer to the bound
|
|
o = o.right
|
|
} else if (to !== null && Y.utils.smaller(to, o.val.id)) {
|
|
// o is not within the bound, maybe one of the left elements is..
|
|
if (o.left !== null) {
|
|
o = o.left
|
|
} else {
|
|
// there is no left element. Search for the prev smaller element,
|
|
// this should be within the bounds
|
|
return o.prev()
|
|
}
|
|
} else {
|
|
return o
|
|
}
|
|
}
|
|
}
|
|
}
|
|
* findWithLowerBound (from) {
|
|
var n = this.findNodeWithLowerBound(from)
|
|
return n == null ? null : n.val
|
|
}
|
|
* findWithUpperBound (to) {
|
|
var n = this.findNodeWithUpperBound(to)
|
|
return n == null ? null : n.val
|
|
}
|
|
* iterate (t, from, to, f) {
|
|
var o = this.findNodeWithLowerBound(from)
|
|
while (o !== null && (to === null || Y.utils.smaller(o.val.id, to) || Y.utils.compareIds(o.val.id, to))) {
|
|
yield* f.call(t, o.val)
|
|
o = o.next()
|
|
}
|
|
return true
|
|
}
|
|
* logTable (from, to, filter) {
|
|
if (filter == null) {
|
|
filter = function () {
|
|
return true
|
|
}
|
|
}
|
|
if (from == null) { from = null }
|
|
if (to == null) { to = null }
|
|
var os = []
|
|
yield* this.iterate(this, from, to, function * (o) {
|
|
if (filter(o)) {
|
|
var o_ = {}
|
|
for (var key in o) {
|
|
if (typeof o[key] === 'object') {
|
|
o_[key] = JSON.stringify(o[key])
|
|
} else {
|
|
o_[key] = o[key]
|
|
}
|
|
}
|
|
os.push(o_)
|
|
}
|
|
})
|
|
if (console.table != null) {
|
|
console.table(os)
|
|
}
|
|
}
|
|
* find (id) {
|
|
var n
|
|
return (n = this.findNode(id)) ? n.val : null
|
|
}
|
|
findNode (id) {
|
|
if (id == null || id.constructor !== Array) {
|
|
throw new Error('Expect id to be an array!')
|
|
}
|
|
var o = this.root
|
|
if (o === null) {
|
|
return false
|
|
} else {
|
|
while (true) {
|
|
if (o === null) {
|
|
return false
|
|
}
|
|
if (Y.utils.smaller(id, o.val.id)) {
|
|
o = o.left
|
|
} else if (Y.utils.smaller(o.val.id, id)) {
|
|
o = o.right
|
|
} else {
|
|
return o
|
|
}
|
|
}
|
|
}
|
|
}
|
|
* delete (id) {
|
|
if (id == null || id.constructor !== Array) {
|
|
throw new Error('id is expected to be an Array!')
|
|
}
|
|
var d = this.findNode(id)
|
|
if (d == null) {
|
|
throw new Error('Element does not exist!')
|
|
}
|
|
this.length--
|
|
if (d.left !== null && d.right !== null) {
|
|
// switch d with the greates element in the left subtree.
|
|
// o should have at most one child.
|
|
var o = d.left
|
|
// find
|
|
while (o.right !== null) {
|
|
o = o.right
|
|
}
|
|
// switch
|
|
d.val = o.val
|
|
d = o
|
|
}
|
|
// d has at most one child
|
|
// let n be the node that replaces d
|
|
var isFakeChild
|
|
var child = d.left || d.right
|
|
if (child === null) {
|
|
isFakeChild = true
|
|
child = new N({id: 0})
|
|
child.blacken()
|
|
d.right = child
|
|
} else {
|
|
isFakeChild = false
|
|
}
|
|
|
|
if (d.parent === null) {
|
|
if (!isFakeChild) {
|
|
this.root = child
|
|
child.blacken()
|
|
child._parent = null
|
|
} else {
|
|
this.root = null
|
|
}
|
|
return
|
|
} else if (d.parent.left === d) {
|
|
d.parent.left = child
|
|
} else if (d.parent.right === d) {
|
|
d.parent.right = child
|
|
} else {
|
|
throw new Error('Impossible!')
|
|
}
|
|
if (d.isBlack()) {
|
|
if (child.isRed()) {
|
|
child.blacken()
|
|
} else {
|
|
this._fixDelete(child)
|
|
}
|
|
}
|
|
this.root.blacken()
|
|
if (isFakeChild) {
|
|
if (child.parent.left === child) {
|
|
child.parent.left = null
|
|
} else if (child.parent.right === child) {
|
|
child.parent.right = null
|
|
} else {
|
|
throw new Error('Impossible #3')
|
|
}
|
|
}
|
|
}
|
|
_fixDelete (n) {
|
|
function isBlack (node) {
|
|
return node !== null ? node.isBlack() : true
|
|
}
|
|
function isRed (node) {
|
|
return node !== null ? node.isRed() : false
|
|
}
|
|
if (n.parent === null) {
|
|
// this can only be called after the first iteration of fixDelete.
|
|
return
|
|
}
|
|
// d was already replaced by the child
|
|
// d is not the root
|
|
// d and child are black
|
|
var sibling = n.sibling
|
|
if (isRed(sibling)) {
|
|
// make sibling the grandfather
|
|
n.parent.redden()
|
|
sibling.blacken()
|
|
if (n === n.parent.left) {
|
|
n.parent.rotateLeft(this)
|
|
} else if (n === n.parent.right) {
|
|
n.parent.rotateRight(this)
|
|
} else {
|
|
throw new Error('Impossible #2')
|
|
}
|
|
sibling = n.sibling
|
|
}
|
|
// parent, sibling, and children of n are black
|
|
if (n.parent.isBlack() &&
|
|
sibling.isBlack() &&
|
|
isBlack(sibling.left) &&
|
|
isBlack(sibling.right)
|
|
) {
|
|
sibling.redden()
|
|
this._fixDelete(n.parent)
|
|
} else if (n.parent.isRed() &&
|
|
sibling.isBlack() &&
|
|
isBlack(sibling.left) &&
|
|
isBlack(sibling.right)
|
|
) {
|
|
sibling.redden()
|
|
n.parent.blacken()
|
|
} else {
|
|
if (n === n.parent.left &&
|
|
sibling.isBlack() &&
|
|
isRed(sibling.left) &&
|
|
isBlack(sibling.right)
|
|
) {
|
|
sibling.redden()
|
|
sibling.left.blacken()
|
|
sibling.rotateRight(this)
|
|
sibling = n.sibling
|
|
} else if (n === n.parent.right &&
|
|
sibling.isBlack() &&
|
|
isRed(sibling.right) &&
|
|
isBlack(sibling.left)
|
|
) {
|
|
sibling.redden()
|
|
sibling.right.blacken()
|
|
sibling.rotateLeft(this)
|
|
sibling = n.sibling
|
|
}
|
|
sibling.color = n.parent.color
|
|
n.parent.blacken()
|
|
if (n === n.parent.left) {
|
|
sibling.right.blacken()
|
|
n.parent.rotateLeft(this)
|
|
} else {
|
|
sibling.left.blacken()
|
|
n.parent.rotateRight(this)
|
|
}
|
|
}
|
|
}
|
|
* put (v) {
|
|
if (v == null || v.id == null || v.id.constructor !== Array) {
|
|
throw new Error('v is expected to have an id property which is an Array!')
|
|
}
|
|
var node = new N(v)
|
|
if (this.root !== null) {
|
|
var p = this.root // p abbrev. parent
|
|
while (true) {
|
|
if (Y.utils.smaller(node.val.id, p.val.id)) {
|
|
if (p.left === null) {
|
|
p.left = node
|
|
break
|
|
} else {
|
|
p = p.left
|
|
}
|
|
} else if (Y.utils.smaller(p.val.id, node.val.id)) {
|
|
if (p.right === null) {
|
|
p.right = node
|
|
break
|
|
} else {
|
|
p = p.right
|
|
}
|
|
} else {
|
|
p.val = node.val
|
|
return p
|
|
}
|
|
}
|
|
this._fixInsert(node)
|
|
} else {
|
|
this.root = node
|
|
}
|
|
this.length++
|
|
this.root.blacken()
|
|
return node
|
|
}
|
|
_fixInsert (n) {
|
|
if (n.parent === null) {
|
|
n.blacken()
|
|
return
|
|
} else if (n.parent.isBlack()) {
|
|
return
|
|
}
|
|
var uncle = n.getUncle()
|
|
if (uncle !== null && uncle.isRed()) {
|
|
// Note: parent: red, uncle: red
|
|
n.parent.blacken()
|
|
uncle.blacken()
|
|
n.grandparent.redden()
|
|
this._fixInsert(n.grandparent)
|
|
} else {
|
|
// Note: parent: red, uncle: black or null
|
|
// Now we transform the tree in such a way that
|
|
// either of these holds:
|
|
// 1) grandparent.left.isRed
|
|
// and grandparent.left.left.isRed
|
|
// 2) grandparent.right.isRed
|
|
// and grandparent.right.right.isRed
|
|
if (n === n.parent.right && n.parent === n.grandparent.left) {
|
|
n.parent.rotateLeft(this)
|
|
// Since we rotated and want to use the previous
|
|
// cases, we need to set n in such a way that
|
|
// n.parent.isRed again
|
|
n = n.left
|
|
} else if (n === n.parent.left && n.parent === n.grandparent.right) {
|
|
n.parent.rotateRight(this)
|
|
// see above
|
|
n = n.right
|
|
}
|
|
// Case 1) or 2) hold from here on.
|
|
// Now traverse grandparent, make parent a black node
|
|
// on the highest level which holds two red nodes.
|
|
n.parent.blacken()
|
|
n.grandparent.redden()
|
|
if (n === n.parent.left) {
|
|
// Case 1
|
|
n.grandparent.rotateRight(this)
|
|
} else {
|
|
// Case 2
|
|
n.grandparent.rotateLeft(this)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Y.utils.RBTree = RBTree
|
|
}
|
|
|
|
},{}]},{},[1])
|
|
|