#!/bin/sh
set -eu

root=$(CDPATH= cd -- "$(dirname -- "$0")/../../../.." && pwd -P)
input=$root/repo-pre-15/tests/pre15/gates/P15-006-input.json
mode=
base=
output=
oracle=

while test "$#" -gt 0; do
	case "$1" in
	--base)
		test "$#" -ge 2 || { printf 'missing --base value\n' >&2; exit 2; }
		mode=base
		base=$2
		shift 2
		;;
	--worktree)
		mode=worktree
		shift
		;;
	--output)
		test "$#" -ge 2 || { printf 'missing --output value\n' >&2; exit 2; }
		output=$2
		shift 2
		;;
	--oracle)
		test "$#" -ge 2 || { printf 'missing --oracle value\n' >&2; exit 2; }
		oracle=$2
		shift 2
		;;
	*)
		printf 'unknown argument: %s\n' "$1" >&2
		exit 2
		;;
	esac
done

test -n "$mode" || { printf 'use --base COMMIT or --worktree\n' >&2; exit 2; }
test -n "$output" || { printf 'missing --output DIR\n' >&2; exit 2; }
test -f "$input" || { printf 'missing gate input: %s\n' "$input" >&2; exit 2; }
for tool in cc git python3; do
	command -v "$tool" >/dev/null 2>&1 || {
		printf 'missing required host tool: %s\n' "$tool" >&2
		exit 2
	}
done

python3 - "$root" "$input" "$mode" "$base" "$output" "$oracle" <<'PY'
from __future__ import annotations

import hashlib
import json
from pathlib import Path
import re
import struct
import subprocess
import sys
from typing import Any


ROOT = Path(sys.argv[1])
INPUT = Path(sys.argv[2])
MODE = sys.argv[3]
REQUESTED_BASE = sys.argv[4]
OUTPUT = Path(sys.argv[5])
ORACLE = Path(sys.argv[6]) if sys.argv[6] else None
SPEC = json.loads(INPUT.read_text(encoding="ascii"))

UINT64_MAX = (1 << 64) - 1
UINT32_MAX = (1 << 32) - 1
NULL_ADDR = UINT64_MAX
META_NID = 1 << 63
MAPPED = 0x0001
META = 0x0002
PARTIAL_MAPPED = 0x0004
PARTIAL_REF = 0x0008
FRAGMENT = 0x0010
EIO = 5
ENODEV = 19
EOPNOTSUPP = 45
EOVERFLOW = 84
EINTEGRITY = 97
PLEN_PARTIAL = 1 << 27
PLEN_FMT_BIT = 28
PCLUSTER_MAX = 1024 * 1024
PLEN_MASK = (PCLUSTER_MAX << 1) - 1
PCLUSTER_MAX_DSIZE = 12 * 1024 * 1024
COMPRESSION_MAX = 4
COMPRESSION_SHIFTED = 4
COMPRESSION_INTERLACED = 5
COMPRESSION_RUNTIME_MAX = 6


def run(argv: list[str], **kwargs: Any) -> subprocess.CompletedProcess[str]:
    return subprocess.run(argv, check=False, text=True, **kwargs)


def sha256_bytes(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def canonical(value: Any) -> bytes:
    return json.dumps(
        value, ensure_ascii=True, separators=(",", ":"), sort_keys=True
    ).encode("ascii")


def extract_function(text: str, name: str) -> str:
    match = re.search(r"^" + re.escape(name) + r"\s*\(", text, re.MULTILINE)
    if not match:
        raise SystemExit(f"function not found: {name}")
    start = text.rfind("\n\n", 0, match.start()) + 2
    brace = text.find("{", match.end())
    if brace < 0:
        raise SystemExit(f"function body not found: {name}")
    depth = 0
    state = "code"
    index = brace
    while index < len(text):
        char = text[index]
        following = text[index + 1] if index + 1 < len(text) else ""
        if state == "code":
            if char == "/" and following == "*":
                state = "block"
                index += 2
                continue
            if char == "/" and following == "/":
                state = "line"
                index += 2
                continue
            if char == '"':
                state = "string"
            elif char == "'":
                state = "character"
            elif char == "{":
                depth += 1
            elif char == "}":
                depth -= 1
                if depth == 0:
                    return text[start : index + 1] + "\n"
        elif state == "block" and char == "*" and following == "/":
            state = "code"
            index += 2
            continue
        elif state == "line" and char == "\n":
            state = "code"
        elif state in {"string", "character"}:
            if char == "\\":
                index += 2
                continue
            if (state == "string" and char == '"') or (
                state == "character" and char == "'"
            ):
                state = "code"
        index += 1
    raise SystemExit(f"unterminated function: {name}")


def source_at(path: str, resolved: str) -> str:
    if MODE == "base":
        completed = run(
            ["git", "-C", str(ROOT), "show", f"{resolved}:{path}"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        if completed.returncode != 0:
            raise SystemExit(f"cannot read {path} at {resolved}: {completed.stderr}")
        return completed.stdout
    return (ROOT / path).read_text(encoding="utf-8")


if SPEC.get("schema") != 1 or SPEC.get("candidate") != "P15-006":
    raise SystemExit("invalid P15-006 input schema")
if OUTPUT.exists():
    raise SystemExit(f"refusing to overwrite output: {OUTPUT}")
OUTPUT.mkdir(parents=True)

rev = REQUESTED_BASE if MODE == "base" else "HEAD"
resolved_run = run(
    ["git", "-C", str(ROOT), "rev-parse", f"{rev}^{{commit}}"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
if resolved_run.returncode != 0:
    raise SystemExit(f"cannot resolve {rev}: {resolved_run.stderr}")
resolved = resolved_run.stdout.strip()
if MODE == "base" and resolved != SPEC["required_base"]:
    raise SystemExit(
        f"P15-006 baseline mismatch: expected {SPEC['required_base']}, got {resolved}"
    )

sources = {path: source_at(path, resolved) for path in SPEC["source_paths"]}
data_source = sources["repo-pre-15/src/data.c"]
zmap_source = sources["repo-pre-15/src/zmap.c"]
internal_source = sources["repo-pre-15/src/internal.h"]

candidate_mode = re.search(
    r"^erofs_map_blocks\s*\([^)]*struct erofs_map_blocks \*map\s*\)",
    data_source,
    re.MULTILINE | re.DOTALL,
) is not None
if candidate_mode and not re.search(
    r"^erofs_map_blocks_legacy\s*\(", data_source, re.MULTILINE
):
    raise SystemExit("candidate map adapter has no file-local legacy target")
if not candidate_mode and MODE == "worktree" and ORACLE is not None:
    raise SystemExit("candidate replay requested but common map adapter is absent")

source_hashes = {
    path: sha256_bytes(text.encode("utf-8")) for path, text in sources.items()
}
protected_hashes: dict[str, str] = {}
for key, expected_hash in SPEC["protected_function_sha256"].items():
    filename, function = key.split(":", 1)
    text = data_source if filename == "data.c" else zmap_source
    actual_hash = sha256_bytes(extract_function(text, function).encode("utf-8"))
    protected_hashes[key] = actual_hash
    if actual_hash != expected_hash:
        raise SystemExit(f"protected producer/GEOM body changed: {key}")

if candidate_mode:
    legacy_body = extract_function(data_source, "erofs_map_blocks_legacy")
    expected_legacy_hash = SPEC["candidate_legacy_map_sha256"]
else:
    legacy_body = extract_function(data_source, "erofs_map_blocks")
    expected_legacy_hash = SPEC["baseline_legacy_map_sha256"]
if sha256_bytes(legacy_body.encode("utf-8")) != expected_legacy_hash:
    raise SystemExit("plain/chunk legacy producer body changed")

map_match = re.search(
    r"^struct erofs_map_blocks \{\n.*?^\};\n",
    internal_source,
    re.MULTILINE | re.DOTALL,
)
if map_match is None:
    raise SystemExit("struct erofs_map_blocks is absent")
map_struct_hash = sha256_bytes(map_match.group(0).encode("utf-8"))
if map_struct_hash != SPEC["map_struct_sha256"]:
    raise SystemExit("map object fields or ordering changed before B07 producers")


def tuple_record(**values: int) -> dict[str, int]:
    record = {
        "m_la": 0,
        "m_pa": 0,
        "m_llen": 0,
        "m_plen": 0,
        "m_deviceid": 0,
        "m_flags": 0,
        "m_algorithmformat": 0,
        "errno": 0,
        "acquire_count": 0,
        "release_count": 0,
    }
    record.update(values)
    return record


def data_case(
    case_id: str,
    category: str,
    subcategories: list[str],
    request: int,
    *,
    layout: int = 0,
    size: int = 12288,
    startblk: int = 32,
    inode_off: int = 0,
    inode_isize: int = 64,
    xattr_isize: int = 0,
    chunkbits: int = 12,
    chunkformat: int = 0,
    nid: int = 1,
    block_size: int = 4096,
    blkszbits: int = 12,
    blocks: int = 1 << 20,
    device_id_mask: int = 0xFFFF,
    metabox_present: bool = False,
    metabox_size: int = 0,
    reader_base: int = 0,
    reader_bytes: bytes = b"",
    reader_error: bool = False,
) -> dict[str, Any]:
    return {
        "id": case_id,
        "engine": "data",
        "category": category,
        "subcategories": subcategories,
        "request": request,
        "sbi": {
            "block_size": block_size,
            "blkszbits": blkszbits,
            "blocks": blocks,
            "device_id_mask": device_id_mask,
            "metabox_present": metabox_present,
            "metabox_size": metabox_size,
        },
        "inode": {
            "nid": nid,
            "size": size,
            "datalayout": layout,
            "startblk": startblk,
            "inode_off": inode_off,
            "inode_isize": inode_isize,
            "xattr_isize": xattr_isize,
            "chunkbits": chunkbits,
            "chunkformat": chunkformat,
        },
        "reader": {
            "base": reader_base,
            "bytes": reader_bytes,
            "error": reader_error,
        },
    }


def raw32(block: int) -> bytes:
    return struct.pack("<I", block & UINT32_MAX)


def index_entry(block: int, device: int, use_48bit: bool) -> bytes:
    high = (block >> 32) & 0xFFFF if use_48bit else 0
    return struct.pack("<HHI", high, device & 0xFFFF, block & UINT32_MAX)


def make_data_cases() -> list[dict[str, Any]]:
    cases = [
        data_case("plain-start", "plain", ["plain-boundaries"], 0),
        data_case("plain-block-last", "plain", ["plain-boundaries"], 4095),
        data_case("plain-second-start", "plain", ["plain-boundaries"], 4096),
        data_case("plain-final-byte", "plain", ["plain-boundaries"], 12287),
        data_case("plain-eof", "post-EOF", ["plain-boundaries"], 12288),
        data_case("plain-post-eof", "post-EOF", ["plain-boundaries"], 12305),
        data_case("plain-empty", "post-EOF", ["plain-boundaries"], 0, size=0),
        data_case("plain-hole-start", "hole", ["plain-boundaries"], 0, startblk=NULL_ADDR),
        data_case("plain-hole-edge", "hole", ["plain-boundaries"], 4095, startblk=NULL_ADDR),
        data_case(
            "plain-shift-overflow", "overflow", ["plain-overflow"], 0,
            startblk=(UINT64_MAX >> 12) + 1,
        ),
        data_case(
            "plain-add-overflow", "overflow", ["plain-overflow"], 4096,
            startblk=UINT64_MAX >> 12,
        ),
        data_case("plain-invalid-layout", "invalid", ["plain-boundaries"], 0, layout=7),
        data_case("inline-head-start", "inline", ["inline-boundaries"], 0, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-head-last", "inline", ["inline-boundaries"], 4095, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-tail-start", "inline", ["inline-boundaries"], 4096, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-tail-middle", "inline", ["inline-boundaries"], 4500, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-final-byte", "inline", ["inline-boundaries"], 4999, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-eof", "post-EOF", ["inline-boundaries"], 5000, layout=2, size=5000, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-head-hole", "hole", ["inline-boundaries"], 0, layout=2, size=5000, startblk=NULL_ADDR, inode_off=8192, xattr_isize=32),
        data_case("inline-one-block", "inline", ["inline-boundaries"], 0, layout=2, size=4096, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-one-byte-tail-head", "inline", ["inline-boundaries"], 4095, layout=2, size=4097, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-one-byte-tail", "inline", ["inline-boundaries"], 4096, layout=2, size=4097, startblk=40, inode_off=8192, xattr_isize=32),
        data_case("inline-inode-add-overflow", "overflow", ["inline-overflow"], 4096, layout=2, size=5000, inode_off=UINT64_MAX - 31, inode_isize=64),
        data_case("inline-xattr-add-overflow", "overflow", ["inline-overflow"], 4096, layout=2, size=5000, inode_off=UINT64_MAX - 100, inode_isize=50, xattr_isize=100),
        data_case("inline-offset-add-overflow", "overflow", ["inline-overflow"], 6000, layout=2, size=8192, inode_off=UINT64_MAX - 1000),
    ]

    cases.extend([
        data_case("chunk-raw32-start", "chunk", ["chunk-raw32"], 0, layout=4, reader_base=64, reader_bytes=raw32(77)),
        data_case("chunk-raw32-last", "chunk", ["chunk-raw32"], 4095, layout=4, reader_base=64, reader_bytes=raw32(77)),
        data_case("chunk-raw32-next", "chunk", ["chunk-raw32"], 4096, layout=4, reader_base=68, reader_bytes=raw32(78)),
        data_case("chunk-raw32-final", "chunk", ["chunk-raw32"], 9999, layout=4, size=10000, reader_base=72, reader_bytes=raw32(79)),
        data_case("chunk-raw32-hole", "hole", ["chunk-holes", "chunk-raw32"], 512, layout=4, reader_base=64, reader_bytes=raw32(UINT32_MAX)),
        data_case("chunk-index32-mask", "multidevice", ["chunk-index32", "device-mask"], 0, layout=4, chunkformat=0x20, device_id_mask=3, reader_base=64, reader_bytes=index_entry(77, 7, False)),
        data_case("chunk-index32-middle", "chunk", ["chunk-index32"], 9000, layout=4, size=20000, chunkbits=13, chunkformat=0x20, reader_base=72, reader_bytes=index_entry(77, 0, False)),
        data_case("chunk-index32-hole", "hole", ["chunk-holes", "chunk-index32"], 0, layout=4, chunkformat=0x20, reader_base=64, reader_bytes=index_entry(UINT32_MAX, 9, False)),
        data_case("chunk-index48-start", "multidevice", ["chunk-index48"], 0, layout=4, chunkformat=0x60, reader_base=64, reader_bytes=index_entry((1 << 32) + 5, 2, True)),
        data_case("chunk-index48-middle", "multidevice", ["chunk-index48"], 12345, layout=4, size=24576, chunkbits=13, chunkformat=0x60, reader_base=72, reader_bytes=index_entry((1 << 32) + 5, 2, True)),
        data_case("chunk-index48-hole", "hole", ["chunk-holes", "chunk-index48"], 0, layout=4, chunkformat=0x60, reader_base=64, reader_bytes=index_entry((1 << 48) - 1, 2, True)),
        data_case("chunk-device-mask-zero", "multidevice", ["chunk-index48", "device-mask"], 0, layout=4, chunkformat=0x60, device_id_mask=0, reader_base=64, reader_bytes=index_entry(81, 0xFFFF, True)),
        data_case("chunk-eof", "post-EOF", ["chunk-index32"], 4096, layout=4, size=4096, chunkformat=0x20),
        data_case("chunk-inode-add-overflow", "overflow", ["chunk-overflow", "cleanup-before-acquire"], 0, layout=4, inode_off=UINT64_MAX - 10, inode_isize=20, chunkformat=0x20),
        data_case("chunk-xattr-add-overflow", "overflow", ["chunk-overflow", "cleanup-before-acquire"], 0, layout=4, inode_off=UINT64_MAX - 100, inode_isize=50, xattr_isize=100, chunkformat=0x20),
        data_case("chunk-align-overflow", "overflow", ["chunk-overflow", "cleanup-before-acquire"], 0, layout=4, inode_off=UINT64_MAX - 3, inode_isize=0, chunkformat=0x20),
        data_case("chunk-index-multiply-overflow", "overflow", ["chunk-overflow", "cleanup-before-acquire"], UINT64_MAX - 1, layout=4, size=UINT64_MAX, chunkbits=0, chunkformat=0x20),
        data_case("chunk-image-size-overflow", "overflow", ["chunk-overflow", "cleanup-before-acquire"], 0, layout=4, blocks=(UINT64_MAX >> 17) + 1, block_size=1 << 17, blkszbits=17, chunkformat=0x20),
        data_case("chunk-primary-bounds", "bounds", ["chunk-bounds", "cleanup-before-acquire"], 0, layout=4, inode_off=4096, inode_isize=0, blocks=1, chunkformat=0x20),
        data_case("chunk-primary-tail-bounds", "bounds", ["chunk-bounds", "cleanup-before-acquire"], 0, layout=4, inode_off=4090, inode_isize=0, blocks=1, chunkformat=0x20),
        data_case("chunk-metabox-missing", "bounds", ["chunk-bounds", "cleanup-before-acquire"], 0, layout=4, nid=META_NID | 2, chunkformat=0x20),
        data_case("chunk-metabox-bounds", "bounds", ["chunk-bounds", "cleanup-before-acquire"], 0, layout=4, nid=META_NID | 2, inode_off=64, chunkformat=0x20, metabox_present=True, metabox_size=71),
        data_case("chunk-reader-short", "bounds", ["chunk-bounds", "cleanup-before-acquire"], 0, layout=4, chunkformat=0x20, reader_base=64, reader_bytes=b"\0\0\0\0", reader_error=True),
        data_case("chunk-shift-overflow", "overflow", ["chunk-overflow", "cleanup-after-acquire"], 0, layout=4, chunkformat=0x60, block_size=1 << 17, blkszbits=17, reader_base=64, reader_bytes=index_entry(1 << 47, 0, True)),
        data_case("chunk-offset-overflow", "overflow", ["chunk-overflow", "cleanup-after-acquire"], 4096, layout=4, size=8192, chunkbits=13, chunkformat=0x60, reader_base=64, reader_bytes=index_entry(UINT64_MAX >> 12, 4, True)),
    ])
    return cases


def z_advise_for_recsize(recsz: int, extra: int = 0) -> int:
    shift = {4: 0, 8: 1, 16: 2, 32: 3}[recsz]
    return 1 | (shift << 1) | extra


def zmap_case(
    case_id: str,
    category: str,
    subcategories: list[str],
    request: int,
    *,
    recsz: int = 16,
    records: list[dict[str, int]] | None = None,
    base_pa: int = 0,
    size: int = 4096,
    inode_off: int = 0,
    inode_isize: int = 0,
    xattr_isize: int = 0,
    z_extents: int | None = None,
    z_advise_extra: int = 0,
    z_lclusterbits: int = 12,
    available_compr_algs: int = 1,
    packed_size: int = 1 << 20,
    packed_nid: int = 999,
    reader_error: bool = False,
) -> dict[str, Any]:
    records = records or []
    if z_extents is None:
        z_extents = len(records)
    return {
        "id": case_id,
        "engine": "zmap",
        "category": category,
        "subcategories": subcategories,
        "request": request,
        "recsz": recsz,
        "base_pa": base_pa,
        "records": records,
        "reader_error": reader_error,
        "sbi": {
            "block_size": 4096,
            "blkszbits": 12,
            "available_compr_algs": available_compr_algs,
            "packed_size": packed_size,
            "packed_nid": packed_nid,
        },
        "inode": {
            "nid": 2,
            "size": size,
            "datalayout": 1,
            "inode_off": inode_off,
            "inode_isize": inode_isize,
            "xattr_isize": xattr_isize,
            "z_advise": z_advise_for_recsize(recsz, z_advise_extra),
            "z_lclusterbits": z_lclusterbits,
            "z_extents": z_extents,
        },
    }


def rec(plen: int, pstart: int, lstart: int = 0) -> dict[str, int]:
    return {"plen": plen, "pstart": pstart, "lstart": lstart}


def make_zmap_cases() -> list[dict[str, Any]]:
    compressed_1k = (1 << PLEN_FMT_BIT) | 1024
    compressed_2k = (1 << PLEN_FMT_BIT) | 2048
    ext4 = [rec(compressed_1k, 0), rec(compressed_2k, 0)]
    ext8 = [rec(compressed_1k, 0x20000), rec(compressed_2k, 0x30000)]
    ext16 = [
        rec(compressed_1k, 0x40000, 0),
        rec(compressed_2k, 0x50000, 4096),
        rec(compressed_1k, 0x60000, 8192),
    ]
    cases = [
        zmap_case("extent4-start", "compressed", ["extent-4", "compressed-flags"], 0, recsz=4, records=ext4, base_pa=0x10000, size=8192),
        zmap_case("extent4-first-last", "compressed", ["extent-4"], 4095, recsz=4, records=ext4, base_pa=0x10000, size=8192),
        zmap_case("extent4-second-start", "compressed", ["extent-4"], 4096, recsz=4, records=ext4, base_pa=0x10000, size=8192),
        zmap_case("extent4-second-middle", "compressed", ["extent-4"], 5000, recsz=4, records=ext4, base_pa=0x10000, size=8192),
        zmap_case("extent8-start", "compressed", ["extent-8", "compressed-flags"], 0, recsz=8, records=ext8, size=8192),
        zmap_case("extent8-second-start", "compressed", ["extent-8"], 4096, recsz=8, records=ext8, size=8192),
        zmap_case("extent8-hole", "hole", ["extent-8", "compressed-flags"], 4096, recsz=8, records=[ext8[0], rec(0, 0, 4096)], size=8192),
        zmap_case("extent16-start", "compressed", ["extent-16", "compressed-flags"], 0, records=ext16, size=12288),
        zmap_case("extent16-first-last", "compressed", ["extent-16"], 4095, records=ext16, size=12288),
        zmap_case("extent16-second-start", "compressed", ["extent-16"], 4096, records=ext16, size=12288),
        zmap_case("extent16-second-middle", "compressed", ["extent-16"], 7000, records=ext16, size=12288),
        zmap_case("extent16-third-start", "compressed", ["extent-16"], 8192, records=ext16, size=12288),
        zmap_case("extent16-physical-48bit", "compressed", ["extent-16"], 0, records=[rec(compressed_1k, (1 << 40) + 9, 0)]),
        zmap_case("extent32-logical-64bit", "compressed", ["extent-32"], 1 << 32, recsz=32, records=[rec(compressed_1k, 0x70000, 0), rec(compressed_2k, 0x80000, 1 << 32)], size=(1 << 32) + 4096),
        zmap_case("extent-partial-reference", "partial-reference", ["extent-16", "compressed-flags"], 0, records=[rec(PLEN_PARTIAL | compressed_1k, 0x90000, 0)]),
        zmap_case("extent-fragment-tail", "fragment", ["extent-16", "compressed-flags"], 4096, records=[rec(8192, 0, 4096)], size=6144, z_advise_extra=0x20),
        zmap_case("extent-interlaced-shifted", "compressed", ["extent-16", "compressed-flags"], 0, records=[rec(4096, 0xA0000, 0)], z_advise_extra=0x10),
        zmap_case("extent-algorithm-one", "compressed", ["extent-16", "compressed-flags"], 0, records=[rec((2 << PLEN_FMT_BIT) | 1024, 0xB0000, 0)], available_compr_algs=3),
        zmap_case("extent-algorithm-unavailable", "invalid", ["compressed-invalid", "cleanup-after-acquire"], 0, records=[rec((2 << PLEN_FMT_BIT) | 1024, 0xB0000, 0)], available_compr_algs=1),
        zmap_case("extent-algorithm-runtime-invalid", "invalid", ["compressed-invalid", "cleanup-after-acquire"], 0, records=[rec((7 << PLEN_FMT_BIT) | 1024, 0xC0000, 0)]),
        zmap_case("extent-plen-too-large", "invalid", ["compressed-invalid", "cleanup-after-acquire"], 0, records=[rec(PCLUSTER_MAX + 1, 0xD0000, 0)]),
        zmap_case("extent-full-short-logical", "invalid", ["compressed-invalid", "cleanup-after-acquire"], 0, records=[rec((1 << PLEN_FMT_BIT) | 8192, 0xE0000, 0)]),
        zmap_case("extent-pa-add-overflow", "overflow", ["compressed-overflow", "cleanup-after-acquire"], 0, records=[rec(compressed_1k, UINT64_MAX - 511, 0)]),
        zmap_case("extent-physical-48bit-limit", "bounds", ["compressed-bounds", "cleanup-after-acquire"], 0, records=[rec(compressed_1k, (1 << 60), 0)]),
        zmap_case("extent-reader-short", "bounds", ["compressed-bounds", "cleanup-before-acquire"], 0, records=[rec(compressed_1k, 0xF0000, 0)], reader_error=True),
        zmap_case("extent-table-overflow", "overflow", ["compressed-overflow", "cleanup-before-acquire"], 0, records=[rec(compressed_1k, 0x100000, 0)], inode_off=UINT64_MAX - 3),
        zmap_case("extent-record-multiply-overflow", "overflow", ["compressed-overflow", "cleanup-before-acquire"], 0, recsz=32, records=[], size=UINT64_MAX, z_extents=UINT64_MAX),
        zmap_case("extent-fragment-bounds", "bounds", ["compressed-bounds", "cleanup-after-acquire"], 4096, records=[rec(8192, 0, 4096)], size=6144, z_advise_extra=0x20, packed_size=1024),
        zmap_case("compressed-eof", "post-EOF", ["extent-16"], 4096, records=[rec(compressed_1k, 0x110000, 0)]),
        zmap_case("compressed-post-eof", "post-EOF", ["extent-16"], 4113, records=[rec(compressed_1k, 0x110000, 0)]),
    ]
    return cases


def make_device_cases() -> list[dict[str, Any]]:
    def device(
        case_id: str,
        *,
        pa: int,
        plen: int,
        deviceid: int,
        primary_blocks: int = 1024,
        extra: list[dict[str, Any]] | None = None,
        flatdev: bool = False,
        devs_present: bool = True,
    ) -> dict[str, Any]:
        return {
            "id": case_id,
            "subcategories": ["device-resolution"],
            "input": {"pa": pa, "plen": plen, "deviceid": deviceid},
            "sbi": {
                "blkszbits": 12,
                "primary_blocks": primary_blocks,
                "extra": extra or [],
                "flatdev": flatdev,
                "devs_present": devs_present,
            },
        }

    good = lambda blocks, uniaddr=0: {
        "blocks": blocks, "uniaddr": uniaddr, "provider": True
    }
    missing = lambda blocks, uniaddr=0: {
        "blocks": blocks, "uniaddr": uniaddr, "provider": False
    }
    return [
        device("device-primary", pa=4096, plen=512, deviceid=0),
        device("device-explicit-one", pa=4096, plen=512, deviceid=1, extra=[good(32)]),
        device("device-explicit-two", pa=8192, plen=1024, deviceid=2, extra=[good(32), good(64)]),
        device("device-id-out-of-range", pa=0, plen=1, deviceid=3, extra=[good(32), good(32)]),
        device("device-table-missing", pa=0, plen=1, deviceid=1, extra=[good(32)], devs_present=False),
        device("device-explicit-bounds", pa=4096 * 32 - 128, plen=256, deviceid=1, extra=[good(32)]),
        device("device-explicit-provider-missing", pa=0, plen=1, deviceid=1, extra=[missing(32)]),
        device("device-flat-explicit", pa=512, plen=512, deviceid=1, extra=[good(32, 100)], flatdev=True),
        device("device-flat-add-overflow", pa=UINT64_MAX - 1024, plen=512, deviceid=1, extra=[good(32, 1)], flatdev=True),
        device("device-flat-primary", pa=4096, plen=512, deviceid=0, primary_blocks=32, extra=[good(32, 100)], flatdev=True),
        device("device-flat-implicit-extra", pa=100 * 4096 + 512, plen=512, deviceid=0, primary_blocks=32, extra=[good(32, 100)], flatdev=True),
        device("device-separate-implicit-extra", pa=100 * 4096 + 512, plen=512, deviceid=0, primary_blocks=32, extra=[good(32, 100)]),
        device("device-blocks-shift-overflow", pa=0, plen=1, deviceid=1, extra=[good((UINT64_MAX >> 12) + 1)]),
    ]


def add_u64(left: int, right: int) -> tuple[bool, int]:
    value = left + right
    return (value > UINT64_MAX, value & UINT64_MAX)


def roundup_u64(value: int, alignment: int) -> tuple[bool, int]:
    overflow, rounded = add_u64(value, alignment - 1)
    return overflow, rounded & ~(alignment - 1)


def model_data(case: dict[str, Any]) -> dict[str, int]:
    request = case["request"]
    inode = case["inode"]
    sbi = case["sbi"]
    result = tuple_record(m_la=request)
    if request >= inode["size"]:
        return result
    remain = inode["size"] - request
    layout = inode["datalayout"]
    block_size = sbi["block_size"]
    if layout == 0:
        run_len = min(remain, block_size - (request & (block_size - 1)))
        result["m_llen"] = result["m_plen"] = run_len
        if inode["startblk"] == NULL_ADDR:
            return result
        if inode["startblk"] > (UINT64_MAX >> sbi["blkszbits"]):
            result["errno"] = EINTEGRITY
            return result
        overflow, pa = add_u64(inode["startblk"] << sbi["blkszbits"], request)
        if overflow:
            result["errno"] = EINTEGRITY
            return result
        result["m_pa"] = pa
        result["m_flags"] = MAPPED
        return result
    if layout == 2:
        tail_start = 0 if inode["size"] == 0 else (
            (inode["size"] + block_size - 1) & ~(block_size - 1)
        ) - block_size
        if request < tail_start:
            run_len = min(
                remain,
                tail_start - request,
                block_size - (request & (block_size - 1)),
            )
            result["m_llen"] = result["m_plen"] = run_len
            if inode["startblk"] == NULL_ADDR:
                return result
            if inode["startblk"] > (UINT64_MAX >> sbi["blkszbits"]):
                result["errno"] = EINTEGRITY
                return result
            overflow, pa = add_u64(
                inode["startblk"] << sbi["blkszbits"], request
            )
            if overflow:
                result["errno"] = EINTEGRITY
                return result
            result["m_pa"] = pa
            result["m_flags"] = MAPPED
            return result
        run_len = min(
            remain, block_size - ((request - tail_start) & (block_size - 1))
        )
        result["m_llen"] = result["m_plen"] = run_len
        overflow, pa = add_u64(inode["inode_off"], inode["inode_isize"])
        if not overflow:
            overflow, pa = add_u64(pa, inode["xattr_isize"])
        if not overflow:
            overflow, pa = add_u64(pa, request - tail_start)
        if overflow:
            result["m_pa"] = pa
            result["errno"] = EINTEGRITY
            return result
        result["m_pa"] = pa
        result["m_flags"] = MAPPED | META
        return result
    if layout != 4:
        result["errno"] = EOPNOTSUPP
        return result

    chunk_size = 1 << inode["chunkbits"]
    chunk_index = request >> inode["chunkbits"]
    chunk_offset = request & (chunk_size - 1)
    entry_size = 8 if inode["chunkformat"] & 0x20 else 4
    overflow, index_base = add_u64(inode["inode_off"], inode["inode_isize"])
    if overflow:
        result["errno"] = EOVERFLOW
        return result
    overflow, index_base = add_u64(index_base, inode["xattr_isize"])
    if overflow:
        result["errno"] = EOVERFLOW
        return result
    overflow, index_base = roundup_u64(index_base, entry_size)
    if overflow:
        result["errno"] = EOVERFLOW
        return result
    if chunk_index > (UINT64_MAX - index_base) // entry_size:
        result["errno"] = EOVERFLOW
        return result
    index_offset = index_base + chunk_index * entry_size
    if inode["nid"] & META_NID:
        if (
            not sbi["metabox_present"]
            or index_offset > sbi["metabox_size"]
            or entry_size > sbi["metabox_size"] - index_offset
        ):
            result["errno"] = EINTEGRITY
            return result
    else:
        if sbi["blocks"] > (UINT64_MAX >> sbi["blkszbits"]):
            result["errno"] = EOVERFLOW
            return result
        image_size = sbi["blocks"] << sbi["blkszbits"]
        if index_offset > image_size or entry_size > image_size - index_offset:
            result["errno"] = EINTEGRITY
            return result
    reader = case["reader"]
    raw = reader["bytes"]
    if (
        reader["error"]
        or index_offset < reader["base"]
        or entry_size > len(raw)
        or index_offset - reader["base"] > len(raw) - entry_size
    ):
        result["errno"] = EIO
        return result
    entry = raw[index_offset - reader["base"] : index_offset - reader["base"] + entry_size]
    result["acquire_count"] = result["release_count"] = 1
    if inode["chunkformat"] & 0x20:
        high, raw_device, low = struct.unpack("<HHI", entry)
        block = low | ((high << 32) if inode["chunkformat"] & 0x40 else 0)
        address_mask = (1 << 48) - 1 if inode["chunkformat"] & 0x40 else UINT32_MAX
    else:
        block = struct.unpack("<I", entry)[0]
        raw_device = 0
        address_mask = UINT32_MAX
    run_len = min(chunk_size - chunk_offset, inode["size"] - request)
    result["m_llen"] = result["m_plen"] = run_len
    if not ((block ^ NULL_ADDR) & address_mask):
        return result
    result["m_deviceid"] = raw_device & sbi["device_id_mask"]
    if block > (UINT64_MAX >> sbi["blkszbits"]):
        result["errno"] = EOVERFLOW
        return result
    overflow, pa = add_u64(block << sbi["blkszbits"], chunk_offset)
    if overflow:
        result["errno"] = EOVERFLOW
        return result
    result["m_pa"] = pa
    result["m_flags"] = MAPPED
    return result


def extent_table_pos(inode: dict[str, int], recsz: int) -> tuple[int, int]:
    overflow, pos = add_u64(inode["inode_off"], inode["inode_isize"])
    if not overflow:
        overflow, pos = add_u64(pos, inode["xattr_isize"])
    if not overflow:
        overflow, pos = roundup_u64(pos, 8)
    if not overflow:
        overflow, pos = add_u64(pos, 8)
    if not overflow:
        overflow, pos = roundup_u64(pos, recsz)
    return (EINTEGRITY if overflow else 0, pos)


def record_pos(
    inode: dict[str, int], table_pos: int, recsz: int, index: int
) -> tuple[int, int]:
    if index >= inode["z_extents"] or index > UINT64_MAX // recsz:
        return EINTEGRITY, 0
    overflow, pos = add_u64(table_pos, index * recsz)
    return (EINTEGRITY if overflow else 0, pos)


def read_extent(
    case: dict[str, Any], table_pos: int, recsz: int, index: int, counters: dict[str, int]
) -> tuple[int, dict[str, int] | None]:
    error, _ = record_pos(case["inode"], table_pos, recsz, index)
    if error:
        return error, None
    if case["reader_error"] or index >= len(case["records"]):
        return EIO, None
    counters["acquire"] += 1
    counters["release"] += 1
    return 0, case["records"][index]


def model_zmap(case: dict[str, Any]) -> dict[str, int]:
    request = case["request"]
    inode = case["inode"]
    sbi = case["sbi"]
    result = tuple_record(m_la=request)
    if request >= inode["size"]:
        result["m_llen"] = request + 1 - inode["size"]
        result["m_la"] = inode["size"]
        return result
    recsz = case["recsz"]
    error, table_pos = extent_table_pos(inode, recsz)
    if error:
        result["errno"] = error
        return result
    pos = table_pos
    logical_end = inode["size"]
    cluster_size = 1 << inode["z_lclusterbits"]
    counters = {"acquire": 0, "release": 0}
    result["m_flags"] = 0
    last = False
    lstart = logical_end
    if recsz <= 8:
        if recsz <= 4:
            if case["reader_error"]:
                result["errno"] = EIO
                return result
            counters["acquire"] += 1
            counters["release"] += 1
            pa = case["base_pa"]
            overflow, pos = add_u64(pos, 8)
            if overflow:
                result["errno"] = EINTEGRITY
                return result
            lstart = 0
            extent_index = 0
        else:
            lstart = request & ~(cluster_size - 1)
            extent_index = lstart >> inode["z_lclusterbits"]
            pa = NULL_ADDR
        while True:
            error, extent = read_extent(case, pos, recsz, extent_index, counters)
            if error:
                result["errno"] = error
                result["acquire_count"] = counters["acquire"]
                result["release_count"] = counters["release"]
                return result
            assert extent is not None
            result["m_plen"] = extent["plen"]
            if pa != NULL_ADDR:
                result["m_pa"] = pa
                overflow, pa = add_u64(pa, result["m_plen"] & PLEN_MASK)
                if overflow:
                    result["errno"] = EINTEGRITY
                    result["acquire_count"] = counters["acquire"]
                    result["release_count"] = counters["release"]
                    return result
            else:
                result["m_pa"] = extent["pstart"] & UINT32_MAX
            if extent_index == UINT64_MAX:
                result["errno"] = EINTEGRITY
                result["acquire_count"] = counters["acquire"]
                result["release_count"] = counters["release"]
                return result
            extent_index += 1
            overflow, next_lstart = add_u64(lstart, cluster_size)
            if overflow:
                result["errno"] = EINTEGRITY
                result["acquire_count"] = counters["acquire"]
                result["release_count"] = counters["release"]
                return result
            lstart = next_lstart
            if lstart > request:
                break
        overflow, rounded_end = roundup_u64(logical_end, cluster_size)
        if overflow:
            result["errno"] = EINTEGRITY
            result["acquire_count"] = counters["acquire"]
            result["release_count"] = counters["release"]
            return result
        last = lstart >= rounded_end
        logical_end = min(lstart, logical_end)
        lstart -= cluster_size
    else:
        left, right = 0, inode["z_extents"]
        while left < right:
            middle = left + (right - left) // 2
            error, extent = read_extent(case, table_pos, recsz, middle, counters)
            if error:
                result["errno"] = error
                result["acquire_count"] = counters["acquire"]
                result["release_count"] = counters["release"]
                return result
            assert extent is not None
            logical = extent["lstart"] & (UINT64_MAX if recsz == 32 else UINT32_MAX)
            physical = extent["pstart"]
            if logical > request:
                right = middle
                if logical > logical_end:
                    result["errno"] = EINTEGRITY
                    result["acquire_count"] = counters["acquire"]
                    result["release_count"] = counters["release"]
                    return result
                logical_end = logical
            else:
                left = middle + 1
                if request == logical:
                    right = min(left + 1, right)
                lstart = logical
                result["m_plen"] = extent["plen"]
                result["m_pa"] = physical
        last = left >= inode["z_extents"]

    if lstart < logical_end:
        result["m_la"] = lstart
        if last and inode["z_advise"] & 0x20:
            result["m_flags"] = FRAGMENT
            fragment_offset = result["m_plen"]
            if recsz > 4:
                fragment_offset |= result["m_pa"] << 32
        elif result["m_plen"] & PLEN_MASK:
            result["m_flags"] = MAPPED
            fmt = result["m_plen"] >> PLEN_FMT_BIT
            if result["m_plen"] & PLEN_PARTIAL:
                result["m_flags"] |= PARTIAL_REF
            result["m_plen"] &= PLEN_MASK
            if fmt:
                result["m_algorithmformat"] = fmt - 1
            elif (
                inode["z_advise"] & 0x10
                and ((result["m_pa"] | result["m_plen"]) & (sbi["block_size"] - 1)) == 0
            ):
                result["m_algorithmformat"] = COMPRESSION_INTERLACED
            else:
                result["m_algorithmformat"] = COMPRESSION_SHIFTED
    result["m_llen"] = logical_end - result["m_la"]
    result["acquire_count"] = counters["acquire"]
    result["release_count"] = counters["release"]

    sanity_error = 0
    if result["m_flags"] & FRAGMENT:
        if (
            result["m_flags"] & (MAPPED | META)
            or sbi["packed_nid"] == inode["nid"]
            or fragment_offset > sbi["packed_size"]
            or result["m_llen"] > sbi["packed_size"] - fragment_offset
        ):
            sanity_error = EINTEGRITY
    elif result["m_flags"] & MAPPED:
        algorithm = result["m_algorithmformat"] & 0xFF
        if algorithm >= COMPRESSION_RUNTIME_MAX:
            sanity_error = EOPNOTSUPP
        elif algorithm < COMPRESSION_MAX:
            if not (sbi["available_compr_algs"] & (1 << algorithm)):
                sanity_error = EINTEGRITY
            elif not (result["m_flags"] & (PARTIAL_MAPPED | PARTIAL_REF)) and result["m_llen"] < result["m_plen"]:
                sanity_error = EINTEGRITY
        elif result["m_llen"] > result["m_plen"]:
            sanity_error = EINTEGRITY
        if not sanity_error and (
            result["m_plen"] > PCLUSTER_MAX
            or result["m_llen"] > PCLUSTER_MAX_DSIZE
        ):
            sanity_error = EOPNOTSUPP
        if not sanity_error and not (result["m_flags"] & META):
            overflow, physical_end = add_u64(result["m_pa"], result["m_plen"])
            if overflow or (physical_end >> sbi["blkszbits"]) >= (1 << 48):
                sanity_error = EINTEGRITY
    if sanity_error:
        result["errno"] = sanity_error
        result["m_llen"] = 0
    return result


def model_device(case: dict[str, Any]) -> dict[str, int]:
    state = case["sbi"]
    map_state = dict(case["input"])
    extras = state["extra"] if state["devs_present"] else None
    selected = 0

    def check_range(device: dict[str, Any], offset: int, length: int) -> int:
        if device["blocks"] > (UINT64_MAX >> state["blkszbits"]):
            return EINTEGRITY
        overflow, end = add_u64(offset, length)
        if overflow:
            return EINTEGRITY
        return EINTEGRITY if end > device["blocks"] << state["blkszbits"] else 0

    primary = {"blocks": state["primary_blocks"], "uniaddr": 0, "provider": True}
    device_id = map_state["deviceid"]
    if device_id:
        if extras is None or device_id > len(extras):
            error = ENODEV
        else:
            chosen = extras[device_id - 1]
            error = check_range(chosen, map_state["pa"], map_state["plen"])
            if not error and state["flatdev"]:
                if chosen["uniaddr"] > (UINT64_MAX >> state["blkszbits"]):
                    error = EINTEGRITY
                else:
                    overflow, mapped = add_u64(
                        map_state["pa"], chosen["uniaddr"] << state["blkszbits"]
                    )
                    if overflow:
                        error = EINTEGRITY
                    else:
                        map_state["pa"] = mapped
            elif not error:
                if not chosen["provider"]:
                    error = ENODEV
                else:
                    selected = device_id
    elif not state["extra"]:
        error = 0
    elif state["flatdev"]:
        error = check_range(primary, map_state["pa"], map_state["plen"])
        if error:
            for chosen in state["extra"]:
                if not chosen["uniaddr"] or chosen["uniaddr"] > (UINT64_MAX >> state["blkszbits"]):
                    continue
                start = chosen["uniaddr"] << state["blkszbits"]
                if map_state["pa"] < start:
                    continue
                relative = map_state["pa"] - start
                error = check_range(chosen, relative, map_state["plen"])
                if not error:
                    break
                if relative < chosen["blocks"] << state["blkszbits"]:
                    break
            else:
                error = EINTEGRITY
    else:
        error = 0
        for index, chosen in enumerate(state["extra"], 1):
            if not chosen["uniaddr"]:
                continue
            if chosen["uniaddr"] > (UINT64_MAX >> state["blkszbits"]):
                error = EINTEGRITY
                break
            start = chosen["uniaddr"] << state["blkszbits"]
            if map_state["pa"] >= start and map_state["pa"] - start < chosen["blocks"] << state["blkszbits"]:
                relative = map_state["pa"] - start
                error = check_range(chosen, relative, map_state["plen"])
                if error:
                    break
                if not chosen["provider"]:
                    error = ENODEV
                    break
                map_state["pa"] = relative
                selected = index
                break
    return {
        "m_pa": map_state["pa"],
        "m_plen": map_state["plen"],
        "m_deviceid": map_state["deviceid"],
        "selected_device": selected,
        "errno": error,
    }


def extent_bytes(case: dict[str, Any]) -> tuple[int, bytes]:
    error, table_pos = extent_table_pos(case["inode"], case["recsz"])
    if error:
        return 0, b""
    output = bytearray()
    if case["recsz"] == 4:
        output.extend(struct.pack("<Q", case["base_pa"]))
    for extent in case["records"]:
        packed = struct.pack(
            "<IIIII12s",
            extent["plen"] & UINT32_MAX,
            extent["pstart"] & UINT32_MAX,
            (extent["pstart"] >> 32) & UINT32_MAX,
            extent["lstart"] & UINT32_MAX,
            (extent["lstart"] >> 32) & UINT32_MAX,
            b"\0" * 12,
        )
        output.extend(packed[: case["recsz"]])
    return table_pos, bytes(output)


MAP_CASES = make_data_cases() + make_zmap_cases()
DEVICE_CASES = make_device_cases()
MODEL_RECORDS = []
for case in MAP_CASES:
    expected = model_data(case) if case["engine"] == "data" else model_zmap(case)
    if case["engine"] == "data":
        fixture = case["reader"]["bytes"]
    else:
        _, fixture = extent_bytes(case)
    MODEL_RECORDS.append(
        {
            "id": case["id"],
            "engine": case["engine"],
            "category": case["category"],
            "subcategories": case["subcategories"],
            "fixture_sha256": sha256_bytes(fixture),
            "expected": expected,
        }
    )
MODEL_DEVICES = [
    {"id": case["id"], "expected": model_device(case)} for case in DEVICE_CASES
]
MODEL_SHA256 = sha256_bytes(canonical({"maps": MODEL_RECORDS, "devices": MODEL_DEVICES}))
if SPEC["expected_map_case_count"] != len(MAP_CASES):
    raise SystemExit(
        f"map case count is not frozen: actual={len(MAP_CASES)} "
        f"configured={SPEC['expected_map_case_count']}"
    )
if SPEC["expected_device_case_count"] != len(DEVICE_CASES):
    raise SystemExit("device case count is not frozen")
if SPEC["expected_model_sha256"] != MODEL_SHA256:
    raise SystemExit(
        f"independent model digest mismatch: actual={MODEL_SHA256} "
        f"configured={SPEC['expected_model_sha256']}"
    )


COMMON_C = r'''
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EIO 5
#define ENODEV 19
#define EINVAL 22
#define EOPNOTSUPP 45
#define EOVERFLOW 84
#define EINTEGRITY 97
#define UINT64_MAX_VALUE UINT64_MAX
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define roundup2(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
#define rounddown2(x, y) ((x) & ~((y) - 1))
#define bzero(ptr, len) memset((ptr), 0, (len))
#define le16toh(value) (value)
#define le32toh(value) (value)

typedef uint64_t erofs_off_t;
typedef uint64_t erofs_blk_t;
typedef uint64_t erofs_nid_t;

static uint32_t le32dec(const void *pointer)
{
	const unsigned char *bytes = pointer;
	return ((uint32_t)bytes[0] | (uint32_t)bytes[1] << 8 |
	    (uint32_t)bytes[2] << 16 | (uint32_t)bytes[3] << 24);
}

static uint64_t le64dec(const void *pointer)
{
	const unsigned char *bytes = pointer;
	return ((uint64_t)le32dec(bytes) |
	    (uint64_t)le32dec(bytes + 4) << 32);
}

#define EROFS_INODE_FLAT_PLAIN 0
#define EROFS_INODE_COMPRESSED_FULL 1
#define EROFS_INODE_FLAT_INLINE 2
#define EROFS_INODE_COMPRESSED_COMPACT 3
#define EROFS_INODE_CHUNK_BASED 4
#define EROFS_CHUNK_FORMAT_INDEXES 0x20
#define EROFS_CHUNK_FORMAT_48BIT 0x40
#define EROFS_BLOCK_MAP_ENTRY_SIZE 4
#define EROFS_DIRENT_NID_METABOX (1ULL << 63)
#define EROFS_NULL_ADDR UINT64_MAX
#define EROFS_MAP_MAPPED 0x0001
#define EROFS_MAP_META 0x0002
#define EROFS_MAP_PARTIAL_MAPPED 0x0004
#define EROFS_MAP_PARTIAL_REF 0x0008
#define EROFS_MAP_FRAGMENT 0x0010
#define EROFS_MAP_FULL(f) (!((f) & (EROFS_MAP_PARTIAL_MAPPED | EROFS_MAP_PARTIAL_REF)))
#define EROFS_GET_BLOCKS_FIEMAP 0x0001
#define EROFS_GET_BLOCKS_READMORE 0x0002
#define EROFS_GET_BLOCKS_FINDTAIL 0x0004
#define Z_EROFS_COMPRESSION_LZ4 0
#define Z_EROFS_COMPRESSION_LZMA 1
#define Z_EROFS_COMPRESSION_DEFLATE 2
#define Z_EROFS_COMPRESSION_ZSTD 3
#define Z_EROFS_COMPRESSION_MAX 4
#define Z_EROFS_COMPRESSION_SHIFTED 4
#define Z_EROFS_COMPRESSION_INTERLACED 5
#define Z_EROFS_COMPRESSION_RUNTIME_MAX 6
#define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024)
#define Z_EROFS_PCLUSTER_MAX_DSIZE (12 * 1024 * 1024)
#define Z_EROFS_ADVISE_EXTENTS 0x0001
#define Z_EROFS_ADVISE_INTERLACED_PCLUSTER 0x0010
#define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER 0x0020
#define Z_EROFS_ADVISE_EXTRECSZ_BIT 1
#define Z_EROFS_ADVISE_EXTRECSZ_MASK 0x3
#define Z_EROFS_EXTENT_PLEN_PARTIAL (1U << 27)
#define Z_EROFS_EXTENT_PLEN_FMT_BIT 28
#define Z_EROFS_EXTENT_PLEN_MASK ((Z_EROFS_PCLUSTER_MAX_SIZE << 1) - 1)

struct erofs_inode_chunk_index {
	uint16_t startblk_hi;
	uint16_t device_id;
	uint32_t startblk_lo;
} __attribute__((packed));

struct z_erofs_extent {
	uint32_t plen, pstart_lo, pstart_hi, lstart_lo, lstart_hi;
	uint8_t reserved[12];
} __attribute__((packed));

struct z_erofs_map_header {
	uint32_t word0;
	uint16_t h_advise;
	uint8_t h_algorithmtype;
	uint8_t h_clusterbits;
} __attribute__((packed));

struct erofs_device_info {
	void *devvp;
	void *cp;
	erofs_blk_t blocks;
	erofs_blk_t uniaddr;
};

struct erofs_map_blocks {
	erofs_off_t m_pa, m_la;
	uint64_t m_plen, m_llen;
	unsigned short m_deviceid;
	char m_algorithmformat;
	unsigned int m_flags;
};

struct erofs_inode {
	erofs_nid_t nid;
	uint64_t size;
	erofs_off_t inode_off;
	erofs_blk_t startblk;
	uint8_t datalayout;
	uint8_t inode_isize;
	uint32_t xattr_isize;
	uint16_t z_advise;
	uint8_t z_algorithmtype[2];
	uint8_t z_lclusterbits;
	uint16_t z_idata_size;
	erofs_off_t z_fragmentoff;
	uint64_t z_tailextent_headlcn;
	uint64_t z_extents;
	bool z_initialized;
	uint16_t chunkformat;
	uint8_t chunkbits;
	bool fragment;
};

struct erofs_sb_info {
	uint64_t block_size;
	uint8_t blkszbits;
	erofs_blk_t blocks;
	uint16_t device_id_mask;
	struct erofs_inode *metabox_en;
	uint16_t available_compr_algs;
	uint64_t packed_nid;
	struct erofs_inode *packed_inode;
	struct erofs_device_info dif0;
	struct erofs_device_info *devs;
	unsigned int extra_devices;
	bool flatdev;
};

struct erofs_map_dev {
	struct erofs_device_info *m_dif;
	erofs_off_t m_pa;
	unsigned int m_deviceid;
	uint64_t m_plen;
};

struct erofs_buf {
	void *data;
	void (*release)(void *);
};
#define EROFS_BUF_INITIALIZER { .data = NULL, .release = NULL }

static const unsigned char *gate_bytes;
static size_t gate_bytes_len;
static erofs_off_t gate_bytes_base;
static bool gate_reader_error;
static unsigned int gate_acquires;
static unsigned int gate_releases;

static bool erofs_nid_in_metabox(erofs_nid_t nid)
{
	return ((nid & EROFS_DIRENT_NID_METABOX) != 0);
}

static bool __attribute__((unused))
erofs_inode_is_data_compressed(unsigned int datalayout)
{
	return (datalayout == EROFS_INODE_COMPRESSED_FULL ||
	    datalayout == EROFS_INODE_COMPRESSED_COMPACT);
}

static int erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid,
    erofs_off_t off, size_t len, struct erofs_buf *buf)
{
	void *data;
	(void)sbi;
	(void)nid;
	if (gate_reader_error || off < gate_bytes_base ||
	    off - gate_bytes_base > gate_bytes_len ||
	    len > gate_bytes_len - (size_t)(off - gate_bytes_base))
		return (EIO);
	data = malloc(len == 0 ? 1 : len);
	if (data == NULL)
		return (EIO);
	if (len != 0)
		memcpy(data, gate_bytes + (off - gate_bytes_base), len);
	buf->data = data;
	buf->release = NULL;
	++gate_acquires;
	return (0);
}

static void erofs_brelse(void *data)
{
	if (data != NULL) {
		++gate_releases;
		free(data);
	}
}

static void erofs_put_metabuf(struct erofs_buf *buf)
{
	if (buf != NULL && buf->data != NULL) {
		void *data = buf->data;
		buf->data = NULL;
		buf->release = NULL;
		erofs_brelse(data);
	}
}

static unsigned int z_erofs_extent_recsize(unsigned int advise)
{
	return (4U << ((advise >> Z_EROFS_ADVISE_EXTRECSZ_BIT) &
	    Z_EROFS_ADVISE_EXTRECSZ_MASK));
}

static int z_erofs_fill_inode(struct erofs_sb_info *sbi,
    struct erofs_inode *vi)
{
	(void)sbi;
	return (vi->z_initialized ? 0 : EINTEGRITY);
}

static int z_erofs_map_blocks_fo(struct erofs_sb_info *sbi,
    struct erofs_inode *vi, struct erofs_map_blocks *map, int flags)
{
	(void)sbi;
	(void)vi;
	(void)map;
	(void)flags;
	return (EOPNOTSUPP);
}
'''


def c_bytes(data: bytes) -> str:
    return ", ".join(f"0x{byte:02x}" for byte in data) if data else "0"


def c_u64(value: int) -> str:
    return f"UINT64_C({value})"


zmap_names = [
    "z_erofs_extent_add",
    "z_erofs_extent_roundup",
    "z_erofs_extent_table_pos",
    "z_erofs_extent_record_pos",
    "z_erofs_read_extent",
    "z_erofs_extent_lstart",
    "z_erofs_map_blocks_ext",
    "z_erofs_map_sanity_check",
    "z_erofs_map_blocks_iter",
]
program = COMMON_C
program += "int z_erofs_map_blocks_iter(struct erofs_sb_info *, struct erofs_inode *, struct erofs_map_blocks *, int);\n"
program += extract_function(data_source, "erofs_inline_tail_start")
program += extract_function(data_source, "erofs_map_blocks_chunk")
program += legacy_body
if candidate_mode:
    program += extract_function(data_source, "erofs_map_blocks")
for name in zmap_names:
    program += extract_function(zmap_source, name)
program += "\nint\nmain(void)\n{\n\tint error;\n"

for index, case in enumerate(MAP_CASES):
    if case["engine"] == "data":
        raw = case["reader"]["bytes"]
        reader_base = case["reader"]["base"]
        reader_error = case["reader"]["error"]
        inode = case["inode"]
        sbi = case["sbi"]
    else:
        reader_base, raw = extent_bytes(case)
        reader_error = case["reader_error"]
        inode = case["inode"]
        sbi = {
            "block_size": case["sbi"]["block_size"],
            "blkszbits": case["sbi"]["blkszbits"],
            "blocks": 0,
            "device_id_mask": 0xFFFF,
            "metabox_present": False,
            "metabox_size": 0,
        }
    packed_size = case.get("sbi", {}).get("packed_size", 1 << 20)
    packed_nid = case.get("sbi", {}).get("packed_nid", 999)
    available = case.get("sbi", {}).get("available_compr_algs", 1)
    program += f'''
	{{
		static const unsigned char bytes[] = {{ {c_bytes(raw)} }};
		struct erofs_inode metabox = {{ .size = {c_u64(sbi['metabox_size'])} }};
		struct erofs_inode packed = {{ .nid = {c_u64(packed_nid)}, .size = {c_u64(packed_size)} }};
		struct erofs_sb_info sbi = {{
			.block_size = {c_u64(sbi['block_size'])}, .blkszbits = {sbi['blkszbits']},
			.blocks = {c_u64(sbi['blocks'])}, .device_id_mask = {sbi['device_id_mask']},
			.metabox_en = {('&metabox' if sbi['metabox_present'] else 'NULL')},
			.available_compr_algs = {available}, .packed_nid = {c_u64(packed_nid)},
			.packed_inode = &packed,
		}};
		struct erofs_inode vi = {{
			.nid = {c_u64(inode['nid'])}, .size = {c_u64(inode['size'])},
			.inode_off = {c_u64(inode['inode_off'])}, .startblk = {c_u64(inode.get('startblk', 0))},
			.datalayout = {inode['datalayout']}, .inode_isize = {inode['inode_isize']},
			.xattr_isize = {inode['xattr_isize']}, .z_advise = {inode.get('z_advise', 0)},
			.z_lclusterbits = {inode.get('z_lclusterbits', 12)},
			.z_extents = {c_u64(inode.get('z_extents', 0))}, .z_initialized = true,
			.chunkformat = {inode.get('chunkformat', 0)}, .chunkbits = {inode.get('chunkbits', 12)},
		}};
		struct erofs_map_blocks map = {{
			.m_pa = UINT64_C(0xcccccccccccccccc), .m_la = {c_u64(case['request'])},
			.m_plen = UINT64_C(0xdddddddddddddddd), .m_llen = UINT64_C(0xeeeeeeeeeeeeeeee),
			.m_deviceid = 0xaaaa, .m_algorithmformat = 0x55, .m_flags = 0xbbbbbbbb,
		}};
		(void)metabox;
		gate_bytes = bytes; gate_bytes_len = {len(raw)};
		gate_bytes_base = {c_u64(reader_base)}; gate_reader_error = {'true' if reader_error else 'false'};
		gate_acquires = gate_releases = 0;
'''
    if candidate_mode:
        program += "\t\terror = erofs_map_blocks(&sbi, &vi, &map);\n"
    elif case["engine"] == "data":
        program += f'''
		{{
			erofs_off_t pa = 0; unsigned int device = 0; size_t run = 0;
			bool hole = false, metadata = false;
			error = erofs_map_blocks(&sbi, &vi, {c_u64(case['request'])},
			    &pa, &device, &run, &hole, &metadata);
			map = (struct erofs_map_blocks){{
			    .m_pa = pa, .m_la = {c_u64(case['request'])}, .m_plen = run,
			    .m_llen = run, .m_deviceid = device,
			    .m_flags = (error == 0 && run != 0 && !hole ? EROFS_MAP_MAPPED : 0) |
			        (metadata ? EROFS_MAP_META : 0),
			}};
		}}
'''
    else:
        program += "\t\tmap = (struct erofs_map_blocks){ .m_la = map.m_la };\n"
        program += "\t\terror = z_erofs_map_blocks_iter(&sbi, &vi, &map, 0);\n"
    program += f'''
		printf("tuple\\t{case['id']}\\t%llu\\t%llu\\t%llu\\t%llu\\t%u\\t%u\\t%d\\t%d\\t%u\\t%u\\n",
		    (unsigned long long)map.m_la, (unsigned long long)map.m_pa,
		    (unsigned long long)map.m_llen, (unsigned long long)map.m_plen,
		    map.m_deviceid, map.m_flags, (int)map.m_algorithmformat, error,
		    gate_acquires, gate_releases);
	}}
'''
program += "\treturn (0);\n}\n"


device_program = COMMON_C
for name in ["erofs_check_device_range", "erofs_fill_from_devinfo", "erofs_map_dev"]:
    device_program += extract_function(data_source, name)
device_program += "\nint\nmain(void)\n{\n"
for case in DEVICE_CASES:
    state = case["sbi"]
    extras = state["extra"]
    entries = []
    for extra in extras:
        entries.append(
            "{ .devvp = %s, .cp = %s, .blocks = %s, .uniaddr = %s }"
            % (
                "(void *)1" if extra["provider"] else "NULL",
                "(void *)1" if extra["provider"] else "NULL",
                c_u64(extra["blocks"]),
                c_u64(extra["uniaddr"]),
            )
        )
    device_program += f'''
	{{
		struct erofs_device_info devs[{max(1, len(extras))}] = {{ {', '.join(entries) if entries else '{ 0 }'} }};
		struct erofs_sb_info sbi = {{
			.blkszbits = {state['blkszbits']}, .blocks = {c_u64(state['primary_blocks'])},
			.dif0 = {{ .devvp = (void *)1, .cp = (void *)1, .blocks = {c_u64(state['primary_blocks'])} }},
			.devs = {('devs' if state['devs_present'] else 'NULL')},
			.extra_devices = {len(extras)}, .flatdev = {'true' if state['flatdev'] else 'false'},
		}};
		struct erofs_map_dev map = {{ .m_pa = {c_u64(case['input']['pa'])},
		    .m_plen = {c_u64(case['input']['plen'])}, .m_deviceid = {case['input']['deviceid']} }};
		int selected = -1;
		int error = erofs_map_dev(&sbi, &map);
		if (map.m_dif == &sbi.dif0)
			selected = 0;
'''
    for index in range(len(extras)):
        device_program += f"\t\telse if (map.m_dif == &devs[{index}])\n\t\t\tselected = {index + 1};\n"
    device_program += f'''
		printf("device\\t{case['id']}\\t%llu\\t%llu\\t%u\\t%d\\t%d\\n",
		    (unsigned long long)map.m_pa, (unsigned long long)map.m_plen,
		    map.m_deviceid, selected, error);
	}}
'''
device_program += "\treturn (0);\n}\n"

adapter_program = ""
expected_adapter_lines = [
    "adapter\t1\t65\t74566\t4097\t2049\t48879\t31\t5\t0\t1",
    "adapter\t3\t67\t74568\t4099\t2051\t48879\t31\t5\t0\t1",
]
if candidate_mode:
    adapter_program = COMMON_C + r'''
static unsigned int gate_adapter_calls;
int z_erofs_map_blocks_iter(struct erofs_sb_info *sbi,
    struct erofs_inode *vi, struct erofs_map_blocks *map, int flags)
{
	(void)sbi;
	if (flags != 0)
		return (EINVAL);
	++gate_adapter_calls;
	map->m_la = 64 + vi->datalayout;
	map->m_pa = 74565 + vi->datalayout;
	map->m_llen = 4096 + vi->datalayout;
	map->m_plen = 2048 + vi->datalayout;
	map->m_deviceid = 0xbeef;
	map->m_algorithmformat = Z_EROFS_COMPRESSION_INTERLACED;
	map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_META |
	    EROFS_MAP_PARTIAL_MAPPED | EROFS_MAP_PARTIAL_REF |
	    EROFS_MAP_FRAGMENT;
	return (0);
}
'''
    adapter_program += extract_function(data_source, "erofs_inline_tail_start")
    adapter_program += extract_function(data_source, "erofs_map_blocks_chunk")
    adapter_program += legacy_body
    adapter_program += extract_function(data_source, "erofs_map_blocks")
    adapter_program += r'''
int main(void)
{
	const unsigned int layouts[] = {
		EROFS_INODE_COMPRESSED_FULL,
		EROFS_INODE_COMPRESSED_COMPACT,
	};
	struct erofs_sb_info sbi = { .block_size = 4096, .blkszbits = 12 };
	unsigned int index;

	for (index = 0; index < sizeof(layouts) / sizeof(layouts[0]); ++index) {
		struct erofs_inode vi = { .datalayout = layouts[index], .size = 8192 };
		struct erofs_map_blocks map = {
			.m_la = 123, .m_pa = UINT64_MAX, .m_llen = UINT64_MAX,
			.m_plen = UINT64_MAX, .m_deviceid = 0xffff,
			.m_algorithmformat = -1, .m_flags = UINT32_MAX,
		};
		int error;

		gate_adapter_calls = 0;
		error = erofs_map_blocks(&sbi, &vi, &map);
		printf("adapter\t%u\t%llu\t%llu\t%llu\t%llu\t%u\t%u\t%d\t%d\t%u\n",
		    layouts[index], (unsigned long long)map.m_la,
		    (unsigned long long)map.m_pa, (unsigned long long)map.m_llen,
		    (unsigned long long)map.m_plen, map.m_deviceid, map.m_flags,
		    (int)map.m_algorithmformat, error, gate_adapter_calls);
	}
	return (0);
}
'''


def compile_and_run(name: str, source: str) -> list[str]:
    source_path = OUTPUT / f"{name}.c"
    binary_path = OUTPUT / name
    source_path.write_text(source, encoding="ascii")
    compile_flags = ["cc", "-std=c11", "-O2", "-Wall", "-Wextra", "-Werror"]
    if name in {"P15-006-device-extractor", "P15-006-adapter-extractor"}:
        compile_flags.append("-Wno-unused-function")
    compile_run = run(
        compile_flags + ["-o", str(binary_path), str(source_path)],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    (OUTPUT / f"{name}.compile.stdout").write_text(compile_run.stdout, encoding="utf-8")
    (OUTPUT / f"{name}.compile.stderr").write_text(compile_run.stderr, encoding="utf-8")
    if compile_run.returncode != 0:
        raise SystemExit(f"{name} compilation failed")
    execute = run([str(binary_path)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (OUTPUT / f"{name}.stdout").write_text(execute.stdout, encoding="utf-8")
    (OUTPUT / f"{name}.stderr").write_text(execute.stderr, encoding="utf-8")
    if execute.returncode != 0:
        raise SystemExit(f"{name} execution failed")
    return execute.stdout.splitlines()


actual_lines = compile_and_run("P15-006-map-extractor", program)
device_lines = compile_and_run("P15-006-device-extractor", device_program)
adapter_lines = (
    compile_and_run("P15-006-adapter-extractor", adapter_program)
    if candidate_mode
    else []
)
actual_maps: dict[str, dict[str, int]] = {}
for line in actual_lines:
    fields = line.split("\t")
    if len(fields) != 12 or fields[0] != "tuple":
        raise SystemExit(f"invalid tuple extractor line: {line!r}")
    values = [int(value) for value in fields[2:]]
    actual_maps[fields[1]] = dict(zip(SPEC["tuple_fields"], values, strict=True))
actual_devices: dict[str, dict[str, int]] = {}
device_fields = ["m_pa", "m_plen", "m_deviceid", "selected_device", "errno"]
for line in device_lines:
    fields = line.split("\t")
    if len(fields) != 7 or fields[0] != "device":
        raise SystemExit(f"invalid device extractor line: {line!r}")
    actual_devices[fields[1]] = dict(
        zip(device_fields, [int(value) for value in fields[2:]], strict=True)
    )

expected_maps = {record["id"]: record for record in MODEL_RECORDS}
expected_devices = {record["id"]: record for record in MODEL_DEVICES}
failures: list[dict[str, Any]] = []
if candidate_mode and adapter_lines != expected_adapter_lines:
    failures.append({
        "field": "adapter-all-fields-and-flags",
        "expected": expected_adapter_lines,
        "actual": adapter_lines,
    })
if set(actual_maps) != set(expected_maps):
    failures.append({"field": "map-case-set", "actual": sorted(actual_maps), "expected": sorted(expected_maps)})
if set(actual_devices) != set(expected_devices):
    failures.append({"field": "device-case-set", "actual": sorted(actual_devices), "expected": sorted(expected_devices)})

tuple_format = SPEC["tuple_byte_layout"]
records_output = []
for case in MAP_CASES:
    case_id = case["id"]
    actual = actual_maps.get(case_id, tuple_record(errno=-1))
    expected = expected_maps[case_id]["expected"]
    for field in SPEC["tuple_fields"]:
        if actual[field] != expected[field]:
            failures.append({"id": case_id, "field": field, "expected": expected[field], "actual": actual[field]})
    packed = struct.pack(tuple_format, *(actual[field] for field in SPEC["tuple_fields"]))
    if actual["acquire_count"] != actual["release_count"]:
        failures.append({"id": case_id, "field": "cleanup-balance"})
    if actual["errno"] < 0:
        failures.append({"id": case_id, "field": "negative-errno"})
    if actual["errno"] == 0 and case["request"] < case["inode"]["size"] and actual["m_llen"] == 0:
        failures.append({"id": case_id, "field": "H07-positive-run"})
    records_output.append({
        **expected_maps[case_id],
        "actual": actual,
        "tuple_hex": packed.hex(),
    })

devices_output = []
for case in DEVICE_CASES:
    case_id = case["id"]
    actual = actual_devices.get(case_id, {field: -1 for field in device_fields})
    expected = expected_devices[case_id]["expected"]
    for field in device_fields:
        if actual[field] != expected[field]:
            failures.append({"id": case_id, "field": field, "expected": expected[field], "actual": actual[field]})
    if actual["errno"] < 0:
        failures.append({"id": case_id, "field": "negative-errno"})
    devices_output.append({"id": case_id, "expected": expected, "actual": actual})

coverage = sorted({case["category"] for case in MAP_CASES})
subcategories = sorted({value for case in MAP_CASES + DEVICE_CASES for value in case["subcategories"]})
if set(coverage) != set(SPEC["required_categories"]):
    failures.append({"field": "category-coverage", "actual": coverage})
if set(subcategories) != set(SPEC["required_subcategories"]):
    failures.append({"field": "subcategory-coverage", "actual": subcategories})

tuple_bytes = b"".join(bytes.fromhex(record["tuple_hex"]) for record in records_output)
tuple_bytes_sha256 = sha256_bytes(tuple_bytes)
oracle_equal: bool | None = None
if ORACLE is not None:
    oracle_data = json.loads(ORACLE.read_text(encoding="ascii"))
    oracle_records = {record["id"]: record for record in oracle_data["records"]}
    oracle_equal = (
        oracle_data.get("model_sha256") == MODEL_SHA256
        and oracle_data.get("tuple_bytes_sha256") == tuple_bytes_sha256
        and set(oracle_records) == set(actual_maps)
        and all(
            oracle_records[case_id]["tuple_hex"] == next(
                record["tuple_hex"] for record in records_output if record["id"] == case_id
            )
            for case_id in actual_maps
        )
    )
    if not oracle_equal:
        failures.append({"field": "frozen-byte-oracle"})

tuple_output = {
    "schema": 1,
    "candidate": "P15-006",
    "mode": MODE,
    "adapter_mode": candidate_mode,
    "model_sha256": MODEL_SHA256,
    "tuple_byte_layout": tuple_format,
    "tuple_bytes_sha256": tuple_bytes_sha256,
    "coverage": coverage,
    "subcategories": subcategories,
    "records": records_output,
    "authoritative_sources": [
        "independent arithmetic decoder over declared on-disk records",
        "compiled map and zmap function bodies extracted from the frozen/current FreeBSD source",
        "fixed-width tuple byte encoding replayed unchanged after B07a",
    ],
}
device_output = {
    "schema": 1,
    "status": "PASS" if not [failure for failure in failures if failure.get("id", "").startswith("device-")] else "FAIL",
    "records": devices_output,
    "protected_function_sha256": protected_hashes,
}
status = "GO" if not failures else "STOP"
adapter_probe = {
    "status": (
        "PASS" if candidate_mode and adapter_lines == expected_adapter_lines
        else "NOT_APPLICABLE" if not candidate_mode
        else "FAIL"
    ),
    "full_and_compact_dispatch": candidate_mode and adapter_lines == expected_adapter_lines,
    "flag_mask": SPEC["required_adapter_flag_mask"],
    "records": adapter_lines,
}
result = {
    "schema": 1,
    "gate": "G02",
    "candidate": "P15-006",
    "status": status,
    "mode": MODE,
    "requested_base": REQUESTED_BASE or None,
    "resolved_head": resolved,
    "adapter_mode": candidate_mode,
    "adapter_probe": adapter_probe,
    "map_case_count": len(MAP_CASES),
    "device_case_count": len(DEVICE_CASES),
    "coverage": coverage,
    "subcategories": subcategories,
    "model_sha256": MODEL_SHA256,
    "tuple_bytes_sha256": tuple_bytes_sha256,
    "oracle_equal": oracle_equal,
    "map_struct_sha256": map_struct_hash,
    "qemu": "NOT_RUN",
    "full_feature_suite": "NOT_RUN",
    "failures": failures,
}

(OUTPUT / "tuples.json").write_text(json.dumps(tuple_output, indent=2, sort_keys=True) + "\n", encoding="ascii")
(OUTPUT / "devices.json").write_text(json.dumps(device_output, indent=2, sort_keys=True) + "\n", encoding="ascii")
(OUTPUT / "source-sha256.json").write_text(json.dumps(source_hashes, indent=2, sort_keys=True) + "\n", encoding="ascii")
(OUTPUT / "result.json").write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="ascii")
hash_lines = []
for path in sorted(OUTPUT.iterdir()):
    if path.is_file() and path.name != "SHA256SUMS":
        hash_lines.append(f"{sha256_bytes(path.read_bytes())}  {path.name}")
(OUTPUT / "SHA256SUMS").write_text("\n".join(hash_lines) + "\n", encoding="ascii")
print(json.dumps(result, sort_keys=True))
if status != "GO":
    raise SystemExit(1)
PY
