code wiki / (root) / nx_h264_bits.nx

nx_h264_bits.nx source

↩ module page · 106 lines · 3725 B

1// nx_h264_bits.nx -- H.264 bitstream reader + Exp-Golomb (rung 2 keystone). 2// EVERY field in H.264 SPS/PPS/slice headers is coded as ue(v) (unsigned 3// Exp-Golomb) or se(v) (signed), read MSB-first. This is THE primitive the 4// whole decoder stands on -- so it is built and KAT-gated against the canonical 5// Exp-Golomb code words before anything above it is attempted. 6// 7// ue(v): count leadingZeros z (zeros before the first 1, which is consumed), 8// read z more bits 'rest', value = (1<<z) - 1 + rest. 9// codeword "1"->0, "010"->1, "011"->2, "00100"->3, "00101"->4 ... 10// se(v): map k=ue(v): 0->0, 1->+1, 2->-1, 3->+2, 4->-2 (sign + ceil(k/2)). 11// 12// genealogy_id: itu_t_h264_annexB_exp_golomb + teuhola_1978_exp_golomb 13// lineage_id: msb_bitreader + ue_se_exp_golomb 14// license_tier: ORIGINAL 15import "nx_syscalls.nx" 16 17struct BitReader { 18 data: *u8, 19 byte_pos: i64, 20 bit_pos: i64, // 0..7, 0 = MSB of the current byte 21 len: i64, 22 // PER-BLOCK SCRATCH (2026-08-01). These were mmapped INSIDE the residual 23 // helpers, which run once per 4x4 block (~196,000 calls per 1080p frame). 24 // This toolchain has page-granular mmap with NO free, so that leaked GBs and 25 // near-wedged the host. Every helper already takes br, and br_init runs ONCE 26 // per decode, so carrying scratch here fixes it with no caller changes. 27 ct: *i64, 28 level: *i64, 29 run: *i64, 30 pos: *i64, 31 val: *i64, 32 // coeff_token table buffers (2026-08-01): the decode wrappers allocated four 33 // 64-word buffers PER CALL to hand to ct_table_*, once per 4x4 block. 34 ctln: *i64, 35 ctcd: *i64, 36 cttc: *i64, 37 ctto: *i64, 38} 39const NX_BR_BYTES: i64 = 128 // 9 fields x 8 = 72; 128 leaves headroom. All 14 40 // allocation sites use this const, so they grow too. 41 42func br_init(br: *BitReader, data: *u8, len: i64) -> i64 { 43 br.data = data 44 br.byte_pos = 0 45 br.bit_pos = 0 46 br.len = len 47 br.ct = sys_mmap(64) as *i64 48 br.level = sys_mmap(32 * 8) as *i64 49 br.run = sys_mmap(32 * 8) as *i64 50 br.pos = sys_mmap(32 * 8) as *i64 51 br.val = sys_mmap(32 * 8) as *i64 52 br.ctln = sys_mmap(64 * 8) as *i64 53 br.ctcd = sys_mmap(64 * 8) as *i64 54 br.cttc = sys_mmap(64 * 8) as *i64 55 br.ctto = sys_mmap(64 * 8) as *i64 56 return 0 57} 58 59// read one bit MSB-first; 0 past end (caller guards real streams) 60func br_read_bit(br: *BitReader) -> i64 { 61 if br.byte_pos >= br.len { return 0 } 62 let bit: i64 = ((br.data[br.byte_pos] as i64) >> (7 - br.bit_pos)) & 1 63 br.bit_pos = br.bit_pos + 1 64 if br.bit_pos == 8 { br.bit_pos = 0; br.byte_pos = br.byte_pos + 1 } 65 return bit 66} 67 68// read n bits MSB-first as an unsigned value 69func br_read_bits(br: *BitReader, n: i64) -> i64 { 70 var v: i64 = 0 71 var i: i64 = 0 72 while i < n { 73 v = (v << 1) | br_read_bit(br) 74 i = i + 1 75 } 76 return v 77} 78 79// unsigned Exp-Golomb ue(v) 80func br_read_ue(br: *BitReader) -> i64 { 81 var z: i64 = 0 82 var seen: i64 = 0 83 while seen == 0 { 84 if br.byte_pos >= br.len { return 0 } 85 let b: i64 = br_read_bit(br) 86 if b == 1 { seen = 1 } 87 if b == 0 { z = z + 1 } 88 } 89 let rest: i64 = br_read_bits(br, z) 90 return (1 << z) - 1 + rest 91} 92 93// signed Exp-Golomb se(v) 94func br_read_se(br: *BitReader) -> i64 { 95 let k: i64 = br_read_ue(br) 96 if k == 0 { return 0 } 97 let mag: i64 = (k + 1) / 2 98 if (k & 1) == 1 { return mag } 99 return 0 - mag 100} 101 102// byte-align the reader (used between NAL parse stages) 103func br_align(br: *BitReader) -> i64 { 104 if br.bit_pos != 0 { br.bit_pos = 0; br.byte_pos = br.byte_pos + 1 } 105 return 0 106}