sync-playground/compression.ts
Charlotte Som 76541aea85 add compaction for the wire protocol
this batches contiguous runs of characters from a host into a single
object
2025-03-06 09:42:49 +00:00

81 lines
2.1 KiB
TypeScript

import { Timestamp } from "./sync/common.ts";
import { CausalTree, CausalTreeOp, WeakCausalTreeOp } from "./sync/ordt/causal-tree.ts";
import { PlainTextOperation } from "./sync/ordt/plain-text.ts";
type VerbatimRun = WeakCausalTreeOp<PlainTextOperation>;
type CompressedRun = { st: Timestamp; p: Timestamp | undefined; seq: string };
type Run = VerbatimRun | CompressedRun;
export function compressOps(ops: CausalTreeOp<PlainTextOperation>[]): Run[] {
if (ops.length === 0) throw new Error("can't create a compressed run!");
const runs: Run[] = [];
let seq = "";
let cnt = 0;
let start: Timestamp | undefined;
let firstParent: Timestamp | undefined;
for (let i = 0; i < ops.length; i++) {
const op = ops[i];
const lastOp = ops[i - 1];
if (!start) start = op.at;
if (!firstParent) firstParent = op.parent?.at;
if (
op.type === "insert" &&
op.sequence.length === 1 &&
(cnt === 0 ||
(op.parent?.at?.[0] === lastOp.at[0] &&
op.parent?.at?.[1] === lastOp.at[1] &&
op.at[0] === lastOp.at[0] &&
op.at[1] === lastOp.at[1] + 1))
) {
seq += op.sequence;
cnt++;
} else {
if (cnt > 0) {
runs.push({ st: start!, p: firstParent, seq });
i--;
} else {
runs.push(CausalTree.toWeakOp(op));
}
start = undefined;
firstParent = undefined;
seq = "";
cnt = 0;
}
}
if (cnt > 1) {
runs.push({ st: start!, p: firstParent, seq });
}
return runs;
}
export function decompressOps(runs: Run[]): WeakCausalTreeOp<PlainTextOperation>[] {
const ops: WeakCausalTreeOp<PlainTextOperation>[] = [];
for (const run of runs) {
if ("st" in run) {
let i = 0;
let parent: Timestamp | undefined = run.p;
for (const c of run.seq) {
const op = {
at: [run.st[0], run.st[1] + i] as Timestamp,
parent,
type: "insert",
sequence: c,
} as const;
parent = op.at;
ops.push(op);
i++;
}
} else {
ops.push(run);
}
}
return ops;
}