@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.
Files changed (110) hide show
  1. package/.eslintrc.js +20 -21
  2. package/.mocharc.js +2 -2
  3. package/README.md +21 -21
  4. package/api-extractor.json +2 -2
  5. package/bench/bsp-set-optimizations.md +5 -5
  6. package/bench/src/index.ts +38 -20
  7. package/bench/src/read/map.ts +16 -10
  8. package/bench/src/read/nativearray.ts +16 -10
  9. package/bench/src/read/test.ts +17 -19
  10. package/bench/src/read/tiled.ts +240 -181
  11. package/bench/src/util.ts +19 -18
  12. package/bench/tsconfig.json +8 -13
  13. package/dist/bspSet.d.ts.map +1 -1
  14. package/dist/bspSet.js.map +1 -1
  15. package/dist/handlecache.d.ts.map +1 -1
  16. package/dist/handlecache.js +1 -3
  17. package/dist/handlecache.js.map +1 -1
  18. package/dist/handletable.d.ts.map +1 -1
  19. package/dist/handletable.js +7 -3
  20. package/dist/handletable.js.map +1 -1
  21. package/dist/matrix.d.ts +1 -1
  22. package/dist/matrix.d.ts.map +1 -1
  23. package/dist/matrix.js +28 -19
  24. package/dist/matrix.js.map +1 -1
  25. package/dist/ops.d.ts.map +1 -1
  26. package/dist/ops.js.map +1 -1
  27. package/dist/packageVersion.d.ts +1 -1
  28. package/dist/packageVersion.js +1 -1
  29. package/dist/packageVersion.js.map +1 -1
  30. package/dist/permutationvector.d.ts.map +1 -1
  31. package/dist/permutationvector.js +15 -8
  32. package/dist/permutationvector.js.map +1 -1
  33. package/dist/productSet.d.ts.map +1 -1
  34. package/dist/productSet.js +6 -3
  35. package/dist/productSet.js.map +1 -1
  36. package/dist/range.d.ts.map +1 -1
  37. package/dist/range.js.map +1 -1
  38. package/dist/runtime.d.ts.map +1 -1
  39. package/dist/runtime.js.map +1 -1
  40. package/dist/serialization.d.ts.map +1 -1
  41. package/dist/serialization.js.map +1 -1
  42. package/dist/sparsearray2d.d.ts.map +1 -1
  43. package/dist/sparsearray2d.js +12 -12
  44. package/dist/sparsearray2d.js.map +1 -1
  45. package/dist/split.d.ts.map +1 -1
  46. package/dist/split.js +5 -3
  47. package/dist/split.js.map +1 -1
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js.map +1 -1
  50. package/dist/undoprovider.d.ts.map +1 -1
  51. package/dist/undoprovider.js.map +1 -1
  52. package/lib/bspSet.d.ts.map +1 -1
  53. package/lib/bspSet.js.map +1 -1
  54. package/lib/handlecache.d.ts.map +1 -1
  55. package/lib/handlecache.js +1 -3
  56. package/lib/handlecache.js.map +1 -1
  57. package/lib/handletable.d.ts.map +1 -1
  58. package/lib/handletable.js +7 -3
  59. package/lib/handletable.js.map +1 -1
  60. package/lib/matrix.d.ts +1 -1
  61. package/lib/matrix.d.ts.map +1 -1
  62. package/lib/matrix.js +29 -20
  63. package/lib/matrix.js.map +1 -1
  64. package/lib/ops.d.ts.map +1 -1
  65. package/lib/ops.js.map +1 -1
  66. package/lib/packageVersion.d.ts +1 -1
  67. package/lib/packageVersion.js +1 -1
  68. package/lib/packageVersion.js.map +1 -1
  69. package/lib/permutationvector.d.ts.map +1 -1
  70. package/lib/permutationvector.js +15 -8
  71. package/lib/permutationvector.js.map +1 -1
  72. package/lib/productSet.d.ts.map +1 -1
  73. package/lib/productSet.js +6 -3
  74. package/lib/productSet.js.map +1 -1
  75. package/lib/range.d.ts.map +1 -1
  76. package/lib/range.js.map +1 -1
  77. package/lib/runtime.d.ts.map +1 -1
  78. package/lib/runtime.js.map +1 -1
  79. package/lib/serialization.d.ts.map +1 -1
  80. package/lib/serialization.js.map +1 -1
  81. package/lib/sparsearray2d.d.ts.map +1 -1
  82. package/lib/sparsearray2d.js +12 -12
  83. package/lib/sparsearray2d.js.map +1 -1
  84. package/lib/split.d.ts.map +1 -1
  85. package/lib/split.js +5 -3
  86. package/lib/split.js.map +1 -1
  87. package/lib/types.d.ts.map +1 -1
  88. package/lib/types.js.map +1 -1
  89. package/lib/undoprovider.d.ts.map +1 -1
  90. package/lib/undoprovider.js +1 -1
  91. package/lib/undoprovider.js.map +1 -1
  92. package/package.json +52 -52
  93. package/prettier.config.cjs +1 -1
  94. package/src/bspSet.ts +507 -434
  95. package/src/handlecache.ts +114 -112
  96. package/src/handletable.ts +66 -62
  97. package/src/matrix.ts +781 -710
  98. package/src/ops.ts +11 -11
  99. package/src/packageVersion.ts +1 -1
  100. package/src/permutationvector.ts +425 -368
  101. package/src/productSet.ts +852 -788
  102. package/src/range.ts +8 -8
  103. package/src/runtime.ts +35 -35
  104. package/src/serialization.ts +13 -9
  105. package/src/sparsearray2d.ts +196 -192
  106. package/src/split.ts +111 -90
  107. package/src/types.ts +3 -3
  108. package/src/undoprovider.ts +161 -144
  109. package/tsconfig.esnext.json +6 -6
  110. package/tsconfig.json +8 -12
@@ -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
- 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
53
- ? this.handles[index]
54
- : this.cacheMiss(position);
55
- }
56
-
57
- /** Update the cache when a handle has been allocated for a given position. */
58
- public addHandle(position: number, handle: Handle) {
59
- assert(isHandleValid(handle), 0x017 /* "Trying to add invalid handle!" */);
60
-
61
- const index = this.getIndex(position);
62
- if (index < this.handles.length) {
63
- assert(!isHandleValid(this.handles[index]),
64
- 0x018 /* "Trying to insert handle into position with already valid handle!" */);
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(this.getHandles(this.start + this.handles.length, _position + 1));
107
- return this.handles[this.handles.length - 1];
108
- }
109
- }
110
-
111
- // #region IVectorConsumer
112
-
113
- itemsChanged(start: number, removedCount: number, insertedCount: number): void {
114
- // If positions were inserted/removed, our current policy is to trim the array
115
- // at the beginning of the invalidate range and lazily repopulate the handles
116
- // on demand.
117
- //
118
- // Some alternatives to consider that preserve the previously cached handles
119
- // that are still valid:
120
- //
121
- // * Eagerly populate the 'handles[]' with the newly insert values (currently guaranteed
122
- // to be Handle.unallocated, so we don't even need to look them up.)
123
- //
124
- // * Use a sentinel value or other mechanism to allow "holes" in the cache.
125
-
126
- const index = this.getIndex(start);
127
- if (index < this.handles.length) {
128
- this.handles.length = index;
129
- }
130
- }
131
-
132
- // #endregion IVectorConsumer
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
  }
@@ -4,11 +4,11 @@
4
4
  */
5
5
 
6
6
  export const enum Handle {
7
- /** Minimum valid handle. */
8
- valid = 1,
7
+ /** Minimum valid handle. */
8
+ valid = 1,
9
9
 
10
- /** Sentinel representing an unallocated Handle. */
11
- unallocated = -0x80000000,
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
- // 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]) { }
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
- 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
- }
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
- * 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
- }
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
- * 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
- }
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
- * 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
- }
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
- * 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
- }
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
- * Set the value `T` associated with the given handle.
70
- */
71
- public set(handle: Handle, value: T) {
72
- this.handles[handle] = value;
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
- // 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() { return this.handles[0] as Handle; }
78
- private set next(handle: Handle) { this.handles[0] = handle; }
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
- public getSummaryContent() {
81
- return this.handles;
82
- }
84
+ public getSummaryContent() {
85
+ return this.handles;
86
+ }
83
87
 
84
- public static load<T>(data: (Handle | T)[]) {
85
- return new HandleTable<T>(data);
86
- }
88
+ public static load<T>(data: (Handle | T)[]) {
89
+ return new HandleTable<T>(data);
90
+ }
87
91
  }