code wiki / (root) / nx_asset_meta.nx

nx_asset_meta.nx source

↩ module page · 99 lines · 5946 B

1// nx_asset_meta.nx -- METADATA ENRICHMENT (R3 of the universal organization-tooling arc). 2// 3// Makes the catalog know WHAT each asset IS: it populates the type-specific metadata block plus the 4// Dublin Core core spine ON an asset record, so downstream search/dedup/dashboard can answer "show me 5// 1920x1080 PNGs" or "docs about taxes" without re-opening the bytes (org_research.tsv CONFIRMED: 6// schema-mediaobject = width/height/duration/encodingFormat; dublin-core-core = title/creator/date/ 7// subject; iptc-image-embedded = read intrinsic image facts from the bytes). 8// 9// ZERO NEW STORAGE/IDENTITY -- pure COMPOSITION over what already exists: 10// * record field model + canonical encode/decode + CID -> nx_asset_record (ar_fields/ar_addf/ 11// ar_encode/ar_decode/ar_get/ar_cid/ar_cpy) 12// * REAL image header extraction (width/height/format) -> nx_png_header (png_check_sig / 13// png_parse_ihdr / PngIhdr) [iptc-image-embedded] 14// 15// HONEST EXTRACTION BOUNDARY: 16// * IMAGE (PNG): width / height / encoding_format are REAL-EXTRACTED from the actual bytes via the 17// in-tree PNG header decoder (signature + IHDR). No PNG signature -> not a PNG; we do NOT fabricate 18// dimensions (am_enrich_image returns AM_NOT_PNG and writes nothing -- deny-by-default honesty). 19// * DOC / DATASET: there is no universal binary header carrying a title; the title/subject are 20// PROVIDED (Dublin Core descriptive metadata is authored, not intrinsic). The model + API is the 21// deliverable; real per-format extraction (e.g. JPEG/EXIF, MP4 boxes) wires in exactly here as new 22// am_enrich_* paths, each reusing the same ar_addf record-extension idiom (#19 / #25). 23// 24// Pure/deterministic; no hardware/persistent-firmware writes (Rule 26). 25// license_tier: ORIGINAL 26import "nx_syscalls.nx" 27import "nx_asset_record.nx" 28import "nx_png_header.nx" 29 30const AM_NOT_PNG: i64 = 0 - 1 // bytes carry no PNG signature -> we refuse to invent dimensions 31 32// integer -> null-terminated decimal string in caller buffer `out` (>= 24 bytes). Returns digit count. 33// Record field VALUES are strings (canon_encode is a string KV store), so an extracted width 1920 must 34// become the bytes "1920\0" before ar_addf. Deterministic; handles 0; non-negative dimensions only. 35func am_itoa(v: i64, out: *u8) -> i64 { 36 if v < 0 { v = 0 } 37 let t: *u8 = sys_mmap(24) 38 var k: i64 = 0 39 if v == 0 { t[0] = 48 as u8; k = 1 } 40 while v > 0 { t[k] = (48 + (v % 10)) as u8; v = v / 10; k = k + 1 } 41 var i: i64 = 0 42 while i < k { out[i] = t[k - 1 - i]; i = i + 1 } 43 out[k] = 0 as u8 44 return k 45} 46 47// ---- IMAGE enrichment: REAL extraction of width/height/encoding_format from PNG bytes ---- 48// Reads the actual signature + IHDR (nx_png_header), formats width/height as decimal strings into the 49// caller-provided w_out/h_out buffers (>=24 each) and stamps encoding_format = "image/png" into 50// fmt_out. Returns 0 on a real PNG (buffers populated, ready for ar_addf), AM_NOT_PNG otherwise 51// (nothing written -- we never fabricate dimensions for non-PNG bytes). 52func am_extract_image(bytes: *u8, len: i64, w_out: *u8, h_out: *u8, fmt_out: *u8) -> i64 { 53 if png_check_sig(bytes, len) != 0 { return AM_NOT_PNG } 54 let ih_raw: *u8 = sys_mmap(64) 55 let ih: *PngIhdr = ih_raw as *PngIhdr 56 if png_parse_ihdr(bytes, len, ih) != 0 { return AM_NOT_PNG } 57 am_itoa(ih.width, w_out) 58 am_itoa(ih.height, h_out) 59 ar_cpy(fmt_out, "image/png\x00" as *u8) 60 return 0 61} 62 63// am_enrich_image: REAL-extract image facts from `bytes`, then EXTEND an existing field set 64// (keys[]/vals[], with kv[0] = current field count) by adding width/height/encoding_format -- exactly 65// the ar_addf idiom R0 uses, so the enriched fields key-sort + round-trip through ar_encode/ar_decode 66// identically. The w_buf/h_buf/fmt_buf are caller-mmap'd (>=24/24/16) and OUTLIVE this call because 67// ar_addf stores pointers into the field set (no copy). Returns the new field count, or AM_NOT_PNG 68// (field set untouched) if the bytes are not a PNG. 69func am_enrich_image(bytes: *u8, len: i64, keys: *i64, vals: *i64, kv: *i64, 70 w_buf: *u8, h_buf: *u8, fmt_buf: *u8) -> i64 { 71 if am_extract_image(bytes, len, w_buf, h_buf, fmt_buf) != 0 { return AM_NOT_PNG } 72 ar_addf(keys, vals, kv, "width\x00" as *u8, w_buf) 73 ar_addf(keys, vals, kv, "height\x00" as *u8, h_buf) 74 ar_addf(keys, vals, kv, "encoding_format\x00" as *u8, fmt_buf) 75 return kv[0] 76} 77 78// ---- DOC / DATASET enrichment: Dublin Core descriptive spine (PROVIDED, not intrinsic) ---- 79// am_enrich_doc: extend a field set with dc:title + dc:subject (faceted-findability). These are 80// AUTHORED descriptive metadata (a doc's bytes carry no universal title header), so they are provided 81// by the caller -- the same ar_addf record-extension contract. Empty values are skipped by ar_addf 82// (ar_present), so a title-only doc simply omits subject. Returns the new field count. 83func am_enrich_doc(title: *u8, subject: *u8, keys: *i64, vals: *i64, kv: *i64) -> i64 { 84 ar_addf(keys, vals, kv, "title\x00" as *u8, title) 85 ar_addf(keys, vals, kv, "subject\x00" as *u8, subject) 86 return kv[0] 87} 88 89// am_enrich_dc: the full Dublin Core core spine in one call (dublin-core-core: title/creator/date/ 90// subject). Any empty field is skipped (ar_present). Reusable across every asset type -- the 91// descriptive layer is type-agnostic, unlike the media block. Returns the new field count. 92func am_enrich_dc(title: *u8, creator: *u8, date: *u8, subject: *u8, 93 keys: *i64, vals: *i64, kv: *i64) -> i64 { 94 ar_addf(keys, vals, kv, "title\x00" as *u8, title) 95 ar_addf(keys, vals, kv, "creator\x00" as *u8, creator) 96 ar_addf(keys, vals, kv, "date\x00" as *u8, date) 97 ar_addf(keys, vals, kv, "subject\x00" as *u8, subject) 98 return kv[0] 99}