doc maintains ds

This commit is contained in:
Kevin Jahns
2025-04-10 21:07:59 +02:00
parent 46347ee6ec
commit 6360297e33
7 changed files with 52 additions and 21 deletions

View File

@@ -99,6 +99,7 @@ export {
UpdateDecoderV2,
snapshotContainsUpdate,
// idset
IdSet,
equalIdSets,
createDeleteSetFromStructStore
} from './internals.js'

View File

@@ -2,6 +2,7 @@ import {
AbstractStruct,
addStruct,
addStructToIdSet,
addToIdSet,
UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line
} from '../internals.js'
@@ -38,6 +39,7 @@ export class GC extends AbstractStruct {
this.id.clock += offset
this.length -= offset
}
addToIdSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
addStructToIdSet(transaction.insertSet, this)
addStruct(transaction.doc.store, this)
}

View File

@@ -191,6 +191,25 @@ export const mergeIdSets = idSets => {
return merged
}
/**
* @param {IdSet} dest
* @param {IdSet} src
*/
export const insertIntoIdSet = (dest, src) => {
src.clients.forEach((srcRanges, client) => {
const targetRanges = dest.clients.get(client)
if (targetRanges) {
array.appendTo(targetRanges.getIds(), srcRanges.getIds())
targetRanges.sorted = false
} else {
const res = new IdRanges(srcRanges.getIds().slice())
res.sorted = true
dest.clients.set(client, res)
}
})
}
/**
* Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not
* in `exclude`.

View File

@@ -1,7 +1,8 @@
import {
GC,
splitItem,
Transaction, ID, Item, DSDecoderV2 // eslint-disable-line
IdSet,
Transaction, ID, Item // eslint-disable-line
} from '../internals.js'
import * as math from 'lib0/math'
@@ -13,6 +14,7 @@ export class StructStore {
* @type {Map<number,Array<GC|Item>>}
*/
this.clients = new Map()
this.ds = new IdSet()
/**
* @type {null | { missing: Map<number, number>, update: Uint8Array }}
*/

View File

@@ -10,7 +10,8 @@ import {
generateNewClientId,
createID,
cleanupYTextAfterTransaction,
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line
insertIntoIdSet
} from '../internals.js'
import * as error from 'lib0/error'
@@ -307,6 +308,7 @@ const cleanupTransactions = (transactionCleanups, i) => {
const store = doc.store
const ds = transaction.deleteSet
const mergeStructs = transaction._mergeStructs
insertIntoIdSet(store.ds, ds)
try {
doc.emit('beforeObserverCalls', [transaction, doc])
/**

View File

@@ -2,6 +2,7 @@ import * as t from 'lib0/testing'
import * as d from '../src/utils/IdSet.js'
import * as prng from 'lib0/prng'
import * as math from 'lib0/math'
import { compareIdSets } from './testHelper.js'
/**
* @param {Array<[number, number, number]>} ops
@@ -14,25 +15,6 @@ const simpleConstructIdSet = ops => {
return ds
}
/**
* @param {d.IdSet} idSet1
* @param {d.IdSet} idSet2
*/
const compareIdSets = (idSet1, idSet2) => {
if (idSet1.clients.size !== idSet2.clients.size) return false
for (const [client, _items1] of idSet1.clients.entries()) {
const items1 = _items1.getIds()
const items2 = idSet2.clients.get(client)?.getIds()
t.assert(items2 !== undefined && items1.length === items2.length)
for (let i = 0; i < items1.length; i++) {
const di1 = items1[i]
const di2 = /** @type {Array<d.IdRange>} */ (items2)[i]
t.assert(di1.clock === di2.clock && di1.len === di2.len)
}
}
return true
}
/**
* @param {t.TestCase} _tc
*/

View File

@@ -304,6 +304,26 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
return /** @type {any} */ (result)
}
/**
* @param {Y.IdSet} idSet1
* @param {Y.IdSet} idSet2
*/
export const compareIdSets = (idSet1, idSet2) => {
if (idSet1.clients.size !== idSet2.clients.size) return false
for (const [client, _items1] of idSet1.clients.entries()) {
const items1 = _items1.getIds()
const items2 = idSet2.clients.get(client)?.getIds()
t.assert(items2 !== undefined && items1.length === items2.length)
for (let i = 0; i < items1.length; i++) {
const di1 = items1[i]
const di2 = /** @type {Array<import('../src/utils/IdSet.js').IdRange>} */ (items2)[i]
t.assert(di1.clock === di2.clock && di1.len === di2.len)
}
}
return true
}
/**
* 1. reconnect and flush all
* 2. user 0 gc
@@ -366,6 +386,9 @@ export const compare = users => {
compareStructStores(users[i].store, users[i + 1].store)
t.compare(Y.encodeSnapshot(Y.snapshot(users[i])), Y.encodeSnapshot(Y.snapshot(users[i + 1])))
}
users.forEach(user => {
compareIdSets(user.store.ds, Y.createDeleteSetFromStructStore(user.store))
})
users.map(u => u.destroy())
}