nx_sovgit_obj.nx source
↩ module page · 219 lines · 8430 B
1// nx_sovgit_obj.nx -- SOVEREIGN GIT OBJECT STORE (roadmap rung X0). Content-addressed blob/tree/commit:
2// frame = "<type> <len>\0<content>"
3// id = SHA-256(frame) [git object-format=sha256, 64 lowercase hex]
4// loose = zlib(frame) at <objroot>/<id[0:2]>/<id[2:64]>
5// Reuses the sovereign substrate, no reinvention: sha256_digest (nx_sha256), dfe_compress (nx_deflate_enc,
6// raw deflate -> we wrap 0x78 0x01 + adler32), nx_zlib_inflate + adler32 (nx_zlib_wrap/nx_adler32).
7// Proven byte-exact against STOCK git 2.39 sha256 ids in nx_sovgit_obj_gate.nx.
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_sha256.nx"
11import "nx_deflate_enc.nx"
12import "nx_zlib_wrap.nx"
13import "nx_adler32.nx"
14const K_MAGIC_1040: i64 = 1040
15const K_MAGIC_65536: i64 = 65536
16const K_MAGIC_4096: i64 = 4096
17
18// ---- byte helpers ----
19func sg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
20
21func sg_cpy(dst: *u8, dsto: i64, src: *u8, srco: i64, n: i64) -> i64 {
22 var i: i64 = 0
23 while i < n { dst[dsto + i] = src[srco + i]; i = i + 1 }
24 return dsto + n
25}
26
27// write decimal ascii of v (>= 0) into out at `at`; return new offset
28func sg_dec(out: *u8, at: i64, v: i64) -> i64 {
29 let tmp: *u8 = sys_mmap(32)
30 var x: i64 = v
31 var i: i64 = 31
32 if x == 0 { tmp[i] = 48 as u8; i = i - 1 }
33 while x > 0 { tmp[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 }
34 var a: i64 = at
35 var j: i64 = i + 1
36 while j < 32 { out[a] = tmp[j]; a = a + 1; j = j + 1 }
37 return a
38}
39
40// 32-byte digest -> 64 lowercase hex chars at out
41func sg_hex(dig: *u8, out: *u8) -> i64 {
42 var i: i64 = 0
43 while i < 32 {
44 let b: i64 = dig[i] as i64
45 let hi: i64 = (b >> 4) & 15
46 let lo: i64 = b & 15
47 var ch: i64 = 48 + hi
48 if hi > 9 { ch = 87 + hi }
49 out[i * 2] = ch as u8
50 var cl: i64 = 48 + lo
51 if lo > 9 { cl = 87 + lo }
52 out[i * 2 + 1] = cl as u8
53 i = i + 1
54 }
55 return 0
56}
57
58// big-endian u32 into out at `at`
59func sg_be32(out: *u8, at: i64, v: i64) -> i64 {
60 out[at] = ((v >> 24) & 255) as u8
61 out[at + 1] = ((v >> 16) & 255) as u8
62 out[at + 2] = ((v >> 8) & 255) as u8
63 out[at + 3] = (v & 255) as u8
64 return at + 4
65}
66
67// ---- git object framing ----
68// "<typ> <clen>\0<content>" into out; return frame length. typ is NUL-terminated.
69func sg_frame(typ: *u8, content: *u8, clen: i64, out: *u8) -> i64 {
70 var o: i64 = 0
71 o = sg_cpy(out, o, typ, 0, sg_slen(typ))
72 out[o] = 32 as u8; o = o + 1
73 o = sg_dec(out, o, clen)
74 out[o] = 0 as u8; o = o + 1
75 o = sg_cpy(out, o, content, 0, clen)
76 return o
77}
78
79// raw 32-byte object id of (typ, content)
80// bounded-VSZ 2026-07-20: the frame buffer is munmapped before return -- callers that hash one object
81// PER FILE (the sovereign pusher walking a tree) would otherwise leak sum(filesizes) of VSZ.
82func sg_oid_raw(typ: *u8, content: *u8, clen: i64, out32: *u8) -> i64 {
83 let fbuf: *u8 = sys_mmap(clen + 64)
84 let flen: i64 = sg_frame(typ, content, clen, fbuf)
85 sha256_digest(fbuf, flen, out32)
86 sys_munmap(fbuf, clen + 64)
87 return 0
88}
89
90// hex (64 chars) object id of (typ, content)
91func sg_oid_hex(typ: *u8, content: *u8, clen: i64, outhex: *u8) -> i64 {
92 let dig: *u8 = sys_mmap(32)
93 sg_oid_raw(typ, content, clen, dig)
94 sg_hex(dig, outhex)
95 return 0
96}
97
98// ---- zlib wrap (git loose object body) ----
99// wrap `frame` (flen bytes) as a full zlib stream into zout; return zlib length.
100// ⚠CONTRACT: zout MUST be sized >= flen + (flen >> 1) + 64. Fixed-Huffman deflate EXPANDS
101// high-entropy input (literals >=144 cost 9 bits; worst case with 3-byte matches ~1.30x) --
102// the old "flen + 128" contract overflowed on random/binary payloads >= ~256KB (caught by
103// round-trip probe 2026-07-20: firstdiff=0 err=adler from 262144 up; the sovgit big-blob bug).
104func sg_zwrap(frame: *u8, flen: i64, zout: *u8) -> i64 {
105 zout[0] = 0x78 as u8
106 zout[1] = 0x01 as u8
107 let body: *u8 = (zout as i64 + 2) as *u8
108 let clen: i64 = dfe_compress(frame, flen, body)
109 var zo: i64 = 2 + clen
110 sg_be32(zout, zo, adler32(frame, flen)); zo = zo + 4
111 return zo
112}
113
114// ---- loose object store ----
115// path <objroot>/<hex[0:2]>/<hex[2:64]> into pbuf (NUL-terminated); return length.
116func sg_loose_path(objroot: *u8, hex: *u8, pbuf: *u8) -> i64 {
117 var o: i64 = sg_cpy(pbuf, 0, objroot, 0, sg_slen(objroot))
118 pbuf[o] = 47 as u8; o = o + 1
119 pbuf[o] = hex[0]; o = o + 1
120 pbuf[o] = hex[1]; o = o + 1
121 pbuf[o] = 47 as u8; o = o + 1
122 var i: i64 = 2
123 while i < 64 { pbuf[o] = hex[i]; o = o + 1; i = i + 1 }
124 pbuf[o] = 0 as u8
125 return o
126}
127
128// write (typ, content) as a loose object under objroot; write 64-hex id to outhex; return 0 ok / negative err.
129// Bounded-VSZ: every internal mmap is munmapped (hot-loop callers -- pack unpack -- stay flat).
130// Short/failed writes return -2 (fail LOUD; a truncated loose object must never report ok).
131func sg_write_loose(objroot: *u8, typ: *u8, content: *u8, clen: i64, outhex: *u8) -> i64 {
132 sg_oid_hex(typ, content, clen, outhex)
133 let rl: i64 = sg_slen(objroot)
134 sys_mkdir(objroot, 0x1ed)
135 let dbuf: *u8 = sys_mmap(rl + 8)
136 var d: i64 = sg_cpy(dbuf, 0, objroot, 0, rl)
137 dbuf[d] = 47 as u8; d = d + 1
138 dbuf[d] = outhex[0]; d = d + 1
139 dbuf[d] = outhex[1]; d = d + 1
140 dbuf[d] = 0 as u8
141 sys_mkdir(dbuf, 0x1ed)
142 let fbuf: *u8 = sys_mmap(clen + 64)
143 let flen: i64 = sg_frame(typ, content, clen, fbuf)
144 let zcap: i64 = flen + (flen >> 1) + 128
145 let zbuf: *u8 = sys_mmap(zcap)
146 let zlen: i64 = sg_zwrap(fbuf, flen, zbuf)
147 let pbuf: *u8 = sys_mmap(rl + 80)
148 sg_loose_path(objroot, outhex, pbuf)
149 let fd: i64 = sys_openat_wr(pbuf, 0x1a4)
150 var ret: i64 = 0
151 if fd < 0 { ret = 0 - 1 } else {
152 let wn: i64 = sys_write(fd, zbuf, zlen)
153 if wn != zlen { ret = 0 - 2 }
154 sys_close(fd)
155 }
156 sys_munmap(dbuf, rl + 8)
157 sys_munmap(fbuf, clen + 64)
158 sys_munmap(zbuf, zcap)
159 sys_munmap(pbuf, rl + 80)
160 return ret
161}
162
163// read a loose object (64-hex id) under objroot; content -> out, type (NUL-term) -> typ_out;
164// return content length, or negative on error.
165// Inflate cap is INPUT-DERIVED (zlib expansion bounded ~1032x), clamped to the 64MiB object
166// envelope -- objects > 1MiB read correctly (the old fixed 1<<20 cap silently dropped them).
167// Bounded-VSZ: all mappings munmapped (a full-repo pack walk calls this per object).
168func sg_read_loose(objroot: *u8, hex: *u8, out: *u8, typ_out: *u8) -> i64 {
169 let pl: i64 = sg_slen(objroot) + 80
170 let pbuf: *u8 = sys_mmap(pl)
171 sg_loose_path(objroot, hex, pbuf)
172 let szp: *i64 = sys_mmap(16) as *i64
173 let zbuf: *u8 = sys_read_file(pbuf, szp)
174 sys_munmap(pbuf, pl)
175 if (zbuf as i64) == 0 { sys_munmap(szp as *u8, 16); return 0 - 1 }
176 let zn: i64 = szp[0]
177 sys_munmap(szp as *u8, 16)
178 var cap: i64 = zn * K_MAGIC_1040 + K_MAGIC_65536
179 if cap > (1 << 26) { cap = 1 << 26 }
180 let r: *NxZlibResult = nx_zlib_inflate(zbuf, zn, cap)
181 var ret: i64 = 0 - 2
182 if r.error_code == 0 {
183 let frame: *u8 = r.output_data
184 var i: i64 = 0
185 var t: i64 = 0
186 while frame[i] != (32 as u8) { typ_out[t] = frame[i]; t = t + 1; i = i + 1 }
187 typ_out[t] = 0 as u8
188 i = i + 1
189 var clen: i64 = 0
190 while frame[i] != (0 as u8) { clen = clen * 10 + ((frame[i] as i64) - 48); i = i + 1 }
191 i = i + 1
192 sg_cpy(out, 0, frame, i, clen)
193 ret = clen
194 }
195 if (r.output_data as i64) != 0 { sys_munmap(r.output_data, cap) }
196 sys_munmap(r as *u8, K_MAGIC_4096)
197 sys_munmap(zbuf, zn)
198 return ret
199}
200
201// ---- tree entry ----
202// append "<mode> <name>\0<rawhash32>" to out at `at`; return new offset. mode/name are NUL-terminated.
203func sg_tree_entry(out: *u8, at: i64, mode: *u8, name: *u8, rawhash: *u8) -> i64 {
204 var o: i64 = sg_cpy(out, at, mode, 0, sg_slen(mode))
205 out[o] = 32 as u8; o = o + 1
206 o = sg_cpy(out, o, name, 0, sg_slen(name))
207 out[o] = 0 as u8; o = o + 1
208 o = sg_cpy(out, o, rawhash, 0, 32)
209 return o
210}
211
212// compile-only smoke: empty-blob id must begin with git's documented sha256 constant "47..."
213func main() -> i64 {
214 let hx: *u8 = sys_mmap(80)
215 sg_oid_hex("blob" as *u8, "" as *u8, 0, hx)
216 if hx[0] != (52 as u8) { return 1 }
217 if hx[1] != (55 as u8) { return 1 }
218 return 0
219}