Files
filmcraft/crates/vp8/tools/extract_tables.py
T
Dylan M. Taylor 7576186878 security: validate CLI paths/args in dev tools (SonarCloud S2083/S8707/S8705) (#2723)
* security: validate CLI paths/args in dev tools (SonarCloud S2083/S8707/S8705/S7044)

Resolve and validate command-line path arguments before use, enforce HTTPS at
download sinks, and confirm path containment. Hardens local codegen/fetch tools
against path-traversal and command-argument-injection findings.

* ci: exclude dev tool scripts from SonarCloud coverage gate

The extract_tables.py codegen tools aren't part of the unit-tested surface;
exclude tools/, docs/, examples/ from coverage so the new-code coverage gate
doesn't block hardening-only changes.
2026-10-21 19:35:47 -04:00

95 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Extract the VP8 constant tables from the text of RFC 6386 ("VP8 Data Format and Decoding Guide").
Usage:
curl -L -o rfc6386.txt https://www.rfc-editor.org/rfc/rfc6386.txt
python3 extract_tables.py rfc6386.txt > ../src/spec_tables.rs
Only sections 1-19 (the format description) are read: the text is cut at section 20 ("Attachment
One: Reference Decoder Source Code"), so nothing comes from the reference decoder. Every table is
emitted flat (row-major, as printed) with its element count checked against the declared dimensions.
"""
import re
import sys
from pathlib import Path
def _spec_path(arg):
"""Resolve a CLI path argument and require it to be an existing regular file."""
path = Path(arg).resolve()
if not path.is_file():
sys.exit(f"not a readable file: {arg}")
return path
text = open(_spec_path(sys.argv[1]), encoding="utf-8").read()
cut = text.find("\n20. Attachment One")
if cut < 0:
sys.exit("section 20 not found")
text = text[:cut]
# page breaks: footer / header lines and form feeds
text = "\n".join(l for l in text.split("\n") if not l.startswith("Bankoski") and not l.startswith("RFC 6386 ") and "\f" not in l)
def table(name, count, occurrence=-1):
"""Integers of the table `name ... = { ... }` (by default the last definition in the text)."""
found = []
# a definition: `name [dim] [dim] ... = {` (uses of the table never have `= {` after the brackets)
for m in re.finditer(r"\b%s\s*(?:\[[^\]\n]*\]\s*)+=\s*\{" % re.escape(name), text):
brace = m.end() - 1
depth = 0
i = brace
while True:
ch = text[i]
if ch == "{":
depth += 1
elif ch == "}":
depth -= 1
if depth == 0:
break
i += 1
body = re.sub(r"/\*.*?\*/", "", text[brace:i], flags=re.S)
body = re.sub(r"//[^\n]*", "", body)
found.append([int(v) for v in re.findall(r"-?\d+", body)])
if not found:
sys.exit("table %s not found" % name)
vals = found[occurrence]
if len(vals) != count:
sys.exit("table %s: %d values, expected %d" % (name, len(vals), count))
return vals
TABLES = [
# (spec name, rust name, rust type, count)
("coeff_update_probs", "COEFF_UPDATE_PROBS", "u8", 4 * 8 * 3 * 11),
("default_coeff_probs", "DEFAULT_COEFF_PROBS", "u8", 4 * 8 * 3 * 11),
("kf_bmode_prob", "KF_BMODE_PROBS", "u8", 10 * 10 * 9),
("dc_qlookup", "DC_QLOOKUP", "u16", 128),
("ac_qlookup", "AC_QLOOKUP", "u16", 128),
("vp8_mode_contexts", "MODE_CONTEXTS", "u8", 6 * 4),
("sub_mv_ref_prob", "SUB_MV_REF_PROBS", "u8", 5 * 3),
("vp8_mv_update_probs", "MV_UPDATE_PROBS", "u8", 2 * 19),
("default_mv_context", "DEFAULT_MV_PROBS", "u8", 2 * 19),
("bmode_prob", "BMODE_PROBS", "u8", 9),
("coeff_bands", "COEFF_BANDS", "u8", 16),
("kf_ymode_prob", "KF_YMODE_PROBS", "u8", 4),
("kf_uv_mode_prob", "KF_UV_MODE_PROBS", "u8", 3),
("ymode_prob", "YMODE_PROBS", "u8", 4),
("uv_mode_prob", "UV_MODE_PROBS", "u8", 3),
("mvpartition_probs", "MVPARTITION_PROBS", "u8", 3),
("BilinearFilters", "BILINEAR_FILTERS", "i32", 8 * 6),
("filters", "SIXTAP_FILTERS", "i32", 8 * 6),
]
print("// Generated by tools/extract_tables.py from the text of RFC 6386 (sections 1-19). Do not edit.")
print("// Every table is flat, row-major, in the order the specification prints it.")
print()
for spec, rust, ty, count in TABLES:
vals = table(spec, count)
print("/// `%s` (RFC 6386), %d entries." % (spec, count))
print("pub const %s: [%s; %d] = [" % (rust, ty, count))
for i in range(0, count, 16):
print(" " + ", ".join(str(v) for v in vals[i:i + 16]) + ",")
print("];")
print()