fix most gc bugs - test suite running again
This commit is contained in:
parent
d915c8dd13
commit
ef6eb08335
@ -1,6 +1,7 @@
|
|||||||
import { getStruct } from '../Util/structReferences.js'
|
import { getStruct } from '../Util/structReferences.js'
|
||||||
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
||||||
import { logID } from './messageToString.js'
|
import { logID } from './messageToString.js'
|
||||||
|
import GC from '../Struct/GC.js';
|
||||||
|
|
||||||
class MissingEntry {
|
class MissingEntry {
|
||||||
constructor (decoder, missing, struct) {
|
constructor (decoder, missing, struct) {
|
||||||
@ -24,7 +25,14 @@ function _integrateRemoteStructHelper (y, struct) {
|
|||||||
if (y.ss.getState(id.user) > id.clock) {
|
if (y.ss.getState(id.user) > id.clock) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
struct._integrate(y)
|
if (struct.constructor === GC || (struct._parent.constructor !== GC && struct._parent._deleted === false)) {
|
||||||
|
// Is either a GC or Item with an undeleted parent
|
||||||
|
// save to integrate
|
||||||
|
struct._integrate(y)
|
||||||
|
} else {
|
||||||
|
// Is an Item. parent was deleted.
|
||||||
|
struct._gc(y)
|
||||||
|
}
|
||||||
let msu = y._missingStructs.get(id.user)
|
let msu = y._missingStructs.get(id.user)
|
||||||
if (msu != null) {
|
if (msu != null) {
|
||||||
let clock = id.clock
|
let clock = id.clock
|
||||||
|
@ -52,7 +52,7 @@ export default class DeleteStore extends Tree {
|
|||||||
if (rightD !== null && rightD._id.user === id.user) {
|
if (rightD !== null && rightD._id.user === id.user) {
|
||||||
if (rightD._id.clock < id.clock + length && id.clock <= rightD._id.clock && id.clock + length < rightD._id.clock + rightD.len) { // we only consider the case where we resize the node
|
if (rightD._id.clock < id.clock + length && id.clock <= rightD._id.clock && id.clock + length < rightD._id.clock + rightD.len) { // we only consider the case where we resize the node
|
||||||
const d = id.clock + length - rightD._id.clock
|
const d = id.clock + length - rightD._id.clock
|
||||||
rightD._id.clock += d
|
rightD._id = new ID(rightD._id.user, rightD._id.clock + d)
|
||||||
rightD.len -= d
|
rightD.len -= d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import Tree from '../Util/Tree.js'
|
|||||||
import RootID from '../Util/ID/RootID.js'
|
import RootID from '../Util/ID/RootID.js'
|
||||||
import { getStruct } from '../Util/structReferences.js'
|
import { getStruct } from '../Util/structReferences.js'
|
||||||
import { logID } from '../MessageHandler/messageToString.js'
|
import { logID } from '../MessageHandler/messageToString.js'
|
||||||
|
import GC from '../Struct/GC.js'
|
||||||
|
|
||||||
export default class OperationStore extends Tree {
|
export default class OperationStore extends Tree {
|
||||||
constructor (y) {
|
constructor (y) {
|
||||||
@ -11,17 +12,25 @@ export default class OperationStore extends Tree {
|
|||||||
logTable () {
|
logTable () {
|
||||||
const items = []
|
const items = []
|
||||||
this.iterate(null, null, function (item) {
|
this.iterate(null, null, function (item) {
|
||||||
items.push({
|
if (item.constructor === GC) {
|
||||||
id: logID(item),
|
items.push({
|
||||||
origin: logID(item._origin === null ? null : item._origin._lastId),
|
id: logID(item),
|
||||||
left: logID(item._left === null ? null : item._left._lastId),
|
content: item._length,
|
||||||
right: logID(item._right),
|
deleted: 'GC'
|
||||||
right_origin: logID(item._right_origin),
|
})
|
||||||
parent: logID(item._parent),
|
} else {
|
||||||
parentSub: item._parentSub,
|
items.push({
|
||||||
deleted: item._deleted,
|
id: logID(item),
|
||||||
content: JSON.stringify(item._content)
|
origin: logID(item._origin === null ? null : item._origin._lastId),
|
||||||
})
|
left: logID(item._left === null ? null : item._left._lastId),
|
||||||
|
right: logID(item._right),
|
||||||
|
right_origin: logID(item._right_origin),
|
||||||
|
parent: logID(item._parent),
|
||||||
|
parentSub: item._parentSub,
|
||||||
|
deleted: item._deleted,
|
||||||
|
content: JSON.stringify(item._content)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
console.table(items)
|
console.table(items)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import { getStructReference } from '../Util/structReferences.js'
|
||||||
|
import { RootFakeUserID } from '../Util/ID/RootID.js'
|
||||||
|
import ID from '../Util/ID/ID.js'
|
||||||
|
|
||||||
|
// TODO should have the same base class as Item
|
||||||
export default class GC {
|
export default class GC {
|
||||||
constructor () {
|
constructor () {
|
||||||
this._id = null
|
this._id = null
|
||||||
@ -9,7 +13,38 @@ export default class GC {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
integrate () {
|
_integrate (y) {
|
||||||
|
const id = this._id
|
||||||
|
const userState = y.ss.getState(id.user)
|
||||||
|
if (id.clock === userState) {
|
||||||
|
y.ss.setState(id.user, id.clock + this._length)
|
||||||
|
}
|
||||||
|
y.ds.mark(this._id, this._length, true)
|
||||||
|
let n = y.os.put(this)
|
||||||
|
const prev = n.prev().val
|
||||||
|
if (prev !== null && prev.constructor === GC && prev._id.user === n.val._id.user && prev._id.clock + prev._length === n.val._id.clock) {
|
||||||
|
// TODO: do merging for all items!
|
||||||
|
prev._length += n.val._length
|
||||||
|
y.os.delete(n.val._id)
|
||||||
|
n = prev
|
||||||
|
}
|
||||||
|
if (n.val) {
|
||||||
|
n = n.val
|
||||||
|
}
|
||||||
|
const next = y.os.findNext(n._id)
|
||||||
|
if (next !== null && next.constructor === GC && next._id.user === n._id.user && next._id.clock === n._id.clock + n._length) {
|
||||||
|
n._length += next._length
|
||||||
|
y.os.delete(next._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.user !== RootFakeUserID) {
|
||||||
|
if (y.connector !== null && (y.connector._forwardAppliedStructs || id.user === y.userID)) {
|
||||||
|
y.connector.broadcastStruct(this)
|
||||||
|
}
|
||||||
|
if (y.persistence !== null) {
|
||||||
|
y.persistence.saveStruct(y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,8 +72,17 @@ export default class GC {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_fromBinary (y, decoder) {
|
_fromBinary (y, decoder) {
|
||||||
this._id = decoder.readID()
|
const id = decoder.readID()
|
||||||
|
this._id = id
|
||||||
this._length = decoder.readVarUint()
|
this._length = decoder.readVarUint()
|
||||||
return []
|
const missing = []
|
||||||
}
|
if (y.ss.getState(id.user) < id.clock) {
|
||||||
|
missing.push(new ID(id.user, id.clock - 1))
|
||||||
|
}
|
||||||
|
return missing
|
||||||
|
}
|
||||||
|
|
||||||
|
_splitAt () {
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
@ -240,27 +240,11 @@ export default class Item {
|
|||||||
_gcChildren (y) {}
|
_gcChildren (y) {}
|
||||||
|
|
||||||
_gc (y) {
|
_gc (y) {
|
||||||
y.ds.mark(this._id, this._length, true)
|
|
||||||
const gc = new GC()
|
const gc = new GC()
|
||||||
gc._id = this._id
|
gc._id = this._id
|
||||||
gc._length = this._length
|
gc._length = this._length
|
||||||
y.os.delete(this._id)
|
y.os.delete(this._id)
|
||||||
let n = y.os.put(gc)
|
gc._integrate(y)
|
||||||
const prev = n.prev().val
|
|
||||||
if (prev !== null && prev.constructor === GC && prev._id.user === n.val._id.user && prev._id.clock + prev._length === n.val._id.clock) {
|
|
||||||
// TODO: do merging for all items!
|
|
||||||
prev._length += n.val._length
|
|
||||||
y.os.delete(n.val._id)
|
|
||||||
n = prev
|
|
||||||
}
|
|
||||||
if (n.val) {
|
|
||||||
n = n.val
|
|
||||||
}
|
|
||||||
const next = y.os.findNext(n._id)
|
|
||||||
if (next !== null && next.constructor === GC && next._id.user === n._id.user && next._id.clock === n._id.clock + n._length) {
|
|
||||||
n._length += next._length
|
|
||||||
y.os.delete(n._id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -511,9 +495,19 @@ export default class Item {
|
|||||||
}
|
}
|
||||||
} else if (this._parent === null) {
|
} else if (this._parent === null) {
|
||||||
if (this._origin !== null) {
|
if (this._origin !== null) {
|
||||||
this._parent = this._origin._parent
|
if (this._origin.constructor === GC) {
|
||||||
|
// if origin is a gc, set parent also gc'd
|
||||||
|
this._parent = this._origin
|
||||||
|
} else {
|
||||||
|
this._parent = this._origin._parent
|
||||||
|
}
|
||||||
} else if (this._right_origin !== null) {
|
} else if (this._right_origin !== null) {
|
||||||
this._parent = this._right_origin._parent
|
// if origin is a gc, set parent also gc'd
|
||||||
|
if (this._right_origin.constructor === GC) {
|
||||||
|
this._parent = this._right_origin
|
||||||
|
} else {
|
||||||
|
this._parent = this._right_origin._parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info & 0b1000) {
|
if (info & 0b1000) {
|
||||||
|
@ -8,6 +8,7 @@ import ItemJSON from '../src/Struct/ItemJSON.js'
|
|||||||
import ItemString from '../src/Struct/ItemString.js'
|
import ItemString from '../src/Struct/ItemString.js'
|
||||||
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
||||||
import Quill from 'quill'
|
import Quill from 'quill'
|
||||||
|
import GC from '../src/Struct/GC.js';
|
||||||
|
|
||||||
export const Y = _Y
|
export const Y = _Y
|
||||||
|
|
||||||
@ -110,13 +111,22 @@ export async function compareUsers (t, users) {
|
|||||||
var data = {}
|
var data = {}
|
||||||
let ops = []
|
let ops = []
|
||||||
u.os.iterate(null, null, function (op) {
|
u.os.iterate(null, null, function (op) {
|
||||||
const json = {
|
let json
|
||||||
id: op._id,
|
if (op.constructor === GC) {
|
||||||
left: op._left === null ? null : op._left._lastId,
|
json = {
|
||||||
right: op._right === null ? null : op._right._id,
|
type: 'GC',
|
||||||
length: op._length,
|
id: op._id,
|
||||||
deleted: op._deleted,
|
length: op._length
|
||||||
parent: op._parent._id
|
}
|
||||||
|
} else {
|
||||||
|
json = {
|
||||||
|
id: op._id,
|
||||||
|
left: op._left === null ? null : op._left._lastId,
|
||||||
|
right: op._right === null ? null : op._right._id,
|
||||||
|
length: op._length,
|
||||||
|
deleted: op._deleted,
|
||||||
|
parent: op._parent._id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (op instanceof ItemJSON || op instanceof ItemString) {
|
if (op instanceof ItemJSON || op instanceof ItemString) {
|
||||||
json.content = op._content
|
json.content = op._content
|
||||||
@ -186,7 +196,7 @@ export async function initArrays (t, opts) {
|
|||||||
y.dom = dom
|
y.dom = dom
|
||||||
y.on('afterTransaction', function () {
|
y.on('afterTransaction', function () {
|
||||||
for (let missing of y._missingStructs.values()) {
|
for (let missing of y._missingStructs.values()) {
|
||||||
if (Array.from(missing.values()).length > 0) {
|
if (missing.size > 0) {
|
||||||
console.error(new Error('Test check in "afterTransaction": missing should be empty!'))
|
console.error(new Error('Test check in "afterTransaction": missing should be empty!'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user