reddb_engine/storage/
overflow.rs1use super::page::{Page, PageType, CONTENT_SIZE, HEADER_SIZE, PAGE_SIZE};
24use super::page_store::PageStore;
25use super::pager::PagerError;
26use std::collections::HashSet;
27
28pub const OVERFLOW_PAGE_HEADER: usize = 8;
30
31pub const OVERFLOW_PAYLOAD_PER_PAGE: usize = CONTENT_SIZE - OVERFLOW_PAGE_HEADER;
33
34const _: () = assert!(OVERFLOW_PAYLOAD_PER_PAGE == PAGE_SIZE - HEADER_SIZE - OVERFLOW_PAGE_HEADER);
35
36#[derive(Debug)]
38pub enum OverflowError {
39 Pager(PagerError),
41 NotOverflowPage { page_id: u32 },
43 PayloadTooLarge { page_id: u32, len: u32 },
45 LengthMismatch { expected: u64, actual: u64 },
47 InvalidHead { page_id: u32 },
49 CycleDetected { page_id: u32 },
51}
52
53impl std::fmt::Display for OverflowError {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Pager(e) => write!(f, "pager error: {}", e),
57 Self::NotOverflowPage { page_id } => {
58 write!(f, "page {} is not an overflow page", page_id)
59 }
60 Self::PayloadTooLarge { page_id, len } => {
61 write!(
62 f,
63 "overflow page {} declares payload_len {} (max {})",
64 page_id, len, OVERFLOW_PAYLOAD_PER_PAGE
65 )
66 }
67 Self::LengthMismatch { expected, actual } => write!(
68 f,
69 "overflow chain length mismatch: expected {} bytes, chain holds {}",
70 expected, actual
71 ),
72 Self::InvalidHead { page_id } => {
73 write!(f, "free called with non-overflow head page {}", page_id)
74 }
75 Self::CycleDetected { page_id } => {
76 write!(f, "cyclic overflow chain repeats page {}", page_id)
77 }
78 }
79 }
80}
81
82impl std::error::Error for OverflowError {}
83
84impl From<PagerError> for OverflowError {
85 fn from(e: PagerError) -> Self {
86 Self::Pager(e)
87 }
88}
89
90pub fn pages_needed(len: usize) -> usize {
92 if len == 0 {
93 1
94 } else {
95 len.div_ceil(OVERFLOW_PAYLOAD_PER_PAGE)
96 }
97}
98
99fn read_chain_header(page: &Page) -> Result<(u32, u32), OverflowError> {
100 if page.page_type().map_err(PagerError::from)? != PageType::Overflow {
101 return Err(OverflowError::NotOverflowPage {
102 page_id: page.page_id(),
103 });
104 }
105 let content = page.content();
106 let next = u32::from_le_bytes([content[0], content[1], content[2], content[3]]);
107 let len = u32::from_le_bytes([content[4], content[5], content[6], content[7]]);
108 if len as usize > OVERFLOW_PAYLOAD_PER_PAGE {
109 return Err(OverflowError::PayloadTooLarge {
110 page_id: page.page_id(),
111 len,
112 });
113 }
114 Ok((next, len))
115}
116
117fn write_chain_header(page: &mut Page, next: u32, payload_len: u32) {
118 let content = page.content_mut();
119 content[0..4].copy_from_slice(&next.to_le_bytes());
120 content[4..8].copy_from_slice(&payload_len.to_le_bytes());
121}
122
123pub struct OverflowChain<'p, P: PageStore> {
129 pager: &'p P,
130}
131
132impl<'p, P: PageStore> OverflowChain<'p, P> {
133 pub fn new(pager: &'p P) -> Self {
134 Self { pager }
135 }
136
137 pub fn store(&self, bytes: &[u8]) -> Result<(u32, u64), OverflowError> {
143 let total_len = bytes.len() as u64;
144 let n_pages = pages_needed(bytes.len());
145
146 let mut page_ids = Vec::with_capacity(n_pages);
147 for _ in 0..n_pages {
148 let page = self.pager.allocate_page(PageType::Overflow)?;
149 page_ids.push(page.page_id());
150 }
151
152 let mut offset = 0usize;
153 for (i, &pid) in page_ids.iter().enumerate() {
154 let next = if i + 1 < page_ids.len() {
155 page_ids[i + 1]
156 } else {
157 0
158 };
159 let chunk_end = (offset + OVERFLOW_PAYLOAD_PER_PAGE).min(bytes.len());
160 let chunk = &bytes[offset..chunk_end];
161 offset = chunk_end;
162
163 let mut page = Page::new(PageType::Overflow, pid);
164 write_chain_header(&mut page, next, chunk.len() as u32);
165 page.content_mut()[OVERFLOW_PAGE_HEADER..OVERFLOW_PAGE_HEADER + chunk.len()]
166 .copy_from_slice(chunk);
167
168 self.pager.write_page(pid, page)?;
169 }
170
171 Ok((page_ids[0], total_len))
172 }
173
174 pub fn read(&self, head_page_id: u32, total_len: u64) -> Result<Vec<u8>, OverflowError> {
180 let expected = total_len as usize;
181 let mut out = Vec::with_capacity(expected);
182 let mut current = head_page_id;
183 let mut collected: u64 = 0;
184 let mut visited = HashSet::new();
185
186 while current != 0 {
187 if !visited.insert(current) {
188 return Err(OverflowError::CycleDetected { page_id: current });
189 }
190 let page = self.pager.read_page(current).map_err(OverflowError::from)?;
191 let (next, len) = read_chain_header(&page)?;
192 let len_usize = len as usize;
193 collected += len as u64;
194
195 if collected > total_len {
196 return Err(OverflowError::LengthMismatch {
197 expected: total_len,
198 actual: collected,
199 });
200 }
201
202 let payload = &page.content()[OVERFLOW_PAGE_HEADER..OVERFLOW_PAGE_HEADER + len_usize];
203 out.extend_from_slice(payload);
204 current = next;
205 }
206
207 if collected != total_len {
208 return Err(OverflowError::LengthMismatch {
209 expected: total_len,
210 actual: collected,
211 });
212 }
213
214 Ok(out)
215 }
216
217 pub fn free(&self, head_page_id: u32) -> Result<(), OverflowError> {
220 let mut current = head_page_id;
221 let mut first = true;
222 let mut visited = HashSet::new();
223 while current != 0 {
224 if !visited.insert(current) {
225 return Err(OverflowError::CycleDetected { page_id: current });
226 }
227 let page = self.pager.read_page(current).map_err(OverflowError::from)?;
228 if page.page_type().map_err(PagerError::from)? != PageType::Overflow {
229 return Err(if first {
230 OverflowError::InvalidHead { page_id: current }
231 } else {
232 OverflowError::NotOverflowPage { page_id: current }
233 });
234 }
235 let (next, _) = read_chain_header(&page)?;
236 self.pager.free_page(current)?;
237 current = next;
238 first = false;
239 }
240 Ok(())
241 }
242}