nx_jpeg_dht_test.nx source
↩ module page · 195 lines · 8561 B
1// nx_jpeg_dht_test.nx -- KAT for DHT parser + Huffman decode.
2//
3// Test cases:
4// A. Synthetic 2-symbol table: BITS={1,1,0...}, HUFFVAL={0x05, 0x06}.
5// Symbol 0x05 -> code "0" (1 bit); symbol 0x06 -> code "10" (2 bits).
6// B. Decode test: bitstream "0 10 0 0" packed MSB-first = 0x40,
7// should yield symbols [0x05, 0x06, 0x05, 0x05].
8// C. Dual-table payload (luma DC + luma AC stacked).
9// D. Malformed -- BITS sum >256 truncation.
10//
11// expect_exit: 0
12// license_tier: ORIGINAL
13
14import "nx_syscalls.nx"
15import "nx_jpeg_dht.nx"
16
17func _fail(n: i64) -> i64 {
18 let b: *u8 = sys_mmap(16)
19 b[0]=0x46; b[1]=0x41; b[2]=0x49; b[3]=0x4C; b[4]=0x3D
20 sys_write(2, b, 5)
21 var x: i64 = n
22 if x < 0 { let m: *u8 = sys_mmap(4); m[0]=0x2D; sys_write(2, m, 1); x = 0 - x }
23 if x == 0 { let z: *u8 = sys_mmap(4); z[0]=0x30; sys_write(2, z, 1) }
24 else {
25 let buf: *u8 = sys_mmap(16)
26 var pos: i64 = 0
27 while x > 0 { buf[pos] = (0x30 + (x % 10)) as u8; x = x / 10; pos = pos + 1 }
28 let out: *u8 = sys_mmap(16)
29 var i: i64 = 0
30 while i < pos { out[i] = buf[pos - 1 - i]; i = i + 1 }
31 sys_write(2, out, pos)
32 }
33 let nl: *u8 = sys_mmap(4); nl[0]=0x0A; sys_write(2, nl, 1)
34 return 0
35}
36
37func main() -> i64 {
38 let tables: *NxJpegHTable = sys_mmap(NX_JPEG_HTABLE_BYTES * 4) as *NxJpegHTable
39 let bits_pool: *i64 = sys_mmap(4 * 17 * 8) as *i64
40 let hv_pool: *i64 = sys_mmap(4 * 256 * 8) as *i64
41 let mc_pool: *i64 = sys_mmap(4 * 17 * 8) as *i64
42 let xc_pool: *i64 = sys_mmap(4 * 17 * 8) as *i64
43 let vp_pool: *i64 = sys_mmap(4 * 17 * 8) as *i64
44 let count_p: *i64 = sys_mmap(8) as *i64
45
46 // ============================================================
47 // Section A: synthetic 2-symbol DHT
48 // Tc=0 (DC), Th=0 -> tc_th = 0x00
49 // BITS = {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
50 // HUFFVAL = {0x05, 0x06}
51 // Total payload bytes = 1 + 16 + 2 = 19
52 // ============================================================
53 let p_a: *u8 = sys_mmap(32)
54 p_a[0] = 0x00 // Tc=0, Th=0
55 p_a[1] = 1 // BITS[1]
56 p_a[2] = 1 // BITS[2]
57 var i: i64 = 3
58 while i <= 16 { p_a[i] = 0; i = i + 1 }
59 p_a[17] = 0x05 // HUFFVAL[0]
60 p_a[18] = 0x06 // HUFFVAL[1]
61
62 let rc_a: i64 = nx_jpeg_dht_parse(p_a, 19, tables, bits_pool, hv_pool, mc_pool, xc_pool, vp_pool, count_p)
63 if rc_a != NX_JPEG_DHT_OK { _fail(1); return 1 }
64 if count_p[0] != 1 { _fail(2); return 2 }
65
66 let t0: *NxJpegHTable = tables
67 if t0.tc != 0 { _fail(3); return 3 }
68 if t0.th != 0 { _fail(4); return 4 }
69 if t0.n_symbols != 2 { _fail(5); return 5 }
70 if t0.huffval[0] != 0x05 { _fail(6); return 6 }
71 if t0.huffval[1] != 0x06 { _fail(7); return 7 }
72
73 // Decode-table assertions per T.81 Fig C.3.
74 if t0.mincode[1] != 0 { _fail(10); return 10 }
75 if t0.maxcode[1] != 0 { _fail(11); return 11 }
76 if t0.valptr[1] != 0 { _fail(12); return 12 }
77 if t0.mincode[2] != 2 { _fail(13); return 13 }
78 if t0.maxcode[2] != 2 { _fail(14); return 14 }
79 if t0.valptr[2] != 1 { _fail(15); return 15 }
80 if t0.maxcode[3] != (0 - 1) { _fail(16); return 16 }
81 if t0.maxcode[16] != (0 - 1) { _fail(17); return 17 }
82
83 // ============================================================
84 // Section B: decode bitstream
85 // bits: 0 10 0 0 (MSB-first) = 0b01000000 = 0x40
86 // expected symbols: 0x05, 0x06, 0x05, 0x05
87 // ============================================================
88 let bit_src: *u8 = sys_mmap(8)
89 bit_src[0] = 0x40
90 bit_src[1] = 0x00 // padding (not consumed)
91
92 let bit_off_p: *i64 = sys_mmap(8) as *i64
93 let byte_idx_p: *i64 = sys_mmap(8) as *i64
94 bit_off_p[0] = 0
95 byte_idx_p[0] = 0
96
97 let s1: i64 = nx_jpeg_huff_decode_symbol(t0, bit_src, bit_off_p, byte_idx_p, 2)
98 if s1 != 0x05 { _fail(20); return 20 }
99 let s2: i64 = nx_jpeg_huff_decode_symbol(t0, bit_src, bit_off_p, byte_idx_p, 2)
100 if s2 != 0x06 { _fail(21); return 21 }
101 let s3: i64 = nx_jpeg_huff_decode_symbol(t0, bit_src, bit_off_p, byte_idx_p, 2)
102 if s3 != 0x05 { _fail(22); return 22 }
103 let s4: i64 = nx_jpeg_huff_decode_symbol(t0, bit_src, bit_off_p, byte_idx_p, 2)
104 if s4 != 0x05 { _fail(23); return 23 }
105
106 // ============================================================
107 // Section C: dual-table payload (DC + AC).
108 // First: same as Section A (Tc=0, Th=0, 2 symbols).
109 // Second: Tc=1 Th=0, BITS={2,0...}, HUFFVAL={0x11, 0x22}.
110 // ============================================================
111 let p_c: *u8 = sys_mmap(64)
112 p_c[0] = 0x00; p_c[1] = 1; p_c[2] = 1
113 i = 3
114 while i <= 16 { p_c[i] = 0; i = i + 1 }
115 p_c[17] = 0x05; p_c[18] = 0x06
116 p_c[19] = 0x10 // Tc=1 (AC), Th=0
117 p_c[20] = 2 // BITS[1]=2 -- two codes of length 1
118 i = 21
119 while i <= 35 { p_c[i] = 0; i = i + 1 }
120 p_c[36] = 0x11
121 p_c[37] = 0x22
122
123 let rc_c: i64 = nx_jpeg_dht_parse(p_c, 38, tables, bits_pool, hv_pool, mc_pool, xc_pool, vp_pool, count_p)
124 if rc_c != NX_JPEG_DHT_OK { _fail(30); return 30 }
125 if count_p[0] != 2 { _fail(31); return 31 }
126
127 let tc0: *NxJpegHTable = tables
128 let tc1: *NxJpegHTable = (tables as i64 + NX_JPEG_HTABLE_BYTES) as *NxJpegHTable
129 if tc0.tc != 0 { _fail(32); return 32 }
130 if tc1.tc != 1 { _fail(33); return 33 }
131 if tc1.n_symbols != 2 { _fail(34); return 34 }
132 if tc1.huffval[0] != 0x11 { _fail(35); return 35 }
133 if tc1.huffval[1] != 0x22 { _fail(36); return 36 }
134 // AC table has 2 codes of length 1: 0 -> 0x11, 1 -> 0x22.
135 if tc1.mincode[1] != 0 { _fail(37); return 37 }
136 if tc1.maxcode[1] != 1 { _fail(38); return 38 }
137
138 // ============================================================
139 // Section D: malformed -- truncated payload
140 // ============================================================
141 let p_d: *u8 = sys_mmap(16)
142 p_d[0] = 0x00; p_d[1] = 1; p_d[2] = 1
143 i = 3
144 while i <= 16 { p_d[i] = 0; i = i + 1 }
145 // claim 2 symbols but only provide 1 byte
146 p_d[17] = 0x05
147 let rc_d: i64 = nx_jpeg_dht_parse(p_d, 18, tables, bits_pool, hv_pool, mc_pool, xc_pool, vp_pool, count_p)
148 if rc_d != NX_JPEG_DHT_TRUNC { _fail(40); return 40 }
149
150 // ============================================================
151 // Section E: 0xFF 0x00 byte-stuffing skip during decode
152 // Construct a bitstream that puts a 0xFF byte mid-stream
153 // followed by 0x00 stuffing. Decoder should skip the 0x00.
154 // ============================================================
155 // table from Section A is in `t0`. Bitstream: 8 ones followed by
156 // another byte that starts with another code.
157 // With t0 (sym 0x05 = "0", sym 0x06 = "10"), an all-1s byte 0xFF
158 // followed by 0x00 stuffing then 0x40 means: each 1 starts a
159 // failed match in length 1, so they cascade. Simpler: just put
160 // bits "0 0 0 0 0 0 0 0" = 0x00 then a stuffed 0xFF that should
161 // be parsed as data byte 0xFF (not a marker) but we don't have
162 // raw 0xFF here -- we'd have to manually inject FF 00 sequence.
163 //
164 // Easiest test: arrange bits so first 8 symbols consume exactly
165 // one byte, hitting boundary. bitstream = 0xFF 0x00 0x40 means:
166 // - read byte 0xFF (8 bits of 1s)
167 // - encounter stuffing: skip 0x00
168 // - read byte 0x40 (= "01000000")
169 // With t0: 8 ones means decode tries len 1 (1>0 fail), len 2
170 // (3>maxcode[2]=2 fail), all 16 lengths fail -> returns -1.
171 // We don't want that. Use the AC table (tc1) which has codes
172 // of length 1: 0 -> 0x11, 1 -> 0x22. So 8 ones decode to
173 // eight 0x22 symbols.
174 let bs2: *u8 = sys_mmap(8)
175 bs2[0] = 0xFF
176 bs2[1] = 0x00 // stuffing byte (should be skipped)
177 bs2[2] = 0xFF // 8 more ones
178 bs2[3] = 0x00 // stuffing
179 bit_off_p[0] = 0
180 byte_idx_p[0] = 0
181 var n_decoded: i64 = 0
182 var loop_i: i64 = 0
183 while loop_i < 16 {
184 let s: i64 = nx_jpeg_huff_decode_symbol(tc1, bs2, bit_off_p, byte_idx_p, 4)
185 if s != 0x22 { _fail(50 + loop_i); return 50 }
186 n_decoded = n_decoded + 1
187 loop_i = loop_i + 1
188 }
189 if n_decoded != 16 { _fail(70); return 70 }
190
191 let pass: *u8 = sys_mmap(16)
192 pass[0]=0x50; pass[1]=0x41; pass[2]=0x53; pass[3]=0x53; pass[4]=0x0A
193 sys_write(1, pass, 5)
194 return 0
195}