Skip to content

Commit c24cea3

Browse files
committed
feat(json-crdt): 🎸 start debug file format implementation
1 parent 1796627 commit c24cea3

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

src/json-crdt/file/File.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {Model} from "../model";
2+
import {PatchLog} from "./PatchLog";
3+
import {FileModelEncoding} from "./constants";
4+
import {Encoder as SidecarEncoder} from '../codec/sidecar/binary/Encoder';
5+
import {Encoder as StructuralEncoderCompact} from '../codec/structural/compact/Encoder';
6+
import {Encoder as StructuralEncoderVerbose} from '../codec/structural/verbose/Encoder';
7+
import {encode as encodeCompact} from '../../json-crdt-patch/codec/compact/encode';
8+
import {encode as encodeVerbose} from '../../json-crdt-patch/codec/verbose/encode';
9+
import type * as types from "./types";
10+
11+
export class File {
12+
public static fromModel(model: Model): File {
13+
return new File(model, PatchLog.fromModel(model));
14+
}
15+
16+
constructor(
17+
public readonly model: Model,
18+
public readonly history: PatchLog,
19+
) {}
20+
21+
public serialize(params: types.FileSerializeParams = {}): types.FileWriteSequence {
22+
const view = this.model.view();
23+
const metadata: types.FileMetadata = [
24+
{},
25+
FileModelEncoding.SidecarBinary,
26+
];
27+
let model: Uint8Array | unknown | null = null;
28+
const modelFormat = params.model ?? 'sidecar';
29+
switch (modelFormat) {
30+
case 'sidecar': {
31+
metadata[1] = FileModelEncoding.SidecarBinary;
32+
const encoder = new SidecarEncoder();
33+
const [, uint8] = encoder.encode(this.model);
34+
model = uint8;
35+
break;
36+
}
37+
case 'binary': {
38+
metadata[1] = FileModelEncoding.StructuralBinary;
39+
model = this.model.toBinary();
40+
break;
41+
}
42+
case 'compact': {
43+
metadata[1] = FileModelEncoding.StructuralCompact;
44+
model = new StructuralEncoderCompact().encode(this.model);
45+
break;
46+
}
47+
case 'verbose': {
48+
metadata[1] = FileModelEncoding.StructuralVerbose;
49+
model = new StructuralEncoderVerbose().encode(this.model);
50+
break;
51+
}
52+
default:
53+
throw new Error(`Invalid model format: ${modelFormat}`);
54+
}
55+
const history: types.FileWriteSequenceHistory = [
56+
null,
57+
[],
58+
];
59+
const patchFormat = params.history ?? 'binary';
60+
switch (patchFormat) {
61+
case 'binary': {
62+
history[0] = this.history.start.toBinary();
63+
this.history.patches.forEach(({v}) => {
64+
history[1].push(v.toBinary());
65+
});
66+
break;
67+
}
68+
case 'compact': {
69+
history[0] = new StructuralEncoderCompact().encode(this.history.start);
70+
this.history.patches.forEach(({v}) => {
71+
history[1].push(encodeCompact(v));
72+
});
73+
break;
74+
}
75+
case 'verbose': {
76+
history[0] = new StructuralEncoderVerbose().encode(this.history.start);
77+
this.history.patches.forEach(({v}) => {
78+
history[1].push(encodeVerbose(v));
79+
});
80+
break;
81+
}
82+
default:
83+
throw new Error(`Invalid history format: ${patchFormat}`);
84+
}
85+
return [
86+
view,
87+
metadata,
88+
model,
89+
history,
90+
];
91+
}
92+
}

src/json-crdt/file/PatchLog.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {ITimestampStruct, Patch, ServerClockVector, compare} from "../../json-crdt-patch";
2+
import {AvlMap} from "../../util/trees/avl/AvlMap";
3+
import {Model} from "../model";
4+
5+
export class PatchLog {
6+
public static fromModel (model: Model): PatchLog {
7+
const start = new Model(model.clock.clone());
8+
const log = new PatchLog(start);
9+
if (model.api.builder.patch.ops.length) {
10+
const patch = model.api.flush();
11+
log.push(patch);
12+
}
13+
return log;
14+
}
15+
16+
public readonly patches = new AvlMap<ITimestampStruct, Patch>(compare);
17+
18+
constructor (public readonly start: Model) {}
19+
20+
public push(patch: Patch): void {
21+
const id = patch.getId();
22+
if (!id) return;
23+
this.patches.set(id, patch);
24+
}
25+
}

src/json-crdt/file/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const enum FileModelEncoding {
2+
None = 0,
3+
SidecarBinary = 1,
4+
StructuralBinary = 5,
5+
StructuralCompact = 6,
6+
StructuralVerbose = 7,
7+
}

src/json-crdt/file/types.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type {FileModelEncoding} from "./constants";
2+
3+
export type FileMetadata = [
4+
map: {},
5+
modelFormat: FileModelEncoding,
6+
];
7+
8+
export type FileWriteSequence = [
9+
view: unknown | null,
10+
metadata: FileMetadata,
11+
model: Uint8Array | unknown | null,
12+
history: FileWriteSequenceHistory,
13+
];
14+
15+
export type FileWriteSequenceHistory = [
16+
model: Uint8Array | unknown | null,
17+
patches: Array<Uint8Array | unknown>,
18+
];
19+
20+
export type FileReadSequence = [
21+
...FileWriteSequence,
22+
...frontier: Array<Uint8Array | unknown>,
23+
];
24+
25+
export interface FileSerializeParams {
26+
noView?: boolean;
27+
model?: 'sidecar' | 'binary' | 'compact' | 'verbose';
28+
history?: 'binary' | 'compact' | 'verbose';
29+
}

0 commit comments

Comments
 (0)