@fluidframework/matrix 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1

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 +117 -116
  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
package/src/range.ts CHANGED
@@ -3,14 +3,14 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- /**
7
- * Ensures that 0 \<= 'value' \< 'limit'. Throws a RangeError otherwise.
8
- */
6
+ /**
7
+ * Ensures that 0 \<= 'value' \< 'limit'. Throws a RangeError otherwise.
8
+ */
9
9
  export function ensureRange(value: number, limit: number) {
10
- // Coerce 'value' to Uint32 so that we can range check with a single branch.
11
- const _value = value >>> 0; // eslint-disable-line no-bitwise
10
+ // Coerce 'value' to Uint32 so that we can range check with a single branch.
11
+ const _value = value >>> 0; // eslint-disable-line no-bitwise
12
12
 
13
- if (_value >= limit) {
14
- throw new RangeError("Invalid (row, col) coordinate.");
15
- }
13
+ if (_value >= limit) {
14
+ throw new RangeError("Invalid (row, col) coordinate.");
15
+ }
16
16
  }
package/src/runtime.ts CHANGED
@@ -4,49 +4,49 @@
4
4
  */
5
5
 
6
6
  import {
7
- IChannelAttributes,
8
- IFluidDataStoreRuntime,
9
- IChannelServices,
10
- IChannel,
11
- IChannelFactory,
7
+ IChannelAttributes,
8
+ IFluidDataStoreRuntime,
9
+ IChannelServices,
10
+ IChannel,
11
+ IChannelFactory,
12
12
  } from "@fluidframework/datastore-definitions";
13
13
  import { pkgVersion } from "./packageVersion";
14
14
  import { SharedMatrix } from "./matrix";
15
15
 
16
16
  export class SharedMatrixFactory implements IChannelFactory {
17
- public static Type = "https://graph.microsoft.com/types/sharedmatrix";
17
+ public static Type = "https://graph.microsoft.com/types/sharedmatrix";
18
18
 
19
- public static readonly Attributes: IChannelAttributes = {
20
- type: SharedMatrixFactory.Type,
21
- snapshotFormatVersion: "0.1",
22
- packageVersion: pkgVersion,
23
- };
19
+ public static readonly Attributes: IChannelAttributes = {
20
+ type: SharedMatrixFactory.Type,
21
+ snapshotFormatVersion: "0.1",
22
+ packageVersion: pkgVersion,
23
+ };
24
24
 
25
- public get type() {
26
- return SharedMatrixFactory.Type;
27
- }
25
+ public get type() {
26
+ return SharedMatrixFactory.Type;
27
+ }
28
28
 
29
- public get attributes() {
30
- return SharedMatrixFactory.Attributes;
31
- }
29
+ public get attributes() {
30
+ return SharedMatrixFactory.Attributes;
31
+ }
32
32
 
33
- /**
34
- * {@inheritDoc @fluidframework/datastore-definitions#IChannelFactory.load}
35
- */
36
- public async load(
37
- runtime: IFluidDataStoreRuntime,
38
- id: string,
39
- services: IChannelServices,
40
- attributes: IChannelAttributes,
41
- ): Promise<IChannel> {
42
- const matrix = new SharedMatrix(runtime, id, attributes);
43
- await matrix.load(services);
44
- return matrix;
45
- }
33
+ /**
34
+ * {@inheritDoc @fluidframework/datastore-definitions#IChannelFactory.load}
35
+ */
36
+ public async load(
37
+ runtime: IFluidDataStoreRuntime,
38
+ id: string,
39
+ services: IChannelServices,
40
+ attributes: IChannelAttributes,
41
+ ): Promise<IChannel> {
42
+ const matrix = new SharedMatrix(runtime, id, attributes);
43
+ await matrix.load(services);
44
+ return matrix;
45
+ }
46
46
 
47
- public create(document: IFluidDataStoreRuntime, id: string): IChannel {
48
- const matrix = new SharedMatrix(document, id, this.attributes);
49
- matrix.initializeLocal();
50
- return matrix;
51
- }
47
+ public create(document: IFluidDataStoreRuntime, id: string): IChannel {
48
+ const matrix = new SharedMatrix(document, id, this.attributes);
49
+ matrix.initializeLocal();
50
+ return matrix;
51
+ }
52
52
  }
@@ -10,15 +10,19 @@ import { IFluidSerializer } from "@fluidframework/shared-object-base";
10
10
  import { bufferToString } from "@fluidframework/common-utils";
11
11
 
12
12
  export const serializeBlob = (
13
- handle: IFluidHandle,
14
- path: string,
15
- snapshot: Serializable,
16
- serializer: IFluidSerializer,
13
+ handle: IFluidHandle,
14
+ path: string,
15
+ snapshot: Serializable,
16
+ serializer: IFluidSerializer,
17
17
  ) => new BlobTreeEntry(path, serializer.stringify(snapshot, handle));
18
18
 
19
- export async function deserializeBlob(storage: IChannelStorageService, path: string, serializer: IFluidSerializer) {
20
- const blob = await storage.readBlob(path);
21
- const utf8 = bufferToString(blob, "utf8");
22
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
23
- return serializer.parse(utf8);
19
+ export async function deserializeBlob(
20
+ storage: IChannelStorageService,
21
+ path: string,
22
+ serializer: IFluidSerializer,
23
+ ) {
24
+ const blob = await storage.readBlob(path);
25
+ const utf8 = bufferToString(blob, "utf8");
26
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
27
+ return serializer.parse(utf8);
24
28
  }
@@ -12,14 +12,13 @@ import { IMatrixReader, IMatrixWriter } from "@tiny-calc/nano";
12
12
  //
13
13
  // (Lookup table ~17% faster than inlining the bit-twiddling on Node v12 x64)
14
14
  // (Array<T> ~2% faster than typed array on Node v12 x64)
15
- const x8ToInterlacedX16 =
16
- new Array(256).fill(0).map((value, i) => {
17
- let j = i;
18
- j = (j | (j << 4)) & 0x0f0f; // .... 7654 .... 3210
19
- j = (j | (j << 2)) & 0x3333; // ..76 ..54 ..32 ..10
20
- j = (j | (j << 1)) & 0x5555; // .7.6 .5.4 .3.2 .1.0
21
- return j;
22
- });
15
+ const x8ToInterlacedX16 = new Array(256).fill(0).map((value, i) => {
16
+ let j = i;
17
+ j = (j | (j << 4)) & 0x0f0f; // .... 7654 .... 3210
18
+ j = (j | (j << 2)) & 0x3333; // ..76 ..54 ..32 ..10
19
+ j = (j | (j << 1)) & 0x5555; // .7.6 .5.4 .3.2 .1.0
20
+ return j;
21
+ });
23
22
 
24
23
  // Selects individual bytes from a given 32b integer. The left shift are used to
25
24
  // clear upper bits (faster than using masks on Node 10 x64).
@@ -30,205 +29,210 @@ const byte3 = (x32: number) => (x32 << 24) >>> 24;
30
29
 
31
30
  // Given a uint16 returns the corresponding uint32 integer where 0s are
32
31
  // interleaved between the original bits. (e.g., 1111... -> 01010101...).
33
- const interlaceBitsX16 = (x16: number) => (x8ToInterlacedX16[byte2(x16)] << 16) | x8ToInterlacedX16[byte3(x16)];
32
+ const interlaceBitsX16 = (x16: number) =>
33
+ (x8ToInterlacedX16[byte2(x16)] << 16) | x8ToInterlacedX16[byte3(x16)];
34
34
 
35
35
  const r0ToMorton16 = (row: number) => (interlaceBitsX16(row) << 1) >>> 0;
36
36
  const c0ToMorton16 = (col: number) => interlaceBitsX16(col) >>> 0;
37
37
 
38
38
  // Given a 2D uint16 coordinate returns the corresponding unt32 Morton coded
39
39
  // coordinate. (See https://en.wikipedia.org/wiki/Z-order_curve)
40
- const r0c0ToMorton2x16 = (row: number, col: number) => (r0ToMorton16(row) | c0ToMorton16(col)) >>> 0;
40
+ const r0c0ToMorton2x16 = (row: number, col: number) =>
41
+ (r0ToMorton16(row) | c0ToMorton16(col)) >>> 0;
41
42
 
42
43
  type RecurArrayHelper<T> = RecurArray<T> | T;
43
44
  type RecurArray<T> = RecurArrayHelper<T>[];
44
45
 
45
46
  /** Undo JSON serialization's coercion of 'undefined' to null. */
46
- const nullToUndefined = <T>(array: RecurArray<T | null>): RecurArray<T | undefined> => array.map((value) => {
47
- return value === null
48
- ? undefined
49
- : Array.isArray(value)
50
- ? nullToUndefined(value)
51
- : value;
52
- });
47
+ const nullToUndefined = <T>(array: RecurArray<T | null>): RecurArray<T | undefined> =>
48
+ array.map((value) => {
49
+ return value === null ? undefined : Array.isArray(value) ? nullToUndefined(value) : value;
50
+ });
53
51
 
54
52
  type UA<T> = (T | undefined)[];
55
53
 
56
54
  /**
57
55
  * A sparse 4 billion x 4 billion array stored as 16x16 tiles.
58
56
  */
59
- export class SparseArray2D<T> implements IMatrixReader<T | undefined>, IMatrixWriter<T | undefined> {
60
- constructor(private readonly root: UA<UA<UA<UA<UA<T>>>>> = [undefined]) { }
61
-
62
- public get rowCount() { return 0xFFFFFFFF; }
63
- public get colCount() { return 0xFFFFFFFF; }
64
-
65
- public getCell(row: number, col: number): T | undefined {
66
- const keyHi = r0c0ToMorton2x16(row >>> 16, col >>> 16);
67
- const level0 = this.root[keyHi];
68
- if (level0 !== undefined) {
69
- const keyLo = r0c0ToMorton2x16(row, col);
70
- const level1 = level0[byte0(keyLo)];
71
- if (level1 !== undefined) {
72
- const level2 = level1[byte1(keyLo)];
73
- if (level2 !== undefined) {
74
- const level3 = level2[byte2(keyLo)];
75
- if (level3 !== undefined) {
76
- return level3[byte3(keyLo)];
77
- }
78
- }
79
- }
80
- }
81
-
82
- return undefined;
83
- }
84
-
85
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
86
- public get matrixProducer() { return undefined as any; }
87
-
88
- public setCell(row: number, col: number, value: T | undefined) {
89
- const keyHi = r0c0ToMorton2x16(row >>> 16, col >>> 16);
90
- const keyLo = r0c0ToMorton2x16(row, col);
91
-
92
- const level0 = this.getLevel(this.root, keyHi);
93
- const level1 = this.getLevel(level0, byte0(keyLo));
94
- const level2 = this.getLevel(level1, byte1(keyLo));
95
- const level3 = this.getLevel(level2, byte2(keyLo));
96
- level3[byte3(keyLo)] = value;
97
- }
98
-
99
- /**
100
- * Invokes the given 'callback' for each key in a 16 x 16 tile at the indicated row.
101
- *
102
- * (Note that 'rowBits' is the appropriate byte from 'r0ToMorton16' for the current
103
- * level being traversed.)
104
- */
105
- private forEachKeyInRow(rowBits: number, callback: (key: number) => void) {
106
- for (let col = 0; col < 16; col++) {
107
- // Perf: Potentially faster to replace 'c0ToMorton16()' with a short look up table?
108
- callback((rowBits | c0ToMorton16(col)) >>> 0);
109
- }
110
- }
111
-
112
- /**
113
- * Invokes the given 'callback' for each key in a 16 x 16 tile at the indicated col.
114
- *
115
- * (Note that 'colBits' is the appropriate byte from 'c0ToMorton16' for the current
116
- * level being traversed.)
117
- */
118
- private forEachKeyInCol(col: number, callback: (key: number) => void) {
119
- for (let row = 0; row < 16; row++) {
120
- // Perf: Potentially faster to replace 'r0ToMorton16()' with a short look up table?
121
- callback((r0ToMorton16(row) | col) >>> 0);
122
- }
123
- }
124
-
125
- /**
126
- * Invokes the give 'callback' with the next 'level' array for each populated region
127
- * of the given row in the 'currentLevel'.
128
- *
129
- * (Note that 'rowBits' is the appropriate byte from 'r0ToMorton16' for the current
130
- * level being traversed.)
131
- */
132
- private forEachInRow<V extends UA<any>, U extends UA<V>>(
133
- currentLevel: U,
134
- rowBits: number,
135
- callback: (level: V) => void,
136
- ) {
137
- this.forEachKeyInRow(rowBits, (key) => {
138
- const nextLevel = currentLevel[key];
139
- if (nextLevel !== undefined) {
140
- callback(nextLevel);
141
- }
142
- });
143
- }
144
-
145
- /**
146
- * Invokes the give 'callback' with the next 'level' array for each populated region
147
- * of the given col in the 'currentLevel'.
148
- *
149
- * (Note that 'colBits' is the appropriate byte from 'c0ToMorton16' for the current
150
- * level being traversed.)
151
- */
152
- private forEachInCol<V extends UA<any>, U extends UA<V>>(
153
- currentLevel: U,
154
- colBits: number,
155
- callback: (level: V) => void,
156
- ) {
157
- this.forEachKeyInCol(colBits, (key) => {
158
- const nextLevel = currentLevel[key];
159
- if (nextLevel !== undefined) {
160
- callback(nextLevel);
161
- }
162
- });
163
- }
164
-
165
- /** Clears the all cells contained within the specified span of rows. */
166
- public clearRows(rowStart: number, rowCount: number) {
167
- const rowEnd = rowStart + rowCount;
168
- for (let row = rowStart; row < rowEnd; row++) {
169
- const rowHi = r0ToMorton16(row >>> 16);
170
-
171
- // The top level of tree is a 64k x 64k tile. We need to scan all 64k entries.
172
- for (let colHi = 0; colHi < 0x10000; colHi++) {
173
- const keyHi = (rowHi | c0ToMorton16(colHi)) >>> 0;
174
- const level0 = this.root[keyHi];
175
- if (level0 !== undefined) {
176
- // The remainder of the tree is divided in 16 x 16 tiles.
177
- const rowLo = r0ToMorton16(row);
178
- this.forEachInRow(level0, byte0(rowLo), (level1) => {
179
- this.forEachInRow(level1, byte1(rowLo), (level2) => {
180
- this.forEachInRow(level2, byte2(rowLo), (level3) => {
181
- this.forEachKeyInRow(byte3(rowLo), (key) => {
182
- level3[key] = undefined;
183
- });
184
- });
185
- });
186
- });
187
- }
188
- }
189
- }
190
- }
191
-
192
- /** Clears the all cells contained within the specifed span of cols. */
193
- public clearCols(colStart: number, colCount: number) {
194
- const colEnd = colStart + colCount;
195
- for (let col = colStart; col < colEnd; col++) {
196
- const colHi = c0ToMorton16(col >>> 16);
197
-
198
- // The top level of tree is a 64k x 64k tile. We need to scan all 64k entries.
199
- for (let rowHi = 0; rowHi < 0x10000; rowHi++) {
200
- const keyHi = (colHi | r0ToMorton16(rowHi)) >>> 0;
201
- const level0 = this.root[keyHi];
202
- if (level0 !== undefined) {
203
- // The remainder of the tree is divided in 16 x 16 tiles.
204
- const colLo = c0ToMorton16(col);
205
- this.forEachInCol(level0, byte0(colLo), (level1) => {
206
- this.forEachInCol(level1, byte1(colLo), (level2) => {
207
- this.forEachInCol(level2, byte2(colLo), (level3) => {
208
- this.forEachKeyInCol(byte3(colLo), (key) => {
209
- level3[key] = undefined;
210
- });
211
- });
212
- });
213
- });
214
- }
215
- }
216
- }
217
- }
218
-
219
- private getLevel<T>(parent: UA<UA<T>>, subKey: number) {
220
- const level = parent[subKey];
221
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
222
- return level === undefined
223
- ? (parent[subKey] = new Array(256).fill(undefined))
224
- : level;
225
- }
226
-
227
- public snapshot() {
228
- return this.root;
229
- }
230
-
231
- public static load<T>(data: RecurArray<T>) {
232
- return new SparseArray2D<T>(nullToUndefined<T>(data) as SparseArray2D<T>["root"]);
233
- }
57
+ export class SparseArray2D<T>
58
+ implements IMatrixReader<T | undefined>, IMatrixWriter<T | undefined>
59
+ {
60
+ constructor(private readonly root: UA<UA<UA<UA<UA<T>>>>> = [undefined]) {}
61
+
62
+ public get rowCount() {
63
+ return 0xffffffff;
64
+ }
65
+ public get colCount() {
66
+ return 0xffffffff;
67
+ }
68
+
69
+ public getCell(row: number, col: number): T | undefined {
70
+ const keyHi = r0c0ToMorton2x16(row >>> 16, col >>> 16);
71
+ const level0 = this.root[keyHi];
72
+ if (level0 !== undefined) {
73
+ const keyLo = r0c0ToMorton2x16(row, col);
74
+ const level1 = level0[byte0(keyLo)];
75
+ if (level1 !== undefined) {
76
+ const level2 = level1[byte1(keyLo)];
77
+ if (level2 !== undefined) {
78
+ const level3 = level2[byte2(keyLo)];
79
+ if (level3 !== undefined) {
80
+ return level3[byte3(keyLo)];
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ return undefined;
87
+ }
88
+
89
+ public get matrixProducer() {
90
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
91
+ return undefined as any;
92
+ }
93
+
94
+ public setCell(row: number, col: number, value: T | undefined) {
95
+ const keyHi = r0c0ToMorton2x16(row >>> 16, col >>> 16);
96
+ const keyLo = r0c0ToMorton2x16(row, col);
97
+
98
+ const level0 = this.getLevel(this.root, keyHi);
99
+ const level1 = this.getLevel(level0, byte0(keyLo));
100
+ const level2 = this.getLevel(level1, byte1(keyLo));
101
+ const level3 = this.getLevel(level2, byte2(keyLo));
102
+ level3[byte3(keyLo)] = value;
103
+ }
104
+
105
+ /**
106
+ * Invokes the given 'callback' for each key in a 16 x 16 tile at the indicated row.
107
+ *
108
+ * (Note that 'rowBits' is the appropriate byte from 'r0ToMorton16' for the current
109
+ * level being traversed.)
110
+ */
111
+ private forEachKeyInRow(rowBits: number, callback: (key: number) => void) {
112
+ for (let col = 0; col < 16; col++) {
113
+ // Perf: Potentially faster to replace 'c0ToMorton16()' with a short look up table?
114
+ callback((rowBits | c0ToMorton16(col)) >>> 0);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Invokes the given 'callback' for each key in a 16 x 16 tile at the indicated col.
120
+ *
121
+ * (Note that 'colBits' is the appropriate byte from 'c0ToMorton16' for the current
122
+ * level being traversed.)
123
+ */
124
+ private forEachKeyInCol(col: number, callback: (key: number) => void) {
125
+ for (let row = 0; row < 16; row++) {
126
+ // Perf: Potentially faster to replace 'r0ToMorton16()' with a short look up table?
127
+ callback((r0ToMorton16(row) | col) >>> 0);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Invokes the give 'callback' with the next 'level' array for each populated region
133
+ * of the given row in the 'currentLevel'.
134
+ *
135
+ * (Note that 'rowBits' is the appropriate byte from 'r0ToMorton16' for the current
136
+ * level being traversed.)
137
+ */
138
+ private forEachInRow<V extends UA<any>, U extends UA<V>>(
139
+ currentLevel: U,
140
+ rowBits: number,
141
+ callback: (level: V) => void,
142
+ ) {
143
+ this.forEachKeyInRow(rowBits, (key) => {
144
+ const nextLevel = currentLevel[key];
145
+ if (nextLevel !== undefined) {
146
+ callback(nextLevel);
147
+ }
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Invokes the give 'callback' with the next 'level' array for each populated region
153
+ * of the given col in the 'currentLevel'.
154
+ *
155
+ * (Note that 'colBits' is the appropriate byte from 'c0ToMorton16' for the current
156
+ * level being traversed.)
157
+ */
158
+ private forEachInCol<V extends UA<any>, U extends UA<V>>(
159
+ currentLevel: U,
160
+ colBits: number,
161
+ callback: (level: V) => void,
162
+ ) {
163
+ this.forEachKeyInCol(colBits, (key) => {
164
+ const nextLevel = currentLevel[key];
165
+ if (nextLevel !== undefined) {
166
+ callback(nextLevel);
167
+ }
168
+ });
169
+ }
170
+
171
+ /** Clears the all cells contained within the specified span of rows. */
172
+ public clearRows(rowStart: number, rowCount: number) {
173
+ const rowEnd = rowStart + rowCount;
174
+ for (let row = rowStart; row < rowEnd; row++) {
175
+ const rowHi = r0ToMorton16(row >>> 16);
176
+
177
+ // The top level of tree is a 64k x 64k tile. We need to scan all 64k entries.
178
+ for (let colHi = 0; colHi < 0x10000; colHi++) {
179
+ const keyHi = (rowHi | c0ToMorton16(colHi)) >>> 0;
180
+ const level0 = this.root[keyHi];
181
+ if (level0 !== undefined) {
182
+ // The remainder of the tree is divided in 16 x 16 tiles.
183
+ const rowLo = r0ToMorton16(row);
184
+ this.forEachInRow(level0, byte0(rowLo), (level1) => {
185
+ this.forEachInRow(level1, byte1(rowLo), (level2) => {
186
+ this.forEachInRow(level2, byte2(rowLo), (level3) => {
187
+ this.forEachKeyInRow(byte3(rowLo), (key) => {
188
+ level3[key] = undefined;
189
+ });
190
+ });
191
+ });
192
+ });
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ /** Clears the all cells contained within the specifed span of cols. */
199
+ public clearCols(colStart: number, colCount: number) {
200
+ const colEnd = colStart + colCount;
201
+ for (let col = colStart; col < colEnd; col++) {
202
+ const colHi = c0ToMorton16(col >>> 16);
203
+
204
+ // The top level of tree is a 64k x 64k tile. We need to scan all 64k entries.
205
+ for (let rowHi = 0; rowHi < 0x10000; rowHi++) {
206
+ const keyHi = (colHi | r0ToMorton16(rowHi)) >>> 0;
207
+ const level0 = this.root[keyHi];
208
+ if (level0 !== undefined) {
209
+ // The remainder of the tree is divided in 16 x 16 tiles.
210
+ const colLo = c0ToMorton16(col);
211
+ this.forEachInCol(level0, byte0(colLo), (level1) => {
212
+ this.forEachInCol(level1, byte1(colLo), (level2) => {
213
+ this.forEachInCol(level2, byte2(colLo), (level3) => {
214
+ this.forEachKeyInCol(byte3(colLo), (key) => {
215
+ level3[key] = undefined;
216
+ });
217
+ });
218
+ });
219
+ });
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ private getLevel<T>(parent: UA<UA<T>>, subKey: number) {
226
+ const level = parent[subKey];
227
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
228
+ return level === undefined ? (parent[subKey] = new Array(256).fill(undefined)) : level;
229
+ }
230
+
231
+ public snapshot() {
232
+ return this.root;
233
+ }
234
+
235
+ public static load<T>(data: RecurArray<T>) {
236
+ return new SparseArray2D<T>(nullToUndefined<T>(data) as SparseArray2D<T>["root"]);
237
+ }
234
238
  }