main.mx raw
1 package main
2
3 import (
4 "fmt"
5 "git.smesh.lol/iskradb/lattice"
6 )
7
8 func main() {
9 testInMemory()
10 fmt.Printf("\n--- disk-backed test ---\n")
11 testDisk()
12 fmt.Printf("\n--- transfer test ---\n")
13 testTransfer()
14 }
15
16 func testInMemory() {
17 t := lattice.NewTree(64)
18 fmt.Printf("iskradb: %d nodes, %d records\n", t.NodeCount(), t.RecordCount())
19
20 key := lattice.HashKey([]byte("hello"))
21 fmt.Printf("hash(hello) = %x\n", key)
22
23 p1 := lattice.MakeTritPath(1, 2, 1)
24 p2 := lattice.MakeTritPath(1, 2, 3)
25 fmt.Printf("p1 depth=%d branch=%d\n", p1.Depth(), p1.Branch())
26 fmt.Printf("distance(p1,p2)=%d\n", lattice.Distance(p1, p2))
27 lca := lattice.LCA(p1, p2)
28 fmt.Printf("lca depth=%d\n", lca.Depth())
29 resolved := lattice.Resolve(p1, 2)
30 fmt.Printf("resolve(p1,2) depth=%d\n", resolved.Depth())
31
32 words := []string{"cat", "dog", "run", "jump", "big", "small", "eat", "fly", "red", "hot"}
33 for _, w := range words {
34 var rec lattice.Record
35 rec.SetInline([]byte(w))
36 t.Insert(lattice.Bnoun, lattice.HashKey([]byte(w)), rec)
37 }
38 fmt.Printf("after insert: %d nodes, %d records\n", t.NodeCount(), t.RecordCount())
39
40 r := t.Lookup(lattice.Bnoun, lattice.HashKey([]byte("dog")))
41 if r != nil {
42 fmt.Printf("lookup(dog) = %s\n", r.InlineData())
43 }
44
45 var nRec, vRec, mRec lattice.Record
46 nRec.SetInline([]byte("bird"))
47 vRec.SetInline([]byte("flies"))
48 mRec.SetInline([]byte("high"))
49 t.InsertTriple(
50 lattice.HashKey([]byte("bird")),
51 lattice.HashKey([]byte("flies")),
52 lattice.HashKey([]byte("high")),
53 nRec, vRec, mRec,
54 )
55
56 birdRI := t.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("bird")))
57 if birdRI != lattice.NullRec {
58 fmt.Printf("cross-walk: ")
59 t.CrossWalk(birdRI, lattice.PatNounVerbMod, func(ri uint32, r *lattice.Record) bool {
60 if r != nil && r.IsInline() {
61 fmt.Printf("%s ", r.InlineData())
62 }
63 return true
64 })
65 fmt.Printf("\n")
66 }
67 }
68
69 func testDisk() {
70 dbPath := "/tmp/iskra_disk.db"
71
72 // phase 1: create, insert, flush, close
73 t, err := lattice.Create(dbPath)
74 if err != nil {
75 fmt.Printf("create error: %s\n", err)
76 return
77 }
78 for i := 0; i < 100; i++ {
79 w := fmt.Sprintf("word%04d", i)
80 var rec lattice.Record
81 rec.SetInline([]byte(w))
82 t.Insert(lattice.Bnoun, lattice.HashKey([]byte(w)), rec)
83 }
84 // insert some triples
85 for i := 0; i < 10; i++ {
86 noun := fmt.Sprintf("noun%d", i)
87 verb := fmt.Sprintf("verb%d", i)
88 mod := fmt.Sprintf("mod%d", i)
89 var nRec, vRec, mRec lattice.Record
90 nRec.SetInline([]byte(noun))
91 vRec.SetInline([]byte(verb))
92 mRec.SetInline([]byte(mod))
93 t.InsertTriple(
94 lattice.HashKey([]byte(noun)),
95 lattice.HashKey([]byte(verb)),
96 lattice.HashKey([]byte(mod)),
97 nRec, vRec, mRec,
98 )
99 }
100 fmt.Printf("created: %d nodes, %d records\n", t.NodeCount(), t.RecordCount())
101 err = t.Close()
102 if err != nil {
103 fmt.Printf("close error: %s\n", err)
104 return
105 }
106
107 // phase 2: reopen, verify lookups
108 t, err = lattice.Open(dbPath)
109 if err != nil {
110 fmt.Printf("open error: %s\n", err)
111 return
112 }
113 found := 0
114 for i := 0; i < 100; i++ {
115 w := fmt.Sprintf("word%04d", i)
116 r := t.Lookup(lattice.Bnoun, lattice.HashKey([]byte(w)))
117 if r != nil && r.IsInline() && string(r.InlineData()) == w {
118 found++
119 }
120 }
121 fmt.Printf("reopened: %d/100 words found\n", found)
122
123 n5ri := t.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("noun5")))
124 if n5ri != lattice.NullRec {
125 fmt.Printf("triple walk noun5: ")
126 t.CrossWalk(n5ri, lattice.PatNounVerbMod, func(ri uint32, r *lattice.Record) bool {
127 if r != nil && r.IsInline() {
128 fmt.Printf("%s ", r.InlineData())
129 }
130 return true
131 })
132 fmt.Printf("\n")
133 }
134
135 // phase 3: insert more, delete some, close
136 for i := 100; i < 110; i++ {
137 w := fmt.Sprintf("word%04d", i)
138 var rec lattice.Record
139 rec.SetInline([]byte(w))
140 t.Insert(lattice.Bnoun, lattice.HashKey([]byte(w)), rec)
141 }
142 for i := 0; i < 5; i++ {
143 w := fmt.Sprintf("word%04d", i)
144 t.Delete(lattice.Bnoun, lattice.HashKey([]byte(w)))
145 }
146 fmt.Printf("after mutations: %d nodes, freeCount=%d\n", t.NodeCount(), t.FreeCount)
147 err = t.Close()
148 if err != nil {
149 fmt.Printf("close error: %s\n", err)
150 return
151 }
152
153 // phase 4: reopen, verify state
154 t, err = lattice.Open(dbPath)
155 if err != nil {
156 fmt.Printf("open error: %s\n", err)
157 return
158 }
159 // deleted words should be gone
160 r := t.Lookup(lattice.Bnoun, lattice.HashKey([]byte("word0000")))
161 if r == nil {
162 fmt.Printf("word0000 after delete+reopen: NOT FOUND (correct)\n")
163 } else {
164 fmt.Printf("word0000 after delete+reopen: FOUND (BUG)\n")
165 }
166 // new words should be present
167 r = t.Lookup(lattice.Bnoun, lattice.HashKey([]byte("word0105")))
168 if r != nil && r.IsInline() {
169 fmt.Printf("word0105 after reopen: %s\n", r.InlineData())
170 } else {
171 fmt.Printf("word0105 after reopen: NOT FOUND (BUG)\n")
172 }
173
174 // compact and verify
175 reclaimed := t.Compact()
176 fmt.Printf("compacted: reclaimed=%d, now %d nodes\n", reclaimed, t.NodeCount())
177 r = t.Lookup(lattice.Bnoun, lattice.HashKey([]byte("word0050")))
178 if r != nil && r.IsInline() {
179 fmt.Printf("post-compact lookup(word0050) = %s\n", r.InlineData())
180 }
181
182 err = t.Close()
183 if err != nil {
184 fmt.Printf("final close error: %s\n", err)
185 }
186 fmt.Printf("disk test complete\n")
187 }
188
189 func testTransfer() {
190 t := lattice.NewTree(256)
191 for i := 0; i < 50; i++ {
192 w := fmt.Sprintf("n%d", i)
193 var rec lattice.Record
194 rec.SetInline([]byte(w))
195 t.Insert(lattice.Bnoun, lattice.HashKey([]byte(w)), rec)
196 }
197 for i := 0; i < 10; i++ {
198 noun := fmt.Sprintf("t_noun%d", i)
199 verb := fmt.Sprintf("t_verb%d", i)
200 mod := fmt.Sprintf("t_mod%d", i)
201 var nRec, vRec, mRec lattice.Record
202 nRec.SetInline([]byte(noun))
203 vRec.SetInline([]byte(verb))
204 mRec.SetInline([]byte(mod))
205 t.InsertTriple(
206 lattice.HashKey([]byte(noun)),
207 lattice.HashKey([]byte(verb)),
208 lattice.HashKey([]byte(mod)),
209 nRec, vRec, mRec,
210 )
211 }
212 fmt.Printf("source: %d nodes, %d records\n", t.NodeCount(), t.RecordCount())
213
214 // 1. ExtractBranch - noun branch only
215 sl := t.ExtractBranch(lattice.Bnoun)
216 fmt.Printf("extract branch noun: %d nodes, %d records\n", sl.Tree.NodeCount(), sl.Tree.RecordCount())
217 r := sl.Tree.Lookup(lattice.Bnoun, lattice.HashKey([]byte("n25")))
218 if r != nil && r.IsInline() {
219 fmt.Printf(" lookup(n25) = %s\n", r.InlineData())
220 } else {
221 fmt.Printf(" lookup(n25) = NOT FOUND (BUG)\n")
222 }
223
224 // 2. ExtractLinked - follow cross-branch links from t_noun5
225 n5ri := t.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("t_noun5")))
226 linked := t.ExtractLinked(n5ri)
227 fmt.Printf("extract linked t_noun5: %d records\n", linked.Tree.RecordCount())
228 lri := linked.Tree.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("t_noun5")))
229 if lri != lattice.NullRec {
230 fmt.Printf(" cross-walk: ")
231 linked.Tree.CrossWalk(lri, lattice.PatNounVerbMod, func(ri uint32, r *lattice.Record) bool {
232 if r != nil && r.IsInline() {
233 fmt.Printf("%s ", r.InlineData())
234 }
235 return true
236 })
237 fmt.Printf("\n")
238 } else {
239 fmt.Printf(" t_noun5 NOT FOUND in extract (BUG)\n")
240 }
241
242 // 3. SaveFile/LoadFile round-trip
243 slPath := "/tmp/iskra_sub.iskr"
244 err := linked.SaveFile(slPath)
245 if err != nil {
246 fmt.Printf(" save error: %s\n", err)
247 return
248 }
249 loaded, err := lattice.LoadSubLattice(slPath)
250 if err != nil {
251 fmt.Printf(" load error: %s\n", err)
252 return
253 }
254 loadedRI := loaded.Tree.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("t_noun5")))
255 if loadedRI != lattice.NullRec {
256 fmt.Printf(" round-trip cross-walk: ")
257 loaded.Tree.CrossWalk(loadedRI, lattice.PatNounVerbMod, func(ri uint32, r *lattice.Record) bool {
258 if r != nil && r.IsInline() {
259 fmt.Printf("%s ", r.InlineData())
260 }
261 return true
262 })
263 fmt.Printf("\n")
264 } else {
265 fmt.Printf(" round-trip t_noun5 NOT FOUND (BUG)\n")
266 }
267
268 // 4. Graft into fresh tree
269 dst := lattice.NewTree(64)
270 grafted := dst.Graft(linked)
271 fmt.Printf("graft into empty: inserted=%d, records=%d\n", grafted, dst.RecordCount())
272 gri := dst.LookupRecIdx(lattice.Bnoun, lattice.HashKey([]byte("t_noun5")))
273 if gri != lattice.NullRec {
274 fmt.Printf(" graft cross-walk: ")
275 dst.CrossWalk(gri, lattice.PatNounVerbMod, func(ri uint32, r *lattice.Record) bool {
276 if r != nil && r.IsInline() {
277 fmt.Printf("%s ", r.InlineData())
278 }
279 return true
280 })
281 fmt.Printf("\n")
282 } else {
283 fmt.Printf(" graft t_noun5 NOT FOUND (BUG)\n")
284 }
285
286 // 5. Graft with overlap - dedup test
287 beforeRecs := dst.RecordCount()
288 deduped := dst.Graft(linked)
289 fmt.Printf("graft overlap: inserted=%d (should be 0), records before=%d after=%d\n",
290 deduped, beforeRecs, dst.RecordCount())
291 if dst.RecordCount() == beforeRecs {
292 fmt.Printf(" dedup: correct\n")
293 } else {
294 fmt.Printf(" dedup: FAILED - records grew (BUG)\n")
295 }
296 }
297