nx_bgzf.nx source
↩ module page · 151 lines · 5872 B
1// nx_bgzf.nx -- BGZF (Blocked GZIP Format) container per SAMv1 §4.1.
2//
3// BGZF wraps each block in a gzip member with an extra "BC" subfield
4// carrying BSIZE = total block bytes - 1, allowing random access
5// (a reader can advance exactly one block without inflating).
6//
7// G6.0e.1: nx_bgzf_eof_marker -- the canonical 28-byte EOF marker
8// that every BAM file MUST end with (SAMv1 §4.1.2; htslib uses
9// the literal byte sequence). This is a well-known fixed
10// sequence; encoded as a fixed-Huffman empty DEFLATE block.
11//
12// G6.0e.2: nx_bgzf_write_stored_block -- write a single BGZF block
13// carrying `data` of length `len` using DEFLATE BTYPE=00
14// (stored / no compression). Always succeeds for len up to the
15// stored-block limit (65504 bytes; keeps total ≤ 65535).
16// Composes nx_crc32 (CRC-32 of the uncompressed payload) and
17// nx_le_write_u16/u32 (all multi-byte fields little-endian).
18//
19// Compressed BGZF blocks (BTYPE=01/10) require a DEFLATE ENCODER
20// which the substrate does not yet ship (nx_deflate is decode-only).
21// Stored-block BGZF is a fully spec-conformant BAM container and
22// htslib will read it without warning.
23//
24// expect_exit: 0
25//
26// license_tier: ORIGINAL
27
28import "nx_syscalls.nx"
29import "nx_const.nx"
30import "nx_le.nx"
31import "nx_crc32.nx"
32
33// Stored-block max payload to keep block_size ≤ 65535.
34// block = 12 (gzip hdr+xlen) + 6 (BC extra) + 5 (BTYPE=00 hdr)
35// + L (payload) + 4 (CRC32) + 4 (ISIZE) = 31 + L
36// limit L ≤ 65504
37const NX_BGZF_STORED_PAYLOAD_MAX: i64 = 65504
38const NX_BGZF_STORED_OVERHEAD: i64 = 31 // bytes added around payload
39const NX_BGZF_EOF_MARKER_LEN: i64 = 28
40
41// G6.0e.1 -- canonical 28-byte EOF marker.
42// Wire bytes per SAMv1 §4.1.2 / htslib EOF block:
43// 1f 8b 08 04 00 00 00 00 00 ff gzip hdr (FEXTRA flag set)
44// 06 00 XLEN = 6
45// 42 43 02 00 1b 00 'BC' subfield, SLEN=2, BSIZE=27
46// 03 00 empty fixed-Huffman DEFLATE block
47// 00 00 00 00 CRC32 of empty = 0
48// 00 00 00 00 ISIZE = 0
49// Returns 28 on success, or -1 on capacity error.
50func nx_bgzf_eof_marker(out_bytes: *u8, max_out: i64) -> i64 {
51 if max_out < NX_BGZF_EOF_MARKER_LEN { return -1 }
52 out_bytes[0] = 0x1F as u8
53 out_bytes[1] = 0x8B as u8
54 out_bytes[2] = 0x08 as u8
55 out_bytes[3] = 0x04 as u8
56 out_bytes[4] = 0x00 as u8
57 out_bytes[5] = 0x00 as u8
58 out_bytes[6] = 0x00 as u8
59 out_bytes[7] = 0x00 as u8
60 out_bytes[8] = 0x00 as u8
61 out_bytes[9] = 0xFF as u8
62 out_bytes[10] = 0x06 as u8
63 out_bytes[11] = 0x00 as u8
64 out_bytes[12] = 0x42 as u8 // 'B'
65 out_bytes[13] = 0x43 as u8 // 'C'
66 out_bytes[14] = 0x02 as u8
67 out_bytes[15] = 0x00 as u8
68 out_bytes[16] = 0x1B as u8 // BSIZE = 27
69 out_bytes[17] = 0x00 as u8
70 out_bytes[18] = 0x03 as u8 // empty fixed-Huffman block
71 out_bytes[19] = 0x00 as u8
72 out_bytes[20] = 0x00 as u8 // CRC32 = 0
73 out_bytes[21] = 0x00 as u8
74 out_bytes[22] = 0x00 as u8
75 out_bytes[23] = 0x00 as u8
76 out_bytes[24] = 0x00 as u8 // ISIZE = 0
77 out_bytes[25] = 0x00 as u8
78 out_bytes[26] = 0x00 as u8
79 out_bytes[27] = 0x00 as u8
80 return NX_BGZF_EOF_MARKER_LEN
81}
82
83// G6.0e.2 -- write one BGZF block carrying `data` of length `len`
84// using DEFLATE BTYPE=00 (stored / no compression).
85//
86// Layout (total = 31 + len bytes; BSIZE = total - 1):
87// [0..10) gzip header : 1f 8b 08 04 00 00 00 00 00 ff
88// [10..12) XLEN = 6 : 06 00
89// [12..18) BC extra : 42 43 02 00 BSIZE_LE
90// [18..23) stored hdr : 01 LEN_LE NLEN_LE (BFINAL=1, BTYPE=00)
91// [23..23+len) : payload bytes (verbatim)
92// [+0..+4) : CRC32 LE of payload
93// [+4..+8) : ISIZE LE = len
94//
95// Returns total bytes written, or -1 on payload-overflow or
96// out-buffer-overflow.
97func nx_bgzf_write_stored_block(data: *u8, len: i64,
98 out_bytes: *u8, max_out: i64) -> i64 {
99 if len < 0 { return -1 }
100 if len > NX_BGZF_STORED_PAYLOAD_MAX { return -1 }
101 let total: i64 = NX_BGZF_STORED_OVERHEAD + len
102 if total > max_out { return -1 }
103 let bsize: i64 = total - 1
104
105 // gzip header (10 bytes).
106 out_bytes[0] = 0x1F as u8
107 out_bytes[1] = 0x8B as u8
108 out_bytes[2] = 0x08 as u8 // CM = deflate
109 out_bytes[3] = 0x04 as u8 // FLG = FEXTRA
110 out_bytes[4] = 0x00 as u8 // MTIME = 0
111 out_bytes[5] = 0x00 as u8
112 out_bytes[6] = 0x00 as u8
113 out_bytes[7] = 0x00 as u8
114 out_bytes[8] = 0x00 as u8 // XFL = 0
115 out_bytes[9] = 0xFF as u8 // OS = unknown
116
117 // XLEN = 6 (LE).
118 if nx_le_write_u16(out_bytes, 10, 6) < 0 { return -1 }
119
120 // BC extra subfield: 'B' 'C' SLEN=2 BSIZE.
121 out_bytes[12] = 0x42 as u8 // 'B'
122 out_bytes[13] = 0x43 as u8 // 'C'
123 if nx_le_write_u16(out_bytes, 14, 2) < 0 { return -1 }
124 if nx_le_write_u16(out_bytes, 16, bsize) < 0 { return -1 }
125
126 // DEFLATE stored block header: 01 LEN_LE NLEN_LE.
127 // bit 0 (BFINAL) = 1, bits 1-2 (BTYPE) = 00.
128 // First byte therefore 0x01. Remaining bits skipped to next
129 // byte boundary per RFC 1951 §3.2.4.
130 out_bytes[18] = 0x01 as u8
131 if nx_le_write_u16(out_bytes, 19, len) < 0 { return -1 }
132 let nlen: i64 = (~len) & 0xffff
133 if nx_le_write_u16(out_bytes, 21, nlen) < 0 { return -1 }
134
135 // payload (copy data verbatim).
136 var i: i64 = 0
137 while i < len {
138 out_bytes[23 + i] = data[i]
139 i = i + 1
140 }
141
142 // CRC32 of payload.
143 let crc: i64 = nx_crc32(data, len)
144 let crc_off: i64 = 23 + len
145 if nx_le_write_u32(out_bytes, crc_off, crc) < 0 { return -1 }
146
147 // ISIZE = len.
148 if nx_le_write_u32(out_bytes, crc_off + 4, len) < 0 { return -1 }
149
150 return total
151}