nx_deflate_huffman_test.nx source
↩ module page · 192 lines · 7142 B
1// nx_deflate_huffman_test.nx -- regression smoke for the BTYPE=01
2// (static Huffman) and BTYPE=10 (dynamic Huffman) inflate paths.
3//
4// SURFACED 2026-05-18: nx_deflate.nx self-reports
5// "CAPABILITY_COMPLETENESS: FULL -- all three BTYPE handlers
6// (stored, static Huffman, dynamic Huffman) implemented end-to-end"
7// but only BTYPE=00 (stored) was ever exercised by an existing smoke.
8// Real PNGs from gen-img use BTYPE=01/10 and crashed the substrate.
9// This test pins the Huffman paths with python-zlib ground-truth
10// byte sequences so the false-OK can't return.
11//
12// Fixtures captured via:
13// python3 -c "import zlib; co = zlib.compressobj(level=zlib.Z_BEST_SPEED,
14// wbits=-zlib.MAX_WBITS, strategy=zlib.Z_FIXED);
15// print(co.compress(b'Hello, world!') + co.flush())"
16
17import "nx_syscalls.nx"
18import "nx_runtime.nx"
19import "nx_deflate.nx"
20
21func main() -> nx_int {
22 // ---- Test 1: BTYPE=01 static Huffman, "Hello, world!" ----
23 //
24 // Raw DEFLATE bytes (no zlib wrapper), strategy=Z_FIXED:
25 // 0xf3 0x48 0xcd 0xc9 0xc9 0xd7 0x51 0x28
26 // 0xcf 0x2f 0xca 0x49 0x51 0x04 0x00
27 // (15 bytes encoding 13-byte "Hello, world!")
28 let static_in: *u8 = (sys_mmap(16)) as *u8
29 static_in[0] = 0xf3 as u8
30 static_in[1] = 0x48 as u8
31 static_in[2] = 0xcd as u8
32 static_in[3] = 0xc9 as u8
33 static_in[4] = 0xc9 as u8
34 static_in[5] = 0xd7 as u8
35 static_in[6] = 0x51 as u8
36 static_in[7] = 0x28 as u8
37 static_in[8] = 0xcf as u8
38 static_in[9] = 0x2f as u8
39 static_in[10] = 0xca as u8
40 static_in[11] = 0x49 as u8
41 static_in[12] = 0x51 as u8
42 static_in[13] = 0x04 as u8
43 static_in[14] = 0x00 as u8
44
45 let r_static: *NxDeflateResult = nx_deflate_inflate(static_in, 15, 64)
46 if r_static == (0 as *NxDeflateResult) { return 1 }
47 if r_static.error_code != NX_DEF_OK { return 2 }
48 if r_static.output_size != 13 { return 3 }
49 // Expected: "Hello, world!"
50 let exp_static: *u8 = (sys_mmap(16)) as *u8
51 exp_static[0] = 0x48 as u8 // 'H'
52 exp_static[1] = 0x65 as u8 // 'e'
53 exp_static[2] = 0x6c as u8 // 'l'
54 exp_static[3] = 0x6c as u8 // 'l'
55 exp_static[4] = 0x6f as u8 // 'o'
56 exp_static[5] = 0x2c as u8 // ','
57 exp_static[6] = 0x20 as u8 // ' '
58 exp_static[7] = 0x77 as u8 // 'w'
59 exp_static[8] = 0x6f as u8 // 'o'
60 exp_static[9] = 0x72 as u8 // 'r'
61 exp_static[10] = 0x6c as u8 // 'l'
62 exp_static[11] = 0x64 as u8 // 'd'
63 exp_static[12] = 0x21 as u8 // '!'
64 var i: nx_int = 0
65 while i < 13 {
66 let got: nx_int = (r_static.output_data[i] as nx_int) & 255
67 let want: nx_int = (exp_static[i] as nx_int) & 255
68 if got != want {
69 return 100 + i
70 }
71 i = i + 1
72 }
73
74 // ---- Test 2: BTYPE=01 static Huffman WITH BACK-REFS ----
75 //
76 // Test 1 covered the literal-only path. Real PNGs have lots of
77 // repetition and so the deflate stream uses length/distance
78 // back-references (symbols >256 in the literal/length tree).
79 // Input "ABCABCABCABCABCABCABCABC" (24 bytes) compresses to 7
80 // bytes via Z_FIXED, forcing a back-ref to be exercised:
81 // 0x73 0x74 0x72 0x76 0xc4 0x86 0x00
82 let br_in: *u8 = (sys_mmap(8)) as *u8
83 br_in[0] = 0x73 as u8
84 br_in[1] = 0x74 as u8
85 br_in[2] = 0x72 as u8
86 br_in[3] = 0x76 as u8
87 br_in[4] = 0xc4 as u8
88 br_in[5] = 0x86 as u8
89 br_in[6] = 0x00 as u8
90
91 let r_br: *NxDeflateResult = nx_deflate_inflate(br_in, 7, 64)
92 if r_br == (0 as *NxDeflateResult) { return 200 }
93 if r_br.error_code != NX_DEF_OK { return 201 }
94 if r_br.output_size != 24 { return 202 }
95 // Expected: "ABCABCABCABCABCABCABCABC"
96 var k: nx_int = 0
97 while k < 24 {
98 let got_b: nx_int = (r_br.output_data[k] as nx_int) & 255
99 // Pattern: byte k = 'A'+(k%3) = 65, 66, 67, 65, 66, 67, ...
100 let want_b: nx_int = 65 + (k % 3)
101 if got_b != want_b {
102 return 300 + k
103 }
104 k = k + 1
105 }
106
107 // ---- Test 3: BTYPE=10 DYNAMIC Huffman (the failing-in-prod path) ----
108 //
109 // 50x50 RGB solid-skin PNG-style scanlines compressed by zlib
110 // (level=6 default) produces a 61-byte raw DEFLATE stream that
111 // uses dynamic Huffman. Same shape as real gen-img PNG IDAT
112 // bytes. Expected uncompressed size = 50*(1 + 50*3) = 7550.
113 let dyn_in: *u8 = (sys_mmap(64)) as *u8
114 dyn_in[0] = 0xed as u8
115 dyn_in[1] = 0xce as u8
116 dyn_in[2] = 0x41 as u8
117 dyn_in[3] = 0x0d as u8
118 dyn_in[4] = 0x00 as u8
119 dyn_in[5] = 0x20 as u8
120 dyn_in[6] = 0x0c as u8
121 dyn_in[7] = 0x00 as u8
122 dyn_in[8] = 0xb1 as u8
123 dyn_in[9] = 0xf9 as u8
124 dyn_in[10] = 0x17 as u8
125 dyn_in[11] = 0x80 as u8
126 dyn_in[12] = 0x08 as u8
127 dyn_in[13] = 0x94 as u8
128 dyn_in[14] = 0x4c as u8
129 dyn_in[15] = 0x16 as u8
130 dyn_in[16] = 0x32 as u8
131 dyn_in[17] = 0xb8 as u8
132 dyn_in[18] = 0x47 as u8
133 dyn_in[19] = 0x93 as u8
134 dyn_in[20] = 0x0a as u8
135 dyn_in[21] = 0xe8 as u8
136 dyn_in[22] = 0xec as u8
137 dyn_in[23] = 0x3d as u8
138 dyn_in[24] = 0x41 as u8
139 dyn_in[25] = 0xf3 as u8
140 dyn_in[26] = 0x7d as u8
141 dyn_in[27] = 0xa0 as u8
142 dyn_in[28] = 0xa5 as u8
143 dyn_in[29] = 0xa5 as u8
144 dyn_in[30] = 0xa5 as u8
145 dyn_in[31] = 0xa5 as u8
146 dyn_in[32] = 0xa5 as u8
147 dyn_in[33] = 0xd5 as u8
148 dyn_in[34] = 0xa0 as u8
149 dyn_in[35] = 0xa5 as u8
150 dyn_in[36] = 0xa5 as u8
151 dyn_in[37] = 0x55 as u8
152 dyn_in[38] = 0xa0 as u8
153 dyn_in[39] = 0xa5 as u8
154 dyn_in[40] = 0xa5 as u8
155 dyn_in[41] = 0x55 as u8
156 dyn_in[42] = 0xa0 as u8
157 dyn_in[43] = 0xa5 as u8
158 dyn_in[44] = 0xa5 as u8
159 dyn_in[45] = 0x55 as u8
160 dyn_in[46] = 0xa0 as u8
161 dyn_in[47] = 0xa5 as u8
162 dyn_in[48] = 0xa5 as u8
163 dyn_in[49] = 0x55 as u8
164 dyn_in[50] = 0xa0 as u8
165 dyn_in[51] = 0xa5 as u8
166 dyn_in[52] = 0xa5 as u8
167 dyn_in[53] = 0x55 as u8
168 dyn_in[54] = 0xa0 as u8
169 dyn_in[55] = 0xa5 as u8
170 dyn_in[56] = 0xa5 as u8
171 dyn_in[57] = 0x55 as u8
172 dyn_in[58] = 0x10 as u8
173 dyn_in[59] = 0x6d as u8
174 dyn_in[60] = 0x3d as u8
175
176 let r_dyn: *NxDeflateResult = nx_deflate_inflate(dyn_in, 61, 8192)
177 if r_dyn == (0 as *NxDeflateResult) { return 400 }
178 if r_dyn.error_code != NX_DEF_OK { return 401 }
179 if r_dyn.output_size != 7550 { return 402 }
180 // Verify scanline structure: byte 0 = filter (0x00), byte 1 = R (0xc8),
181 // byte 2 = G (0xaa), byte 3 = B (0x96), then repeat for 50 pixels.
182 // Each scanline is 1 + 50*3 = 151 bytes. Scanline 1 starts at 151.
183 if (r_dyn.output_data[0] as nx_int) & 255 != 0x00 { return 403 }
184 if (r_dyn.output_data[1] as nx_int) & 255 != 0xc8 { return 404 }
185 if (r_dyn.output_data[2] as nx_int) & 255 != 0xaa { return 405 }
186 if (r_dyn.output_data[3] as nx_int) & 255 != 0x96 { return 406 }
187 if (r_dyn.output_data[151] as nx_int) & 255 != 0x00 { return 407 }
188 if (r_dyn.output_data[152] as nx_int) & 255 != 0xc8 { return 408 }
189 if (r_dyn.output_data[7549] as nx_int) & 255 != 0x96 { return 409 }
190
191 return 0
192}