nx_engram.nx source
↩ module page · 130 lines · 4259 B
1// nx_engram.nx -- short-term memory / cache (hippocampal-class).
2//
3// Biology: an engram is a physical/chemical change that represents
4// a stored memory. Hippocampal short-term memory consolidates into
5// long-term storage via repeated activation.
6
7import "nx_syscalls.nx"
8import "nx_tier.nx"
9
10const NX_EG_V_OK: nx_int = 0
11const NX_EG_V_HIT: nx_int = 1
12const NX_EG_V_MISS: nx_int = 2
13const NX_EG_V_EVICTED: nx_int = 3
14const NX_EG_V_NULL: nx_int = 4
15const NX_EG_V_N: nx_int = 5
16
17struct NxEngramEntry {
18 key_hash: nx_size,
19 value_hash: nx_size,
20 inserted_at_us: nx_size,
21 last_accessed_us: nx_size,
22 access_count: nx_int,
23 consolidation_score: nx_int, // 0=fresh; high=consolidated to long-term
24}
25
26const NX_EG_ENT_BYTES: nx_int = 40
27
28struct NxEngramCache {
29 entries: *u8, // array of NxEngramEntry
30 n_entries: nx_int,
31 capacity: nx_int,
32 next_idx: nx_int, // ring buffer head
33}
34
35const NX_EG_CACHE_BYTES: nx_int = 32
36
37func nx_eg_v_is_valid(v: nx_int) -> nx_int {
38 if v < 0 { return 0 }
39 if v >= NX_EG_V_N { return 0 }
40 return 1
41}
42
43func _eg_at(c: *NxEngramCache, idx: nx_int) -> *NxEngramEntry {
44 if idx < 0 { return 0 as *NxEngramEntry }
45 if idx >= c.capacity { return 0 as *NxEngramEntry }
46 return (c.entries + idx * NX_EG_ENT_BYTES) as *NxEngramEntry
47}
48
49func nx_eg_cache_new(capacity: nx_int) -> *NxEngramCache {
50 if capacity <= 0 { return 0 as *NxEngramCache }
51 let raw: *u8 = sys_mmap(NX_EG_CACHE_BYTES)
52 let c: *NxEngramCache = raw as *NxEngramCache
53 c.entries = sys_mmap(capacity * NX_EG_ENT_BYTES)
54 c.n_entries = 0
55 c.capacity = capacity
56 c.next_idx = 0
57 return c
58}
59
60func nx_eg_find(c: *NxEngramCache, key_hash: nx_size) -> *NxEngramEntry {
61 if (c as i64) == 0 { return 0 as *NxEngramEntry }
62 var i: nx_int = 0
63 while i < c.n_entries {
64 let e: *NxEngramEntry = _eg_at(c, i)
65 if e.key_hash == key_hash { return e }
66 i = i + 1
67 }
68 return 0 as *NxEngramEntry
69}
70
71func nx_eg_insert(c: *NxEngramCache,
72 key_hash: nx_size,
73 value_hash: nx_size,
74 now_us: nx_size) -> nx_int {
75 if (c as i64) == 0 { return NX_EG_V_NULL }
76 let existing: *NxEngramEntry = nx_eg_find(c, key_hash)
77 if (existing as i64) != 0 {
78 existing.value_hash = value_hash
79 existing.last_accessed_us = now_us
80 existing.access_count = existing.access_count + 1
81 if existing.access_count >= 3 {
82 existing.consolidation_score = existing.consolidation_score + 1
83 }
84 return NX_EG_V_OK
85 }
86 // Ring-buffer insertion
87 let slot: *NxEngramEntry = _eg_at(c, c.next_idx)
88 let evicted: nx_int = 0
89 var v: nx_int = NX_EG_V_OK
90 if c.n_entries >= c.capacity { v = NX_EG_V_EVICTED }
91 slot.key_hash = key_hash
92 slot.value_hash = value_hash
93 slot.inserted_at_us = now_us
94 slot.last_accessed_us = now_us
95 slot.access_count = 1
96 slot.consolidation_score = 0
97 c.next_idx = c.next_idx + 1
98 if c.next_idx >= c.capacity { c.next_idx = 0 }
99 if c.n_entries < c.capacity { c.n_entries = c.n_entries + 1 }
100 return v
101}
102
103func nx_eg_lookup(c: *NxEngramCache,
104 key_hash: nx_size,
105 now_us: nx_size) -> nx_int {
106 if (c as i64) == 0 { return NX_EG_V_NULL }
107 let e: *NxEngramEntry = nx_eg_find(c, key_hash)
108 if (e as i64) == 0 { return NX_EG_V_MISS }
109 e.last_accessed_us = now_us
110 e.access_count = e.access_count + 1
111 if e.access_count >= 3 {
112 e.consolidation_score = e.consolidation_score + 1
113 }
114 return NX_EG_V_HIT
115}
116
117func nx_eg_value_of(c: *NxEngramCache, key_hash: nx_size) -> nx_size {
118 if (c as i64) == 0 { return 0 }
119 let e: *NxEngramEntry = nx_eg_find(c, key_hash)
120 if (e as i64) == 0 { return 0 }
121 return e.value_hash
122}
123
124func nx_eg_should_consolidate(c: *NxEngramCache, key_hash: nx_size) -> nx_int {
125 if (c as i64) == 0 { return 0 }
126 let e: *NxEngramEntry = nx_eg_find(c, key_hash)
127 if (e as i64) == 0 { return 0 }
128 if e.consolidation_score >= 1 { return 1 }
129 return 0
130}