1use std::borrow::Cow;
4use std::marker::PhantomData;
5
6use wasm_dbms_api::prelude::{
7 DecodeError, Encode, MSize, MemoryError, MemoryResult, Page, PageOffset,
8};
9
10use super::page_ledger::PageLedger;
11use super::raw_record::{RAW_RECORD_HEADER_SIZE, RawRecord};
12use crate::{MemoryAccess, align_up};
13
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16struct Position {
17 page: Page,
18 offset: PageOffset,
19}
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24struct FoundRecord {
25 page: Page,
26 offset: PageOffset,
27 length: MSize,
28 new_position: Option<Position>,
29}
30
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
33pub struct NextRecord<E>
34where
35 E: Encode,
36{
37 pub record: E,
38 pub page: Page,
39 pub offset: PageOffset,
40}
41
42pub struct TableReader<'a, E, MA>
47where
48 E: Encode,
49 MA: MemoryAccess,
50{
51 buffer: Vec<u8>,
53 mm: &'a mut MA,
55 page_ledger: &'a PageLedger,
56 page_size: usize,
57 phantom: PhantomData<E>,
58 position: Option<Position>,
61}
62
63impl<'a, E, MA> TableReader<'a, E, MA>
64where
65 E: Encode,
66 MA: MemoryAccess,
67{
68 pub fn new(page_ledger: &'a PageLedger, mm: &'a mut MA) -> Self {
70 let position = page_ledger.pages().first().map(|page_record| Position {
72 page: page_record.page,
73 offset: 0,
74 });
75 let page_size = mm.page_size() as usize;
76 Self {
77 buffer: vec![0u8; page_size],
78 mm,
79 page_ledger,
80 phantom: PhantomData,
81 position,
82 page_size,
83 }
84 }
85
86 pub fn try_next(&mut self) -> MemoryResult<Option<NextRecord<E>>> {
88 let Some(Position { page, offset }) = self.position else {
89 return Ok(None);
90 };
91
92 let Some(next_record) = self.find_next_record(page, offset)? else {
94 self.position = None;
96 return Ok(None);
97 };
98
99 let start = next_record.offset as usize;
101 let end = start + next_record.length as usize;
102 let record = RawRecord::<E>::decode(Cow::Borrowed(&self.buffer[start..end]))?;
103
104 self.position = next_record.new_position;
106
107 Ok(Some(NextRecord {
108 record: record.data,
109 page: next_record.page,
110 offset: next_record.offset,
111 }))
112 }
113
114 fn find_next_record(
119 &mut self,
120 mut page: Page,
121 mut offset: PageOffset,
122 ) -> MemoryResult<Option<FoundRecord>> {
123 loop {
124 if offset == 0 {
126 self.mm.read_at_raw(page, 0, &mut self.buffer)?;
127 }
128
129 if let Some((next_segment_offset, next_segment_size)) =
131 self.find_next_record_position(&self.buffer, offset as usize)?
132 {
133 let new_offset = next_segment_offset as usize + next_segment_size as usize;
137 let new_position = if new_offset >= self.page_size {
138 self.next_page(page)
140 } else {
141 Some(Position {
142 page,
143 offset: new_offset as PageOffset,
144 })
145 };
146 return Ok(Some(FoundRecord {
147 page,
148 offset: next_segment_offset,
149 length: next_segment_size,
150 new_position,
151 }));
152 }
153
154 match self.next_page(page) {
156 Some(pos) => {
157 page = pos.page;
158 offset = pos.offset;
159 }
160 None => break,
161 }
162 }
163
164 Ok(None)
165 }
166
167 fn next_page(&self, current_page: Page) -> Option<Position> {
169 self.page_ledger
170 .next_page_after(current_page)
171 .map(|page| Position { page, offset: 0 })
172 }
173
174 fn find_next_record_position(
181 &self,
182 buf: &[u8],
183 mut offset: usize,
184 ) -> MemoryResult<Option<(PageOffset, MSize)>> {
185 offset = align_up::<RawRecord<E>>(offset);
187 let mut data_len;
189 loop {
190 if offset + 1 >= buf.len() {
192 return Ok(None);
193 }
194 data_len = u16::from_le_bytes([buf[offset], buf[offset + 1]]) as MSize;
196 if data_len != 0 {
197 break;
198 }
199 offset += <RawRecord<E> as Encode>::ALIGNMENT as usize;
203 }
204
205 let data_offset = offset + RAW_RECORD_HEADER_SIZE as usize;
206 if buf.len() < data_offset + data_len as usize {
207 return Err(MemoryError::DecodeError(DecodeError::TooShort));
208 }
209
210 Ok(Some((
211 offset as PageOffset,
212 data_len + RAW_RECORD_HEADER_SIZE,
213 )))
214 }
215}
216
217#[cfg(test)]
218mod tests {
219
220 use super::*;
221 use crate::table_registry::test_utils::User;
222 use crate::{HeapMemoryProvider, MemoryManager, TableRegistry, TableRegistryPage};
223
224 #[test]
225 fn test_should_read_all_records() {
226 const COUNT: u32 = 4_000;
227 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
228 let table_registry = mock_table_registry(COUNT, &mut mm);
229 let mut reader = mocked(&table_registry, &mut mm);
230
231 let mut id = 0;
233 while let Some(NextRecord { record: user, .. }) =
234 reader.try_next().expect("failed to read user")
235 {
236 assert_eq!(user.id, id);
237 assert_eq!(user.name, format!("User {}", id));
238
239 id += 1;
240 }
241 assert_eq!(id, COUNT);
242 }
243
244 #[test]
245 fn test_should_find_next_page() {
246 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
247 let table_registry = mock_table_registry(4_000, &mut mm);
248 let reader = mocked(&table_registry, &mut mm);
249
250 let page = reader.position.expect("should have position").page;
251
252 let next_page = reader.next_page(page).expect("should have next page");
253 assert_eq!(next_page.page, page + 1);
254 let next_page = reader.next_page(next_page.page);
255 assert!(next_page.is_some());
256 let next_page = reader.next_page(next_page.unwrap().page);
257 assert!(next_page.is_some());
258 let next_page = reader.next_page(next_page.unwrap().page);
259 assert!(
260 next_page.is_none(),
261 "should not have next page, but got {:?}",
262 next_page
263 );
264 }
265
266 #[test]
267 fn test_should_scan_reclaimed_pages_in_ledger_order() {
268 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
269 let mut table_registry = mock_table_registry(0, &mut mm);
270
271 let low = mm.claim_page().expect("claim low");
273 let high = mm.claim_page().expect("claim high");
274 mm.unclaim_page(low).expect("unclaim low");
275 mm.unclaim_page(high).expect("unclaim high");
276
277 for id in 0..2 {
280 let user = User {
281 id,
282 name: String::new(),
283 email: "x".repeat(40_000),
284 age: 20,
285 };
286 table_registry.insert(user, &mut mm).expect("insert");
287 }
288 let ledger_pages: Vec<Page> = table_registry
289 .page_ledger
290 .pages()
291 .iter()
292 .map(|record| record.page)
293 .collect();
294 assert_eq!(ledger_pages, vec![high, low]);
295
296 let mut reader = mocked(&table_registry, &mut mm);
297 let mut ids = Vec::new();
298 while let Some(next) = reader.try_next().expect("scan") {
299 ids.push(next.record.id);
300 }
301 assert_eq!(ids, vec![0, 1]);
302 }
303
304 #[test]
305 fn test_should_read_record_ending_at_page_boundary() {
306 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
307 let mut table_registry = mock_table_registry(0, &mut mm);
308
309 for id in 0..2 {
312 let user = User {
313 id,
314 name: String::new(),
315 email: "x".repeat(32_754),
316 age: 20,
317 };
318 table_registry.insert(user, &mut mm).expect("insert");
319 }
320 assert_eq!(table_registry.page_ledger.pages().len(), 1);
321
322 let mut reader = mocked(&table_registry, &mut mm);
323 let first = reader.try_next().expect("first").expect("first row");
324 assert_eq!((first.record.id, first.offset), (0, 0));
325 let second = reader.try_next().expect("second").expect("second row");
326 assert_eq!((second.record.id, second.offset), (1, 32_768));
327 assert!(reader.try_next().expect("end").is_none());
328 }
329
330 #[test]
331 fn test_should_find_next_record_position() {
332 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
333 let table_registry = mock_table_registry(1, &mut mm);
334 let reader = mocked(&table_registry, &mut mm);
335
336 let mut buf = vec![0u8; User::ALIGNMENT as usize];
337 buf.extend_from_slice(&[5u8, 0u8, 0u8, 0, 0, 0, 0, 0, 0]);
338
339 let (offset, size) = reader
340 .find_next_record_position(&buf, 0)
341 .expect("failed to get next record")
342 .expect("should have next record");
343
344 assert_eq!(offset, 32);
345 assert_eq!(size, 7);
346 }
347
348 #[test]
349 fn test_should_not_find_next_record_position_none() {
350 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
351 let table_registry = mock_table_registry(1, &mut mm);
352 let reader = mocked(&table_registry, &mut mm);
353
354 let buf = vec![0u8; User::ALIGNMENT as usize * 2];
355 let result = reader
356 .find_next_record_position(&buf, 0)
357 .expect("failed to get next record");
358
359 assert!(result.is_none());
360 }
361
362 #[test]
363 fn test_should_not_find_next_record_position_too_short_for_length() {
364 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
365 let table_registry = mock_table_registry(1, &mut mm);
366 let reader = mocked(&table_registry, &mut mm);
367
368 let buf = [5u8, 16u8];
369 let result = reader.find_next_record_position(&buf, 0);
370 assert!(result.is_err(), "expected error but got {:?}", result);
371 let err = result.unwrap_err();
372
373 assert!(matches!(
374 err,
375 MemoryError::DecodeError(DecodeError::TooShort)
376 ));
377 }
378
379 #[test]
380 fn test_should_not_find_next_record_position_too_short_for_data() {
381 let mut mm = MemoryManager::init(HeapMemoryProvider::default());
382 let table_registry = mock_table_registry(1, &mut mm);
383 let reader = mocked(&table_registry, &mut mm);
384
385 let buf = [5u8, 0u8, 0u8, 0, 0];
386 let result = reader.find_next_record_position(&buf, 0);
387
388 assert!(matches!(
389 result,
390 Err(MemoryError::DecodeError(DecodeError::TooShort))
391 ));
392 }
393
394 fn mock_table_registry(
395 entries: u32,
396 mm: &mut MemoryManager<HeapMemoryProvider>,
397 ) -> TableRegistry {
398 let schema_snapshot_page = mm.claim_page().expect("failed to get page");
399 let page_ledger_page = mm.claim_page().expect("failed to get page");
400 let free_segments_page = mm.claim_page().expect("failed to get page");
401 let index_registry_page = mm.claim_page().expect("failed to get page");
402 super::super::test_utils::write_dummy_schema_snapshot(schema_snapshot_page, mm);
403 let mut registry = TableRegistry::load(
404 TableRegistryPage {
405 schema_snapshot_page,
406 pages_list_page: page_ledger_page,
407 free_segments_page,
408 index_registry_page,
409 autoincrement_registry_page: None,
410 },
411 mm,
412 )
413 .expect("failed to load registry");
414
415 for id in 0..entries {
417 let user = User {
418 id,
419 name: format!("User {}", id),
420 email: "new_user@example.com".to_string(),
421 age: 20 + id,
422 };
423 registry.insert(user, mm).expect("failed to insert user");
424 }
425
426 registry
427 }
428
429 fn mocked<'a>(
430 table_registry: &'a TableRegistry,
431 mm: &'a mut MemoryManager<HeapMemoryProvider>,
432 ) -> TableReader<'a, User, MemoryManager<HeapMemoryProvider>> {
433 TableReader::new(&table_registry.page_ledger, mm)
434 }
435}