40 lines
1.4 KiB
TypeScript
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 };
|
|
}
|
|
}
|