import re
import sys
tab_path, out_path = sys.argv[1], sys.argv[2]
raw = open(tab_path).read()
TABLES = [
("sort_660", "SORT_660", 132, "6.60 kbit/s"),
("sort_885", "SORT_885", 177, "8.85 kbit/s"),
("sort_1265", "SORT_1265", 253, "12.65 kbit/s"),
("sort_1425", "SORT_1425", 285, "14.25 kbit/s"),
("sort_1585", "SORT_1585", 317, "15.85 kbit/s"),
("sort_1825", "SORT_1825", 365, "18.25 kbit/s"),
("sort_1985", "SORT_1985", 397, "19.85 kbit/s"),
("sort_2305", "SORT_2305", 461, "23.05 kbit/s"),
("sort_2385", "SORT_2385", 477, "23.85 kbit/s"),
("sort_SID", "SORT_SID", 35, "SID (comfort noise)"),
]
def values(name, count):
body = raw.split(f"{name}[", 1)[1].split("{", 1)[1].split("}", 1)[0]
body = re.sub(r"/\*.*?\*/", "", body, flags=re.S)
vals = [int(x) for x in re.findall(r"-?\d+", body)]
assert len(vals) == count, f"{name}: got {len(vals)}, want {count}"
assert sorted(vals) == list(range(count)), f"{name} is not a permutation"
return vals
parts = []
for c_name, rust_name, count, label in TABLES:
vals = values(c_name, count)
lines = [
f"/// Payload bit order for the {label} mode, {count} bits.",
f"pub const {rust_name}: [u16; {count}] = [",
]
for i in range(0, count, 10):
lines.append(" " + ", ".join(str(v) for v in vals[i : i + 10]) + ",")
lines.append("];")
parts.append("\n".join(lines))
HEADER = '''//! Payload bit-ordering tables for AMR-WB, from the TS 26.173 reference.
//!
//! RFC 4867 carries the codec bits sorted by subjective importance rather than
//! in the order the decoder reads them, so unpacking a frame is a permutation
//! before it is a field walk. Entry `i` of a table gives the codec-bit index
//! that payload bit `i` belongs to:
//!
//! ```text
//! codec_bits[SORT_2385[i]] = payload_bit[i]
//! ```
//!
//! The sorting is what makes unequal error protection possible: put the
//! perceptually critical bits first and a channel codec can protect the front
//! of the frame more heavily than the tail. It is also why the class A bit
//! counts in [`crate::codecs::amr::mode`] are simply prefix lengths.
//!
//! Generated by `tools/gen_sort_tables.py`; each table is checked to be a
//! genuine permutation of `0..n` at generation time.
'''
with open(out_path, "w") as f:
f.write(HEADER)
f.write("\n\n".join(parts) + "\n")
print(f"wrote {out_path}: {len(TABLES)} sorting tables")