code wiki / (root) / nx_zstd_block.nx

nx_zstd_block.nx source

↩ module page · 155 lines · 6801 B

1// nx_zstd_block.nx -- Zstandard compressed-block decompression. 2// 3// The integration layer: literals section + sequences section -> plain bytes. 4// This is what turns the six proven zstd layers into a decoder that actually 5// handles a Compressed block instead of refusing it. 6// 7// WHAT IS WIRED. Raw, RLE and Huffman-coded literals (with a directly-encoded 8// weight table), plus the zero-sequence case where the block's output IS its 9// literals. That is a complete, legal zstd block -- it is what an encoder 10// emits for data it could not match, which is common for already-compressed 11// or high-entropy input. 12// 13// WHAT IS NOT, AND SAYS SO. FSE-coded sequences and FSE-compressed Huffman 14// weights each return their own distinct refusal code. A decoder that quietly 15// emitted the literals for a block that also carried sequences would produce 16// output that is the right LENGTH and the wrong BYTES -- the worst possible 17// failure, because a length check would pass it. 18// 19// THE HUFFMAN TREE DESCRIPTION IS INSIDE THE COMPRESSED SIZE. compressed_size 20// from the literals header covers the tree description AND the stream(s), so 21// the stream length is compressed_size minus the description. Treating 22// compressed_size as the stream alone over-reads by the description length and 23// desynchronises the sequences section that follows. 24// 25// genealogy_id: zstandard_rfc8878_block 26// lineage_id: nx_zstd_block_v1 27// license_tier: ORIGINAL 28 29import "nx_syscalls.nx" 30import "nx_zstd_fse.nx" 31import "nx_zstd_bits.nx" 32import "nx_zstd_huf.nx" 33import "nx_zstd_lit.nx" 34import "nx_zstd_seq.nx" 35import "nx_zstd_fse_dec.nx" 36import "nx_zstd_seqtab.nx" 37import "nx_zstd_seqdec.nx" 38 39const NX_BLK_ERR_MALFORMED: i64 = 0 - 1 40const NX_BLK_ERR_FSE_SEQ: i64 = 0 - 2 41const NX_BLK_ERR_FSE_WEIGHTS: i64 = 0 - 3 42const NX_BLK_ERR_STREAMS4: i64 = 0 - 4 43 44func nx_blk_at(d: *u8, i: i64) -> i64 { return (d[i] as i64) & 255 } 45 46// ===== the Huffman tree description =============================== 47// 48// A header byte below 128 means the weights follow directly. At or above 128 49// they are FSE-compressed, which this layer does not yet build -- refused 50// with its own code so the caller can tell 'unsupported' from 'corrupt'. 51// Returns the description length in bytes, or a negative error. 52 53func nx_zstd_block_tree(d: *u8, n: i64, off: i64, out_t: *i64) -> i64 { 54 if off >= n { return NX_BLK_ERR_MALFORMED } 55 let hdr: i64 = nx_blk_at(d, off) 56 if hdr >= 128 { return NX_BLK_ERR_FSE_WEIGHTS } 57 58 let w: *i64 = sys_mmap(512 * 8) as *i64 59 let count: i64 = nx_zstd_weights_direct(d, n, off + 1, hdr, w) 60 if count < 0 { return NX_BLK_ERR_MALFORMED } 61 let last: i64 = nx_zstd_weights_complete(w, count) 62 if last < 0 { return NX_BLK_ERR_MALFORMED } 63 let tl: i64 = nx_zstd_weights_tablelog(w, count + 1) 64 if tl < 0 { return NX_BLK_ERR_MALFORMED } 65 let t: *NxZstdHuf = nx_zstd_huf_build(w, count + 1, tl) 66 if t == (0 as *NxZstdHuf) { return NX_BLK_ERR_MALFORMED } 67 68 out_t[0] = t as i64 69 // one header byte plus the packed weight nibbles 70 return 1 + ((hdr + 1 + 1) / 2) 71} 72 73// ===== decompress one compressed block ============================ 74// 75// Returns bytes written, or a negative code. d/n is the block payload only 76// (the caller has already consumed the 3-byte block header). 77 78func nx_zstd_block_decompress(d: *u8, n: i64, out: *u8, cap: i64) -> i64 { 79 let lf: *i64 = sys_mmap(128) as *i64 80 if nx_zstd_lit_header(d, n, 0, lf) != 1 { return NX_BLK_ERR_MALFORMED } 81 82 let ltype: i64 = lf[NX_LIT_FLD_TYPE] 83 let regen: i64 = lf[NX_LIT_FLD_REGEN] 84 let csize: i64 = lf[NX_LIT_FLD_COMPSIZE] 85 let hlen: i64 = lf[NX_LIT_FLD_HDRLEN] 86 87 let lit: *u8 = sys_mmap(regen + 64) 88 var got: i64 = 0 - 1 89 90 if ltype == NX_LIT_RAW { 91 got = nx_zstd_lit_raw(d, n, hlen, regen, lit, regen + 64) 92 } else { 93 if ltype == NX_LIT_RLE { 94 got = nx_zstd_lit_rle(d, n, hlen, regen, lit, regen + 64) 95 } else { 96 // Compressed or Treeless: four streams need a jump table this layer 97 // does not read yet, so say so rather than misreading stream one 98 if lf[NX_LIT_FLD_STREAMS] != 1 { return NX_BLK_ERR_STREAMS4 } 99 let tslot: *i64 = sys_mmap(64) as *i64 100 let tlen: i64 = nx_zstd_block_tree(d, n, hlen, tslot) 101 if tlen < 0 { return tlen } 102 let t: *NxZstdHuf = tslot[0] as *NxZstdHuf 103 // the tree description lives INSIDE compressed_size 104 let stream_len: i64 = csize - tlen 105 if stream_len <= 0 { return NX_BLK_ERR_MALFORMED } 106 got = nx_zstd_lit_huf(t, d, n, hlen + tlen, stream_len, regen, lit, regen + 64) 107 } } 108 109 if got < 0 { return NX_BLK_ERR_MALFORMED } 110 if got != regen { return NX_BLK_ERR_MALFORMED } 111 112 // ---- sequences ---- 113 let soff: i64 = hlen + csize 114 let sf: *i64 = sys_mmap(128) as *i64 115 if nx_zstd_seq_header(d, n, soff, sf) != 1 { return NX_BLK_ERR_MALFORMED } 116 let nseq: i64 = sf[NX_SEQ_FLD_COUNT] 117 118 if nseq == 0 { 119 // no sequences: the block's output IS its literals 120 if regen > cap { return NX_BLK_ERR_MALFORMED } 121 var i: i64 = 0 122 while i < regen { out[i] = lit[i]; i = i + 1 } 123 return regen 124 } 125 126 // All three alphabets must use the PREDEFINED distributions. RLE, 127 // FSE_Compressed and Repeat each carry a table description ahead of the 128 // bitstream that this layer does not read yet, so they are refused with 129 // the same distinct code rather than decoded against the wrong tables. 130 if sf[NX_SEQ_FLD_LLMODE] != NX_SEQ_MODE_PREDEFINED { return NX_BLK_ERR_FSE_SEQ } 131 if sf[NX_SEQ_FLD_MLMODE] != NX_SEQ_MODE_PREDEFINED { return NX_BLK_ERR_FSE_SEQ } 132 if sf[NX_SEQ_FLD_OFMODE] != NX_SEQ_MODE_PREDEFINED { return NX_BLK_ERR_FSE_SEQ } 133 134 // the sequence bitstream is everything after the sequences header 135 let bstart: i64 = soff + sf[NX_SEQ_FLD_HDRLEN] 136 let blen: i64 = n - bstart 137 if blen <= 0 { return NX_BLK_ERR_MALFORMED } 138 139 let tll: *NxFseTable = nx_zstd_seqtab_ll_table() 140 let tml: *NxFseTable = nx_zstd_seqtab_ml_table() 141 let tof: *NxFseTable = nx_zstd_seqtab_of_table() 142 if tll == (0 as *NxFseTable) { return NX_BLK_ERR_MALFORMED } 143 if tml == (0 as *NxFseTable) { return NX_BLK_ERR_MALFORMED } 144 if tof == (0 as *NxFseTable) { return NX_BLK_ERR_MALFORMED } 145 146 let sb: *NxZstdBits = nx_zstd_bits_init(d + bstart, blen) 147 if sb == (0 as *NxZstdBits) { return NX_BLK_ERR_MALFORMED } 148 149 let seqs: *i64 = sys_mmap(nseq * 3 * 8 + 64) as *i64 150 if nx_zstd_seq_decode_all(tll, tml, tof, sb, nseq, seqs) != 1 { return NX_BLK_ERR_MALFORMED } 151 152 // execute against the literals; this refuses an offset reaching before the 153 // start of the output rather than copying from nowhere 154 return nx_zstd_seq_execute(lit, regen, seqs, nseq, out, cap) 155}