refactor the whole damn thing
This commit is contained in:
113
src/Store/DeleteStore.js
Normal file
113
src/Store/DeleteStore.js
Normal 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
|
||||
}
|
||||
}
|
||||
88
src/Store/OperationStore.js
Normal file
88
src/Store/OperationStore.js
Normal 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
30
src/Store/StateStore.js
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user