rehashComponent

tcmts.core.hash.rehashComponent · #mmxlkcjc · term · runs on the server

uses tcmts.core.hash.hashIR, which uses node:crypto

A component's hashes again after definitions it refers to outside itself moved (swap gives each one's new hash): what analyzing its code would give, worked out from its members' IR as stored (by member index, with references inside the cycle as ["Cyc", i]). A cycle's members are ordered as the analyzer orders them, by a hash that leaves those references out, then by what they refer to; undefined if two can't be told apart that way (the analyzer orders those as written, which the IR doesn't say). index[i] is where member i goes.

import { hashIR } from "#codebase/tcmts/core/hash";
import type { IR } from "#codebase/tcmts/core/hash";

export function rehashComponent(
  irs: IR[],
  swap: (hash: string) => string,
): { irs: IR[]; componentHash: string; hashes: string[]; index: number[] } | undefined {
  const outside = (ir: IR): IR =>
    Array.isArray(ir)
      ? ir[0] === "Ref" && typeof ir[1] === "string"
        ? ["Ref", swap(ir[1])]
        : ir.map(outside)
      : ir;
  const isCyc = (ir: IR): ir is [string, string] =>
    Array.isArray(ir) && ir.length === 2 && ir[0] === "Cyc" && typeof ir[1] === "string";
  const cyc = (ir: IR, f: (i: number) => IR): IR =>
    isCyc(ir) ? f(Number(ir[1])) : Array.isArray(ir) ? ir.map((c) => cyc(c, f)) : ir;
  const cycRefs = (ir: IR, out: number[] = []): number[] => {
    if (isCyc(ir)) out.push(Number(ir[1]));
    else if (Array.isArray(ir)) for (const c of ir) cycRefs(c, out);
    return out;
  };
  const swapped = irs.map(outside);
  if (swapped.length === 1) {
    const h = hashIR(["Component", swapped[0]!]);
    return { irs: swapped, componentHash: h, hashes: [h], index: [0] };
  }
  const pre = swapped.map((ir) => hashIR(cyc(ir, () => ["Cyc", "?"])));
  const refined = swapped.map((ir, i) => hashIR([pre[i]!, ...cycRefs(ir).map((j) => pre[j]!)]));
  if (new Set(swapped.map((_, i) => `${pre[i]} ${refined[i]}`)).size < swapped.length)
    return undefined;
  const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0);
  const order = swapped
    .map((_, i) => i)
    .sort((a, b) => cmp(pre[a]!, pre[b]!) || cmp(refined[a]!, refined[b]!));
  const index: number[] = [];
  order.forEach((old, i) => (index[old] = i));
  const out = order.map((old) => cyc(swapped[old]!, (j) => ["Cyc", String(index[j])]));
  const componentHash = hashIR(["Component", ...out]);
  return {
    irs: out,
    componentHash,
    hashes: out.map((_, i) => hashIR(["Elem", componentHash, String(i)])),
    index,
  };
}

uses

used by