code wiki / (root) / nx_zstd_fse_dec.nx

nx_zstd_fse_dec.nx source

↩ module page · 91 lines · 3797 B

1// nx_zstd_fse_dec.nx -- the FSE decoding state machine for zstd sequences. 2// 3// nx_zstd_fse.nx builds the table; this drives it. An FSE decoder is a state 4// that indexes the table: the entry names the symbol, and the NEXT state is 5// that entry's baseline plus a few freshly-read bits. Literal lengths, match 6// lengths and offsets each run their own independent state over the SAME 7// backward bitstream. 8// 9// THE SYMBOL COMES BEFORE THE UPDATE. Read the symbol at the CURRENT state, 10// then consume bits to move to the next. Doing it the other way round yields 11// a stream that is off by one symbol from the first sequence onward -- it 12// decodes, it is the right length, and every value is wrong. 13// 14// THE THREE STATES INTERLEAVE IN A FIXED ORDER. zstd initialises literal 15// length, then offset, then match length; and on each sequence it updates them 16// in the reverse order. They share one bit reader, so any deviation shifts 17// every subsequent read. This module exposes init/symbol/next as separate 18// operations precisely so the caller can express that ordering explicitly 19// rather than burying it in a loop. 20// 21// WHAT IS PROVEN HERE: the state machine's mechanics and invariants. The 22// PREDEFINED distribution tables for the three sequence alphabets are spec 23// DATA, not derivable, and are deliberately NOT included -- transcribing them 24// from memory would produce a decoder that looks right and disagrees with 25// every other implementation. They need to come from the specification text. 26// 27// genealogy_id: zstandard_rfc8878_fse_decoding 28// lineage_id: nx_zstd_fse_dec_v1 29// license_tier: ORIGINAL 30 31import "nx_syscalls.nx" 32import "nx_zstd_fse.nx" 33import "nx_zstd_bits.nx" 34 35// ===== state init ================================================= 36// 37// The initial state is tableLog raw bits off the backward stream. 38// Returns -1 if the table is absent or the stream is exhausted. 39 40func nx_zstd_fse_state_init(t: *NxFseTable, b: *NxZstdBits) -> i64 { 41 if t == (0 as *NxFseTable) { return 0 - 1 } 42 if b == (0 as *NxZstdBits) { return 0 - 1 } 43 let s: i64 = nx_zstd_bits_read(b, t.table_log) 44 if b.overflow == 1 { return 0 - 1 } 45 if s < 0 { return 0 - 1 } 46 if s >= t.table_size { return 0 - 1 } 47 return s 48} 49 50// ===== the symbol at the CURRENT state ============================ 51// 52// Always read before advancing. Reversing the two is a silent off-by-one 53// across the whole sequence list. 54 55func nx_zstd_fse_state_symbol(t: *NxFseTable, state: i64) -> i64 { 56 return nx_fse_cell_symbol(t, state) 57} 58 59// ===== advance to the next state ================================== 60// 61// newState = baseline + the next nbBits from the stream. nbBits may be ZERO, 62// which is legal and common for high-probability symbols -- a decoder that 63// treats zero as an error or reads a bit anyway desynchronises immediately. 64 65func nx_zstd_fse_state_next(t: *NxFseTable, b: *NxZstdBits, state: i64) -> i64 { 66 if t == (0 as *NxFseTable) { return 0 - 1 } 67 if b == (0 as *NxZstdBits) { return 0 - 1 } 68 let nb: i64 = nx_fse_cell_nbbits(t, state) 69 if nb < 0 { return 0 - 1 } 70 let base: i64 = nx_fse_cell_newstate(t, state) 71 if base < 0 { return 0 - 1 } 72 var add: i64 = 0 73 if nb > 0 { 74 add = nx_zstd_bits_read(b, nb) 75 if b.overflow == 1 { return 0 - 1 } 76 } 77 let ns: i64 = base + add 78 if ns < 0 { return 0 - 1 } 79 if ns >= t.table_size { return 0 - 1 } 80 return ns 81} 82 83// ===== bits remaining before a state can still advance ============ 84// 85// A caller decoding a known number of sequences needs to know whether the 86// stream can still feed them. Returns the bit count the next advance from 87// this state would consume, or -1. 88 89func nx_zstd_fse_state_cost(t: *NxFseTable, state: i64) -> i64 { 90 return nx_fse_cell_nbbits(t, state) 91}