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