qualia_core_db/storage/
mmap.rs1use crate::NQuin;
2#[cfg(not(target_arch = "wasm32"))]
3use memmap2::MmapMut;
4#[cfg(not(target_arch = "wasm32"))]
5use std::fs::OpenOptions;
6
7#[cfg(not(target_arch = "wasm32"))]
8pub struct MmapStore {
9 mmap: MmapMut,
10 capacity_quins: usize,
11 active_quins: usize,
12}
13
14#[cfg(not(target_arch = "wasm32"))]
15impl MmapStore {
16 pub fn open(path: &str, capacity_quins: usize) -> Result<Self, std::io::Error> {
18 let file = OpenOptions::new()
19 .read(true)
20 .write(true)
21 .create(true)
22 .open(path)?;
23
24 let required_len = (capacity_quins * 48) as u64;
25 file.set_len(required_len)?;
26
27 let mmap = unsafe { MmapMut::map_mut(&file)? };
28
29 Ok(Self {
30 mmap,
31 capacity_quins,
32 active_quins: 0,
33 })
34 }
35
36 pub fn append(&mut self, quin: &NQuin) -> Result<(), std::io::Error> {
38 if self.active_quins >= self.capacity_quins {
39 return Err(std::io::Error::new(
40 std::io::ErrorKind::WriteZero,
41 "MmapStore capacity exceeded",
42 ));
43 }
44
45 let offset = self.active_quins * 48;
46 let bytes = bytemuck::bytes_of(quin);
47 self.mmap[offset..offset + 48].copy_from_slice(bytes);
48
49 self.active_quins += 1;
50 Ok(())
51 }
52
53 pub fn as_slice(&self) -> &[NQuin] {
55 let active_bytes = self.active_quins * 48;
56 let slice = &self.mmap[..active_bytes];
57 bytemuck::cast_slice(slice)
58 }
59}
60
61#[cfg(target_arch = "wasm32")]
62pub struct MmapStore {
63 data: Vec<NQuin>,
64 capacity_quins: usize,
65}
66
67#[cfg(target_arch = "wasm32")]
68impl MmapStore {
69 pub fn open(_path: &str, capacity_quins: usize) -> Result<Self, std::io::Error> {
70 Ok(Self {
71 data: Vec::with_capacity(capacity_quins),
72 capacity_quins,
73 })
74 }
75
76 pub fn append(&mut self, quin: &NQuin) -> Result<(), std::io::Error> {
77 if self.data.len() >= self.capacity_quins {
78 return Err(std::io::Error::new(
79 std::io::ErrorKind::WriteZero,
80 "MmapStore capacity exceeded",
81 ));
82 }
83
84 self.data.push(*quin);
85 Ok(())
86 }
87
88 pub fn as_slice(&self) -> &[NQuin] {
89 &self.data
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use tempfile::NamedTempFile;
97
98 #[test]
99 fn test_mmap_store_open() {
100 let temp_file = NamedTempFile::new().unwrap();
101 let path = temp_file.path().to_str().unwrap();
102 let store = MmapStore::open(path, 10).unwrap();
103 assert_eq!(store.capacity_quins, 10);
104 assert_eq!(store.active_quins, 0);
105 }
106
107 #[test]
108 fn test_mmap_store_append_and_slice() {
109 let temp_file = NamedTempFile::new().unwrap();
110 let path = temp_file.path().to_str().unwrap();
111 let mut store = MmapStore::open(path, 10).unwrap();
112
113 let quin = NQuin {
114 subject: 1,
115 predicate: 2,
116 object: 3,
117 context: 4,
118 metadata: 5,
119 parity: 6,
120 };
121
122 store.append(&quin).unwrap();
123 assert_eq!(store.active_quins, 1);
124
125 let slice = store.as_slice();
126 assert_eq!(slice.len(), 1);
127 assert_eq!(slice[0].subject, 1);
128 assert_eq!(slice[0].predicate, 2);
129 assert_eq!(slice[0].object, 3);
130 assert_eq!(slice[0].context, 4);
131 assert_eq!(slice[0].metadata, 5);
132 assert_eq!(slice[0].parity, 6);
133 }
134}