refactor the whole damn thing

This commit is contained in:
Kevin Jahns
2017-10-11 03:41:54 +02:00
parent d9ee67d2f3
commit 82015d5a37
43 changed files with 2194 additions and 4848 deletions

113
src/Store/DeleteStore.js Normal file
View File

@@ -0,0 +1,113 @@
import Tree from '../Util/Tree'
import ID from '../Util/ID'
class DSNode {
constructor (id, len, gc) {
this.id = id
this.len = len
this.gc = gc
}
clone () {
return new DSNode(this.id, this.len, this.gc)
}
}
export default class DeleteStore extends Tree {
isDeleted (id) {
var n = this.ds.findWithUpperBound(id)
return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len
}
/*
* Mark an operation as deleted. returns the deleted node
*/
markDeleted (id, length) {
if (length == null) {
throw new Error('length must be defined')
}
var n = this.findWithUpperBound(id)
if (n != null && n.id.user === id.user) {
if (n.id.clock <= id.clock && id.clock <= n.id.clock + n.len) {
// id is in n's range
var diff = id.clock + length - (n.id.clock + n.len) // overlapping right
if (diff > 0) {
// id+length overlaps n
if (!n.gc) {
n.len += diff
} else {
diff = n.id.clock + n.len - id.clock // overlapping left (id till n.end)
if (diff < length) {
// a partial deletion
let nId = id.clone()
nId.clock += diff
n = new DSNode(nId, length - diff, false)
this.ds.put(n)
} else {
// already gc'd
throw new Error(
'DS reached an inconsistent state. Please report this issue!'
)
}
}
} else {
// no overlapping, already deleted
return n
}
} else {
// cannot extend left (there is no left!)
n = new DSNode(id, length, false)
this.ds.put(n) // TODO: you double-put !!
}
} else {
// cannot extend left
n = new DSNode(id, length, false)
this.ds.put(n)
}
// can extend right?
var next = this.ds.findNext(n.id)
if (
next != null &&
n.id.user === next.id.user &&
n.id.clock + n.len >= next.id.clock
) {
diff = n.id.clock + n.len - next.id.clock // from next.start to n.end
while (diff >= 0) {
// n overlaps with next
if (next.gc) {
// gc is stronger, so reduce length of n
n.len -= diff
if (diff >= next.len) {
// delete the missing range after next
diff = diff - next.len // missing range after next
if (diff > 0) {
this.put(n) // unneccessary? TODO!
this.markDeleted(new ID(next.id.user, next.id.clock + next.len), diff)
}
}
break
} else {
// we can extend n with next
if (diff > next.len) {
// n is even longer than next
// get next.next, and try to extend it
var _next = this.findNext(next.id)
this.delete(next.id)
if (_next == null || n.id.user !== _next.id.user) {
break
} else {
next = _next
diff = n.id.clock + n.len - next.id.clock // from next.start to n.end
// continue!
}
} else {
// n just partially overlaps with next. extend n, delete next, and break this loop
n.len += next.len - diff
this.delete(next.id)
break
}
}
}
}
this.put(n)
return n
}
}

View File

@@ -0,0 +1,88 @@
import Tree from '../Util/Tree'
import RootID from '../Util/ID'
import { getStruct } from '../Util/structReferences'
export default class OperationStore extends Tree {
constructor () {
}
get (id) {
let struct = this.find(id)
if (struct === null && id instanceof RootID) {
let Constr = getStruct(id.type)
struct = new Constr()
struct._id = id
this.put(struct)
}
return struct
}
getItem (id) {
var item = this.findWithUpperBound(id)
if (item == null) {
return null
}
var len = item.content != null ? item.content.length : 1 // in case of opContent
if (id[0] === item.id[0] && id[1] < item.id[1] + len) {
return item
} else {
return null
}
}
// Return an insertion such that id is the first element of content
// This function manipulates an operation, if necessary
getInsertionCleanStart (id) {
var ins = this.getInsertion(id)
if (ins != null) {
if (ins.id[1] === id[1]) {
return ins
} else {
var left = Y.utils.copyObject(ins)
ins.content = left.content.splice(id[1] - ins.id[1])
ins.id = id
var leftLid = Y.utils.getLastId(left)
ins.origin = leftLid
left.originOf = [ins.id]
left.right = ins.id
ins.left = leftLid
// debugger // check
this.setOperation(left)
this.setOperation(ins)
if (left.gc) {
this.store.queueGarbageCollector(ins.id)
}
return ins
}
} else {
return null
}
}
// Return an insertion such that id is the last element of content
// This function manipulates an operation, if necessary
getInsertionCleanEnd (id) {
var ins = this.getInsertion(id)
if (ins != null) {
if (ins.content == null || (ins.id[1] + ins.content.length - 1 === id[1])) {
return ins
} else {
var right = Y.utils.copyObject(ins)
right.content = ins.content.splice(id[1] - ins.id[1] + 1) // cut off remainder
right.id = [id[0], id[1] + 1]
var insLid = Y.utils.getLastId(ins)
right.origin = insLid
ins.originOf = [right.id]
ins.right = right.id
right.left = insLid
// debugger // check
this.setOperation(right)
this.setOperation(ins)
if (ins.gc) {
this.store.queueGarbageCollector(right.id)
}
return ins
}
} else {
return null
}
}
}

30
src/Store/StateStore.js Normal file
View File

@@ -0,0 +1,30 @@
import ID from '../Util/ID'
export default class StateStore {
constructor (y) {
this.y = y
this.state = new Map()
this.currentClock = 0
}
getNextID (len) {
let id = new ID(this.y.userID, this.currentClock)
this.currentClock += len
return id
}
updateRemoteState (struct) {
let user = struct._id.user
let userState = this.state.get(user)
while (struct !== null && struct._id.clock === userState) {
userState += struct._length
struct = this.y.os.get(new ID(user, userState))
}
this.state.set(user, userState)
}
getState (user) {
let state = this.state.get(user)
if (state == null) {
return 0
}
return state
}
}