sync-playground/sync/ordt/causal-tree.ts

40 lines
1.4 KiB
TypeScript

import { Timestamp, timestampCompare } from "../common.ts";
export type CausalTreeOp<T> = T & { at: Timestamp; parent?: CausalTreeOp<T> };
// parent is referred to via timestamp instead of reference here
// makes the api simpler
export type WeakCausalTreeOp<T> = T & { at: Timestamp; parent?: Timestamp };
export type AnyCausalTreeOp<T> = CausalTreeOp<T> | WeakCausalTreeOp<T>;
export class CausalTree<T> {
operations: CausalTreeOp<T>[] = [];
clock: number = 0;
/** WARN: mutates 'op' for performance (in-place conversion from weak op to strong) */
apply(op: AnyCausalTreeOp<T>): number {
const opParent = op.parent;
const parentOpIdx = this.operations.findIndex(
Array.isArray(opParent)
? it => timestampCompare(it.at, opParent) === 0
: it => it === opParent,
);
if (opParent && parentOpIdx === -1) throw new Error("parent was not found in op log");
let idx = parentOpIdx + 1;
for (; idx < this.operations.length; idx++) {
const curr = this.operations[idx];
if (timestampCompare(curr.at, op.at) < 0) break;
}
const storedOp = op as CausalTreeOp<T>;
storedOp.parent = this.operations[parentOpIdx];
this.operations.splice(idx, 0, storedOp);
this.clock = Math.max(this.clock, storedOp.at[1]);
return idx;
}
static toWeakOp<T>(op: CausalTreeOp<T>): WeakCausalTreeOp<T> {
return { ...op, parent: op.parent?.at };
}
}