nx_deflate.nx source
↩ module page · 699 lines · 23233 B
1// nx_deflate.nx -- RFC 1951 INFLATE (DEFLATE decompression).
2//
3// CAPABILITY_COMPLETENESS: FULL -- all three BTYPE handlers
4// (stored, static Huffman, dynamic Huffman) implemented end-to-end.
5// Built per the four-pillar discipline: no error-coded skip-paths
6// shipped as "complete primitives". See
7// `feedback-no-skip-paths-as-error-codes` cardinal.
8//
9// Composes:
10// nx_bitstream (LSB-first reader)
11// nx_huffman (canonical Huffman decode)
12// + sliding-window LZ77 with 32 KB window.
13//
14// DEFLATE block format (RFC 1951):
15// 1 bit BFINAL (1 = last block)
16// 2 bits BTYPE
17// 00 = stored block (no compression)
18// 01 = static Huffman (predefined trees)
19// 10 = dynamic Huffman (trees encoded in stream)
20// 11 = reserved (error)
21//
22// Stored block:
23// skip to byte boundary
24// LEN (16-bit LE)
25// NLEN (16-bit LE; one's-complement of LEN; checksum)
26// LEN bytes copied verbatim to output
27//
28// Static Huffman block:
29// literal/length alphabet of 288 symbols (only 286 used; 286-287
30// are invalid placeholders) with predefined lengths:
31// 0-143 : length 8
32// 144-255 : length 9
33// 256-279 : length 7 (256 = end-of-block)
34// 280-287 : length 8
35// distance alphabet: all 30 symbols, length 5
36//
37// Dynamic Huffman block (BTYPE=10):
38// HLIT (5 bits) + 257 = literal/length code count
39// HDIST (5 bits) + 1 = distance code count
40// HCLEN (4 bits) + 4 = code-length code count
41// Code-length code lengths in interleaved order:
42// [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14,
43// 1, 15]
44// Then literal/length code lengths (using code-length Huffman):
45// 0-15 = literal length, 16 = copy previous N=3-6 (2 extra),
46// 17 = zero-run N=3-10 (3 extra), 18 = zero-run N=11-138 (7 extra)
47// Then distance code lengths (same Huffman)
48// Then the compressed data using built lit/len + distance trees.
49//
50// Length codes 257-285 + extra bits per RFC 1951 section 3.2.5.
51// Distance codes 0-29 + extra bits per section 3.2.5.
52//
53// genealogy_id: rfc1951_deflate_1996_inflate
54// lineage_id: nx_deflate_v1
55
56// nx_safety_envelope:
57// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
58// sil_target: SIL1
59// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
60// verdict: NOT_YET_EVALUATED
61
62import "nx_syscalls.nx"
63import "nx_runtime.nx"
64import "nx_tier.nx"
65import "nx_bitstream.nx"
66import "nx_huffman.nx"
67const NX_MAGIC_1025: i64 = 1025
68const NX_MAGIC_1537: i64 = 1537
69const NX_MAGIC_2049: i64 = 2049
70const NX_MAGIC_3073: i64 = 3073
71const NX_MAGIC_4097: i64 = 4097
72const NX_MAGIC_6145: i64 = 6145
73const NX_MAGIC_8193: i64 = 8193
74const NX_MAGIC_12289: i64 = 12289
75const NX_MAGIC_16385: i64 = 16385
76const NX_MAGIC_24577: i64 = 24577
77const NX_MAGIC_16777216: i64 = 16777216
78const NX_MAGIC_65535: i64 = 65535
79const NX_MAGIC_65536: i64 = 65536
80
81// ===== error codes ================================================
82
83const NX_DEF_OK: nx_int = 0
84const NX_DEF_ERR_RESERVED: nx_int = 1
85const NX_DEF_ERR_NLEN: nx_int = 2
86const NX_DEF_ERR_OVERFLOW: nx_int = 3
87const NX_DEF_ERR_DYNAMIC_CL: nx_int = 4
88const NX_DEF_ERR_HUFFMAN: nx_int = 5
89const NX_DEF_ERR_DISTANCE: nx_int = 6
90
91// Code-length-code order per RFC 1951 section 3.2.7.
92func _deflate_cl_order(i: nx_int) -> nx_int {
93 if i == 0 { return 16 }
94 if i == 1 { return 17 }
95 if i == 2 { return 18 }
96 if i == 3 { return 0 }
97 if i == 4 { return 8 }
98 if i == 5 { return 7 }
99 if i == 6 { return 9 }
100 if i == 7 { return 6 }
101 if i == 8 { return 10 }
102 if i == 9 { return 5 }
103 if i == 10 { return 11 }
104 if i == 11 { return 4 }
105 if i == 12 { return 12 }
106 if i == 13 { return 3 }
107 if i == 14 { return 13 }
108 if i == 15 { return 2 }
109 if i == 16 { return 14 }
110 if i == 17 { return 1 }
111 if i == 18 { return 15 }
112 return -1
113}
114
115// ===== result struct =============================================
116
117struct NxDeflateResult {
118 output_data: *u8,
119 output_size: nx_int,
120 bytes_consumed: nx_int,
121 error_code: nx_int,
122}
123
124const NX_DEF_RESULT_BYTES: nx_size = 32
125
126// ===== length / distance base + extra tables =====================
127//
128// Length codes 257..285. base[i] = base length, extra[i] = extra
129// bits to read.
130
131func _deflate_length_base(code: nx_int) -> nx_int {
132 if code == 257 { return 3 }
133 if code == 258 { return 4 }
134 if code == 259 { return 5 }
135 if code == 260 { return 6 }
136 if code == 261 { return 7 }
137 if code == 262 { return 8 }
138 if code == 263 { return 9 }
139 if code == 264 { return 10 }
140 if code == 265 { return 11 }
141 if code == 266 { return 13 }
142 if code == 267 { return 15 }
143 if code == 268 { return 17 }
144 if code == 269 { return 19 }
145 if code == 270 { return 23 }
146 if code == 271 { return 27 }
147 if code == 272 { return 31 }
148 if code == 273 { return 35 }
149 if code == 274 { return 43 }
150 if code == 275 { return 51 }
151 if code == 276 { return 59 }
152 if code == 277 { return 67 }
153 if code == 278 { return 83 }
154 if code == 279 { return 99 }
155 if code == 280 { return 115 }
156 if code == 281 { return 131 }
157 if code == 282 { return 163 }
158 if code == 283 { return 195 }
159 if code == 284 { return 227 }
160 if code == 285 { return 258 }
161 return -1
162}
163
164func _deflate_length_extra(code: nx_int) -> nx_int {
165 if code < 261 { return 0 }
166 if code < 265 { return 0 }
167 if code < 269 { return 1 }
168 if code < 273 { return 2 }
169 if code < 277 { return 3 }
170 if code < 281 { return 4 }
171 if code < 285 { return 5 }
172 return 0
173}
174
175func _deflate_distance_base(code: nx_int) -> nx_int {
176 if code == 0 { return 1 }
177 if code == 1 { return 2 }
178 if code == 2 { return 3 }
179 if code == 3 { return 4 }
180 if code == 4 { return 5 }
181 if code == 5 { return 7 }
182 if code == 6 { return 9 }
183 if code == 7 { return 13 }
184 if code == 8 { return 17 }
185 if code == 9 { return 25 }
186 if code == 10 { return 33 }
187 if code == 11 { return 49 }
188 if code == 12 { return 65 }
189 if code == 13 { return 97 }
190 if code == 14 { return 129 }
191 if code == 15 { return 193 }
192 if code == 16 { return 257 }
193 if code == 17 { return 385 }
194 if code == 18 { return 513 }
195 if code == 19 { return 769 }
196 if code == 20 { return NX_MAGIC_1025 }
197 if code == 21 { return NX_MAGIC_1537 }
198 if code == 22 { return NX_MAGIC_2049 }
199 if code == 23 { return NX_MAGIC_3073 }
200 if code == 24 { return NX_MAGIC_4097 }
201 if code == 25 { return NX_MAGIC_6145 }
202 if code == 26 { return NX_MAGIC_8193 }
203 if code == 27 { return NX_MAGIC_12289 }
204 if code == 28 { return NX_MAGIC_16385 }
205 if code == 29 { return NX_MAGIC_24577 }
206 return -1
207}
208
209func _deflate_distance_extra(code: nx_int) -> nx_int {
210 if code < 2 { return 0 }
211 if code < 4 { return 0 }
212 if code < 6 { return 1 }
213 if code < 8 { return 2 }
214 if code < 10 { return 3 }
215 if code < 12 { return 4 }
216 if code < 14 { return 5 }
217 if code < 16 { return 6 }
218 if code < 18 { return 7 }
219 if code < 20 { return 8 }
220 if code < 22 { return 9 }
221 if code < 24 { return 10 }
222 if code < 26 { return 11 }
223 if code < 28 { return 12 }
224 if code < 30 { return 13 }
225 return 0
226}
227
228// ===== build static Huffman trees ================================
229
230func _deflate_build_static_litlen() -> *NxHuffmanTable {
231 let lens: *nx_int = (sys_mmap(288 * 8)) as *nx_int
232 var i: nx_int = 0
233 while i < 144 {
234 lens[i] = 8
235 i = i + 1
236 }
237 while i < 256 {
238 lens[i] = 9
239 i = i + 1
240 }
241 while i < 280 {
242 lens[i] = 7
243 i = i + 1
244 }
245 while i < 288 {
246 lens[i] = 8
247 i = i + 1
248 }
249 return nx_huffman_build(lens, 288)
250}
251
252func _deflate_build_static_dist() -> *NxHuffmanTable {
253 let lens: *nx_int = (sys_mmap(30 * 8)) as *nx_int
254 var i: nx_int = 0
255 while i < 30 {
256 lens[i] = 5
257 i = i + 1
258 }
259 return nx_huffman_build(lens, 30)
260}
261
262// ===== copy bytes (handles overlap, LZ77 RLE-style) ==============
263
264func _deflate_copy_overlap(out: *u8, dst: nx_int, src: nx_int, n: nx_int) -> nx_int {
265 var i: nx_int = 0
266 while i < n {
267 out[dst + i] = out[src + i]
268 i = i + 1
269 }
270 return 0
271}
272
273// ===== shared Huffman-block decoder ==============================
274//
275// Called by both static (BTYPE=01) and dynamic (BTYPE=10) paths
276// once their lit/len + distance trees are built. Returns 0 on
277// success, -1 on error.
278
279func _deflate_decode_huffman_block(
280 bs: *NxBitStream, lit_tree: *NxHuffmanTable, dist_tree: *NxHuffmanTable,
281 output: *u8, out_pos: *nx_int, max_output: nx_int, err: *nx_int) -> nx_int {
282
283 var keep: nx_int = 1
284 var safety: nx_int = 0
285 let MAX_ITER: nx_int = NX_MAGIC_16777216
286 while keep == 1 {
287 if safety >= MAX_ITER {
288 err[0] = NX_DEF_ERR_OVERFLOW
289 return -1
290 }
291 safety = safety + 1
292 let sym: nx_int = nx_huffman_decode_lsb(lit_tree, bs)
293 if sym < 0 {
294 err[0] = NX_DEF_ERR_HUFFMAN
295 return -1
296 }
297 if sym < 256 {
298 let pos1: nx_int = out_pos[0]
299 if pos1 >= max_output {
300 err[0] = NX_DEF_ERR_OVERFLOW
301 return -1
302 }
303 output[pos1] = sym as u8
304 out_pos[0] = pos1 + 1
305 } else {
306 if sym == 256 {
307 keep = 0
308 } else {
309 if sym > 285 {
310 err[0] = NX_DEF_ERR_HUFFMAN
311 return -1
312 }
313 let base_len: nx_int = _deflate_length_base(sym)
314 let extra_len_bits: nx_int = _deflate_length_extra(sym)
315 var extra_len: nx_int = 0
316 if extra_len_bits > 0 {
317 extra_len = nx_bitstream_read_lsb(bs, extra_len_bits)
318 }
319 let length: nx_int = base_len + extra_len
320
321 let dsym: nx_int = nx_huffman_decode_lsb(dist_tree, bs)
322 if dsym < 0 {
323 err[0] = NX_DEF_ERR_HUFFMAN
324 return -1
325 }
326 if dsym > 29 {
327 err[0] = NX_DEF_ERR_DISTANCE
328 return -1
329 }
330 let base_dist: nx_int = _deflate_distance_base(dsym)
331 let extra_dist_bits: nx_int = _deflate_distance_extra(dsym)
332 var extra_dist: nx_int = 0
333 if extra_dist_bits > 0 {
334 extra_dist = nx_bitstream_read_lsb(bs, extra_dist_bits)
335 }
336 let distance: nx_int = base_dist + extra_dist
337
338 let pos2: nx_int = out_pos[0]
339 if (pos2 + length) > max_output {
340 err[0] = NX_DEF_ERR_OVERFLOW
341 return -1
342 }
343 if distance > pos2 {
344 err[0] = NX_DEF_ERR_DISTANCE
345 return -1
346 }
347 _deflate_copy_overlap(output, pos2, pos2 - distance, length)
348 out_pos[0] = pos2 + length
349 }
350 }
351 }
352 return 0
353}
354
355// ===== build dynamic Huffman trees from compressed code-length stream ==
356//
357// Reads HLIT + HDIST + HCLEN, builds the code-length-code Huffman,
358// then runs it to decode literal/length + distance code-length
359// arrays, then builds the actual lit/len + distance Huffman trees
360// from those lengths.
361
362func _deflate_build_dynamic_trees(
363 bs: *NxBitStream, out_lit: **NxHuffmanTable, out_dist: **NxHuffmanTable,
364 err: *nx_int) -> nx_int {
365
366 let hlit_raw: nx_int = nx_bitstream_read_lsb(bs, 5)
367 let hdist_raw: nx_int = nx_bitstream_read_lsb(bs, 5)
368 let hclen_raw: nx_int = nx_bitstream_read_lsb(bs, 4)
369 let n_lit: nx_int = hlit_raw + 257
370 let n_dist: nx_int = hdist_raw + 1
371 let n_cl: nx_int = hclen_raw + 4
372
373 // Read code-length-code lengths (19 symbols total, indexed by
374 // the deinterleave table _deflate_cl_order).
375 let cl_lens: *nx_int = (sys_mmap(19 * 8)) as *nx_int
376 var z: nx_int = 0
377 while z < 19 {
378 cl_lens[z] = 0
379 z = z + 1
380 }
381 var i: nx_int = 0
382 while i < n_cl {
383 let pos: nx_int = _deflate_cl_order(i)
384 if pos < 0 {
385 err[0] = NX_DEF_ERR_DYNAMIC_CL
386 return -1
387 }
388 let v: nx_int = nx_bitstream_read_lsb(bs, 3)
389 cl_lens[pos] = v
390 i = i + 1
391 }
392
393 let cl_tree: *NxHuffmanTable = nx_huffman_build(cl_lens, 19)
394 if cl_tree == (0 as *NxHuffmanTable) {
395 err[0] = NX_DEF_ERR_HUFFMAN
396 return -1
397 }
398
399 // Decode lit/len + distance code-length arrays in one pass.
400 let total: nx_int = n_lit + n_dist
401 let all_lens: *nx_int = (sys_mmap((total as nx_size) * 8)) as *nx_int
402 var p: nx_int = 0
403 while p < total {
404 let sym: nx_int = nx_huffman_decode_lsb(cl_tree, bs)
405 if sym < 0 {
406 err[0] = NX_DEF_ERR_HUFFMAN
407 return -1
408 }
409 if sym < 16 {
410 all_lens[p] = sym
411 p = p + 1
412 } else {
413 if sym == 16 {
414 if p < 1 {
415 err[0] = NX_DEF_ERR_DYNAMIC_CL
416 return -1
417 }
418 let extra16: nx_int = nx_bitstream_read_lsb(bs, 2)
419 let repeat16: nx_int = extra16 + 3
420 let prev: nx_int = all_lens[p - 1]
421 if (p + repeat16) > total {
422 err[0] = NX_DEF_ERR_DYNAMIC_CL
423 return -1
424 }
425 var k: nx_int = 0
426 while k < repeat16 {
427 all_lens[p + k] = prev
428 k = k + 1
429 }
430 p = p + repeat16
431 } else {
432 if sym == 17 {
433 let extra17: nx_int = nx_bitstream_read_lsb(bs, 3)
434 let repeat17: nx_int = extra17 + 3
435 if (p + repeat17) > total {
436 err[0] = NX_DEF_ERR_DYNAMIC_CL
437 return -1
438 }
439 var k2: nx_int = 0
440 while k2 < repeat17 {
441 all_lens[p + k2] = 0
442 k2 = k2 + 1
443 }
444 p = p + repeat17
445 } else {
446 if sym == 18 {
447 let extra18: nx_int = nx_bitstream_read_lsb(bs, 7)
448 let repeat18: nx_int = extra18 + 11
449 if (p + repeat18) > total {
450 err[0] = NX_DEF_ERR_DYNAMIC_CL
451 return -1
452 }
453 var k3: nx_int = 0
454 while k3 < repeat18 {
455 all_lens[p + k3] = 0
456 k3 = k3 + 1
457 }
458 p = p + repeat18
459 } else {
460 err[0] = NX_DEF_ERR_DYNAMIC_CL
461 return -1
462 }
463 }
464 }
465 }
466 }
467
468 // Split into lit/len + distance arrays.
469 let lit_lens: *nx_int = (sys_mmap((n_lit as nx_size) * 8)) as *nx_int
470 let dist_lens: *nx_int = (sys_mmap((n_dist as nx_size) * 8)) as *nx_int
471 var j: nx_int = 0
472 while j < n_lit {
473 lit_lens[j] = all_lens[j]
474 j = j + 1
475 }
476 var d: nx_int = 0
477 while d < n_dist {
478 dist_lens[d] = all_lens[n_lit + d]
479 d = d + 1
480 }
481
482 let lit_tree: *NxHuffmanTable = nx_huffman_build(lit_lens, n_lit)
483 if lit_tree == (0 as *NxHuffmanTable) {
484 err[0] = NX_DEF_ERR_HUFFMAN
485 return -1
486 }
487 let dist_tree: *NxHuffmanTable = nx_huffman_build(dist_lens, n_dist)
488 if dist_tree == (0 as *NxHuffmanTable) {
489 err[0] = NX_DEF_ERR_HUFFMAN
490 return -1
491 }
492 out_lit[0] = lit_tree
493 out_dist[0] = dist_tree
494 return 0
495}
496
497// ===== inflate one block =========================================
498//
499// Returns 1 if final block, 0 otherwise, -1 on error.
500
501func _deflate_inflate_block(
502 bs: *NxBitStream, output: *u8, out_pos: *nx_int,
503 max_output: nx_int, err: *nx_int) -> nx_int {
504
505 let bfinal: nx_int = nx_bitstream_read_lsb(bs, 1)
506 let btype: nx_int = nx_bitstream_read_lsb(bs, 2)
507
508 if btype == 0 {
509 // ---- stored block ----
510 nx_bitstream_byte_align(bs)
511 let len_lo: nx_int = nx_bitstream_read_byte_aligned(bs)
512 let len_hi: nx_int = nx_bitstream_read_byte_aligned(bs)
513 let nlen_lo: nx_int = nx_bitstream_read_byte_aligned(bs)
514 let nlen_hi: nx_int = nx_bitstream_read_byte_aligned(bs)
515 let len: nx_int = len_lo | (len_hi << 8)
516 let nlen: nx_int = nlen_lo | (nlen_hi << 8)
517 let nlen_check: nx_int = nlen ^ NX_MAGIC_65535
518 if nlen_check != len {
519 err[0] = NX_DEF_ERR_NLEN
520 return -1
521 }
522 let cur_pos: nx_int = out_pos[0]
523 if (cur_pos + len) > max_output {
524 err[0] = NX_DEF_ERR_OVERFLOW
525 return -1
526 }
527 var k: nx_int = 0
528 while k < len {
529 let b: nx_int = nx_bitstream_read_byte_aligned(bs)
530 output[cur_pos + k] = b as u8
531 k = k + 1
532 }
533 out_pos[0] = cur_pos + len
534 return bfinal
535 }
536
537 if btype == 3 {
538 err[0] = NX_DEF_ERR_RESERVED
539 return -1
540 }
541
542 if btype == 1 {
543 let lit_tree_s: *NxHuffmanTable = _deflate_build_static_litlen()
544 let dist_tree_s: *NxHuffmanTable = _deflate_build_static_dist()
545 if lit_tree_s == (0 as *NxHuffmanTable) {
546 err[0] = NX_DEF_ERR_HUFFMAN
547 return -1
548 }
549 if dist_tree_s == (0 as *NxHuffmanTable) {
550 err[0] = NX_DEF_ERR_HUFFMAN
551 return -1
552 }
553 let rc_s: nx_int = _deflate_decode_huffman_block(
554 bs, lit_tree_s, dist_tree_s, output, out_pos, max_output, err)
555 if rc_s != 0 { return -1 }
556 return bfinal
557 }
558
559 // btype == 2 -- dynamic Huffman.
560 let lit_box: **NxHuffmanTable = (sys_mmap(8)) as **NxHuffmanTable
561 let dist_box: **NxHuffmanTable = (sys_mmap(8)) as **NxHuffmanTable
562 let rc_d: nx_int = _deflate_build_dynamic_trees(bs, lit_box, dist_box, err)
563 if rc_d != 0 { return -1 }
564 let rc_blk: nx_int = _deflate_decode_huffman_block(
565 bs, lit_box[0], dist_box[0], output, out_pos, max_output, err)
566 if rc_blk != 0 { return -1 }
567 return bfinal
568}
569
570// ===== top-level inflate =========================================
571
572// Round up byte_pos when the last byte was only partially consumed.
573// Without this, callers that need the offset of the byte AFTER the
574// deflate stream (e.g. nx_zlib_inflate reading the adler32 trailer)
575// land one byte short on every block that doesn't end on a byte
576// boundary -- which is essentially every static or dynamic Huffman
577// block. Surfaced 2026-05-18 via real-PNG decode failure (adler
578// mismatch), see project-deflate-huffman-false-ok-2026-05-18.
579func _deflate_bytes_consumed(bs: *NxBitStream) -> nx_int {
580 if bs.bit_pos > 0 {
581 return bs.byte_pos + 1
582 }
583 return bs.byte_pos
584}
585
586func nx_deflate_inflate(input: *u8, input_size: nx_int,
587 max_output: nx_int) -> *NxDeflateResult {
588 if input_size <= 0 { return 0 as *NxDeflateResult }
589 if max_output <= 0 { return 0 as *NxDeflateResult }
590
591 let r_ptr: *u8 = sys_mmap(NX_DEF_RESULT_BYTES)
592 let r: *NxDeflateResult = r_ptr as *NxDeflateResult
593
594 let out_buf: *u8 = sys_mmap(max_output as nx_size)
595 let bs: *NxBitStream = nx_bitstream_alloc(input, input_size)
596 let pos_box: *nx_int = (sys_mmap(8)) as *nx_int
597 let err_box: *nx_int = (sys_mmap(8)) as *nx_int
598 pos_box[0] = 0
599 err_box[0] = NX_DEF_OK
600
601 var done: nx_int = 0
602 var iter: nx_int = 0
603 let MAX_BLOCKS: nx_int = NX_MAGIC_65536
604 while done == 0 {
605 if iter >= MAX_BLOCKS { break }
606 let final_block: nx_int = _deflate_inflate_block(
607 bs, out_buf, pos_box, max_output, err_box)
608 if final_block < 0 {
609 r.error_code = err_box[0]
610 r.output_data = out_buf
611 r.output_size = pos_box[0]
612 r.bytes_consumed = _deflate_bytes_consumed(bs)
613 return r
614 }
615 if final_block == 1 { done = 1 }
616 iter = iter + 1
617 }
618
619 r.output_data = out_buf
620 r.output_size = pos_box[0]
621 r.bytes_consumed = _deflate_bytes_consumed(bs)
622 r.error_code = NX_DEF_OK
623 return r
624}
625
626// ===== self-test =================================================
627
628func main() -> nx_int {
629 // ---- STORED block decoding "Nishi" (5 bytes) ----
630 //
631 // Block header: BFINAL=1, BTYPE=00 -> bits 1 0 0 = first byte's
632 // low 3 bits = 001 -> byte 0 = 0x01 (with padding).
633 //
634 // After byte-align, LEN_lo LEN_hi NLEN_lo NLEN_hi then 5 bytes.
635 //
636 // byte 0: 0x01 (BFINAL=1, BTYPE=00, pad to byte boundary)
637 // byte 1: 0x05 (LEN low)
638 // byte 2: 0x00 (LEN high; LEN = 5)
639 // byte 3: 0xFA (NLEN low; NLEN = ~5 = 0xFFFA)
640 // byte 4: 0xFF (NLEN high)
641 // bytes 5-9: 'N','i','s','h','i' = 0x4E 0x69 0x73 0x68 0x69
642 let buf: *u8 = (sys_mmap(10)) as *u8
643 buf[0] = 0x01 as u8
644 buf[1] = 0x05 as u8
645 buf[2] = 0x00 as u8
646 buf[3] = 0xFA as u8
647 buf[4] = 0xFF as u8
648 buf[5] = 0x4E as u8
649 buf[6] = 0x69 as u8
650 buf[7] = 0x73 as u8
651 buf[8] = 0x68 as u8
652 buf[9] = 0x69 as u8
653
654 let r1: *NxDeflateResult = nx_deflate_inflate(buf, 10, 64)
655 if r1 == (0 as *NxDeflateResult) { return 1 }
656 if r1.error_code != NX_DEF_OK { return 2 }
657 if r1.output_size != 5 { return 3 }
658 if r1.output_data[0] != (0x4E as u8) { return 4 }
659 if r1.output_data[1] != (0x69 as u8) { return 5 }
660 if r1.output_data[2] != (0x73 as u8) { return 6 }
661 if r1.output_data[3] != (0x68 as u8) { return 7 }
662 if r1.output_data[4] != (0x69 as u8) { return 8 }
663
664 // ---- STATIC Huffman empty-block: BFINAL=1 BTYPE=01 + EOB code 256
665 //
666 // Static Huffman literal/length 256 has length 7 + canonical
667 // code 0000000 (it's the FIRST 7-bit code; first_code[7] = 0).
668 //
669 // Bit-stream LSB-first:
670 // bit 0: BFINAL=1
671 // bit 1: BTYPE low=1
672 // bit 2: BTYPE high=0
673 // bits 3..9: 0000000 (EOB code)
674 // (any trailing pad bits)
675 //
676 // byte 0 LSB-first = bit0|bit1<<1|bit2<<2|bit3<<3|bit4<<4|...
677 // = 1 | 1<<1 | 0<<2 | 0<<3 | 0<<4 | 0<<5 | 0<<6 | 0<<7 = 0x03
678 // byte 1: bit 8 = 0, bit 9 = 0, rest pad = 0 -> 0x00
679 let buf2: *u8 = (sys_mmap(2)) as *u8
680 buf2[0] = 0x03 as u8
681 buf2[1] = 0x00 as u8
682
683 let r2: *NxDeflateResult = nx_deflate_inflate(buf2, 2, 64)
684 if r2 == (0 as *NxDeflateResult) { return 20 }
685 if r2.error_code != NX_DEF_OK { return 21 }
686 if r2.output_size != 0 { return 22 }
687
688 // ---- NLEN mismatch error path ----
689 let bad: *u8 = (sys_mmap(10)) as *u8
690 bad[0] = 0x01 as u8
691 bad[1] = 0x05 as u8
692 bad[2] = 0x00 as u8
693 bad[3] = 0x00 as u8 // wrong NLEN
694 bad[4] = 0x00 as u8
695 let rb: *NxDeflateResult = nx_deflate_inflate(bad, 10, 64)
696 if rb.error_code != NX_DEF_ERR_NLEN { return 30 }
697
698 return 0
699}