compact wire protocol

This commit is contained in:
Charlotte Som 2025-03-06 09:52:36 +00:00
parent 76541aea85
commit 90ab38c989
4 changed files with 31 additions and 18 deletions

View file

@ -1,4 +1,5 @@
import diff from "npm:fast-diff";
import { decompressOps } from "../compression.ts";
import { Packet } from "../proto.ts";
import { CausalTree, CausalTreeOp } from "../sync/ordt/causal-tree.ts";
import { PlainTextOperation, PlainTextORDT } from "../sync/ordt/plain-text.ts";
@ -31,7 +32,8 @@ const initialized = Promise.withResolvers<Packet & { t: "init" }>();
socket.addEventListener("message", ev => {
const packet = JSON.parse(ev.data) as Packet;
if (packet.t === "init") {
for (const op of packet.ops) pt.apply(op);
const ops = decompressOps(packet.ops);
for (const op of ops) pt.apply(op);
initialized.resolve(packet);
} else if (packet.t === "op") {
pt.apply(packet.op);

View file

@ -2,14 +2,15 @@ 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 VerbatimOp = 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!");
export type CompactedOperations = (VerbatimOp | CompressedRun)[];
const runs: Run[] = [];
export function compressOps(ops: CausalTreeOp<PlainTextOperation>[]): CompactedOperations {
if (ops.length === 0) return [];
const runs: CompactedOperations = [];
let seq = "";
let cnt = 0;
let start: Timestamp | undefined;
@ -18,8 +19,10 @@ export function compressOps(ops: CausalTreeOp<PlainTextOperation>[]): Run[] {
const op = ops[i];
const lastOp = ops[i - 1];
if (!start) start = op.at;
if (!firstParent) firstParent = op.parent?.at;
if (!start) {
start = op.at;
firstParent = op.parent?.at;
}
if (
op.type === "insert" &&
@ -54,16 +57,18 @@ export function compressOps(ops: CausalTreeOp<PlainTextOperation>[]): Run[] {
return runs;
}
export function decompressOps(runs: Run[]): WeakCausalTreeOp<PlainTextOperation>[] {
export function decompressOps(
compacted: CompactedOperations,
): WeakCausalTreeOp<PlainTextOperation>[] {
const ops: WeakCausalTreeOp<PlainTextOperation>[] = [];
for (const run of runs) {
if ("st" in run) {
for (const item of compacted) {
if ("st" in item) {
let i = 0;
let parent: Timestamp | undefined = run.p;
for (const c of run.seq) {
let parent: Timestamp | undefined = item.p;
for (const c of item.seq) {
const op = {
at: [run.st[0], run.st[1] + i] as Timestamp,
at: [item.st[0], item.st[1] + i] as Timestamp,
parent,
type: "insert",
sequence: c,
@ -73,7 +78,7 @@ export function decompressOps(runs: Run[]): WeakCausalTreeOp<PlainTextOperation>
i++;
}
} else {
ops.push(run);
ops.push(item);
}
}

View file

@ -1,6 +1,7 @@
import { CompactedOperations } from "./compression.ts";
import { WeakCausalTreeOp } from "./sync/ordt/causal-tree.ts";
import { PlainTextOperation } from "./sync/ordt/plain-text.ts";
export type Packet =
| { t: "init"; ops: WeakCausalTreeOp<PlainTextOperation>[]; you: string }
| { t: "init"; ops: CompactedOperations; you: string }
| { t: "op"; op: WeakCausalTreeOp<PlainTextOperation> };

View file

@ -1,4 +1,5 @@
import { Application, Router } from "@oak/oak";
import { compressOps } from "./compression.ts";
import { Packet } from "./proto.ts";
import { CausalTree } from "./sync/ordt/causal-tree.ts";
import { PlainTextORDT } from "./sync/ordt/plain-text.ts";
@ -13,10 +14,14 @@ const sockets = new Set<WebSocket>();
router.get("/api/connect", ctx => {
const socket = ctx.upgrade();
sockets.add(socket);
const connectionId = crypto.randomUUID();
socket.addEventListener("message", event => {
if (typeof event.data !== "string") return;
const op = JSON.parse(event.data);
op.at[0] = connectionId;
pt.apply(op); // mutates op to strong
const weakOp = CausalTree.toWeakOp(op);
@ -33,8 +38,8 @@ router.get("/api/connect", ctx => {
socket.send(
JSON.stringify({
t: "init",
ops: pt.operations.map(it => CausalTree.toWeakOp(it)),
you: crypto.randomUUID(),
ops: compressOps(pt.operations),
you: connectionId,
} satisfies Packet),
);
};