type._map points to the last element instead to enable merging of deletes in Map
This commit is contained in:
parent
654510f3ff
commit
9fe47e98d5
@ -140,6 +140,9 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
o = this.left.right
|
o = this.left.right
|
||||||
} else if (parentSub !== null) {
|
} else if (parentSub !== null) {
|
||||||
o = parent._map.get(parentSub) || null
|
o = parent._map.get(parentSub) || null
|
||||||
|
while (o !== null && o.left !== null) {
|
||||||
|
o = o.left
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
o = parent._start
|
o = parent._start
|
||||||
}
|
}
|
||||||
@ -180,39 +183,39 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
const right = this.left.right
|
const right = this.left.right
|
||||||
this.right = right
|
this.right = right
|
||||||
this.left.right = this
|
this.left.right = this
|
||||||
if (right !== null) {
|
|
||||||
right.left = this
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let r
|
let r
|
||||||
if (parentSub !== null) {
|
if (parentSub !== null) {
|
||||||
const pmap = parent._map
|
r = parent._map.get(parentSub) || null
|
||||||
r = pmap.get(parentSub) || null
|
while (r !== null && r.left !== null) {
|
||||||
pmap.set(parentSub, this)
|
r = r.left
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
r = parent._start
|
r = parent._start
|
||||||
parent._start = this
|
parent._start = this
|
||||||
}
|
}
|
||||||
this.right = r
|
this.right = r
|
||||||
if (r !== null) {
|
}
|
||||||
r.left = this
|
if (this.right !== null) {
|
||||||
|
this.right.left = this
|
||||||
|
} else if (parentSub !== null) {
|
||||||
|
// set as current parent value if right === null and this is parentSub
|
||||||
|
parent._map.set(parentSub, this)
|
||||||
|
if (this.left !== null) {
|
||||||
|
// this is the current attribute value of parent. delete right
|
||||||
|
this.left.delete(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// adjust the length of parent
|
// adjust length of parent
|
||||||
if (parentSub === null && this.countable && !this.deleted) {
|
if (parentSub === null && this.countable && !this.deleted) {
|
||||||
parent._length += length
|
parent._length += length
|
||||||
}
|
}
|
||||||
addStruct(store, this)
|
addStruct(store, this)
|
||||||
if (parent !== null) {
|
|
||||||
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
|
maplib.setIfUndefined(transaction.changed, parent, set.create).add(parentSub)
|
||||||
}
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if ((parent._item !== null && parent._item.deleted) || (this.left !== null && parentSub !== null)) {
|
if ((parent._item !== null && parent._item.deleted) || (this.right !== null && parentSub !== null)) {
|
||||||
// delete if parent is deleted or if this is not the current attribute value of parent
|
// delete if parent is deleted or if this is not the current attribute value of parent
|
||||||
this.delete(transaction)
|
this.delete(transaction)
|
||||||
} else if (parentSub !== null && this.left === null && this.right !== null) {
|
|
||||||
// this is the current attribute value of parent. delete right
|
|
||||||
this.right.delete(transaction)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,9 +284,9 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
left = this.left
|
left = this.left
|
||||||
right = this
|
right = this
|
||||||
} else {
|
} else {
|
||||||
// Is a map item. Insert at the start
|
// Is a map item. Insert as current value
|
||||||
left = null
|
left = parent.type._map.get(this.parentSub)
|
||||||
right = parent.type._map.get(this.parentSub)
|
right = null
|
||||||
}
|
}
|
||||||
// make sure that parent is redone
|
// make sure that parent is redone
|
||||||
if (parent._deleted === true && parent.redone === null) {
|
if (parent._deleted === true && parent.redone === null) {
|
||||||
@ -308,7 +311,7 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
if (right.redone !== null && right.redone.parent === parent) {
|
if (right.redone !== null && right.redone.parent === parent) {
|
||||||
right = right.redone
|
right = right.redone
|
||||||
}
|
}
|
||||||
right = right._right
|
right = right.right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.redone = this.copy(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, this.parentSub)
|
this.redone = this.copy(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, this.parentSub)
|
||||||
@ -411,18 +414,15 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
r = new GC(this.id, this.length)
|
r = new GC(this.id, this.length)
|
||||||
} else {
|
} else {
|
||||||
r = new ItemDeleted(this.id, this.left, this.origin, this.right, this.rightOrigin, this.parent, this.parentSub, this.length)
|
r = new ItemDeleted(this.id, this.left, this.origin, this.right, this.rightOrigin, this.parent, this.parentSub, this.length)
|
||||||
if (r.left !== null) {
|
|
||||||
r.left.right = r
|
|
||||||
}
|
|
||||||
if (r.right !== null) {
|
if (r.right !== null) {
|
||||||
r.right.left = r
|
r.right.left = r
|
||||||
}
|
} else if (r.parentSub !== null) {
|
||||||
if (r.left === null) {
|
|
||||||
if (r.parentSub === null) {
|
|
||||||
r.parent._start = r
|
|
||||||
} else {
|
|
||||||
r.parent._map.set(r.parentSub, r)
|
r.parent._map.set(r.parentSub, r)
|
||||||
}
|
}
|
||||||
|
if (r.left !== null) {
|
||||||
|
r.left.right = r
|
||||||
|
} else if (r.parentSub === null) {
|
||||||
|
r.parent._start = r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replaceStruct(store, this, r)
|
replaceStruct(store, this, r)
|
||||||
|
@ -21,18 +21,6 @@ import {
|
|||||||
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
import * as encoding from 'lib0/encoding.js' // eslint-disable-line
|
||||||
import * as decoding from 'lib0/decoding.js'
|
import * as decoding from 'lib0/decoding.js'
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {AbstractItem | null} item
|
|
||||||
*/
|
|
||||||
const gcChildren = (transaction, store, item) => {
|
|
||||||
while (item !== null) {
|
|
||||||
item.gc(transaction, store)
|
|
||||||
item = item.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const structTypeRefNumber = 7
|
export const structTypeRefNumber = 7
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,34 +101,26 @@ export class ItemType extends AbstractItem {
|
|||||||
super.delete(transaction)
|
super.delete(transaction)
|
||||||
transaction.changed.delete(this.type)
|
transaction.changed.delete(this.type)
|
||||||
transaction.changedParentTypes.delete(this.type)
|
transaction.changedParentTypes.delete(this.type)
|
||||||
// delete map types
|
|
||||||
for (let value of this.type._map.values()) {
|
|
||||||
if (!value.deleted) {
|
|
||||||
value.delete(transaction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delete array types
|
|
||||||
let t = this.type._start
|
|
||||||
while (t !== null) {
|
|
||||||
if (!t.deleted) {
|
|
||||||
t.delete(transaction)
|
|
||||||
}
|
|
||||||
t = t.right
|
|
||||||
}
|
|
||||||
if (gcChildren) {
|
|
||||||
this.gcChildren(transaction, transaction.y.store)
|
this.gcChildren(transaction, transaction.y.store)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
*/
|
*/
|
||||||
gcChildren (transaction, store) {
|
gcChildren (transaction, store) {
|
||||||
gcChildren(transaction, store, this.type._start)
|
let item = this.type._start
|
||||||
|
while (item !== null) {
|
||||||
|
item.gc(transaction, store)
|
||||||
|
item = item.right
|
||||||
|
}
|
||||||
this.type._start = null
|
this.type._start = null
|
||||||
this.type._map.forEach(item => {
|
this.type._map.forEach(item => {
|
||||||
gcChildren(transaction, store, item)
|
while (item !== null) {
|
||||||
|
item.gc(transaction, store)
|
||||||
|
// @ts-ignore
|
||||||
|
item = item.left
|
||||||
|
}
|
||||||
})
|
})
|
||||||
this._map = new Map()
|
this._map = new Map()
|
||||||
}
|
}
|
||||||
|
@ -447,9 +447,9 @@ export const typeMapDelete = (transaction, parent, key) => {
|
|||||||
* @param {Object|number|Array<any>|string|ArrayBuffer|AbstractType<any>} value
|
* @param {Object|number|Array<any>|string|ArrayBuffer|AbstractType<any>} value
|
||||||
*/
|
*/
|
||||||
export const typeMapSet = (transaction, parent, key, value) => {
|
export const typeMapSet = (transaction, parent, key, value) => {
|
||||||
const right = parent._map.get(key) || null
|
const left = parent._map.get(key) || null
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
new ItemJSON(nextID(transaction), null, null, right, right === null ? null : right.id, parent, key, [value]).integrate(transaction)
|
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch (value.constructor) {
|
switch (value.constructor) {
|
||||||
@ -457,14 +457,14 @@ export const typeMapSet = (transaction, parent, key, value) => {
|
|||||||
case Object:
|
case Object:
|
||||||
case Array:
|
case Array:
|
||||||
case String:
|
case String:
|
||||||
new ItemJSON(nextID(transaction), null, null, right, right === null ? null : right.id, parent, key, [value]).integrate(transaction)
|
new ItemJSON(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, [value]).integrate(transaction)
|
||||||
break
|
break
|
||||||
case ArrayBuffer:
|
case ArrayBuffer:
|
||||||
new ItemBinary(nextID(transaction), null, null, right, right === null ? null : right.id, parent, key, value).integrate(transaction)
|
new ItemBinary(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (value instanceof AbstractType) {
|
if (value instanceof AbstractType) {
|
||||||
new ItemType(nextID(transaction), null, null, right, right === null ? null : right.id, parent, key, value).integrate(transaction)
|
new ItemType(nextID(transaction), left, left === null ? null : left.lastId, null, null, parent, key, value).integrate(transaction)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected content type')
|
throw new Error('Unexpected content type')
|
||||||
}
|
}
|
||||||
@ -492,7 +492,7 @@ export const typeMapGetAll = (parent) => {
|
|||||||
let res = {}
|
let res = {}
|
||||||
for (const [key, value] of parent._map) {
|
for (const [key, value] of parent._map) {
|
||||||
if (!value.deleted) {
|
if (!value.deleted) {
|
||||||
res[key] = value.getContent()[0]
|
res[key] = value.getContent()[value.length - 1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
@ -517,9 +517,9 @@ export const typeMapHas = (parent, key) => {
|
|||||||
export const typeMapGetSnapshot = (parent, key, snapshot) => {
|
export const typeMapGetSnapshot = (parent, key, snapshot) => {
|
||||||
let v = parent._map.get(key) || null
|
let v = parent._map.get(key) || null
|
||||||
while (v !== null && (!snapshot.sm.has(v.id.client) || v.id.clock >= (snapshot.sm.get(v.id.client) || 0))) {
|
while (v !== null && (!snapshot.sm.has(v.id.client) || v.id.clock >= (snapshot.sm.get(v.id.client) || 0))) {
|
||||||
v = v.right
|
v = v.left
|
||||||
}
|
}
|
||||||
return v !== null && isVisible(v, snapshot) ? v.getContent()[0] : undefined
|
return v !== null && isVisible(v, snapshot) ? v.getContent()[v.length - 1] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,6 +190,10 @@ export const transact = (y, f) => {
|
|||||||
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
||||||
if (left.mergeWith(right)) {
|
if (left.mergeWith(right)) {
|
||||||
structs.splice(pos, 1)
|
structs.splice(pos, 1)
|
||||||
|
if (right instanceof AbstractItem && right.parentSub !== null && right.parent._map.get(right.parentSub) === right) {
|
||||||
|
// @ts-ignore we already did a constructor check above
|
||||||
|
right.parent._map.set(right.parentSub, left)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,6 +214,7 @@ export const transact = (y, f) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// try to merge replacedItems
|
// try to merge replacedItems
|
||||||
|
// TODO: replacedItems should hold ids
|
||||||
for (const replacedItem of transaction._replacedItems) {
|
for (const replacedItem of transaction._replacedItems) {
|
||||||
const id = replacedItem.id
|
const id = replacedItem.id
|
||||||
const client = id.client
|
const client = id.client
|
||||||
|
@ -117,7 +117,7 @@ export const testGetAndSetOfMapPropertyWithConflict = tc => {
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.getMap('map')
|
var u = user.getMap('map')
|
||||||
t.compare(u.get('stuff'), 'c0')
|
t.compare(u.get('stuff'), 'c1')
|
||||||
}
|
}
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
@ -128,8 +128,8 @@ export const testGetAndSetOfMapPropertyWithConflict = tc => {
|
|||||||
export const testGetAndSetAndDeleteOfMapProperty = tc => {
|
export const testGetAndSetAndDeleteOfMapProperty = tc => {
|
||||||
const { testConnector, users, map0, map1 } = init(tc, { users: 3 })
|
const { testConnector, users, map0, map1 } = init(tc, { users: 3 })
|
||||||
map0.set('stuff', 'c0')
|
map0.set('stuff', 'c0')
|
||||||
map0.delete('stuff')
|
|
||||||
map1.set('stuff', 'c1')
|
map1.set('stuff', 'c1')
|
||||||
|
map1.delete('stuff')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.getMap('map')
|
var u = user.getMap('map')
|
||||||
@ -150,7 +150,7 @@ export const testGetAndSetOfMapPropertyWithThreeConflicts = tc => {
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.getMap('map')
|
var u = user.getMap('map')
|
||||||
t.compare(u.get('stuff'), 'c0')
|
t.compare(u.get('stuff'), 'c3')
|
||||||
}
|
}
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
@ -166,10 +166,10 @@ export const testGetAndSetAndDeleteOfMapPropertyWithThreeConflicts = tc => {
|
|||||||
map2.set('stuff', 'c3')
|
map2.set('stuff', 'c3')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
map0.set('stuff', 'deleteme')
|
map0.set('stuff', 'deleteme')
|
||||||
map0.delete('stuff')
|
|
||||||
map1.set('stuff', 'c1')
|
map1.set('stuff', 'c1')
|
||||||
map2.set('stuff', 'c2')
|
map2.set('stuff', 'c2')
|
||||||
map3.set('stuff', 'c3')
|
map3.set('stuff', 'c3')
|
||||||
|
map3.delete('stuff')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.getMap('map')
|
var u = user.getMap('map')
|
||||||
@ -348,7 +348,7 @@ const mapTransactions = [
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testRepeatGeneratingYmapTests10 = tc => {
|
export const testRepeatGeneratingYmapTests10 = tc => {
|
||||||
applyRandomTests(tc, mapTransactions, 10)
|
applyRandomTests(tc, mapTransactions, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user