code wiki / (root) / nx_mvault_tag.nx

nx_mvault_tag.nx source

↩ module page · 101 lines · 3853 B

1// nx_mvault_tag.nx -- extensible, searchable metadata planes for vault items. 2// 3// DESIGN (operator 2026-07-17: "hammer searchability + extensibility BEFORE the 4// heavy reclassify ... performer/pose/body-type ... booru but far beyond ... 5// lossless language aka hip-to-waist ratios ... feed the future joy classifier"): 6// three ADDITIVE planes over the immutable core record, so new auto-tag 7// capabilities slot in with ZERO schema migration -- 8// FACETS numeric fields (size/recency/popularity/dims) -> SORT KEYS for 9// fast large/recent/popular finding (byte-lex order == numeric). 10// TAGS booru-style OPEN namespaces "ns:value" (performer:/pose:/hair:/ 11// body:/ethnicity:/nipple:/cup:...) -> inverted index per namespace. 12// ATTRS lossless quantitative "key=ivalue/scale" (hip_waist_ratio=700/1000 13// = exact 0.700, no-float) -> range-searchable. The booru-beyond bit. 14// 15// All CID-keyed + additive; searchability rides the librarian info-finding index 16// (coordinate, don't rebuild). This module is deliberately OPEN -- it hardcodes 17// NO tag list, so performer/pose/body/etc. are just data. license_tier: ORIGINAL 18 19import "nx_syscalls.nx" 20 21// append unsigned decimal of v to dst[off..]; returns new offset. 22func mv_u_dec(dst: *u8, off: i64, v: i64) -> i64 { 23 if v == 0 { dst[off] = 48 as u8; return off + 1 } 24 let tmp: *u8 = sys_mmap(24) 25 var m: i64 = v 26 var k: i64 = 0 27 while m > 0 { tmp[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 28 var o: i64 = off 29 var j: i64 = k - 1 30 while j >= 0 { dst[o] = tmp[j]; o = o + 1; j = j - 1 } 31 return o 32} 33 34// ===== TAGS: booru-style namespaced, OPEN ===== 35// "ns:value" -> out (NUL-terminated); returns length. 36func mv_tag_make(out: *u8, ns: *u8, value: *u8) -> i64 { 37 var o: i64 = 0 38 var i: i64 = 0 39 while ns[i] != (0 as u8) { out[o] = ns[i]; o = o + 1; i = i + 1 } 40 out[o] = 58 as u8; o = o + 1 // ':' 41 i = 0 42 while value[i] != (0 as u8) { out[o] = value[i]; o = o + 1; i = i + 1 } 43 out[o] = 0 as u8 44 return o 45} 46 47// length of the namespace (chars before ':'); -1 if untagged. 48func mv_tag_ns_len(tag: *u8) -> i64 { 49 var i: i64 = 0 50 while tag[i] != (0 as u8) { 51 if tag[i] == (58 as u8) { return i } 52 i = i + 1 53 } 54 return 0 - 1 55} 56 57// ===== ATTRS: lossless quantitative ===== 58// "key=ivalue/scale"; the exact value is ivalue/scale (scale keeps decimals, 59// so nothing is rounded away -- "lossless"). 60func mv_attr_make(out: *u8, key: *u8, ivalue: i64, scale: i64) -> i64 { 61 var o: i64 = 0 62 var i: i64 = 0 63 while key[i] != (0 as u8) { out[o] = key[i]; o = o + 1; i = i + 1 } 64 out[o] = 61 as u8; o = o + 1 // '=' 65 o = mv_u_dec(out, o, ivalue) 66 out[o] = 47 as u8; o = o + 1 // '/' 67 o = mv_u_dec(out, o, scale) 68 out[o] = 0 as u8 69 return o 70} 71 72// ===== FACETS: sortable keys for large/recent/popular ===== 73// "<field>|" + 8 big-endian bytes of uvalue => byte-lexicographic order equals 74// numeric order, so a byte-sorted index answers range + top-N. Binary key (may 75// contain NUL); caller stores with the returned length. 76func mv_facet_key(out: *u8, field: *u8, uvalue: i64) -> i64 { 77 var o: i64 = 0 78 var i: i64 = 0 79 while field[i] != (0 as u8) { out[o] = field[i]; o = o + 1; i = i + 1 } 80 out[o] = 124 as u8; o = o + 1 // '|' 81 var s: i64 = 56 82 while s >= 0 { 83 out[o] = ((uvalue >> s) & 0xff) as u8 84 o = o + 1 85 s = s - 8 86 } 87 return o 88} 89 90// byte-compare two equal-length buffers: -1 / 0 / 1 91func mv_bcmp(a: *u8, b: *u8, n: i64) -> i64 { 92 var i: i64 = 0 93 while i < n { 94 let av: i64 = a[i] 95 let bv: i64 = b[i] 96 if av < bv { return 0 - 1 } 97 if av > bv { return 1 } 98 i = i + 1 99 } 100 return 0 101}