code wiki / (root) / nx_licdetect_lib.nx

nx_licdetect_lib.nx source

↩ module page · 228 lines · 9446 B

1// nx_licdetect_lib.nx -- WHICH LICENCE IS THIS TEXT? Read from the bytes, never from a declaration. 2// 3// THE MEASUREMENT THAT JUSTIFIES A TEXT DETECTOR AT ALL. Across 124,278 licence chains surveyed in the 4// field, 96.5% of datasets and 95.8% of models LACK the required licence text, and only 2.3% / 3.2% 5// satisfy both the licence-text and copyright requirements. The survey's conclusion is this lib's 6// design rule: LICENCE FILES AND NOTICES, NOT METADATA, ARE THE SOURCE OF LEGAL TRUTH. 7// It is also what the librarian measured directly: of 26 NAVER releases, 2 carried any licence string 8// on the listing, and the one we then FETCHED turned out to be 483 bytes off the canonical Apache text 9// because it adds a copyright preamble and drops the appendix. A declaration is a claim; bytes are not. 10// 11// THREE OUTCOMES, AND ONLY ONE OF THEM IS AN ANSWER: 12// DETECTED exactly one licence's REQUIRE markers all matched and none of its EXCLUDE markers did 13// UNKNOWN nothing matched -- fail-closed, the artifact does NOT advance to evidence=READ 14// AMBIGUOUS two or more matched -- ALSO fail-closed. A text matching two licences is not a decision, 15// and taking the first would be a coin flip wearing a verdict. 16// 17// MARKERS ARE DATA (knowledge/license_markers.conf), so a new licence costs a row and never a rebuild, 18// and every marker set is auditable by a human reading the conf rather than the binary. 19// 20// NO GUESSED CAPS: every array here is sized from the CONF'S OWN LINE COUNT, counted before allocation. 21// A marker table cannot overflow a bound derived from the file that defines it. 22// license_tier: ORIGINAL 23import "nx_syscalls.nx" 24 25const LD_DETECTED: i64 = 0 26const LD_UNKNOWN: i64 = 1 27const LD_AMBIGUOUS: i64 = 2 28 29const LD_TAB: i64 = 9 30const LD_NL: i64 = 10 31const LD_HASH: i64 = 35 32const LD_M: i64 = 77 // 'M' marker row 33const LD_V: i64 = 86 // 'V' verified-against row 34const LD_WORD: i64 = 8 35const LD_ROW_STRIDE: i64 = 5 // lic_off, lic_len, kind, mk_off, mk_len 36const LD_KIND_REQUIRE: i64 = 1 37const LD_KIND_EXCLUDE: i64 = 2 38const LD_CTX_SLOTS: i64 = 8 // buf, n, rows, nrows, vrows, nv 39 40func ld_streq_n(a: *u8, ao: i64, an: i64, b: *u8, bo: i64, bn: i64) -> i64 { 41 if an != bn { return 0 } 42 var i: i64 = 0 43 while i < an { if a[ao + i] != b[bo + i] { return 0 } i = i + 1 } 44 return 1 45} 46 47// literal substring search. Returns 1 if needle occurs in text[0,n). 48func ld_contains(text: *u8, n: i64, buf: *u8, mo: i64, mn: i64) -> i64 { 49 if mn <= 0 { return 0 } 50 if mn > n { return 0 } 51 var i: i64 = 0 52 let last: i64 = n - mn 53 while i <= last { 54 var j: i64 = 0 55 var ok: i64 = 1 56 while j < mn { 57 if text[i + j] != buf[mo + j] { ok = 0; j = mn } else { j = j + 1 } 58 } 59 if ok == 1 { return 1 } 60 i = i + 1 61 } 62 return 0 63} 64 65// TAB field [idx] of row [s,e) -> box[0]=off box[1]=len ; len -1 when the row has too few fields. 66func ld_field(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64 { 67 var f: i64 = 0 68 var i: i64 = s 69 while f < idx { 70 if i >= e { box[0] = 0 - 1; box[1] = 0 - 1; return 0 - 1 } 71 if buf[i] == (LD_TAB as u8) { f = f + 1 } 72 i = i + 1 73 } 74 var j: i64 = i 75 while j < e { if buf[j] == (LD_TAB as u8) { break } j = j + 1 } 76 box[0] = i 77 box[1] = j - i 78 return j - i 79} 80 81// ctx[0]=buf ctx[1]=n ctx[2]=rows ctx[3]=nrows ctx[4]=vrows ctx[5]=nv ; ctx[3] = -1 means unreadable. 82func ld_ctx() -> *i64 { 83 let ctx: *i64 = sys_mmap(LD_CTX_SLOTS * LD_WORD) as *i64 84 let lp: *i64 = sys_mmap(LD_WORD * 2) as *i64 85 let buf: *u8 = sys_read_file("knowledge/license_markers.conf" as *u8, lp) 86 ctx[3] = 0 - 1 87 ctx[5] = 0 88 if (buf as i64) == 0 { return ctx } 89 let n: i64 = lp[0] 90 ctx[0] = buf as i64 91 ctx[1] = n 92 if n <= 0 { return ctx } 93 94 // CAP DERIVED FROM THE DATA: a marker row cannot outnumber the conf's own lines. 95 var lines: i64 = 1 96 var c: i64 = 0 97 while c < n { if buf[c] == (LD_NL as u8) { lines = lines + 1 } c = c + 1 } 98 let rows: *i64 = sys_mmap(lines * LD_ROW_STRIDE * LD_WORD) as *i64 99 let vrows: *i64 = sys_mmap(lines * 2 * LD_WORD) as *i64 100 let box: *i64 = sys_mmap(32) as *i64 101 102 var nrows: i64 = 0 103 var nv: i64 = 0 104 var p: i64 = 0 105 while p < n { 106 var q: i64 = p 107 var scan: i64 = 1 108 while scan == 1 { 109 if q >= n { scan = 0 } 110 else { if buf[q] == (LD_NL as u8) { scan = 0 } else { q = q + 1 } } 111 } 112 if q > p + 1 { 113 if buf[p + 1] == (LD_TAB as u8) { 114 if buf[p] == (LD_M as u8) { 115 if ld_field(buf, p, q, 1, box) >= 0 { 116 let lo: i64 = box[0] 117 let ll: i64 = box[1] 118 if ld_field(buf, p, q, 2, box) >= 0 { 119 // REQUIRE and EXCLUDE are both 7 characters, so the LENGTH admits the 120 // field and the FIRST BYTE discriminates ('R'=82, 'E'=69). A row whose 121 // kind word is neither keeps kind 0 and is therefore matched by no branch 122 // in ld_detect -- an unrecognised kind is INERT, never silently REQUIRE. 123 var kind: i64 = 0 124 let ko: i64 = box[0] 125 let kl: i64 = box[1] 126 if kl == 7 { 127 if buf[ko] == (82 as u8) { kind = LD_KIND_REQUIRE } 128 if buf[ko] == (69 as u8) { kind = LD_KIND_EXCLUDE } 129 } 130 if ld_field(buf, p, q, 3, box) >= 0 { 131 rows[nrows * LD_ROW_STRIDE + 0] = lo 132 rows[nrows * LD_ROW_STRIDE + 1] = ll 133 rows[nrows * LD_ROW_STRIDE + 2] = kind 134 rows[nrows * LD_ROW_STRIDE + 3] = box[0] 135 rows[nrows * LD_ROW_STRIDE + 4] = box[1] 136 nrows = nrows + 1 137 } 138 } 139 } 140 } 141 if buf[p] == (LD_V as u8) { 142 if ld_field(buf, p, q, 1, box) >= 0 { 143 vrows[nv * 2 + 0] = box[0] 144 vrows[nv * 2 + 1] = box[1] 145 nv = nv + 1 146 } 147 } 148 } 149 } 150 p = q + 1 151 } 152 ctx[2] = rows as i64 153 ctx[3] = nrows 154 ctx[4] = vrows as i64 155 ctx[5] = nv 156 return ctx 157} 158 159// is this licence's marker set PROVEN against a licence text we actually hold? 160func ld_marker_set_verified(ctx: *i64, lo: i64, ll: i64) -> i64 { 161 let buf: *u8 = ctx[0] as *u8 162 let vrows: *i64 = ctx[4] as *i64 163 let nv: i64 = ctx[5] 164 var i: i64 = 0 165 while i < nv { 166 if ld_streq_n(buf, lo, ll, buf, vrows[i * 2 + 0], vrows[i * 2 + 1]) == 1 { return 1 } 167 i = i + 1 168 } 169 return 0 170} 171 172// THE DETECTION. Fills out_lo/out_ll with the winning licence id's offset+len in the conf buffer. 173// Returns LD_DETECTED / LD_UNKNOWN / LD_AMBIGUOUS. 174func ld_detect(ctx: *i64, text: *u8, n: i64, out: *i64) -> i64 { 175 out[0] = 0 - 1 176 out[1] = 0 - 1 177 if ctx[3] < 0 { return LD_UNKNOWN } 178 let buf: *u8 = ctx[0] as *u8 179 let rows: *i64 = ctx[2] as *i64 180 let nrows: i64 = ctx[3] 181 var hits: i64 = 0 182 var i: i64 = 0 183 while i < nrows { 184 let lo: i64 = rows[i * LD_ROW_STRIDE + 0] 185 let ll: i64 = rows[i * LD_ROW_STRIDE + 1] 186 // only evaluate a licence the FIRST time its id appears, so a licence with k markers is not 187 // scored k times -- that would make the ambiguity count meaningless. 188 var first: i64 = 1 189 var b: i64 = 0 190 while b < i { 191 if ld_streq_n(buf, lo, ll, buf, rows[b * LD_ROW_STRIDE + 0], rows[b * LD_ROW_STRIDE + 1]) == 1 { first = 0; b = i } 192 else { b = b + 1 } 193 } 194 if first == 1 { 195 var all_req: i64 = 1 196 var any_exc: i64 = 0 197 var j: i64 = 0 198 while j < nrows { 199 if ld_streq_n(buf, lo, ll, buf, rows[j * LD_ROW_STRIDE + 0], rows[j * LD_ROW_STRIDE + 1]) == 1 { 200 let kind: i64 = rows[j * LD_ROW_STRIDE + 2] 201 let present: i64 = ld_contains(text, n, buf, rows[j * LD_ROW_STRIDE + 3], rows[j * LD_ROW_STRIDE + 4]) 202 if kind == LD_KIND_REQUIRE { if present == 0 { all_req = 0 } } 203 if kind == LD_KIND_EXCLUDE { if present == 1 { any_exc = 1 } } 204 } 205 j = j + 1 206 } 207 if all_req == 1 { 208 if any_exc == 0 { 209 hits = hits + 1 210 out[0] = lo 211 out[1] = ll 212 } 213 } 214 } 215 i = i + 1 216 } 217 if hits == 0 { out[0] = 0 - 1; out[1] = 0 - 1; return LD_UNKNOWN } 218 // TWO MATCHES IS NOT A TIE TO BREAK, IT IS A REFUSAL. Clearing the output makes it impossible for a 219 // caller to read a winner out of an ambiguous result by ignoring the return code. 220 if hits > 1 { out[0] = 0 - 1; out[1] = 0 - 1; return LD_AMBIGUOUS } 221 return LD_DETECTED 222} 223 224func ld_result_name(r: i64) -> *u8 { 225 if r == LD_DETECTED { return "DETECTED" as *u8 } 226 if r == LD_AMBIGUOUS { return "AMBIGUOUS" as *u8 } 227 return "UNKNOWN" as *u8 228}