code wiki / (root) / nx_codec_caps.nx

nx_codec_caps.nx source

↩ module page · 279 lines · 10356 B

1// nx_codec_caps.nx -- the SINGLE source of truth for what we may advertise. 2// 3// THE DEFECT THIS EXISTS TO KILL. Thirteen call sites across the fetchers and 4// the browser each hand-wrote their own negotiation headers. Two of them lie: 5// nx_http_client sends `Accept: ...,image/avif,image/webp,...` and 6// nx_polite_browser sends `Accept-Encoding: gzip, deflate, br`. We have no 7// AVIF pixel codec and no Brotli decoder. A server that believes either one 8// answers with bytes we cannot read -- and the failure is not an error, it is 9// a blank image or a body of binary noise handed to an HTML parser. 10// 11// The other nine sites have the opposite defect: they send 12// `Accept-Encoding: identity` while nx_gzip_inflate and nx_zlib_inflate are 13// both in-tree and complete. That is a real capability left on the floor on 14// every fetch. 15// 16// A FIX THAT NEEDS MEMORY AT THIRTEEN SITES IS NOT A FIX. So the header is not 17// written anywhere -- it is DERIVED, here, from a registry of what actually 18// decodes. Adding a decoder means flipping one flag in one table and every 19// call site that composes this becomes honest in the same act. Drift is not 20// discouraged, it is unrepresentable. 21// 22// WHAT IS DECLARED HAVE, AND WHY (verified 2026-07-31 by reading the tree, 23// not by recalling it): 24// image/gif -- nx_gif_decode.nx (gif_decode -> w*h grayscale, LZW, + gate) 25// WIRED into nx_img_bytes_to_rgb 2026-08-04; before that the 26// decoder existed but no image path called it. 27// 28// WITHDRAWN 2026-08-04 -- image/webp, and WHY (measured, not recalled): 29// The row above claimed "VP8L lossless + VP8 keyframe". VP8L is real. VP8 30// KEYFRAME IS NOT: nx_vp8.nx is the boolean entropy decoder + frame-header 31// parser ONLY -- no macroblock modes, no coefficient decode, no dequant/IDCT, 32// no intra prediction, no reconstruction. And webp_decode() walks chunks to 33// the VP8L fourcc and returns 0 for anything else. 34// MEASURED (nx_webpprobe over a 101-file real-web corpus): 25 of 25 WebP were 35// VP8 LOSSY, and webp_decode returned 0 on every one. Zero VP8L in the wild. 36// CONSEQUENCE OF THE LIE: because we advertised image/webp, content-negotiating 37// origins served WebP for URLs ending .jpg (proven: a Wikimedia 38// 250px-*.jpg came back RIFF/WEBP), so the advertisement CREATED the 39// undecodable corpus it claimed to be able to read. 40// Restore have=1 only when a VP8 keyframe decoder reconstructs real pixels. 41// image/png -- nx_png_decoder.nx 42// image/jpeg -- nx_jpeg_decode_image.nx (baseline + progressive) 43// gzip -- nx_gzip_inflate (nx_gzip_wrap.nx) over nx_deflate + nx_crc32 44// deflate -- nx_zlib_inflate (nx_zlib_wrap.nx), i.e. RFC 1950 zlib, 45// which is what HTTP `deflate` actually means 46// 47// WHAT IS DECLARED ABSENT, AND WHY: 48// image/avif -- eight AV1 layers exist (entropy coder, OBU, sequence, tile, 49// frame, transforms, DCT, intra) but no frame decoder yet 50// image/apng -- nx_png_decoder handles a single IDAT stream, not acTL/fcTL 51// br -- no Brotli decoder; the 122KB static dictionary is unbuilt 52// zstd -- ten gated modules exist but no real .zst has been decoded 53// end to end, so the bytes are unproven. An UNPROVEN decoder 54// must not appear in a header: advertising it is a promise. 55// 56// license_tier: ORIGINAL 57import "nx_syscalls.nx" 58const NX_MAGIC_1024: i64 = 1024 59 60// ===== registry: image formats ==================================== 61 62const NX_CC_N_IMAGE: i64 = 8 63 64func cc_image_name(i: i64) -> *u8 { 65 if i == 0 { return "image/webp" as *u8 } 66 if i == 1 { return "image/png" as *u8 } 67 if i == 2 { return "image/jpeg" as *u8 } 68 if i == 3 { return "image/gif" as *u8 } 69 if i == 4 { return "image/avif" as *u8 } 70 if i == 5 { return "image/apng" as *u8 } 71 if i == 6 { return "image/bmp" as *u8 } 72 if i == 7 { return "image/tiff" as *u8 } 73 return 0 as *u8 74} 75 76func cc_image_have(i: i64) -> i64 { 77 if i == 0 { return 0 } // image/webp: LOSSY VP8 IS NOT DECODABLE -- see the note above (2026-08-04) 78 if i == 1 { return 1 } 79 if i == 2 { return 1 } // + DRI/RST restart markers 2026-08-05 (nx_jpeg_rst_gate) 80 if i == 3 { return 1 } // + palette colour + de-interlace 2026-08-05 (nx_gif_color_gate) 81 if i == 4 { return 0 } 82 if i == 5 { return 0 } 83 if i == 6 { return 1 } // image/bmp: nx_bmp_decode 2026-08-05 (nx_bmp_tiff_gate) -- BI_RGB 1/4/8/24/32 + RLE8 84 if i == 7 { return 1 } // image/tiff: nx_tiff_decode 2026-08-05 (nx_bmp_tiff_gate) -- baseline strips, none+PackBits 85 return 0 86} 87 88// ===== registry: content encodings ================================ 89// 90// `identity` is not in the table -- it is the floor, always available, and 91// emitted when nothing else is. Putting it in the table would let a future 92// edit set have=0 on it and produce an empty header. 93 94const NX_CC_N_ENCODING: i64 = 4 95 96func cc_encoding_name(i: i64) -> *u8 { 97 if i == 0 { return "gzip" as *u8 } 98 if i == 1 { return "deflate" as *u8 } 99 if i == 2 { return "br" as *u8 } 100 if i == 3 { return "zstd" as *u8 } 101 return 0 as *u8 102} 103 104func cc_encoding_have(i: i64) -> i64 { 105 if i == 0 { return 1 } 106 if i == 1 { return 1 } 107 if i == 2 { return 0 } 108 if i == 3 { return 0 } 109 return 0 110} 111 112// ===== small string helpers ======================================= 113 114func nx_cc_len(s: *u8) -> i64 { 115 var n: i64 = 0 116 if s == (0 as *u8) { return 0 } 117 while s[n] != (0 as u8) { n = n + 1 } 118 return n 119} 120 121func nx_cc_eq(a: *u8, b: *u8) -> i64 { 122 var i: i64 = 0 123 if a == (0 as *u8) { return 0 } 124 if b == (0 as *u8) { return 0 } 125 while 1 == 1 { 126 if a[i] != b[i] { return 0 } 127 if a[i] == (0 as u8) { return 1 } 128 i = i + 1 129 } 130 return 0 131} 132 133// Append s at out[off]; returns the new offset. Always leaves out 134// NUL-terminated so a partially built header is still a valid C string. 135func nx_cc_put(out: *u8, off: i64, s: *u8) -> i64 { 136 var i: i64 = 0 137 var o: i64 = off 138 if s == (0 as *u8) { return off } 139 while s[i] != (0 as u8) { 140 out[o] = s[i] 141 o = o + 1 142 i = i + 1 143 } 144 out[o] = 0 as u8 145 return o 146} 147 148// Index of needle in hay, or -1. Used by callers that must assert a 149// header does NOT carry a token. 150func nx_cc_find(hay: *u8, needle: *u8) -> i64 { 151 let hn: i64 = nx_cc_len(hay) 152 let nn: i64 = nx_cc_len(needle) 153 var i: i64 = 0 154 var j: i64 = 0 155 var hit: i64 = 0 156 if nn == 0 { return 0 } 157 if nn > hn { return 0 - 1 } 158 while i <= hn - nn { 159 j = 0 160 hit = 1 161 while j < nn { 162 if hay[i + j] != needle[j] { hit = 0; j = nn } else { j = j + 1 } 163 } 164 if hit == 1 { return i } 165 i = i + 1 166 } 167 return 0 - 1 168} 169 170// Count comma-separated tokens in s. Empty string is zero tokens. 171// The gate uses this to prove the emitted header carries EXACTLY as 172// many entries as the registry declares available -- a token count 173// that drifts from the registry means something was hand-added. 174func nx_cc_count_tokens(s: *u8) -> i64 { 175 let n: i64 = nx_cc_len(s) 176 var i: i64 = 0 177 var count: i64 = 1 178 if n == 0 { return 0 } 179 while i < n { 180 if s[i] == (0x2c as u8) { count = count + 1 } 181 i = i + 1 182 } 183 return count 184} 185 186// ===== capability queries ========================================= 187// 188// An unknown name returns 0. A wrong answer here would put a format we 189// cannot decode into a live header, which is the exact defect. 190 191func nx_codec_caps_have_image(name: *u8) -> i64 { 192 var i: i64 = 0 193 while i < NX_CC_N_IMAGE { 194 if nx_cc_eq(cc_image_name(i), name) == 1 { return cc_image_have(i) } 195 i = i + 1 196 } 197 return 0 198} 199 200func nx_codec_caps_have_encoding(name: *u8) -> i64 { 201 var i: i64 = 0 202 if nx_cc_eq("identity" as *u8, name) == 1 { return 1 } 203 while i < NX_CC_N_ENCODING { 204 if nx_cc_eq(cc_encoding_name(i), name) == 1 { return cc_encoding_have(i) } 205 i = i + 1 206 } 207 return 0 208} 209 210// ===== header emitters ============================================ 211// 212// Both walk the registry. Neither contains a literal format name, so 213// neither can disagree with the queries above. 214 215func nx_codec_caps_accept_image(out: *u8) -> i64 { 216 var i: i64 = 0 217 var o: i64 = 0 218 var first: i64 = 1 219 out[0] = 0 as u8 220 while i < NX_CC_N_IMAGE { 221 if cc_image_have(i) == 1 { 222 if first == 0 { o = nx_cc_put(out, o, "," as *u8) } 223 o = nx_cc_put(out, o, cc_image_name(i)) 224 first = 0 225 } 226 i = i + 1 227 } 228 return o 229} 230 231// Never empty: with no decoders at all this still emits `identity`, 232// which is the one encoding that is true by construction. 233func nx_codec_caps_accept_encoding(out: *u8) -> i64 { 234 var i: i64 = 0 235 var o: i64 = 0 236 var first: i64 = 1 237 out[0] = 0 as u8 238 while i < NX_CC_N_ENCODING { 239 if cc_encoding_have(i) == 1 { 240 if first == 0 { o = nx_cc_put(out, o, ", " as *u8) } 241 o = nx_cc_put(out, o, cc_encoding_name(i)) 242 first = 0 243 } 244 i = i + 1 245 } 246 if first == 1 { o = nx_cc_put(out, o, "identity" as *u8) } 247 return o 248} 249 250// The full Accept value for a document fetch. The image sublist is the 251// SAME string accept_image produces -- spliced, not re-typed -- so the 252// document and image negotiations cannot drift apart. 253func nx_codec_caps_accept_document(out: *u8) -> i64 { 254 let imgs: *u8 = sys_mmap(512) 255 var o: i64 = 0 256 nx_codec_caps_accept_image(imgs) 257 out[0] = 0 as u8 258 o = nx_cc_put(out, o, "text/html,application/xhtml+xml,application/xml;q=0.9," as *u8) 259 o = nx_cc_put(out, o, imgs) 260 o = nx_cc_put(out, o, ",*/*;q=0.8" as *u8) 261 return o 262} 263 264// The complete request-header block a fetcher should send. One call, 265// so a new fetcher cannot forget half of it. 266func nx_codec_caps_headers(out: *u8) -> i64 { 267 let acc: *u8 = sys_mmap(NX_MAGIC_1024) 268 let enc: *u8 = sys_mmap(256) 269 var o: i64 = 0 270 nx_codec_caps_accept_document(acc) 271 nx_codec_caps_accept_encoding(enc) 272 out[0] = 0 as u8 273 o = nx_cc_put(out, o, "Accept: " as *u8) 274 o = nx_cc_put(out, o, acc) 275 o = nx_cc_put(out, o, "\r\nAccept-Encoding: " as *u8) 276 o = nx_cc_put(out, o, enc) 277 o = nx_cc_put(out, o, "\r\n" as *u8) 278 return o 279}