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