@fluidframework/matrix 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.0
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/.eslintrc.js +20 -21
- package/.mocharc.js +2 -2
- package/README.md +21 -21
- package/api-extractor.json +2 -2
- package/bench/bsp-set-optimizations.md +5 -5
- package/bench/src/index.ts +38 -20
- package/bench/src/read/map.ts +16 -10
- package/bench/src/read/nativearray.ts +16 -10
- package/bench/src/read/test.ts +17 -19
- package/bench/src/read/tiled.ts +240 -181
- package/bench/src/util.ts +19 -18
- package/bench/tsconfig.json +8 -13
- package/dist/bspSet.d.ts.map +1 -1
- package/dist/bspSet.js.map +1 -1
- package/dist/handlecache.d.ts.map +1 -1
- package/dist/handlecache.js +1 -3
- package/dist/handlecache.js.map +1 -1
- package/dist/handletable.d.ts.map +1 -1
- package/dist/handletable.js +7 -3
- package/dist/handletable.js.map +1 -1
- package/dist/matrix.d.ts +1 -1
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +28 -19
- package/dist/matrix.js.map +1 -1
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/permutationvector.d.ts.map +1 -1
- package/dist/permutationvector.js +15 -8
- package/dist/permutationvector.js.map +1 -1
- package/dist/productSet.d.ts.map +1 -1
- package/dist/productSet.js +6 -3
- package/dist/productSet.js.map +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js.map +1 -1
- package/dist/sparsearray2d.d.ts.map +1 -1
- package/dist/sparsearray2d.js +12 -12
- package/dist/sparsearray2d.js.map +1 -1
- package/dist/split.d.ts.map +1 -1
- package/dist/split.js +5 -3
- package/dist/split.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/undoprovider.d.ts.map +1 -1
- package/dist/undoprovider.js.map +1 -1
- package/lib/bspSet.d.ts.map +1 -1
- package/lib/bspSet.js.map +1 -1
- package/lib/handlecache.d.ts.map +1 -1
- package/lib/handlecache.js +1 -3
- package/lib/handlecache.js.map +1 -1
- package/lib/handletable.d.ts.map +1 -1
- package/lib/handletable.js +7 -3
- package/lib/handletable.js.map +1 -1
- package/lib/matrix.d.ts +1 -1
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.js +29 -20
- package/lib/matrix.js.map +1 -1
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/permutationvector.d.ts.map +1 -1
- package/lib/permutationvector.js +15 -8
- package/lib/permutationvector.js.map +1 -1
- package/lib/productSet.d.ts.map +1 -1
- package/lib/productSet.js +6 -3
- package/lib/productSet.js.map +1 -1
- package/lib/range.d.ts.map +1 -1
- package/lib/range.js.map +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/serialization.d.ts.map +1 -1
- package/lib/serialization.js.map +1 -1
- package/lib/sparsearray2d.d.ts.map +1 -1
- package/lib/sparsearray2d.js +12 -12
- package/lib/sparsearray2d.js.map +1 -1
- package/lib/split.d.ts.map +1 -1
- package/lib/split.js +5 -3
- package/lib/split.js.map +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/undoprovider.d.ts.map +1 -1
- package/lib/undoprovider.js +1 -1
- package/lib/undoprovider.js.map +1 -1
- package/package.json +52 -52
- package/prettier.config.cjs +1 -1
- package/src/bspSet.ts +507 -434
- package/src/handlecache.ts +114 -112
- package/src/handletable.ts +66 -62
- package/src/matrix.ts +781 -710
- package/src/ops.ts +11 -11
- package/src/packageVersion.ts +1 -1
- package/src/permutationvector.ts +425 -368
- package/src/productSet.ts +852 -788
- package/src/range.ts +8 -8
- package/src/runtime.ts +35 -35
- package/src/serialization.ts +13 -9
- package/src/sparsearray2d.ts +196 -192
- package/src/split.ts +111 -90
- package/src/types.ts +3 -3
- package/src/undoprovider.ts +161 -144
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
package/src/handlecache.ts
CHANGED
|
@@ -18,116 +18,118 @@ import { ensureRange } from "./range";
|
|
|
18
18
|
* so far there's no measurable perf penalty for being a separate object (node 12 x64)
|
|
19
19
|
*/
|
|
20
20
|
export class HandleCache implements IVectorConsumer<Handle> {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
21
|
+
private handles: Handle[] = [];
|
|
22
|
+
private start = 0;
|
|
23
|
+
|
|
24
|
+
constructor(public readonly vector: PermutationVector) {}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the index of the given position in the 'handles' array as a Uint32.
|
|
28
|
+
* (If the position is not in the array, returns an integer greater than 'handles.length').
|
|
29
|
+
*/
|
|
30
|
+
private getIndex(position: number) {
|
|
31
|
+
return (position - this.start) >>> 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns the handle currently assigned to the given 'position' (if any). Check
|
|
36
|
+
* the result with 'isValidHandle(..)' to see if a handle has been allocated for
|
|
37
|
+
* the given position.
|
|
38
|
+
*
|
|
39
|
+
* Throws a 'RangeError' if the provided 'position' is out-of-bounds wrt. the
|
|
40
|
+
* PermutationVector's length.
|
|
41
|
+
*/
|
|
42
|
+
public getHandle(position: number) {
|
|
43
|
+
const index = this.getIndex(position);
|
|
44
|
+
|
|
45
|
+
// Perf: To encourage inlining, handling of the 'cacheMiss(..)' case has been extracted
|
|
46
|
+
// to a separate method.
|
|
47
|
+
|
|
48
|
+
// Perf: A cache hit implies that 'position' was in bounds. Therefore, we can defer
|
|
49
|
+
// checking that 'position' is in bounds until 'cacheMiss(..)'. This yields an
|
|
50
|
+
// ~40% speedup when the position is in the cache (node v12 x64).
|
|
51
|
+
|
|
52
|
+
return index < this.handles.length ? this.handles[index] : this.cacheMiss(position);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Update the cache when a handle has been allocated for a given position. */
|
|
56
|
+
public addHandle(position: number, handle: Handle) {
|
|
57
|
+
assert(isHandleValid(handle), 0x017 /* "Trying to add invalid handle!" */);
|
|
58
|
+
|
|
59
|
+
const index = this.getIndex(position);
|
|
60
|
+
if (index < this.handles.length) {
|
|
61
|
+
assert(
|
|
62
|
+
!isHandleValid(this.handles[index]),
|
|
63
|
+
0x018 /* "Trying to insert handle into position with already valid handle!" */,
|
|
64
|
+
);
|
|
65
|
+
this.handles[index] = handle;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Used by 'CacheMiss()' to retrieve handles for a range of positions. */
|
|
70
|
+
private getHandles(start: number, end: number) {
|
|
71
|
+
// TODO: This can be accelerated substantially using 'walkSegments()'. The only catch
|
|
72
|
+
// is that
|
|
73
|
+
|
|
74
|
+
const handles: Handle[] = [];
|
|
75
|
+
const { vector } = this;
|
|
76
|
+
|
|
77
|
+
for (let pos = start; pos < end; pos++) {
|
|
78
|
+
const { segment, offset } = vector.getContainingSegment(pos);
|
|
79
|
+
const asPerm = segment as PermutationSegment;
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
81
|
+
handles.push(asPerm.start + offset!);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return handles;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private cacheMiss(position: number) {
|
|
88
|
+
// Coercing 'position' to an Uint32 allows us to handle a negative 'position' value
|
|
89
|
+
// with the same logic that handles 'position' >= length.
|
|
90
|
+
const _position = position >>> 0;
|
|
91
|
+
|
|
92
|
+
// TODO: To bound memory usage, there should be a limit on the maximum size of
|
|
93
|
+
// handle[].
|
|
94
|
+
|
|
95
|
+
// TODO: To reduce MergeTree lookups, this code should opportunistically grow
|
|
96
|
+
// the cache to the next MergeTree segment boundary (within the limits of
|
|
97
|
+
// the handle cache).
|
|
98
|
+
|
|
99
|
+
if (_position < this.start) {
|
|
100
|
+
this.handles = this.getHandles(_position, this.start).concat(this.handles);
|
|
101
|
+
this.start = _position;
|
|
102
|
+
return this.handles[0];
|
|
103
|
+
} else {
|
|
104
|
+
ensureRange(_position, this.vector.getLength());
|
|
105
|
+
|
|
106
|
+
this.handles = this.handles.concat(
|
|
107
|
+
this.getHandles(this.start + this.handles.length, _position + 1),
|
|
108
|
+
);
|
|
109
|
+
return this.handles[this.handles.length - 1];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// #region IVectorConsumer
|
|
114
|
+
|
|
115
|
+
itemsChanged(start: number, removedCount: number, insertedCount: number): void {
|
|
116
|
+
// If positions were inserted/removed, our current policy is to trim the array
|
|
117
|
+
// at the beginning of the invalidate range and lazily repopulate the handles
|
|
118
|
+
// on demand.
|
|
119
|
+
//
|
|
120
|
+
// Some alternatives to consider that preserve the previously cached handles
|
|
121
|
+
// that are still valid:
|
|
122
|
+
//
|
|
123
|
+
// * Eagerly populate the 'handles[]' with the newly insert values (currently guaranteed
|
|
124
|
+
// to be Handle.unallocated, so we don't even need to look them up.)
|
|
125
|
+
//
|
|
126
|
+
// * Use a sentinel value or other mechanism to allow "holes" in the cache.
|
|
127
|
+
|
|
128
|
+
const index = this.getIndex(start);
|
|
129
|
+
if (index < this.handles.length) {
|
|
130
|
+
this.handles.length = index;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// #endregion IVectorConsumer
|
|
133
135
|
}
|
package/src/handletable.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export const enum Handle {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/** Minimum valid handle. */
|
|
8
|
+
valid = 1,
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/** Sentinel representing an unallocated Handle. */
|
|
11
|
+
unallocated = -0x80000000,
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const isHandleValid = (handle: Handle) => handle >= Handle.valid;
|
|
@@ -17,71 +17,75 @@ export const isHandleValid = (handle: Handle) => handle >= Handle.valid;
|
|
|
17
17
|
* A handle table provides a fast mapping from an integer `handle` to a value `T`.
|
|
18
18
|
*/
|
|
19
19
|
export class HandleTable<T> {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// Note: the first slot of the 'handles' array is reserved to store the pointer to the first
|
|
21
|
+
// free handle. We initialize this slot with a pointer to slot '1', which will cause
|
|
22
|
+
// us to delay allocate the following slot in the array on the first allocation.
|
|
23
|
+
public constructor(private readonly handles: (Handle | T)[] = [1]) {}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
public clear() {
|
|
26
|
+
// Restore the HandleTable's initial state by deleting all items in the handles array
|
|
27
|
+
// and then re-inserting the value '1' in the 0th slot. (See comment at `handles` decl
|
|
28
|
+
// for explanation.)
|
|
29
|
+
this.handles.splice(0, this.handles.length, 1);
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Allocates and returns the next available handle. Note that freed handles are recycled.
|
|
34
|
+
*/
|
|
35
|
+
public allocate(): Handle {
|
|
36
|
+
const free = this.next;
|
|
37
|
+
this.next = (this.handles[free] as Handle) ?? free + 1;
|
|
38
|
+
this.handles[free] = 0;
|
|
39
|
+
return free;
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Allocates and returns the next available `count` handles.
|
|
44
|
+
*/
|
|
45
|
+
public allocateMany(count: Handle) {
|
|
46
|
+
const handles = new Uint32Array(count);
|
|
47
|
+
for (let i = 0; i < count; i++) {
|
|
48
|
+
handles[i] = this.allocate();
|
|
49
|
+
}
|
|
50
|
+
return handles;
|
|
51
|
+
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Returns the given handle to the free list.
|
|
55
|
+
*/
|
|
56
|
+
public free(handle: Handle) {
|
|
57
|
+
this.handles[handle] = this.next;
|
|
58
|
+
this.next = handle;
|
|
59
|
+
}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Get the value `T` associated with the given handle, if any.
|
|
63
|
+
*/
|
|
64
|
+
public get(handle: Handle): T {
|
|
65
|
+
return this.handles[handle] as T;
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Set the value `T` associated with the given handle.
|
|
70
|
+
*/
|
|
71
|
+
public set(handle: Handle, value: T) {
|
|
72
|
+
this.handles[handle] = value;
|
|
73
|
+
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
// Private helpers to get/set the head of the free list, which is stored in the 0th slot
|
|
76
|
+
// of the handle array.
|
|
77
|
+
private get next() {
|
|
78
|
+
return this.handles[0] as Handle;
|
|
79
|
+
}
|
|
80
|
+
private set next(handle: Handle) {
|
|
81
|
+
this.handles[0] = handle;
|
|
82
|
+
}
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
public getSummaryContent() {
|
|
85
|
+
return this.handles;
|
|
86
|
+
}
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
public static load<T>(data: (Handle | T)[]) {
|
|
89
|
+
return new HandleTable<T>(data);
|
|
90
|
+
}
|
|
87
91
|
}
|