nx_mediafacts_lib.nx source
↩ module page · 155 lines · 7742 B
1// nx_mediafacts_lib.nx -- MEDIA FACT EXTRACTION CORE (R1 of the render-docs ingest program,
2// knowledge/render_docs_ingest_program.txt). Turns raw media bytes into a CLOSED VOCABULARY of
3// integer measurements -- the substrate rows the refcorpus admission contract was built for.
4//
5// COMPOSES INCUMBENTS, BUILDS NO NEW DECODER (the duplicate-inflate lesson, debt 1785854636:
6// ask the corpus by capability before building): dimensions come from nx_img_dims.nx
7// (PNG/GIF/JPEG/WebP header walk, in-tree since the media importer). The WAV walk below is NOT
8// a copy of nx_wav_header.nx -- that parser's contract requires the WHOLE file in memory (it
9// bounds-checks every chunk BODY, incl. the data payload). Fact extraction reads a BOUNDED
10// HEADER WINDOW of possibly-huge files, so it needs a walk that only touches chunk HEADERS and
11// never requires the body to be present: a different contract, not a duplicate.
12//
13// ★THE PII WALL IS STRUCTURAL: no byte from the input file ever flows to the output. Every fact
14// is a computed integer or a fixed label from the enum below. EXIF/XMP are surfaced as a
15// PRESENCE FLAG only -- the segment is never parsed, so GPS coordinates, serial numbers and
16// embedded thumbnails are structurally unreadable through this lane.
17//
18// Fact slots (flat i64 array, MF_N_SLOTS):
19// [0] format 0=unknown 1=png 2=gif 3=jpeg 4=webp 5=wav
20// [1] width_px [2] height_px [3] exif_present (jpeg only)
21// [4] channels [5] sample_rate_hz [6] bits_per_sample [7] duration_ms (wav only)
22// Unfilled slots stay -1: "not measured" is DISTINCT from 0 -- a fabricated zero is the exact
23// plausible-number failure the soft-tissue lane banked.
24// license_tier: ORIGINAL No hw writes (Rule 26).
25import "nx_syscalls.nx"
26import "nx_img_dims.nx"
27
28const MF_N_SLOTS: i64 = 8
29const MF_FMT_UNKNOWN: i64 = 0
30const MF_FMT_PNG: i64 = 1
31const MF_FMT_GIF: i64 = 2
32const MF_FMT_JPEG: i64 = 3
33const MF_FMT_WEBP: i64 = 4
34const MF_FMT_WAV: i64 = 5
35
36func mf_b(buf: *u8, o: i64) -> i64 { return buf[o] as i64 & 0xff }
37func mf_u16le(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) }
38func mf_u32le(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) | ((b[o+3] as i64 & 0xff) << 24) }
39func mf_u16be(b: *u8, o: i64) -> i64 { return ((b[o] as i64 & 0xff) << 8) | (b[o+1] as i64 & 0xff) }
40
41// format id from magic bytes. This is DISPATCH, not a sniffer rebuild: nx_media_sniff.nx answers
42// "which NX_MEDIA_TYPE class" for the vault; facts need the concrete container, and only for the
43// five formats this lane measures. Anything else is UNKNOWN by design (never guessed from name).
44func mf_format(buf: *u8, n: i64) -> i64 {
45 if n < 16 { return MF_FMT_UNKNOWN }
46 if mf_b(buf,0) == 0x89 { if mf_b(buf,1) == 0x50 { if mf_b(buf,2) == 0x4e { if mf_b(buf,3) == 0x47 { return MF_FMT_PNG } } } }
47 if mf_b(buf,0) == 0x47 { if mf_b(buf,1) == 0x49 { if mf_b(buf,2) == 0x46 { if mf_b(buf,3) == 0x38 { return MF_FMT_GIF } } } }
48 if mf_b(buf,0) == 0xff { if mf_b(buf,1) == 0xd8 { return MF_FMT_JPEG } }
49 if mf_b(buf,0) == 0x52 { if mf_b(buf,1) == 0x49 { if mf_b(buf,2) == 0x46 { if mf_b(buf,3) == 0x46 {
50 if mf_b(buf,8) == 0x57 { if mf_b(buf,9) == 0x45 { if mf_b(buf,10) == 0x42 { if mf_b(buf,11) == 0x50 { return MF_FMT_WEBP } } } }
51 if mf_b(buf,8) == 0x57 { if mf_b(buf,9) == 0x41 { if mf_b(buf,10) == 0x56 { if mf_b(buf,11) == 0x45 { return MF_FMT_WAV } } } }
52 } } } }
53 return MF_FMT_UNKNOWN
54}
55
56// JPEG APP1/Exif PRESENCE flag. Walks marker headers only; stops at SOS (entropy data begins).
57// Reads exactly 4 payload bytes to match the label "Exif" -- the segment body is never parsed.
58func mf_jpeg_exif(buf: *u8, n: i64) -> i64 {
59 var p: i64 = 2
60 while p + 4 < n {
61 if mf_b(buf, p) != 0xff { return 0 }
62 var m: i64 = mf_b(buf, p + 1)
63 while m == 0xff { p = p + 1; if p + 4 >= n { return 0 } m = mf_b(buf, p + 1) }
64 if m == 0xda { return 0 }
65 if m == 0xd9 { return 0 }
66 if m >= 0xd0 { if m <= 0xd7 { p = p + 2 } else {
67 if m == 0xe1 { if p + 9 < n {
68 if mf_b(buf,p+4) == 0x45 { if mf_b(buf,p+5) == 0x78 { if mf_b(buf,p+6) == 0x69 { if mf_b(buf,p+7) == 0x66 { return 1 } } } } } }
69 let sl: i64 = mf_u16be(buf, p + 2)
70 if sl < 2 { return 0 }
71 p = p + 2 + sl
72 } } else {
73 if m == 0xe1 { if p + 9 < n {
74 if mf_b(buf,p+4) == 0x45 { if mf_b(buf,p+5) == 0x78 { if mf_b(buf,p+6) == 0x69 { if mf_b(buf,p+7) == 0x66 { return 1 } } } } } }
75 let sl2: i64 = mf_u16be(buf, p + 2)
76 if sl2 < 2 { return 0 }
77 p = p + 2 + sl2
78 }
79 }
80 return 0
81}
82
83// WAV facts from a bounded header window. Chunk-header walk: reads tag+size at each boundary and
84// records fmt fields + the data chunk's DECLARED size; never requires a body byte to be present,
85// so a 500MB recording measures from its first kilobytes. Stops when both found or the window ends.
86func mf_wav(buf: *u8, n: i64, facts: *i64) -> i64 {
87 var off: i64 = 12
88 var have_fmt: i64 = 0
89 var have_data: i64 = 0
90 var data_len: i64 = 0
91 while off + 8 <= n {
92 if have_fmt == 1 { if have_data == 1 { off = n + 8 } }
93 if off + 8 <= n {
94 let size: i64 = mf_u32le(buf, off + 4)
95 if mf_b(buf,off) == 0x66 { if mf_b(buf,off+1) == 0x6d { if mf_b(buf,off+2) == 0x74 { if mf_b(buf,off+3) == 0x20 {
96 if size >= 16 { if off + 24 <= n {
97 facts[4] = mf_u16le(buf, off + 10)
98 facts[5] = mf_u32le(buf, off + 12)
99 facts[6] = mf_u16le(buf, off + 22)
100 let brate: i64 = mf_u32le(buf, off + 16)
101 facts[7] = brate
102 have_fmt = 1
103 } }
104 } } } }
105 if mf_b(buf,off) == 0x64 { if mf_b(buf,off+1) == 0x61 { if mf_b(buf,off+2) == 0x74 { if mf_b(buf,off+3) == 0x61 {
106 data_len = size
107 have_data = 1
108 } } } }
109 var adv: i64 = 8 + size
110 if (adv % 2) == 1 { adv = adv + 1 }
111 if adv <= 0 { off = n + 8 } else { off = off + adv }
112 }
113 }
114 if have_fmt == 0 { return 0 - 1 }
115 // duration only when both the declared data size and a sane byte rate are in hand
116 if have_data == 1 { if facts[7] > 0 { facts[7] = (data_len * 1000) / facts[7] } else { facts[7] = 0 - 1 } } else { facts[7] = 0 - 1 }
117 return 0
118}
119
120// probe a byte window -> fill the fact slots. -1 = not measured, never fabricated.
121func mf_probe(buf: *u8, n: i64, facts: *i64) -> i64 {
122 var i: i64 = 0
123 while i < MF_N_SLOTS { facts[i] = 0 - 1; i = i + 1 }
124 let f: i64 = mf_format(buf, n)
125 facts[0] = f
126 if f == MF_FMT_UNKNOWN { return 0 }
127 if f == MF_FMT_WAV { mf_wav(buf, n, facts); return 0 }
128 // image formats: dims via the incumbent header reader
129 let wh: *i64 = sys_mmap(16) as *i64
130 if nx_img_dims(buf, n, wh) == 1 {
131 if wh[0] > 0 { facts[1] = wh[0] }
132 if wh[1] > 0 { facts[2] = wh[1] }
133 }
134 if f == MF_FMT_JPEG { facts[3] = mf_jpeg_exif(buf, n) }
135 return 0
136}
137
138// median of arr[0..n): insertion sort in place, upper median for even n (declared, deterministic).
139func mf_median(arr: *i64, n: i64) -> i64 {
140 if n <= 0 { return 0 - 1 }
141 var i: i64 = 1
142 while i < n {
143 let v: i64 = arr[i]
144 var j: i64 = i - 1
145 var stop: i64 = 0
146 while stop == 0 {
147 if j < 0 { stop = 1 } else {
148 if arr[j] > v { arr[j+1] = arr[j]; j = j - 1 } else { stop = 1 }
149 }
150 }
151 arr[j+1] = v
152 i = i + 1
153 }
154 return arr[n / 2]
155}