@y/y 14.0.0-16 → 14.0.0-17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-DyTeTfmj.js → index-BV-j5wdP.js} +2 -2
- package/dist/{index-R7GxO-36.js.map → index-BV-j5wdP.js.map} +1 -1
- package/dist/{internals.mjs → internals.js} +1 -1
- package/dist/internals.js.map +1 -0
- package/dist/{testHelper.mjs → testHelper.js} +2 -2
- package/dist/testHelper.js.map +1 -0
- package/dist/{yjs.mjs → yjs.js} +2 -2
- package/dist/yjs.js.map +1 -0
- package/package.json +8 -17
- package/dist/Skip-j0kX7pdq.js +0 -12173
- package/dist/Skip-j0kX7pdq.js.map +0 -1
- package/dist/index-DyTeTfmj.js.map +0 -1
- package/dist/index-R7GxO-36.js +0 -165
- package/dist/internals.cjs +0 -286
- package/dist/internals.cjs.map +0 -1
- package/dist/internals.mjs.map +0 -1
- package/dist/testHelper.cjs +0 -780
- package/dist/testHelper.cjs.map +0 -1
- package/dist/testHelper.mjs.map +0 -1
- package/dist/yjs.cjs +0 -151
- package/dist/yjs.cjs.map +0 -1
- package/dist/yjs.mjs.map +0 -1
- package/src/index.js +0 -153
- package/src/internals.js +0 -44
- package/src/structs/AbstractStruct.js +0 -59
- package/src/structs/ContentAny.js +0 -115
- package/src/structs/ContentBinary.js +0 -93
- package/src/structs/ContentDeleted.js +0 -101
- package/src/structs/ContentDoc.js +0 -141
- package/src/structs/ContentEmbed.js +0 -98
- package/src/structs/ContentFormat.js +0 -105
- package/src/structs/ContentJSON.js +0 -119
- package/src/structs/ContentString.js +0 -113
- package/src/structs/ContentType.js +0 -176
- package/src/structs/GC.js +0 -80
- package/src/structs/Item.js +0 -845
- package/src/structs/Skip.js +0 -75
- package/src/types/AbstractType.js +0 -1434
- package/src/types/YArray.js +0 -270
- package/src/types/YMap.js +0 -244
- package/src/types/YText.js +0 -934
- package/src/types/YXmlElement.js +0 -227
- package/src/types/YXmlFragment.js +0 -266
- package/src/types/YXmlHook.js +0 -68
- package/src/types/YXmlText.js +0 -66
- package/src/utils/AbstractConnector.js +0 -25
- package/src/utils/AttributionManager.js +0 -619
- package/src/utils/Doc.js +0 -372
- package/src/utils/EventHandler.js +0 -87
- package/src/utils/ID.js +0 -89
- package/src/utils/IdMap.js +0 -629
- package/src/utils/IdSet.js +0 -823
- package/src/utils/RelativePosition.js +0 -352
- package/src/utils/Snapshot.js +0 -220
- package/src/utils/StructSet.js +0 -137
- package/src/utils/StructStore.js +0 -289
- package/src/utils/Transaction.js +0 -489
- package/src/utils/UndoManager.js +0 -391
- package/src/utils/UpdateDecoder.js +0 -281
- package/src/utils/UpdateEncoder.js +0 -320
- package/src/utils/YEvent.js +0 -216
- package/src/utils/delta-helpers.js +0 -54
- package/src/utils/encoding.js +0 -623
- package/src/utils/isParentOf.js +0 -21
- package/src/utils/logging.js +0 -21
- package/src/utils/types.js +0 -28
- package/src/utils/updates.js +0 -715
- package/tests/testHelper.js +0 -600
package/src/utils/IdMap.js
DELETED
|
@@ -1,629 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
_diffSet,
|
|
3
|
-
findIndexInIdRanges,
|
|
4
|
-
findRangeStartInIdRanges,
|
|
5
|
-
_deleteRangeFromIdSet,
|
|
6
|
-
DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID, // eslint-disable-line
|
|
7
|
-
_insertIntoIdSet,
|
|
8
|
-
_intersectSets
|
|
9
|
-
} from '../internals.js'
|
|
10
|
-
|
|
11
|
-
import * as array from 'lib0/array'
|
|
12
|
-
import * as map from 'lib0/map'
|
|
13
|
-
import * as encoding from 'lib0/encoding'
|
|
14
|
-
import * as decoding from 'lib0/decoding'
|
|
15
|
-
import * as buf from 'lib0/buffer'
|
|
16
|
-
import * as rabin from 'lib0/hash/rabin'
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @todo rename this to `Attribute`
|
|
20
|
-
* @template V
|
|
21
|
-
*/
|
|
22
|
-
export class AttributionItem {
|
|
23
|
-
/**
|
|
24
|
-
* @param {string} name
|
|
25
|
-
* @param {V} val
|
|
26
|
-
*/
|
|
27
|
-
constructor (name, val) {
|
|
28
|
-
this.name = name
|
|
29
|
-
this.val = val
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
hash () {
|
|
33
|
-
const encoder = encoding.createEncoder()
|
|
34
|
-
encoding.writeVarString(encoder, this.name)
|
|
35
|
-
encoding.writeAny(encoder, /** @type {any} */ (this.val))
|
|
36
|
-
return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder)))
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @param {AttributionItem<any>} attr
|
|
42
|
-
*/
|
|
43
|
-
const _hashAttribution = attr => {
|
|
44
|
-
const encoder = encoding.createEncoder()
|
|
45
|
-
encoding.writeVarString(encoder, attr.name)
|
|
46
|
-
encoding.writeAny(encoder, attr.val)
|
|
47
|
-
return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder)))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @todo rename this to `createAttribute`
|
|
52
|
-
* @template V
|
|
53
|
-
* @param {string} name
|
|
54
|
-
* @param {V} val
|
|
55
|
-
* @return {AttributionItem<V>}
|
|
56
|
-
*/
|
|
57
|
-
export const createAttributionItem = (name, val) => new AttributionItem(name, val)
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @template T
|
|
61
|
-
* @param {Array<T>} attrs
|
|
62
|
-
* @param {T} attr
|
|
63
|
-
*
|
|
64
|
-
*/
|
|
65
|
-
const idmapAttrsHas = (attrs, attr) => attrs.find(a => a === attr)
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @template T
|
|
69
|
-
* @param {Array<T>} a
|
|
70
|
-
* @param {Array<T>} b
|
|
71
|
-
*/
|
|
72
|
-
export const idmapAttrsEqual = (a, b) => a.length === b.length && a.every(v => idmapAttrsHas(b, v))
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @template T
|
|
76
|
-
* @param {Array<T>} a
|
|
77
|
-
* @param {Array<T>} b
|
|
78
|
-
*/
|
|
79
|
-
const idmapAttrRangeJoin = (a, b) => a.concat(b.filter(attr => !idmapAttrsHas(a, attr)))
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @template Attrs
|
|
83
|
-
*/
|
|
84
|
-
export class AttrRange {
|
|
85
|
-
/**
|
|
86
|
-
* @param {number} clock
|
|
87
|
-
* @param {number} len
|
|
88
|
-
* @param {Array<AttributionItem<Attrs>>} attrs
|
|
89
|
-
*/
|
|
90
|
-
constructor (clock, len, attrs) {
|
|
91
|
-
/**
|
|
92
|
-
* @readonly
|
|
93
|
-
*/
|
|
94
|
-
this.clock = clock
|
|
95
|
-
/**
|
|
96
|
-
* @readonly
|
|
97
|
-
*/
|
|
98
|
-
this.len = len
|
|
99
|
-
/**
|
|
100
|
-
* @readonly
|
|
101
|
-
*/
|
|
102
|
-
this.attrs = attrs
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @param {number} clock
|
|
107
|
-
* @param {number} len
|
|
108
|
-
*/
|
|
109
|
-
copyWith (clock, len) {
|
|
110
|
-
return new AttrRange(clock, len, this.attrs)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @template Attrs
|
|
116
|
-
* @typedef {{ clock: number, len: number, attrs: Array<AttributionItem<Attrs>>? }} MaybeAttrRange
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @template Attrs
|
|
121
|
-
*
|
|
122
|
-
* @param {number} clock
|
|
123
|
-
* @param {number} len
|
|
124
|
-
* @param {Array<AttributionItem<Attrs>>?} attrs
|
|
125
|
-
* @return {MaybeAttrRange<Attrs>}
|
|
126
|
-
*/
|
|
127
|
-
export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs))
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Whenever this is instantiated, it must receive a fresh array of ops, not something copied.
|
|
131
|
-
*
|
|
132
|
-
* @template Attrs
|
|
133
|
-
*/
|
|
134
|
-
export class AttrRanges {
|
|
135
|
-
/**
|
|
136
|
-
* @param {Array<AttrRange<Attrs>>} ids
|
|
137
|
-
*/
|
|
138
|
-
constructor (ids) {
|
|
139
|
-
this.sorted = false
|
|
140
|
-
/**
|
|
141
|
-
* @private
|
|
142
|
-
*/
|
|
143
|
-
this._ids = ids
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
copy () {
|
|
147
|
-
return new AttrRanges(this._ids.slice())
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* @param {number} clock
|
|
152
|
-
* @param {number} length
|
|
153
|
-
* @param {Array<AttributionItem<Attrs>>} attrs
|
|
154
|
-
*/
|
|
155
|
-
add (clock, length, attrs) {
|
|
156
|
-
if (length === 0) return
|
|
157
|
-
this.sorted = false
|
|
158
|
-
this._ids.push(new AttrRange(clock, length, attrs))
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Return the list of id ranges, sorted and merged.
|
|
163
|
-
*/
|
|
164
|
-
getIds () {
|
|
165
|
-
const ids = this._ids
|
|
166
|
-
if (!this.sorted) {
|
|
167
|
-
this.sorted = true
|
|
168
|
-
ids.sort((a, b) => a.clock - b.clock)
|
|
169
|
-
/**
|
|
170
|
-
* algorithm thoughts:
|
|
171
|
-
* - sort (by clock AND by length), bigger length is to the right (or not, we can't make
|
|
172
|
-
* assumptions abouth length after long length has been split)
|
|
173
|
-
* -- maybe better: sort by clock+length. Then split items from right to left. This way, items are always
|
|
174
|
-
* in the right order. But I also need to swap if left items is smaller after split
|
|
175
|
-
* --- thought: there is no way to go around swapping. Unless, for each item from left to
|
|
176
|
-
* right, when I have to split because one of the look-ahead items is overlapping, i split
|
|
177
|
-
* it and merge the attributes into the following ones (that I also need to split). Best is
|
|
178
|
-
* probably left to right with lookahead.
|
|
179
|
-
* - left to right, split overlapping items so that we can make the assumption that either an
|
|
180
|
-
* item is overlapping with the next 1-on-1 or it is not overlapping at all (when splitting,
|
|
181
|
-
* we can already incorporate the attributes)
|
|
182
|
-
* -- better: for each item, go left to right and add own attributes to overlapping items.
|
|
183
|
-
* Split them if necessary. After split, i must insert the retainer at a valid position.
|
|
184
|
-
* - merge items if neighbor has same attributes
|
|
185
|
-
*/
|
|
186
|
-
for (let i = 0; i < ids.length - 1;) {
|
|
187
|
-
const range = ids[i]
|
|
188
|
-
const nextRange = ids[i + 1]
|
|
189
|
-
// find out how to split range. it must match with next range.
|
|
190
|
-
// 1) we have space. Split if necessary.
|
|
191
|
-
// 2) concat attributes in range to the next range. Split range and splice the remainder at
|
|
192
|
-
// the correct position.
|
|
193
|
-
if (range.clock < nextRange.clock) { // might need to split range
|
|
194
|
-
if (range.clock + range.len > nextRange.clock) {
|
|
195
|
-
// is overlapping
|
|
196
|
-
const diff = nextRange.clock - range.clock
|
|
197
|
-
ids[i] = new AttrRange(range.clock, diff, range.attrs)
|
|
198
|
-
ids.splice(i + 1, 0, new AttrRange(nextRange.clock, range.len - diff, range.attrs))
|
|
199
|
-
}
|
|
200
|
-
i++
|
|
201
|
-
continue
|
|
202
|
-
}
|
|
203
|
-
// now we know that range.clock === nextRange.clock
|
|
204
|
-
// merge range with nextRange
|
|
205
|
-
const largerRange = range.len > nextRange.len ? range : nextRange
|
|
206
|
-
const smallerLen = range.len < nextRange.len ? range.len : nextRange.len
|
|
207
|
-
ids[i] = new AttrRange(range.clock, smallerLen, idmapAttrRangeJoin(range.attrs, nextRange.attrs))
|
|
208
|
-
if (range.len === nextRange.len) {
|
|
209
|
-
ids.splice(i + 1, 1)
|
|
210
|
-
} else {
|
|
211
|
-
ids[i + 1] = new AttrRange(range.clock + smallerLen, largerRange.len - smallerLen, largerRange.attrs)
|
|
212
|
-
array.bubblesortItem(ids, i + 1, (a, b) => a.clock - b.clock)
|
|
213
|
-
}
|
|
214
|
-
if (smallerLen === 0) i++
|
|
215
|
-
}
|
|
216
|
-
while (ids.length > 0 && ids[0].len === 0) {
|
|
217
|
-
ids.splice(0, 1)
|
|
218
|
-
}
|
|
219
|
-
// merge items without filtering or splicing the array.
|
|
220
|
-
// i is the current pointer
|
|
221
|
-
// j refers to the current insert position for the pointed item
|
|
222
|
-
// try to merge dels[i] into dels[j-1] or set dels[j]=dels[i]
|
|
223
|
-
let i, j
|
|
224
|
-
for (i = 1, j = 1; i < ids.length; i++) {
|
|
225
|
-
const left = ids[j - 1]
|
|
226
|
-
const right = ids[i]
|
|
227
|
-
if (left.clock + left.len === right.clock && idmapAttrsEqual(left.attrs, right.attrs)) {
|
|
228
|
-
ids[j - 1] = new AttrRange(left.clock, left.len + right.len, left.attrs)
|
|
229
|
-
} else if (right.len !== 0) {
|
|
230
|
-
if (j < i) {
|
|
231
|
-
ids[j] = right
|
|
232
|
-
}
|
|
233
|
-
j++
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
ids.length = ids.length === 0 ? 0 : (ids[j - 1].len === 0 ? j - 1 : j)
|
|
237
|
-
}
|
|
238
|
-
return ids
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Merge multiple idmaps. Ensures that there are no redundant attribution definitions (two
|
|
244
|
-
* Attributions that describe the same thing).
|
|
245
|
-
*
|
|
246
|
-
* @template T
|
|
247
|
-
* @param {Array<IdMap<T>>} ams
|
|
248
|
-
* @return {IdMap<T>} A fresh IdSet
|
|
249
|
-
*/
|
|
250
|
-
export const mergeIdMaps = ams => {
|
|
251
|
-
/**
|
|
252
|
-
* Maps attribution to the attribution of the merged idmap.
|
|
253
|
-
*
|
|
254
|
-
* @type {Map<AttributionItem<any>,AttributionItem<any>>}
|
|
255
|
-
*/
|
|
256
|
-
const attrMapper = new Map()
|
|
257
|
-
const merged = createIdMap()
|
|
258
|
-
for (let amsI = 0; amsI < ams.length; amsI++) {
|
|
259
|
-
ams[amsI].clients.forEach((rangesLeft, client) => {
|
|
260
|
-
if (!merged.clients.has(client)) {
|
|
261
|
-
// Write all missing keys from current set and all following.
|
|
262
|
-
// If merged already contains `client` current ds has already been added.
|
|
263
|
-
let ids = rangesLeft.getIds().slice()
|
|
264
|
-
for (let i = amsI + 1; i < ams.length; i++) {
|
|
265
|
-
const nextIds = ams[i].clients.get(client)
|
|
266
|
-
if (nextIds) {
|
|
267
|
-
array.appendTo(ids, nextIds.getIds())
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
ids = ids.map(id => new AttrRange(id.clock, id.len, id.attrs.map(attr =>
|
|
271
|
-
map.setIfUndefined(attrMapper, attr, () =>
|
|
272
|
-
_ensureAttrs(merged, [attr])[0]
|
|
273
|
-
)
|
|
274
|
-
)))
|
|
275
|
-
merged.clients.set(client, new AttrRanges(ids))
|
|
276
|
-
}
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
return merged
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* @param {IdSet} idset
|
|
284
|
-
* @param {Array<AttributionItem<any>>} attrs
|
|
285
|
-
*/
|
|
286
|
-
export const createIdMapFromIdSet = (idset, attrs) => {
|
|
287
|
-
const idmap = createIdMap()
|
|
288
|
-
// map attrs to idmap
|
|
289
|
-
attrs = _ensureAttrs(idmap, attrs)
|
|
290
|
-
// filter out duplicates
|
|
291
|
-
/**
|
|
292
|
-
* @type {Array<AttributionItem<any>>}
|
|
293
|
-
*/
|
|
294
|
-
const checkedAttrs = []
|
|
295
|
-
attrs.forEach(attr => {
|
|
296
|
-
if (!idmapAttrsHas(checkedAttrs, attr)) {
|
|
297
|
-
checkedAttrs.push(attr)
|
|
298
|
-
}
|
|
299
|
-
})
|
|
300
|
-
idset.clients.forEach((ranges, client) => {
|
|
301
|
-
const attrRanges = new AttrRanges(ranges.getIds().map(range => new AttrRange(range.clock, range.len, checkedAttrs)))
|
|
302
|
-
attrRanges.sorted = true // is sorted because idset is sorted
|
|
303
|
-
idmap.clients.set(client, attrRanges)
|
|
304
|
-
})
|
|
305
|
-
return idmap
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* @template Attrs
|
|
310
|
-
*/
|
|
311
|
-
export class IdMap {
|
|
312
|
-
constructor () {
|
|
313
|
-
/**
|
|
314
|
-
* @type {Map<number,AttrRanges<Attrs>>}
|
|
315
|
-
*/
|
|
316
|
-
this.clients = new Map()
|
|
317
|
-
/**
|
|
318
|
-
* @type {Map<string, AttributionItem<Attrs>>}
|
|
319
|
-
*/
|
|
320
|
-
this.attrsH = new Map()
|
|
321
|
-
/**
|
|
322
|
-
* @type {Set<AttributionItem<Attrs>>}
|
|
323
|
-
*/
|
|
324
|
-
this.attrs = new Set()
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @param {(attrRange:AttrRange<Attrs>, client:number) => void} f
|
|
329
|
-
*/
|
|
330
|
-
forEach (f) {
|
|
331
|
-
this.clients.forEach((ranges, client) => {
|
|
332
|
-
ranges.getIds().forEach((range) => {
|
|
333
|
-
f(range, client)
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* @param {ID} id
|
|
340
|
-
* @return {boolean}
|
|
341
|
-
*/
|
|
342
|
-
hasId (id) {
|
|
343
|
-
return this.has(id.client, id.clock)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* @param {number} client
|
|
348
|
-
* @param {number} clock
|
|
349
|
-
* @return {boolean}
|
|
350
|
-
*/
|
|
351
|
-
has (client, clock) {
|
|
352
|
-
const dr = this.clients.get(client)
|
|
353
|
-
if (dr) {
|
|
354
|
-
return findIndexInIdRanges(dr.getIds(), clock) !== null
|
|
355
|
-
}
|
|
356
|
-
return false
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Return attributions for a slice of ids.
|
|
361
|
-
*
|
|
362
|
-
* @param {ID} id
|
|
363
|
-
* @param {number} len
|
|
364
|
-
* @return {Array<MaybeAttrRange<Attrs>>}
|
|
365
|
-
*/
|
|
366
|
-
sliceId (id, len) {
|
|
367
|
-
return this.slice(id.client, id.clock, len)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Return attributions for a slice of ids.
|
|
372
|
-
*
|
|
373
|
-
* @param {number} client
|
|
374
|
-
* @param {number} clock
|
|
375
|
-
* @param {number} len
|
|
376
|
-
* @return {Array<MaybeAttrRange<Attrs>>}
|
|
377
|
-
*/
|
|
378
|
-
slice (client, clock, len) {
|
|
379
|
-
const dr = this.clients.get(client)
|
|
380
|
-
/**
|
|
381
|
-
* @type {Array<MaybeAttrRange<Attrs>>}
|
|
382
|
-
*/
|
|
383
|
-
const res = []
|
|
384
|
-
if (dr) {
|
|
385
|
-
/**
|
|
386
|
-
* @type {Array<AttrRange<Attrs>>}
|
|
387
|
-
*/
|
|
388
|
-
const ranges = dr.getIds()
|
|
389
|
-
let index = findRangeStartInIdRanges(ranges, clock)
|
|
390
|
-
if (index !== null) {
|
|
391
|
-
let prev = null
|
|
392
|
-
while (index < ranges.length) {
|
|
393
|
-
let r = ranges[index]
|
|
394
|
-
if (r.clock < clock) {
|
|
395
|
-
r = new AttrRange(clock, r.len - (clock - r.clock), r.attrs)
|
|
396
|
-
}
|
|
397
|
-
if (r.clock + r.len > clock + len) {
|
|
398
|
-
r = new AttrRange(r.clock, clock + len - r.clock, r.attrs)
|
|
399
|
-
}
|
|
400
|
-
if (r.len <= 0) break
|
|
401
|
-
const prevEnd = prev != null ? prev.clock + prev.len : clock
|
|
402
|
-
if (prevEnd < r.clock) {
|
|
403
|
-
res.push(createMaybeAttrRange(prevEnd, r.clock - prevEnd, null))
|
|
404
|
-
}
|
|
405
|
-
prev = r
|
|
406
|
-
res.push(r)
|
|
407
|
-
index++
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (res.length > 0) {
|
|
412
|
-
const last = res[res.length - 1]
|
|
413
|
-
const end = last.clock + last.len
|
|
414
|
-
if (end < clock + len) {
|
|
415
|
-
res.push(createMaybeAttrRange(end, clock + len - end, null))
|
|
416
|
-
}
|
|
417
|
-
} else {
|
|
418
|
-
res.push(createMaybeAttrRange(clock, len, null))
|
|
419
|
-
}
|
|
420
|
-
return res
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* @param {number} client
|
|
425
|
-
* @param {number} clock
|
|
426
|
-
* @param {number} len
|
|
427
|
-
* @param {Array<AttributionItem<Attrs>>} attrs
|
|
428
|
-
*/
|
|
429
|
-
add (client, clock, len, attrs) {
|
|
430
|
-
if (len === 0) return
|
|
431
|
-
attrs = _ensureAttrs(this, attrs)
|
|
432
|
-
const ranges = this.clients.get(client)
|
|
433
|
-
if (ranges == null) {
|
|
434
|
-
this.clients.set(client, new AttrRanges([new AttrRange(clock, len, attrs)]))
|
|
435
|
-
} else {
|
|
436
|
-
ranges.add(clock, len, attrs)
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* @param {number} client
|
|
442
|
-
* @param {number} clock
|
|
443
|
-
* @param {number} len
|
|
444
|
-
*/
|
|
445
|
-
delete (client, clock, len) {
|
|
446
|
-
_deleteRangeFromIdSet(this, client, clock, len)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Efficiently encodes IdMap to a binary form. Ensures that information is de-duplicated when
|
|
452
|
-
* written. Attribute.names are referenced by id. Attributes themselfs are also referenced by id.
|
|
453
|
-
*
|
|
454
|
-
* @template Attr
|
|
455
|
-
* @param {IdSetEncoderV1 | IdSetEncoderV2} encoder
|
|
456
|
-
* @param {IdMap<Attr>} idmap
|
|
457
|
-
*
|
|
458
|
-
* @private
|
|
459
|
-
* @function
|
|
460
|
-
*/
|
|
461
|
-
export const writeIdMap = (encoder, idmap) => {
|
|
462
|
-
encoding.writeVarUint(encoder.restEncoder, idmap.clients.size)
|
|
463
|
-
let lastWrittenClientId = 0
|
|
464
|
-
/**
|
|
465
|
-
* @type {Map<AttributionItem<Attr>, number>}
|
|
466
|
-
*/
|
|
467
|
-
const visitedAttributions = map.create()
|
|
468
|
-
/**
|
|
469
|
-
* @type {Map<string, number>}
|
|
470
|
-
*/
|
|
471
|
-
const visitedAttrNames = map.create()
|
|
472
|
-
// Ensure that the ids are written in a deterministic order (smaller clientids first)
|
|
473
|
-
array.from(idmap.clients.entries())
|
|
474
|
-
.sort((a, b) => a[0] - b[0])
|
|
475
|
-
.forEach(([client, _idRanges]) => {
|
|
476
|
-
const attrRanges = _idRanges.getIds()
|
|
477
|
-
encoder.resetIdSetCurVal()
|
|
478
|
-
const diff = client - lastWrittenClientId
|
|
479
|
-
encoding.writeVarUint(encoder.restEncoder, diff)
|
|
480
|
-
lastWrittenClientId = client
|
|
481
|
-
const len = attrRanges.length
|
|
482
|
-
encoding.writeVarUint(encoder.restEncoder, len)
|
|
483
|
-
for (let i = 0; i < len; i++) {
|
|
484
|
-
const item = attrRanges[i]
|
|
485
|
-
const attrs = item.attrs
|
|
486
|
-
const attrLen = attrs.length
|
|
487
|
-
encoder.writeIdSetClock(item.clock)
|
|
488
|
-
encoder.writeIdSetLen(item.len)
|
|
489
|
-
encoding.writeVarUint(encoder.restEncoder, attrLen)
|
|
490
|
-
for (let j = 0; j < attrLen; j++) {
|
|
491
|
-
const attr = attrs[j]
|
|
492
|
-
const attrId = visitedAttributions.get(attr)
|
|
493
|
-
if (attrId != null) {
|
|
494
|
-
encoding.writeVarUint(encoder.restEncoder, attrId)
|
|
495
|
-
} else {
|
|
496
|
-
const newAttrId = visitedAttributions.size
|
|
497
|
-
visitedAttributions.set(attr, newAttrId)
|
|
498
|
-
encoding.writeVarUint(encoder.restEncoder, newAttrId)
|
|
499
|
-
const attrNameId = visitedAttrNames.get(attr.name)
|
|
500
|
-
// write attr.name
|
|
501
|
-
if (attrNameId != null) {
|
|
502
|
-
encoding.writeVarUint(encoder.restEncoder, attrNameId)
|
|
503
|
-
} else {
|
|
504
|
-
const newAttrNameId = visitedAttrNames.size
|
|
505
|
-
encoding.writeVarUint(encoder.restEncoder, newAttrNameId)
|
|
506
|
-
encoding.writeVarString(encoder.restEncoder, attr.name)
|
|
507
|
-
visitedAttrNames.set(attr.name, newAttrNameId)
|
|
508
|
-
}
|
|
509
|
-
encoding.writeAny(encoder.restEncoder, /** @type {any} */ (attr.val))
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
})
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* @param {IdMap<any>} idmap
|
|
518
|
-
*/
|
|
519
|
-
export const encodeIdMap = idmap => {
|
|
520
|
-
const encoder = new IdSetEncoderV2()
|
|
521
|
-
writeIdMap(encoder, idmap)
|
|
522
|
-
return encoder.toUint8Array()
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* @param {DSDecoderV1 | DSDecoderV2} decoder
|
|
527
|
-
* @return {IdMap<any>}
|
|
528
|
-
*
|
|
529
|
-
* @private
|
|
530
|
-
* @function
|
|
531
|
-
*/
|
|
532
|
-
export const readIdMap = decoder => {
|
|
533
|
-
const idmap = new IdMap()
|
|
534
|
-
const numClients = decoding.readVarUint(decoder.restDecoder)
|
|
535
|
-
/**
|
|
536
|
-
* @type {Array<AttributionItem<any>>}
|
|
537
|
-
*/
|
|
538
|
-
const visitedAttributions = []
|
|
539
|
-
/**
|
|
540
|
-
* @type {Array<string>}
|
|
541
|
-
*/
|
|
542
|
-
const visitedAttrNames = []
|
|
543
|
-
let lastClientId = 0
|
|
544
|
-
for (let i = 0; i < numClients; i++) {
|
|
545
|
-
decoder.resetDsCurVal()
|
|
546
|
-
const client = lastClientId + decoding.readVarUint(decoder.restDecoder)
|
|
547
|
-
lastClientId = client
|
|
548
|
-
const numberOfDeletes = decoding.readVarUint(decoder.restDecoder)
|
|
549
|
-
/**
|
|
550
|
-
* @type {Array<AttrRange<any>>}
|
|
551
|
-
*/
|
|
552
|
-
const attrRanges = []
|
|
553
|
-
for (let i = 0; i < numberOfDeletes; i++) {
|
|
554
|
-
const rangeClock = decoder.readDsClock()
|
|
555
|
-
const rangeLen = decoder.readDsLen()
|
|
556
|
-
/**
|
|
557
|
-
* @type {Array<AttributionItem<any>>}
|
|
558
|
-
*/
|
|
559
|
-
const attrs = []
|
|
560
|
-
const attrsLen = decoding.readVarUint(decoder.restDecoder)
|
|
561
|
-
for (let j = 0; j < attrsLen; j++) {
|
|
562
|
-
const attrId = decoding.readVarUint(decoder.restDecoder)
|
|
563
|
-
if (attrId >= visitedAttributions.length) {
|
|
564
|
-
// attrId not known yet
|
|
565
|
-
const attrNameId = decoding.readVarUint(decoder.restDecoder)
|
|
566
|
-
if (attrNameId >= visitedAttrNames.length) {
|
|
567
|
-
visitedAttrNames.push(decoding.readVarString(decoder.restDecoder))
|
|
568
|
-
}
|
|
569
|
-
visitedAttributions.push(new AttributionItem(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder)))
|
|
570
|
-
}
|
|
571
|
-
attrs.push(visitedAttributions[attrId])
|
|
572
|
-
}
|
|
573
|
-
attrRanges.push(new AttrRange(rangeClock, rangeLen, attrs))
|
|
574
|
-
}
|
|
575
|
-
idmap.clients.set(client, new AttrRanges(attrRanges))
|
|
576
|
-
}
|
|
577
|
-
visitedAttributions.forEach(attr => {
|
|
578
|
-
idmap.attrs.add(attr)
|
|
579
|
-
idmap.attrsH.set(attr.hash(), attr)
|
|
580
|
-
})
|
|
581
|
-
return idmap
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* @param {Uint8Array} data
|
|
586
|
-
* @return {IdMap<any>}
|
|
587
|
-
*/
|
|
588
|
-
export const decodeIdMap = data => readIdMap(new DSDecoderV2(decoding.createDecoder(data)))
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* @template Attrs
|
|
592
|
-
* @param {IdMap<Attrs>} idmap
|
|
593
|
-
* @param {Array<AttributionItem<Attrs>>} attrs
|
|
594
|
-
* @return {Array<AttributionItem<Attrs>>}
|
|
595
|
-
*/
|
|
596
|
-
const _ensureAttrs = (idmap, attrs) => attrs.map(attr =>
|
|
597
|
-
idmap.attrs.has(attr)
|
|
598
|
-
? attr
|
|
599
|
-
: map.setIfUndefined(idmap.attrsH, _hashAttribution(attr), () => {
|
|
600
|
-
idmap.attrs.add(attr)
|
|
601
|
-
return attr
|
|
602
|
-
}))
|
|
603
|
-
|
|
604
|
-
export const createIdMap = () => new IdMap()
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* @template T
|
|
608
|
-
* @param {IdMap<T>} dest
|
|
609
|
-
* @param {IdMap<T>} src
|
|
610
|
-
*/
|
|
611
|
-
export const insertIntoIdMap = _insertIntoIdSet
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Remove all ranges from `exclude` from `ds`. The result is a fresh IdMap containing all ranges from `idSet` that are not
|
|
615
|
-
* in `exclude`.
|
|
616
|
-
*
|
|
617
|
-
* @template {IdMap<any>} ISet
|
|
618
|
-
* @param {ISet} set
|
|
619
|
-
* @param {IdSet | IdMap<any>} exclude
|
|
620
|
-
* @return {ISet}
|
|
621
|
-
*/
|
|
622
|
-
export const diffIdMap = (set, exclude) => {
|
|
623
|
-
const diffed = _diffSet(set, exclude)
|
|
624
|
-
diffed.attrs = set.attrs
|
|
625
|
-
diffed.attrsH = set.attrsH
|
|
626
|
-
return diffed
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
export const intersectMaps = _intersectSets
|