nx_jpeg_dqt.nx source
↩ module page · 113 lines · 4827 B
1// nx_jpeg_dqt.nx -- JPEG quantization-table (DQT) parser per
2// ITU-T Rec. T.81 sec B.2.4.1.
3//
4// DQT payload layout (one or more tables, packed):
5// Pq (4 bits) Tq (4 bits) -- precision + table-id
6// Qk[64] -- table values in zig-zag scan order
7//
8// Pq = 0 means each Qk is an 8-bit unsigned value (64 bytes total).
9// Pq = 1 means each Qk is a 16-bit unsigned big-endian value (128 bytes
10// total). Pq=1 is only legal for sample precision > 8 (T.81 sec B.2.4.1);
11// baseline JPEG (SOF0) is 8-bit so 99% of real-world DQTs are Pq=0.
12//
13// Tq is 0..3. Multiple DQT segments may be concatenated within a single
14// payload; this parser walks the payload and emits each table separately.
15//
16// CRITICAL: JPEG quantization tables are stored in ZIG-ZAG order.
17// Decode pipeline must un-zigzag before dequantization or the IDCT will
18// produce garbage. This parser preserves the on-wire zig-zag order;
19// un-zigzag is the consumer's responsibility (it's a 64-byte permutation
20// lookup -- shipped as nx_jpeg_zigzag.nx in a follow-on stone).
21//
22// nx_safety_envelope:
23// intended_use: "Quantization-table parsing for JPEG decode."
24// sil_target: SIL1
25// evidence: [t81_section_b_2_4_1_canonical_basis,
26// sealed_precision_pq_2_state,
27// bounded_iteration,
28// substrate_honest_truncation_signal]
29// hazard_register: [bug-tape-dqt-precision-confusion,
30// bug-tape-multi-table-payload-overrun,
31// bug-tape-dqt-zero-coefficient-divide-by-zero]
32// residual_risk: "Caller must validate that Qk[*] != 0 before
33// using as divisor; substrate stores values
34// verbatim including zero."
35// verdict: NOT_YET_EVALUATED
36
37import "nx_syscalls.nx"
38
39const NX_JPEG_DQT_OK: i64 = 0
40const NX_JPEG_DQT_TRUNC: i64 = 1 // payload too short for declared precision
41const NX_JPEG_DQT_BAD_TQ: i64 = 2 // Tq > 3
42const NX_JPEG_DQT_BAD_PQ: i64 = 3 // Pq > 1
43const NX_JPEG_DQT_OVERFLOW: i64 = 4 // more than 4 tables in payload
44const NX_JPEG_DQT_RESULT_N: i64 = 5
45
46func nx_jpeg_dqt_result_is_valid(v: i64) -> i64 {
47 if v < 0 { return 0 }
48 if v >= NX_JPEG_DQT_RESULT_N { return 0 }
49 return 1
50}
51
52// One parsed quantization table. `values` always has 64 entries
53// (one per 8x8 DCT coefficient position) regardless of precision.
54struct NxJpegQTable {
55 tq: i64, // table-id 0..3
56 pq: i64, // precision 0 (8-bit) or 1 (16-bit)
57 values: *i64, // 64 entries -- caller-supplied; substrate writes here
58}
59
60const NX_JPEG_QTABLE_BYTES: i64 = 24
61
62// Output array: caller supplies space for up to 4 tables.
63// Parsed table count returned via *count_p.
64//
65// payload = bytes of one DQT segment payload (excluding the 2-byte
66// length prefix that nx_jpeg_seg_next already consumed)
67// payload_len = payload byte count
68// tables = caller buffer for up to 4 NxJpegQTable structs
69// value_bufs = caller-supplied 4 * 64-entry i64 arrays (256 i64 total)
70// count_p = receives number of tables parsed (0..4)
71func nx_jpeg_dqt_parse(payload: *u8, payload_len: i64,
72 tables: *NxJpegQTable,
73 value_bufs: *i64,
74 count_p: *i64) -> i64 {
75 var p: i64 = 0
76 var n_tables: i64 = 0
77 count_p[0] = 0
78 while p < payload_len {
79 if n_tables >= 4 { return NX_JPEG_DQT_OVERFLOW }
80 if p >= payload_len { return NX_JPEG_DQT_TRUNC }
81 let pq_tq: i64 = payload[p] as i64
82 p = p + 1
83 let pq: i64 = pq_tq >> 4
84 let tq: i64 = pq_tq & 0x0F
85 if pq > 1 { return NX_JPEG_DQT_BAD_PQ }
86 if tq > 3 { return NX_JPEG_DQT_BAD_TQ }
87 var elem_bytes: i64 = 1
88 if pq == 1 { elem_bytes = 2 }
89 let needed: i64 = 64 * elem_bytes
90 if p + needed > payload_len { return NX_JPEG_DQT_TRUNC }
91 // Slice value_bufs[n_tables*64 .. n_tables*64+64].
92 let table_values: *i64 = (value_bufs as i64 + n_tables * 64 * 8) as *i64
93 var k: i64 = 0
94 while k < 64 {
95 if elem_bytes == 1 {
96 table_values[k] = payload[p + k] as i64
97 } else {
98 let hi: i64 = payload[p + k * 2] as i64
99 let lo: i64 = payload[p + k * 2 + 1] as i64
100 table_values[k] = (hi << 8) | lo
101 }
102 k = k + 1
103 }
104 let entry: *NxJpegQTable = (tables as i64 + n_tables * NX_JPEG_QTABLE_BYTES) as *NxJpegQTable
105 entry.tq = tq
106 entry.pq = pq
107 entry.values = table_values
108 n_tables = n_tables + 1
109 p = p + needed
110 }
111 count_p[0] = n_tables
112 return NX_JPEG_DQT_OK
113}