nodedb_lite/storage/pagedb_storage/segment_ext/
array.rs1use async_trait::async_trait;
6use pagedb::PagedbError;
7use pagedb::vfs::Vfs;
8
9use crate::error::LiteError;
10use crate::storage::pagedb_storage::types::PagedbStorage;
11
12#[async_trait]
13impl<V: Vfs + Clone + Send + Sync + 'static> crate::storage::array_segment_ext::ArraySegmentExt
14 for PagedbStorage<V>
15where
16 <V as Vfs>::LockHandle: Sync,
17 <V as Vfs>::File: Sync,
18{
19 async fn write_array_segment(
20 &self,
21 array_name: &str,
22 seg_id: u64,
23 bytes: &[u8],
24 ) -> Result<(), LiteError> {
25 use pagedb::{RealmId, SegmentKind};
26
27 let byte_len = bytes.len() as u64;
30 let mut payload = Vec::with_capacity(8 + bytes.len());
31 payload.extend_from_slice(&byte_len.to_le_bytes());
32 payload.extend_from_slice(bytes);
33
34 const PAGE_BODY_CAP: usize = 4096 - 40;
37 let chunks: Vec<&[u8]> = payload.chunks(PAGE_BODY_CAP).collect();
38
39 let realm = RealmId::new([0u8; 16]);
40 let segment_name = format!("arr/tile/{array_name}/{seg_id}");
41
42 let mut writer = self
43 .db
44 .create_segment(realm, SegmentKind::Unspecified)
45 .await
46 .map_err(LiteError::from)?;
47 writer
48 .append_extent(&chunks)
49 .await
50 .map_err(LiteError::from)?;
51 let meta = writer.seal().await.map_err(LiteError::from)?;
52
53 let mut txn = self.db.begin_write().await.map_err(LiteError::from)?;
54
55 let link_result = txn.link_segment(&segment_name, &meta).await;
57 match link_result {
58 Ok(()) => {}
59 Err(PagedbError::AlreadyLinked) => {
60 txn.replace_segment(&segment_name, &meta)
61 .await
62 .map_err(LiteError::from)?;
63 }
64 Err(e) => return Err(LiteError::from(e)),
65 }
66
67 txn.commit().await.map(|_| ()).map_err(LiteError::from)
68 }
69
70 async fn open_array_segment(
71 &self,
72 array_name: &str,
73 seg_id: u64,
74 ) -> Result<Option<Box<[u8]>>, LiteError> {
75 let segment_name = format!("arr/tile/{array_name}/{seg_id}");
76 let txn = self.db.begin_read().await.map_err(LiteError::from)?;
77
78 let reader = match txn.open_segment(&segment_name).await {
79 Ok(r) => r,
80 Err(PagedbError::NotFound) => return Ok(None),
81 Err(e) => return Err(LiteError::from(e)),
82 };
83
84 let meta_page_count = reader.meta().page_count;
86 let index_pages = u64::from(reader.index_page_count());
87 let data_page_count = meta_page_count
88 .checked_sub(2 + index_pages)
89 .ok_or_else(|| LiteError::Storage {
90 detail: format!(
91 "array segment page_count={meta_page_count} too small \
92 (index_pages={index_pages})"
93 ),
94 })?;
95
96 if data_page_count == 0 {
97 return Ok(Some(Box::default()));
98 }
99
100 let count_u32 = u32::try_from(data_page_count).map_err(|_| LiteError::Storage {
101 detail: format!("array segment has too many data pages: {data_page_count}"),
102 })?;
103
104 let pages = reader
105 .read_range(1, count_u32)
106 .await
107 .map_err(|e| LiteError::Storage {
108 detail: format!("pagedb array segment read_range failed: {e}"),
109 })?;
110
111 let total: usize = pages.iter().map(|p| p.len()).sum();
112 let mut flat = Vec::with_capacity(total);
113 for page in pages {
114 flat.extend_from_slice(&page);
115 }
116
117 if flat.len() < 8 {
119 return Err(LiteError::Storage {
120 detail: format!(
121 "array segment {array_name}/{seg_id} too small to contain length prefix: \
122 {} bytes",
123 flat.len()
124 ),
125 });
126 }
127 let byte_len = u64::from_le_bytes(flat[..8].try_into().expect("8-byte slice")) as usize;
128 let end = 8_usize
129 .checked_add(byte_len)
130 .ok_or_else(|| LiteError::Storage {
131 detail: format!(
132 "array segment {array_name}/{seg_id} length prefix overflows: {byte_len}"
133 ),
134 })?;
135 if end > flat.len() {
136 return Err(LiteError::Storage {
137 detail: format!(
138 "array segment {array_name}/{seg_id} declared byte_len={byte_len} \
139 exceeds available data ({} bytes after prefix)",
140 flat.len() - 8
141 ),
142 });
143 }
144 let segment_bytes = flat[8..end].to_vec();
145
146 Ok(Some(segment_bytes.into_boxed_slice()))
147 }
148
149 async fn delete_array_segment(&self, array_name: &str, seg_id: u64) -> Result<(), LiteError> {
150 let segment_name = format!("arr/tile/{array_name}/{seg_id}");
151 let mut txn = self.db.begin_write().await.map_err(LiteError::from)?;
152 match txn.unlink_segment(&segment_name).await {
153 Ok(()) => {}
154 Err(PagedbError::NotLinked) => return Ok(()), Err(e) => return Err(LiteError::from(e)),
156 }
157 txn.commit().await.map(|_| ()).map_err(LiteError::from)
158 }
159}