prelim gc

This commit is contained in:
Kevin Jahns 2018-03-29 18:24:14 +02:00
parent 32207cbca0
commit d915c8dd13
7 changed files with 111 additions and 12 deletions

View File

@ -13,18 +13,18 @@ export function deleteItemRange (y, user, clock, range) {
if (item !== null) {
if (!item._deleted) {
item._splitAt(y, range)
item._delete(y, createDelete)
item._delete(y, createDelete, true)
}
let itemLen = item._length
range -= itemLen
clock += itemLen
if (range > 0) {
let node = y.os.findNode(new ID(user, clock))
while (node !== null && range > 0 && node.val._id.equals(new ID(user, clock))) {
while (node !== null && node.val !== null && range > 0 && node.val._id.equals(new ID(user, clock))) {
const nodeVal = node.val
if (!nodeVal._deleted) {
nodeVal._splitAt(y, range)
nodeVal._delete(y, createDelete)
nodeVal._delete(y, createDelete, true)
}
const nodeLen = nodeVal._length
range -= nodeLen

44
src/Struct/GC.js Normal file
View File

@ -0,0 +1,44 @@
export default class GC {
constructor () {
this._id = null
this._length = 0
}
get _deleted () {
return true
}
integrate () {
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
* @private
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoder.writeID(this._id)
encoder.writeVarUint(this._length)
}
/**
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
* @private
*/
_fromBinary (y, decoder) {
this._id = decoder.readID()
this._length = decoder.readVarUint()
return []
}
}

View File

@ -3,6 +3,7 @@ import ID from '../Util/ID/ID.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
import Delete from './Delete.js'
import { transactionTypeChanged } from '../Transaction.js'
import GC from './GC.js'
/**
* @private
@ -220,7 +221,7 @@ export default class Item {
_delete (y, createDelete = true) {
if (!this._deleted) {
this._deleted = true
y.ds.markDeleted(this._id, this._length)
y.ds.mark(this._id, this._length, false)
let del = new Delete()
del._targetID = this._id
del._length = this._length
@ -236,6 +237,32 @@ export default class Item {
}
}
_gcChildren (y) {}
_gc (y) {
y.ds.mark(this._id, this._length, true)
const gc = new GC()
gc._id = this._id
gc._length = this._length
y.os.delete(this._id)
let n = y.os.put(gc)
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)
}
}
/**
* This is called right before this Item receives any children.
* It can be overwritten to apply pending changes before applying remote changes

View File

@ -30,6 +30,14 @@ export function getListItemIDByPosition (type, i) {
}
}
function gcChildren (y, item) {
while (item !== null) {
item._delete(y, false, true)
item._gc(y)
item = item._right
}
}
/**
* Abstract Yjs Type class
*/
@ -184,6 +192,20 @@ export default class Type extends Item {
}
}
_gcChildren (y) {
gcChildren(y, this._start)
this._start = null
this._map.forEach(item => {
gcChildren(y, item)
})
this._map = new Map()
}
_gc (y) {
this._gcChildren(y)
super._gc(y)
}
/**
* @private
* Mark this Item as deleted.
@ -192,22 +214,25 @@ export default class Type extends Item {
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
*/
_delete (y, createDelete) {
super._delete(y, createDelete)
_delete (y, createDelete, gcChildren = true) {
super._delete(y, createDelete, gcChildren)
y._transaction.changedTypes.delete(this)
// delete map types
for (let value of this._map.values()) {
if (value instanceof Item && !value._deleted) {
value._delete(y, false)
value._delete(y, false, gcChildren)
}
}
// delete array types
let t = this._start
while (t !== null) {
if (!t._deleted) {
t._delete(y, false)
t._delete(y, false, gcChildren)
}
t = t._right
}
if (gcChildren) {
this._gcChildren(y)
}
}
}

View File

@ -126,8 +126,8 @@ export default class YXmlFragment extends YArray {
*
* @private
*/
_delete (y, createDelete) {
super._delete(y, createDelete)
_delete (y, createDelete, gcChildren) {
super._delete(y, createDelete, gcChildren)
}
/**

View File

@ -36,7 +36,7 @@ export default class YXmlText extends YText {
*
* @private
*/
_delete (y, createDelete) {
super._delete(y, createDelete)
_delete (y, createDelete, gcChildren) {
super._delete(y, createDelete, gcChildren)
}
}

View File

@ -8,6 +8,7 @@ import ItemJSON from '../Struct/ItemJSON.js'
import ItemString from '../Struct/ItemString.js'
import ItemFormat from '../Struct/ItemFormat.js'
import ItemEmbed from '../Struct/ItemEmbed.js'
import GC from '../Struct/GC.js'
const structs = new Map()
const references = new Map()
@ -54,3 +55,5 @@ registerStruct(6, YXmlFragment)
registerStruct(7, YXmlElement)
registerStruct(8, YXmlText)
registerStruct(9, YXmlHook)
registerStruct(12, GC)