From 76541aea85aa3e655dae4d652703c577d5e48ecc Mon Sep 17 00:00:00 2001 From: Charlotte Som Date: Thu, 6 Mar 2025 09:42:49 +0000 Subject: [PATCH] add compaction for the wire protocol this batches contiguous runs of characters from a host into a single object --- compression.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ deno.json | 5 ++++ 2 files changed, 86 insertions(+) create mode 100644 compression.ts diff --git a/compression.ts b/compression.ts new file mode 100644 index 0000000..0643745 --- /dev/null +++ b/compression.ts @@ -0,0 +1,81 @@ +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; +type CompressedRun = { st: Timestamp; p: Timestamp | undefined; seq: string }; +type Run = VerbatimRun | CompressedRun; + +export function compressOps(ops: CausalTreeOp[]): 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[] { + const ops: WeakCausalTreeOp[] = []; + + 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; +} diff --git a/deno.json b/deno.json index 8e26a11..e0f0251 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,10 @@ { "version": "0.1.0", + "tasks": { + "build": "deno run -A ./_build.ts", + "watch": "deno run -A ./_build.ts --watch", + "serve": "deno run -A ./server.ts" + }, "imports": { "@char/aftercare": "jsr:@char/aftercare@^0.3.0", "@oak/oak": "jsr:@oak/oak@^17.1.4"