@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/.eslintrc.js
CHANGED
|
@@ -4,27 +4,26 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
|
-
|
|
7
|
+
extends: [require.resolve("@fluidframework/eslint-config-fluid/minimal"), "prettier"],
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
parserOptions: {
|
|
10
|
+
project: ["./tsconfig.json", "./src/test/tsconfig.json"],
|
|
11
|
+
},
|
|
12
|
+
rules: {
|
|
13
|
+
"@typescript-eslint/no-shadow": "off",
|
|
14
|
+
"space-before-function-paren": "off", // Off because it conflicts with typescript-formatter
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
],
|
|
16
|
+
// This library is used in the browser, so we don't want dependencies on most node libraries.
|
|
17
|
+
"import/no-nodejs-modules": ["error", { allow: ["events"] }],
|
|
18
|
+
},
|
|
19
|
+
overrides: [
|
|
20
|
+
{
|
|
21
|
+
// Rules only for test files
|
|
22
|
+
files: ["*.spec.ts", "src/test/**"],
|
|
23
|
+
rules: {
|
|
24
|
+
// Test files are run in node only so additional node libraries can be used.
|
|
25
|
+
"import/no-nodejs-modules": ["error", { allow: ["assert", "events"] }],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
30
29
|
};
|
package/.mocharc.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
"use strict";
|
|
7
7
|
|
|
8
|
-
const getFluidTestMochaConfig = require(
|
|
8
|
+
const getFluidTestMochaConfig = require("@fluidframework/mocha-test-setup/mocharc-common");
|
|
9
9
|
|
|
10
10
|
const packageDir = __dirname;
|
|
11
11
|
const config = getFluidTestMochaConfig(packageDir);
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @fluidframework/matrix
|
|
2
2
|
|
|
3
|
-
SharedMatrix is a rectangular 2D array of values.
|
|
3
|
+
SharedMatrix is a rectangular 2D array of values. Matrix values are a superset of JSON serializable types that includes embedded IFluidHandle references to Fluid object.
|
|
4
4
|
|
|
5
5
|
## Operations
|
|
6
6
|
|
|
7
7
|
The SharedMatrix currently supports the following operations:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- `insertCols(col, numCols)` / `removeCols(col, numCols)`
|
|
10
|
+
- `insertRows(row, numRows)` / `removeRows(row, numRows)`
|
|
11
|
+
- `setCells(row, col, numCols, values)` (values is a 1D array in row-major order)
|
|
12
12
|
|
|
13
13
|
Insertion / removal operations are reconciled using Fluid sequence semantics, while setCells() uses Fluid map semantics.
|
|
14
14
|
|
|
@@ -16,8 +16,8 @@ Insertion / removal operations are reconciled using Fluid sequence semantics, wh
|
|
|
16
16
|
|
|
17
17
|
The SharedMatrix data structure is comprised of:
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
- Two 'PermutationVectors', which are used to process row/col insertion and removal ops
|
|
20
|
+
- A sparse quadtree-like "physical store" for holding the cell values
|
|
21
21
|
|
|
22
22
|
### Permutation Vectors
|
|
23
23
|
|
|
@@ -57,7 +57,7 @@ physical row -> 1 | 8 .
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
The next row/column to be inserted is assigned the next available physical address, regardless of
|
|
60
|
-
where the row/col was logically inserted.
|
|
60
|
+
where the row/col was logically inserted. Deleted rows/cols are recycled after clearing the physical store.
|
|
61
61
|
|
|
62
62
|
This indirection between logical row/col and storage row/col provides three functions:
|
|
63
63
|
|
|
@@ -66,12 +66,12 @@ This indirection between logical row/col and storage row/col provides three func
|
|
|
66
66
|
updated).
|
|
67
67
|
3. It enables us to "time-travel" to previous matrix versions when reconciling ops from remote clients.
|
|
68
68
|
|
|
69
|
-
To support reconciliation, we use a MergeTree for each PermutationVector.
|
|
69
|
+
To support reconciliation, we use a MergeTree for each PermutationVector. MergeTree is a B-Tree of order 7 that
|
|
70
70
|
temporarily maintains some extra metadata to reconcile ops while they are within the current collab window.
|
|
71
71
|
|
|
72
72
|
### Physical Storage
|
|
73
73
|
|
|
74
|
-
Cell data is stored in a quadtree-like data structure that is a recursive subdivision of 16x16 tiles.
|
|
74
|
+
Cell data is stored in a quadtree-like data structure that is a recursive subdivision of 16x16 tiles. The
|
|
75
75
|
implementation leverages [Morton coding](https://en.wikipedia.org/wiki/Z-order_curve) to implement this as a cascade of
|
|
76
76
|
fast 1D array accesses.
|
|
77
77
|
|
|
@@ -81,20 +81,20 @@ const keyLo = r0c0ToMorton2x16((row << 16) >>> 16, (col << 16) >>> 16);
|
|
|
81
81
|
|
|
82
82
|
const level0 = this.root[keyHi];
|
|
83
83
|
if (level0 !== undefined) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
const level1 = level0[byte0(keyLo)];
|
|
85
|
+
if (level1 !== undefined) {
|
|
86
|
+
const level2 = level1[byte1(keyLo)];
|
|
87
|
+
if (level2 !== undefined) {
|
|
88
|
+
const level3 = level2[byte2(keyLo)];
|
|
89
|
+
if (level3 !== undefined) {
|
|
90
|
+
return level3[byte3(keyLo)];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
94
|
}
|
|
95
|
-
return undefined;
|
|
95
|
+
return undefined; // Empty region
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
A benefit of storing the cell data in [Z-order](https://en.wikipedia.org/wiki/Z-order_curve) is that both row-major and
|
|
99
|
-
col-major traversal benefit from prefetching and cache coherence.
|
|
99
|
+
col-major traversal benefit from prefetching and cache coherence. Reading/writing to the physical storage along either
|
|
100
100
|
axis is typically within an order of magnitude compared to sequentially accessing a cache hot native JavaScript array.
|
package/api-extractor.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
|
3
|
+
"extends": "@fluidframework/build-common/api-extractor-common-report.json"
|
|
4
4
|
}
|
|
@@ -69,10 +69,10 @@ the BSP sets in general to be fairly simple.
|
|
|
69
69
|
|
|
70
70
|
Now the memory footprint of the BSP set is thus:
|
|
71
71
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
-
|
|
72
|
+
- Some general overhead for the whole BSP set, i.e. object cost
|
|
73
|
+
- Linear in the number of keys used for the exact representation.
|
|
74
|
+
Usually tiny, because they replace whole subtrees and occur infrequently.
|
|
75
|
+
- About 2.5 bits per node. I.e. **tiny**.
|
|
76
76
|
|
|
77
77
|
All the algorithms recurse over the structure of the tree, so they should be able to run over this
|
|
78
78
|
bit encoding as well. Chances are it would be a lot faster, but definitely consuming a lot less
|
|
@@ -81,4 +81,4 @@ memory than before.
|
|
|
81
81
|
## Application
|
|
82
82
|
|
|
83
83
|
If we did this change, we might be able to use raw product sets as messages inside say a shared dictionary
|
|
84
|
-
because the payload of the messages might be small enough for that.
|
|
84
|
+
because the payload of the messages might be small enough for that.
|
package/bench/src/index.ts
CHANGED
|
@@ -6,25 +6,43 @@
|
|
|
6
6
|
import { run } from "hotloop";
|
|
7
7
|
|
|
8
8
|
(async () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
console.group("Populated");
|
|
10
|
+
await run([
|
|
11
|
+
// { path: "./src/read/nativearray", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true } },
|
|
12
|
+
// { path: "./src/read/map", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true } },
|
|
13
|
+
{
|
|
14
|
+
path: "./src/read/sparsearray2d",
|
|
15
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true },
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
path: "./src/read/matrix/contiguous",
|
|
19
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
path: "./src/read/matrix/fragmented",
|
|
23
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true },
|
|
24
|
+
},
|
|
25
|
+
// { path: "./src/read/tiled", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: true } }
|
|
26
|
+
]);
|
|
27
|
+
console.groupEnd();
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
console.group("Empty");
|
|
30
|
+
await run([
|
|
31
|
+
// { path: "./src/read/nativearray", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false } },
|
|
32
|
+
// { path: "./src/read/map", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false } },
|
|
33
|
+
{
|
|
34
|
+
path: "./src/read/sparsearray2d",
|
|
35
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
path: "./src/read/matrix/contiguous",
|
|
39
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
path: "./src/read/matrix/fragmented",
|
|
43
|
+
args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false },
|
|
44
|
+
},
|
|
45
|
+
// { path: "./src/read/tiled", args: { row: 0, col: 0, rowCount: 256, colCount: 256, fill: false } }
|
|
46
|
+
]);
|
|
47
|
+
console.groupEnd();
|
|
30
48
|
})();
|
package/bench/src/read/map.ts
CHANGED
|
@@ -6,20 +6,26 @@
|
|
|
6
6
|
import { pointwise } from "./test";
|
|
7
7
|
|
|
8
8
|
export class Map256x256<T> {
|
|
9
|
-
|
|
9
|
+
private readonly cells = new Map<number, T>();
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
public get rowCount() {
|
|
12
|
+
return 256;
|
|
13
|
+
}
|
|
14
|
+
public get colCount() {
|
|
15
|
+
return 256;
|
|
16
|
+
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
public getCell(row: number, col: number) {
|
|
19
|
+
return this.cells.get((row << 8) + col);
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
public setCell(row: number, col: number, value: T) {
|
|
23
|
+
this.cells.set((row << 8) + col, value);
|
|
24
|
+
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
public get matrixProducer() {
|
|
27
|
+
return undefined as any;
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
pointwise(undefined, new Map256x256<number>());
|
|
@@ -6,20 +6,26 @@
|
|
|
6
6
|
import { pointwise } from "./test";
|
|
7
7
|
|
|
8
8
|
export class Array256x256<T> {
|
|
9
|
-
|
|
9
|
+
private readonly cells: T[] = new Array(256 * 256).fill(0);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
public get rowCount() {
|
|
12
|
+
return 256;
|
|
13
|
+
}
|
|
14
|
+
public get colCount() {
|
|
15
|
+
return 256;
|
|
16
|
+
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
public getCell(row: number, col: number) {
|
|
19
|
+
return this.cells[(row << 8) + col];
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
public setCell(row: number, col: number, value: T) {
|
|
23
|
+
this.cells[(row << 8) + col] = value;
|
|
24
|
+
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
public get matrixProducer() {
|
|
27
|
+
return undefined as any;
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
pointwise(undefined, new Array256x256<number>());
|
package/bench/src/read/test.ts
CHANGED
|
@@ -9,24 +9,22 @@ import { fill, IMatrix } from "../imports";
|
|
|
9
9
|
const { row, col, rowCount, colCount, fill: shouldFill } = getTestArgs();
|
|
10
10
|
|
|
11
11
|
export function pointwise<T>(name: string | undefined, matrix: IMatrix<T>) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (shouldFill) {
|
|
13
|
+
fill(matrix, row, col, rowCount, colCount);
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
);
|
|
16
|
+
benchmark(
|
|
17
|
+
`SUM ${name !== undefined ? name : matrix.constructor.name} (${
|
|
18
|
+
shouldFill ? "full" : "empty"
|
|
19
|
+
}) Pointwise Read ${rowCount}x${colCount} @${row},${col}`,
|
|
20
|
+
() => {
|
|
21
|
+
let sum = 0;
|
|
22
|
+
for (let r = row; r < rowCount; r++) {
|
|
23
|
+
for (let c = col; c < colCount; c++) {
|
|
24
|
+
sum += matrix.getCell(r, c) as any | 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return sum;
|
|
28
|
+
},
|
|
29
|
+
);
|
|
32
30
|
}
|