#!/usr/bin/env python3
"""
Punktfunk host unsafe census.

Reproducible, compiler-free census of `unsafe` in the HOST-scope crates.
Classifies by WHAT THE UNSAFE DOES (operations), not by block count.

Usage:  python3 unsafe_census.py <repo-root> [--json]

Method (and its error bars) are documented in the report; in short:
  1. Strip line/block comments and string/char literals (a hand-rolled scanner).
  2. Resolve each crate's module tree from lib.rs/main.rs, propagating `#[cfg(..)]`
     from `mod x;` declarations down to files, so cfg attribution is structural
     rather than path-name guessing.
  3. Find every `unsafe` token; classify its FORM (fn / impl / trait / extern / block).
  4. For every unsafe REGION (a `unsafe {}` block body, or the whole body of an
     `unsafe fn` in a file carrying `#![allow(unsafe_op_in_unsafe_fn)]`), count
     unsafe OPERATIONS by regex, deduped by source span so nested blocks don't
     double count.
"""
import os, re, sys, json, collections

# ---------------------------------------------------------------- scope

HOST_ROOTS = [
    "crates/punktfunk-host",
    "crates/punktfunk-encode-worker",
    "crates/punktfunk-tray",
    "crates/pf-capture",
    "crates/pf-encode",
    "crates/pf-inject",
    "crates/pf-vdisplay",
    "crates/pf-win-display",
    "crates/pf-zerocopy",
    "crates/pf-frame",
    "crates/pf-clipboard",
    "crates/pf-gpu",
    "crates/punktfunk-core",
    "crates/pyrowave-sys",
    "crates/libvpl-sys",
    "crates/pf-driver-proto",
    "crates/pf-host-config",
    "crates/pf-paths",
    "packaging/windows/drivers",
]

CLIENT_ONLY_ROOTS = [
    "crates/pf-client-core", "crates/pf-presenter", "crates/pf-vkdecode",
    "crates/pf-dxvadec", "crates/pf-vaadec", "crates/pf-console-ui",
    "crates/pf-bitstream", "clients",
]

EXCLUDE_DIR_PARTS = {"target", ".claude", "node_modules", ".git"}

# ---------------------------------------------------------------- lexing

def strip_noise(src: str) -> str:
    """Replace comments and string/char literal contents with spaces, preserving offsets."""
    out = list(src); i = 0; n = len(src)
    while i < n:
        c = src[i]
        if c == '/' and i + 1 < n and src[i+1] == '/':
            j = src.find('\n', i)
            j = n if j < 0 else j
            for k in range(i, j): out[k] = ' '
            i = j
        elif c == '/' and i + 1 < n and src[i+1] == '*':
            depth = 1; j = i + 2
            while j < n and depth:
                if src[j] == '/' and j+1 < n and src[j+1] == '*': depth += 1; j += 2
                elif src[j] == '*' and j+1 < n and src[j+1] == '/': depth -= 1; j += 2
                else: j += 1
            for k in range(i, min(j, n)):
                if out[k] != '\n': out[k] = ' '
            i = j
        elif c == 'r' and i + 1 < n and src[i+1] in '#"':
            j = i + 1; hashes = 0
            while j < n and src[j] == '#': hashes += 1; j += 1
            if j < n and src[j] == '"':
                term = '"' + '#' * hashes
                e = src.find(term, j + 1)
                e = n if e < 0 else e + len(term)
                for k in range(i, e):
                    if out[k] != '\n': out[k] = ' '
                i = e
            else:
                i += 1
        elif c == '"':
            j = i + 1
            while j < n:
                if src[j] == '\\': j += 2; continue
                if src[j] == '"': j += 1; break
                j += 1
            for k in range(i, min(j, n)):
                if out[k] != '\n': out[k] = ' '
            i = j
        elif c == "'":
            # char literal vs lifetime: a char lit is 'x' or '\n' — short and closed
            m = re.match(r"'(\\.|[^\\'])'", src[i:i+6])
            if m:
                for k in range(i, i + m.end()): out[k] = ' '
                i += m.end()
            else:
                i += 1
        else:
            i += 1
    return ''.join(out)

def strip_comments_only(src: str) -> str:
    out = list(src); i = 0; n = len(src)
    while i < n:
        if src[i] == '/' and i+1 < n and src[i+1] == '/':
            j = src.find('\n', i); j = n if j < 0 else j
            for k in range(i, j): out[k] = ' '
            i = j
        elif src[i] == '/' and i+1 < n and src[i+1] == '*':
            depth = 1; j = i+2
            while j < n and depth:
                if src[j] == '/' and j+1 < n and src[j+1] == '*': depth += 1; j += 2
                elif src[j] == '*' and j+1 < n and src[j+1] == '/': depth -= 1; j += 2
                else: j += 1
            for k in range(i, min(j, n)):
                if out[k] != '\n': out[k] = ' '
            i = j
        else:
            i += 1
    return ''.join(out)

def match_brace(s: str, open_idx: int):
    """open_idx points at '{'. Return index just past matching '}'."""
    depth = 0; i = open_idx; n = len(s)
    while i < n:
        if s[i] == '{': depth += 1
        elif s[i] == '}':
            depth -= 1
            if depth == 0: return i + 1
        i += 1
    return n

# ---------------------------------------------------------------- module tree / cfg

MOD_RE = re.compile(
    r'((?:#\s*\[[^\]]*\]\s*)*)'          # attributes
    r'(?:pub(?:\s*\([^)]*\))?\s+)?'      # visibility
    r'mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;'
)
CFG_RE = re.compile(r'#\s*\[\s*cfg\s*\(')

def extract_cfgs(attr_blob: str):
    """Return list of raw cfg predicate strings from an attribute blob."""
    res = []
    for m in CFG_RE.finditer(attr_blob):
        start = attr_blob.index('(', m.end() - 1)
        depth = 0; i = start
        while i < len(attr_blob):
            if attr_blob[i] == '(': depth += 1
            elif attr_blob[i] == ')':
                depth -= 1
                if depth == 0: break
            i += 1
        res.append(attr_blob[start+1:i])
    return res

FEATURES = ["nvenc", "amf-qsv", "qsv", "vulkan-encode", "pyrowave"]

def classify_cfg(preds):
    """Return (platform, features) from a list of cfg predicate strings."""
    blob = " ".join(preds)
    plat = None
    if re.search(r'\bwindows\b|target_os\s*=\s*"windows"', blob): plat = "windows"
    if re.search(r'target_os\s*=\s*"linux"', blob):
        plat = "linux" if plat is None else "multi"
    if re.search(r'target_os\s*=\s*"(macos|ios|tvos)"|\btarget_vendor\s*=\s*"apple"', blob):
        plat = "apple" if plat is None else "multi"
    feats = set()
    for f in FEATURES:
        if re.search(r'feature\s*=\s*"%s"' % re.escape(f), blob): feats.add(f)
    return plat, feats

def build_module_map(crate_src_root, entry_files):
    """Walk mod declarations from entry files; return {abspath: (platform, frozenset(features))}."""
    result = {}
    queue = []
    for e in entry_files:
        if os.path.exists(e): queue.append((e, None, frozenset()))
    seen = set()
    while queue:
        path, plat, feats = queue.pop()
        if path in seen: continue
        seen.add(path)
        result[path] = (plat, feats)
        try:
            src = strip_comments_only(open(path, encoding='utf-8', errors='replace').read())
        except OSError:
            continue
        d = os.path.dirname(path)
        stem = os.path.splitext(os.path.basename(path))[0]
        # child modules live next to lib.rs/main.rs/mod.rs, else in a dir named after the file
        childdir = d if stem in ("lib", "main", "mod") else os.path.join(d, stem)
        for m in MOD_RE.finditer(src):
            attrs, name = m.group(1), m.group(2)
            cp, cf = classify_cfg(extract_cfgs(attrs))
            nplat = plat if cp is None else (cp if plat is None or plat == cp else "multi")
            nfeat = feats | cf
            pm = re.search(r'#\s*\[\s*path\s*=\s*"([^"]+)"', attrs)
            cands = []
            if pm:
                cands.append(os.path.normpath(os.path.join(d if stem in ("lib","main","mod") else childdir, pm.group(1))))
                cands.append(os.path.normpath(os.path.join(d, pm.group(1))))
            cands += [os.path.join(childdir, name + ".rs"),
                      os.path.join(childdir, name, "mod.rs")]
            for cand in cands:
                if os.path.exists(cand):
                    queue.append((cand, nplat, nfeat))
                    break
    return result

# ---------------------------------------------------------------- operation classification

OPS = [
    # (category, regex)
    ("transmute",        re.compile(r'\btransmute(?:_copy)?\s*(?:::\s*<)?')),
    ("slice_from_raw",   re.compile(r'\bfrom_raw_parts(?:_mut)?\s*\(')),
    ("ptr_read_write",   re.compile(r'\b(?:ptr\s*::\s*)?(?:read|write)(?:_volatile|_unaligned)?\s*\(|\bcopy_nonoverlapping\s*\(|\bptr\s*::\s*copy\s*\(')),


    ("from_raw_owning",  re.compile(r'\b(?:Box|Arc|Rc|CString|OwnedHandle|OwnedFd|File|Weak)\s*::\s*from_raw\w*\s*\(|\bfrom_raw_(?:fd|handle|socket)\s*\(')),
    ("cstr_from_ptr",    re.compile(r'\bC(?:Str|String)\s*::\s*from_ptr\s*\(')),
    ("assume_init",      re.compile(r'\bassume_init\w*\s*\(')),
    ("set_len",          re.compile(r'\.\s*set_len\s*\(')),
    ("get_unchecked",    re.compile(r'\bget_unchecked\w*\s*\(')),
    ("static_mut",       re.compile(r'\bstatic\s+mut\b')),
    ("union_field",      re.compile(r'\.\s*Anonymous\b|\.\s*u\s*\.\s*\w')),
    ("env_set_var",      re.compile(r'\benv\s*::\s*(?:set_var|remove_var)\s*\(')),
]

# Third-party FFI namespaces seen in this tree. A call whose callee path is rooted
# in one of these, or a bare SCREAMING/PascalCase Win32 name, counts as an FFI call.
FFI_ROOTS = r'(?:libc|ash|vk|sys|ffi|gl|egl|cuda|cu|nvenc|amf|vpl|mfx|pw|spa|drm|av|ffmpeg|windows|Foundation|wdk|nt|ntdef|wdf|iddcx|hid|usb|xkb|opus|pyrowave)'
FFI_CALL = re.compile(
    r'\b(?:'
    r'(?:self\s*\.\s*\w+\s*\.\s*)?' + FFI_ROOTS + r'\s*(?:::|\.)\s*\w+\s*\('   # ns::fn( / ns.fn(
    r'|(?:[A-Z][a-z0-9]+){2,}\w*\s*\('                                        # PascalCase Win32
    r'|[A-Z][A-Z0-9_]{3,}\s*\('                                               # SCREAMING_CASE
    r'|(?:cu|vk|egl|gl|av|Wdf|Idd|Hid|Nv|mfx|amf|pw_|spa_|opus_|xkb_)\w+\s*\(' # api prefixes
    r')'
)
# a fn-pointer call through a dlopen'd symbol table: (self.api.foo)(..) / (f.bar)(..)
FNPTR_CALL = re.compile(r'\(\s*(?:self\s*\.\s*)?(?:\w+\s*\.\s*)+\w+\s*\)\s*\(')

class _DerefRx:
    """`*expr` in DEREF position (not multiplication, not a `*mut`/`*const` type)."""
    STAR = re.compile(r'\*')
    def finditer(self, body):
        n = len(body)
        for m in self.STAR.finditer(body):
            i = m.start()
            # a raw-pointer TYPE, not a deref
            if re.match(r'\*\s*(?:mut|const)\b', body[i:i+10]):
                continue
            # look back over whitespace to the previous significant char
            j = i - 1
            while j >= 0 and body[j] in ' \t\r\n':
                j -= 1
            prev = body[j] if j >= 0 else '{'
            # identifier / literal / closing bracket before `*` => multiplication
            if prev.isalnum() or prev in '_)]"\'':
                continue
            # `**` chain: the inner star already handled
            if prev == '*':
                continue
            # what follows must start an expression
            if not re.match(r'\*\s*(?:\(|\*|[A-Za-z_])', body[i:i+6]):
                continue
            yield type('M', (), {'start': lambda s, i=i: i, 'end': lambda s, i=i: i+1})()

DEREF_RX = _DerefRx()

# `x.as_ref()` / `x.as_mut()` as the WHOLE of a tiny unsafe block == ptr::as_ref (the raw-pointer
# one). `Option::as_ref` deeper inside a block is safe and must not be counted.
PTR_AS_REF = re.compile(r'^\s*(?:&\s*)?[\w.]+\s*\.\s*as_(?:ref|mut)\s*\(\s*\)\s*$')

def count_ops(body: str):
    """Return Counter of unsafe operations in a region body (already noise-stripped)."""
    c = collections.Counter()
    spans = []
    def take(cat, rx):
        for m in rx.finditer(body):
            if any(s <= m.start() < e for s, e in spans): continue
            spans.append((m.start(), m.end()))
            c[cat] += 1
    # order matters: specific before generic
    for cat, rx in OPS:
        take(cat, rx)
    take("ptr_deref", DEREF_RX)
    take("ptr_as_ref", PTR_AS_REF)
    take("ffi_call_fnptr", FNPTR_CALL)
    take("ffi_call", FFI_CALL)
    return c

# ---------------------------------------------------------------- per-file census

UNSAFE_TOK = re.compile(r'\bunsafe\b')
FENCE_ALLOW = re.compile(r'#!\s*\[\s*allow\s*\(\s*unsafe_op_in_unsafe_fn')
FORBID = re.compile(r'#!\s*\[\s*forbid\s*\(\s*unsafe_code')
REPRC = re.compile(r'#\s*\[\s*repr\s*\(\s*C')
# NOTE: the `const _: () = { ... };` BLOCK form is as common in this tree as the direct
# `const _: () = assert!(...)` form (18 files use it, incl. abi.rs, gamepad.rs and
# idd_push/probes.rs). An earlier version of this regex matched only the direct form and
# therefore reported 102 unguarded repr(C) declarations where the true number is 60 —
# it counted three well-guarded files as defenceless. Keep both alternatives.
LAYOUT_ASSERT = re.compile(
    r'const\s+_\s*:\s*\(\)\s*=\s*(?:assert!|\{)'
    r'|assert_eq!\s*\(\s*(?:std\s*::\s*mem\s*::\s*)?size_of'
    r'|offset_of!'
    r'|assert!\s*\(\s*(?:std\s*::\s*mem\s*::\s*)?size_of'
)
SAFETY_COMMENT = re.compile(r'//\s*SAFETY|//!\s*SAFETY|/\*\s*SAFETY')

TESTMOD = re.compile(r'#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]\s*(?:pub\s+)?mod\s+\w+\s*\{')

def test_spans(src):
    out = []
    for m in TESTMOD.finditer(src):
        ob = src.index('{', m.end() - 1)
        out.append((ob, match_brace(src, ob)))
    return out

# ⚠ The cache MUST hold a strong reference to `src`. Keying on `id(src)` alone is a real bug:
# CPython recycles object addresses, so once the previous file's source string is collected a
# NEW file's string can be allocated at the SAME address and silently hit the stale entry. That
# made `test_ops` — and therefore the shipped-code metric — vary run to run (observed 694..721
# on an unchanged tree). Storing the string beside its spans keeps the address un-recyclable
# for exactly as long as the entry is live, which is what makes `id()` a sound key here.
_TS_CACHE = {}
def in_test_mod(src, idx):
    key = id(src)
    hit = _TS_CACHE.get(key)
    if hit is None or hit[0] is not src:
        _TS_CACHE.clear()
        hit = (src, test_spans(src))
        _TS_CACHE[key] = hit
    return any(a <= idx < b for a, b in hit[1])

# --- raw handles not owned by a Drop type -------------------------------------------------
STRUCT_RE = re.compile(r'\bstruct\s+([A-Za-z_]\w*)(?:<[^>{;]*>)?\s*(\{|\()')
RAWFIELD = re.compile(r':\s*(?:\*\s*(?:mut|const)\s|HANDLE\b|HWND\b|HGLOBAL\b|HDC\b|HMODULE\b|HHOOK\b|HBITMAP\b|RawFd\b|RawHandle\b|c_int\b)')
DROP_RE = re.compile(r'\bimpl\s*(?:<[^>]*>)?\s*Drop\s+for\s+([A-Za-z_]\w*)')

def handle_ownership(src):
    """Rust-side OWNER types holding a raw handle. `#[repr(C)]` mirrors are plain FFI data,
    not owners, so they are excluded — they belong to the repr(C)-mirror metric instead."""
    drops = set(DROP_RE.findall(src))
    owned, unowned = [], []
    for m in STRUCT_RE.finditer(src):
        name, opener = m.group(1), m.group(2)
        if opener != '{':
            continue
        head = src[max(0, m.start() - 260):m.start()]
        if re.search(r'#\s*\[\s*repr\s*\(\s*C', head.split('struct')[-1] if 'struct' in head else head):
            continue
        ob = src.index('{', m.end() - 1)
        body = src[ob:match_brace(src, ob)]
        if RAWFIELD.search(body):
            (owned if name in drops else unowned).append(name)
    return owned, unowned

def census_file(path, plat, feats):
    raw = open(path, encoding='utf-8', errors='replace').read()
    src = strip_noise(raw)
    rec = {
        "path": path, "platform": plat, "features": sorted(feats),
        "forms": collections.Counter(),
        "ops": collections.Counter(),
        "fence_allow": bool(FENCE_ALLOW.search(raw)),
        "forbid": bool(FORBID.search(raw)),
        "repr_c": len(REPRC.findall(raw)),
        "layout_asserts": len(LAYOUT_ASSERT.findall(raw)),
        "safety_comments": len(SAFETY_COMMENT.findall(raw)),
        "handles_owned": [], "handles_unowned": [],
        "regions": [],
        "test_blocks": 0, "test_ops": collections.Counter(),
    }
    rec["handles_owned"], rec["handles_unowned"] = handle_ownership(src)
    if not UNSAFE_TOK.search(src):
        return rec
    covered = []  # spans of already-counted region bodies
    for m in UNSAFE_TOK.finditer(src):
        i = m.end()
        rest = src[i:i+40]
        if re.match(r'\s*fn\b', rest):
            rec["forms"]["unsafe_fn"] += 1
            continue
        if re.match(r'\s*impl\b', rest):
            # Send / Sync / other
            tail = src[i:i+200]
            if re.search(r'\bSend\b', tail): rec["forms"]["unsafe_impl_send"] += 1
            elif re.search(r'\bSync\b', tail): rec["forms"]["unsafe_impl_sync"] += 1
            else: rec["forms"]["unsafe_impl_other"] += 1
            continue
        if re.match(r'\s*trait\b', rest):
            rec["forms"]["unsafe_trait"] += 1; continue
        if re.match(r'\s*extern\b', rest):
            rec["forms"]["unsafe_extern_block"] += 1; continue
        if re.match(r'\s*\{', rest):
            ob = src.index('{', i)
            end = match_brace(src, ob)
            rec["forms"]["unsafe_block"] += 1
            _is_test = in_test_mod(src, ob)
            if _is_test: rec["test_blocks"] += 1
            if any(s <= ob < e for s, e in covered):
                rec["forms"]["unsafe_block_nested"] += 1
                continue
            covered.append((ob, end))
            body = src[ob+1:end-1]
            ops = count_ops(body)
            rec["ops"].update(ops)
            if _is_test: rec["test_ops"].update(ops)
            rec["regions"].append({"line": src[:ob].count('\n')+1,
                                   "ops": dict(ops), "len": end-ob})
            continue
        rec["forms"]["unsafe_other"] += 1
    # unsafe-fn bodies in fenced files: whole body is an implicit unsafe region
    if rec["fence_allow"]:
        for m in re.finditer(r'\bunsafe\s+(?:extern\s+"[^"]*"\s+)?fn\b', src):
            ob = src.find('{', m.end())
            if ob < 0: continue
            end = match_brace(src, ob)
            body = src[ob+1:end-1]
            # subtract what explicit inner unsafe blocks already counted
            inner = [(s, e) for s, e in covered if ob <= s < end]
            keep = body
            for s, e in sorted(inner, reverse=True):
                keep = keep[:s-ob-1] + " " * (e-s) + keep[e-ob-1:]
            ops = count_ops(keep)
            rec["ops"].update(ops)
            rec["forms"]["fenced_fn_body"] += 1
    return rec

# ---------------------------------------------------------------- driver

def walk_rs(root):
    for dirpath, dirnames, files in os.walk(root):
        dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIR_PARTS]
        for f in files:
            if f.endswith('.rs'):
                yield os.path.join(dirpath, f)

def main():
    repo = os.path.abspath(sys.argv[1])
    as_json = "--json" in sys.argv
    os.chdir(repo)
    allrecs = []
    for root in HOST_ROOTS:
        if not os.path.isdir(root): continue
        # crate entry points (a dir may hold several crates, e.g. packaging/windows/drivers)
        entries = []
        for dirpath, dirnames, files in os.walk(root):
            dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIR_PARTS]
            if 'Cargo.toml' in files:
                for e in ('src/lib.rs', 'src/main.rs'):
                    p = os.path.join(dirpath, e)
                    if os.path.exists(p): entries.append(p)
        modmap = build_module_map(root, entries)
        for p in walk_rs(root):
            plat, feats = modmap.get(p, (None, frozenset()))
            if p not in modmap:
                # not reached from a crate root: tests/, build.rs, examples/ — mark by path
                low = p.replace(os.sep, '/')
                if '/windows' in low or '_windows' in low: plat = 'windows'
                elif '/linux' in low or '_linux' in low: plat = 'linux'
            rec = census_file(p, plat, feats)
            rec["crate_root"] = root
            allrecs.append(rec)
    if as_json:
        for r in allrecs:
            r["forms"] = dict(r["forms"]); r["ops"] = dict(r["ops"]); r["test_ops"] = dict(r["test_ops"])
        print(json.dumps(allrecs, indent=1))
        return
    # ---- summary tables
    def agg(recs, key):
        d = collections.defaultdict(lambda: (collections.Counter(), collections.Counter()))
        for r in recs:
            k = key(r)
            d[k][0].update(r["forms"]); d[k][1].update(r["ops"])
        return d

    print("=" * 100)
    print("TABLE 1 — FORMS by crate root")
    print("=" * 100)
    per = agg(allrecs, lambda r: r["crate_root"])
    hdr = ["blk", "nest", "fn", "impSend", "impSync", "impOth", "extBlk", "fencedFn"]
    keys = ["unsafe_block", "unsafe_block_nested", "unsafe_fn", "unsafe_impl_send",
            "unsafe_impl_sync", "unsafe_impl_other", "unsafe_extern_block", "fenced_fn_body"]
    print(f"{'crate':<38}" + "".join(f"{h:>9}" for h in hdr))
    tot = collections.Counter()
    for k in sorted(per):
        f, _ = per[k]; tot.update(f)
        print(f"{k:<38}" + "".join(f"{f[x]:>9}" for x in keys))
    print(f"{'TOTAL':<38}" + "".join(f"{tot[x]:>9}" for x in keys))

    print()
    print("=" * 100)
    print("TABLE 2 — OPERATIONS by crate root (what the unsafe DOES)")
    print("=" * 100)
    opkeys = ["ffi_call", "ffi_call_fnptr", "ptr_deref", "ptr_as_ref", "ptr_read_write",
              "slice_from_raw", "transmute", "from_raw_owning", "cstr_from_ptr",
              "assume_init", "set_len", "get_unchecked", "static_mut", "union_field", "env_set_var"]
    print(f"{'crate':<30}" + "".join(f"{h[:13]:>14}" for h in opkeys))
    topo = collections.Counter()
    for k in sorted(per):
        _, o = per[k]; topo.update(o)
        print(f"{k:<30}" + "".join(f"{o[x]:>14}" for x in opkeys))
    print(f"{'TOTAL':<30}" + "".join(f"{topo[x]:>14}" for x in opkeys))
    tt = collections.Counter()
    for r in allrecs: tt.update(r["test_ops"])
    ffi = topo["ffi_call"] + topo["ffi_call_fnptr"]
    nonffi = sum(topo[x] for x in opkeys if not x.startswith("ffi_call"))
    print(f"\n  FFI-call operations : {ffi}")
    print(f"  NON-FFI operations  : {nonffi}   <-- PRIMARY METRIC CANDIDATE")
    print(f"  total operations    : {ffi+nonffi}   (non-FFI share {100*nonffi/max(1,ffi+nonffi):.1f}%)")
    tffi = tt["ffi_call"] + tt["ffi_call_fnptr"]
    tnon = sum(v for k, v in tt.items() if not k.startswith("ffi_call"))
    print(f"  of which in #[cfg(test)]: ffi={tffi} nonFFI={tnon}")
    print(f"  SHIPPED non-FFI ops : {nonffi - tnon}   <-- PRIMARY METRIC (test code excluded)")
    abi = [r for r in allrecs if r["path"].endswith("punktfunk-core/src/abi.rs")]
    if abi:
        a = sum(v for k, v in abi[0]["ops"].items() if not k.startswith("ffi_call"))
        print(f"  minus abi.rs (client SDK C boundary, host uses it only in tests): {nonffi - tnon - a}")

    print()
    print("=" * 100)
    print("TABLE 3 — VISIBILITY: what a Linux/macOS default-feature check CANNOT see")
    print("=" * 100)
    vis = collections.defaultdict(lambda: [0, 0, 0])  # blocks, ffi ops, nonffi ops
    for r in allrecs:
        plat = r["platform"] or "portable"
        feats = r["features"]
        bucket = plat if not feats else f"{plat}+feat({','.join(feats)})"
        vis[bucket][0] += r["forms"]["unsafe_block"]
        vis[bucket][1] += r["ops"]["ffi_call"] + r["ops"]["ffi_call_fnptr"]
        vis[bucket][2] += sum(v for k, v in r["ops"].items() if not k.startswith("ffi_call"))
    print(f"{'cfg bucket':<46}{'blocks':>9}{'ffi ops':>10}{'nonFFI ops':>12}")
    for k in sorted(vis, key=lambda x: -vis[x][0]):
        b, f, nf = vis[k]
        print(f"{k:<46}{b:>9}{f:>10}{nf:>12}")

    print()
    print("=" * 100)
    print("TABLE 4 — repr(C) mirrors and layout asserts")
    print("=" * 100)
    for r in sorted(allrecs, key=lambda r: -r["repr_c"]):
        if r["repr_c"]:
            print(f"{r['repr_c']:>4} repr(C) {r['layout_asserts']:>4} asserts  {r['path']}")

    print()
    print("=" * 100)
    print("TABLE 5 — files with an unsafe_op_in_unsafe_fn FENCE")
    print("=" * 100)
    for r in allrecs:
        if r["fence_allow"]:
            o = r["ops"]
            nf = sum(v for k, v in o.items() if not k.startswith("ffi_call"))
            print(f"  {r['path']:<62} ffi={o['ffi_call']+o['ffi_call_fnptr']:>4} nonFFI={nf:>4}")

    print()
    print("=" * 100)
    print("TABLE 5b — RAW HANDLES NOT OWNED BY A Drop TYPE")
    print("=" * 100)
    nun = nown = 0
    for r in allrecs:
        nown += len(r["handles_owned"]); nun += len(r["handles_unowned"])
    print(f"  structs holding a raw handle/pointer WITH an impl Drop : {nown}")
    print(f"  structs holding a raw handle/pointer WITHOUT impl Drop : {nun}   <-- SECONDARY METRIC")
    for r in sorted(allrecs, key=lambda r: -len(r["handles_unowned"]))[:18]:
        if r["handles_unowned"]:
            print(f"   {len(r['handles_unowned']):>3}  {r['path']}  {r['handles_unowned'][:6]}")

    print()
    print("=" * 100)
    print("TABLE 5c — TEST-ONLY unsafe blocks (excluded from any shipped-code metric)")
    print("=" * 100)
    tb = sum(r["test_blocks"] for r in allrecs)
    print(f"  unsafe blocks inside #[cfg(test)] mod : {tb} of {sum(r['forms']['unsafe_block'] for r in allrecs)}")
    for r in sorted(allrecs, key=lambda r: -r["test_blocks"])[:10]:
        if r["test_blocks"]: print(f"   {r['test_blocks']:>3}  {r['path']}")

    print()
    print("=" * 100)
    print("TABLE 6 — TOP files by NON-FFI operations")
    print("=" * 100)
    scored = []
    for r in allrecs:
        nf = sum(v for k, v in r["ops"].items() if not k.startswith("ffi_call"))
        if nf: scored.append((nf, r))
    for nf, r in sorted(scored, key=lambda t: -t[0])[:30]:
        det = ",".join(f"{k}={v}" for k, v in sorted(r["ops"].items())
                       if not k.startswith("ffi_call"))
        print(f"{nf:>5}  {r['path']}\n         {det}")

if __name__ == "__main__":
    main()
