1use wasm_dbms_api::prelude::{
7 DataSize, Encode, MSize, MemoryError, MemoryResult, Page, PageOffset,
8};
9
10use crate::memory_access::MemoryAccess;
11use crate::provider::MemoryProvider;
12
13pub const SCHEMA_PAGE: Page = 0;
15pub const UNCLAIMED_PAGES_PAGE: Page = 1;
17pub const RESERVED_PAGES: u64 = 2;
19
20pub struct MemoryManager<P>
23where
24 P: MemoryProvider,
25{
26 provider: P,
27}
28
29impl<P> MemoryManager<P>
30where
31 P: MemoryProvider,
32{
33 pub fn init(provider: P) -> Self {
40 let mut manager = MemoryManager { provider };
41
42 let current_pages = manager.provider.pages();
44 if current_pages >= RESERVED_PAGES {
45 return manager;
46 }
47
48 let missing = RESERVED_PAGES - current_pages;
50 if let Err(err) = manager.provider.grow(missing) {
51 panic!("Failed to grow memory during initialization: {err}");
52 }
53
54 manager
55 }
56
57 pub const fn schema_page(&self) -> Page {
59 SCHEMA_PAGE
60 }
61
62 pub const fn unclaimed_pages_page(&self) -> Page {
64 UNCLAIMED_PAGES_PAGE
65 }
66
67 #[cfg(test)]
72 pub(crate) fn into_provider(self) -> P {
73 self.provider
74 }
75
76 pub fn last_page(&self) -> Option<Page> {
78 match self.provider.pages() {
79 0 => None,
80 n => Some(n as Page - 1),
81 }
82 }
83
84 pub fn pages_count(&self) -> u64 {
86 self.provider.pages()
87 }
88
89 pub fn flush(&mut self) -> MemoryResult<()> {
95 self.provider.flush()
96 }
97
98 fn absolute_offset(&self, page: Page, offset: PageOffset) -> u64 {
101 (page as u64)
102 .checked_mul(P::PAGE_SIZE)
103 .and_then(|page_offset| page_offset.checked_add(offset as u64))
104 .expect("Overflow when calculating absolute offset")
105 }
106
107 fn check_unallocated_page(
109 &self,
110 page: Page,
111 offset: PageOffset,
112 data_size: MSize,
113 ) -> MemoryResult<()> {
114 if self.last_page().is_none_or(|last_page| page > last_page) {
115 return Err(MemoryError::SegmentationFault {
116 page,
117 offset,
118 data_size: data_size as u64,
119 page_size: P::PAGE_SIZE,
120 });
121 }
122 Ok(())
123 }
124
125 fn check_alignment<E>(&self, offset: PageOffset) -> MemoryResult<()>
128 where
129 E: Encode,
130 {
131 let alignment = E::ALIGNMENT as PageOffset;
132 if alignment != 0 && !offset.is_multiple_of(alignment) {
133 return Err(MemoryError::OffsetNotAligned { offset, alignment });
134 }
135 Ok(())
136 }
137}
138
139impl<P> MemoryAccess for MemoryManager<P>
140where
141 P: MemoryProvider,
142{
143 fn page_size(&self) -> u64 {
144 P::PAGE_SIZE
145 }
146
147 fn grow_one_page(&mut self) -> MemoryResult<Page> {
148 self.provider.grow(1)?;
149
150 self.provider.write(
152 self.absolute_offset(self.last_page().unwrap_or(0), 0),
153 &vec![0u8; P::PAGE_SIZE as usize],
154 )?;
155
156 match self.last_page() {
157 Some(page) => Ok(page),
158 None => Err(MemoryError::FailedToAllocatePage),
159 }
160 }
161
162 fn zero_page(&mut self, page: Page) -> MemoryResult<()> {
163 if self.last_page().is_none_or(|last_page| page > last_page) {
164 return Err(MemoryError::SegmentationFault {
165 page,
166 offset: 0,
167 data_size: P::PAGE_SIZE,
168 page_size: P::PAGE_SIZE,
169 });
170 }
171
172 let absolute_offset = self.absolute_offset(page, 0);
173 let buffer = vec![0u8; P::PAGE_SIZE as usize];
174 self.provider.write(absolute_offset, &buffer)
175 }
176
177 fn read_at<D>(&mut self, page: Page, offset: PageOffset) -> MemoryResult<D>
178 where
179 D: Encode,
180 {
181 self.check_alignment::<D>(offset)?;
182
183 let mut buf = vec![
184 0u8;
185 match D::SIZE {
186 DataSize::Fixed(size) => size as usize,
187 DataSize::Dynamic => (P::PAGE_SIZE as usize).saturating_sub(offset as usize),
188 }
189 ];
190
191 let read = self.read_at_raw(page, offset, &mut buf)?;
192 if read < buf.len() {
195 return Err(MemoryError::SegmentationFault {
196 page,
197 offset,
198 data_size: buf.len() as u64,
199 page_size: P::PAGE_SIZE,
200 });
201 }
202
203 D::decode(std::borrow::Cow::Owned(buf))
204 }
205
206 fn write_at<E>(&mut self, page: Page, offset: PageOffset, data: &E) -> MemoryResult<()>
207 where
208 E: Encode,
209 {
210 self.check_unallocated_page(page, offset, data.size())?;
211 self.check_alignment::<E>(offset)?;
212
213 let encoded = data.encode();
214 let padded_len = align_up::<E>(encoded.len());
218
219 if offset as u64 + padded_len as u64 > P::PAGE_SIZE {
220 return Err(MemoryError::SegmentationFault {
221 page,
222 offset,
223 data_size: padded_len as u64,
224 page_size: P::PAGE_SIZE,
225 });
226 }
227
228 let absolute_offset = self.absolute_offset(page, offset);
229 self.provider.write(absolute_offset, encoded.as_ref())?;
230
231 let padding = padded_len - encoded.len();
233 if padding > 0 {
234 let padding_offset = absolute_offset + encoded.len() as u64;
235 let padding_buffer = vec![0u8; padding];
236 self.provider
237 .write(padding_offset, padding_buffer.as_ref())?;
238 }
239
240 Ok(())
241 }
242
243 fn write_at_raw(&mut self, page: Page, offset: PageOffset, buf: &[u8]) -> MemoryResult<()> {
244 self.check_unallocated_page(page, offset, buf.len() as MSize)?;
245
246 if offset as u64 + buf.len() as u64 > P::PAGE_SIZE {
247 return Err(MemoryError::SegmentationFault {
248 page,
249 offset,
250 data_size: buf.len() as u64,
251 page_size: P::PAGE_SIZE,
252 });
253 }
254
255 let absolute_offset = self.absolute_offset(page, offset);
256 self.provider.write(absolute_offset, buf)
257 }
258
259 fn zero<E>(&mut self, page: Page, offset: PageOffset, data: &E) -> MemoryResult<()>
260 where
261 E: Encode,
262 {
263 self.check_unallocated_page(page, offset, data.size())?;
264 self.check_alignment::<E>(offset)?;
265
266 let length = align_up::<E>(data.size() as usize);
267
268 if offset as u64 + (length as u64) > P::PAGE_SIZE {
269 return Err(MemoryError::SegmentationFault {
270 page,
271 offset,
272 data_size: data.size() as u64,
273 page_size: P::PAGE_SIZE,
274 });
275 }
276
277 let absolute_offset = self.absolute_offset(page, offset);
278 let buffer = vec![0u8; length];
279 self.provider.write(absolute_offset, buffer.as_ref())
280 }
281
282 fn zero_raw(&mut self, page: Page, offset: PageOffset, len: PageOffset) -> MemoryResult<()> {
283 if self.last_page().is_none_or(|last_page| page > last_page) {
284 return Err(MemoryError::SegmentationFault {
285 page,
286 offset,
287 data_size: len as u64,
288 page_size: P::PAGE_SIZE,
289 });
290 }
291
292 if offset as u64 + len as u64 > P::PAGE_SIZE {
293 return Err(MemoryError::SegmentationFault {
294 page,
295 offset,
296 data_size: len as u64,
297 page_size: P::PAGE_SIZE,
298 });
299 }
300
301 let absolute_offset = self.absolute_offset(page, offset);
302 let buffer = vec![0u8; len as usize];
303 self.provider.write(absolute_offset, buffer.as_ref())
304 }
305
306 fn read_at_raw(
307 &mut self,
308 page: Page,
309 offset: PageOffset,
310 buf: &mut [u8],
311 ) -> MemoryResult<usize> {
312 if self.last_page().is_none_or(|last_page| page > last_page) {
313 return Err(MemoryError::SegmentationFault {
314 page,
315 offset,
316 data_size: buf.len() as u64,
317 page_size: P::PAGE_SIZE,
318 });
319 }
320
321 let read_len = ((P::PAGE_SIZE - offset as u64) as usize).min(buf.len());
322
323 let absolute_offset = self.absolute_offset(page, offset);
324 self.provider
325 .read(absolute_offset, buf[..read_len].as_mut())?;
326
327 Ok(read_len)
328 }
329}
330
331#[inline]
334pub const fn align_up<E>(offset: usize) -> usize
335where
336 E: Encode,
337{
338 let alignment = E::ALIGNMENT as usize;
339 offset.div_ceil(alignment) * alignment
340}
341
342#[cfg(test)]
343mod tests {
344 use std::borrow::Cow;
345
346 use wasm_dbms_api::prelude::{
347 DEFAULT_ALIGNMENT, DataSize, MSize, MemoryError, MemoryResult, PageOffset, Text,
348 };
349
350 use super::*;
351 use crate::provider::HeapMemoryProvider;
352
353 const SCRATCH_PAGE: Page = SCHEMA_PAGE;
355
356 fn make_mm() -> MemoryManager<HeapMemoryProvider> {
357 MemoryManager::init(HeapMemoryProvider::default())
358 }
359
360 #[test]
361 fn test_fixed_read_rejects_a_value_truncated_at_the_page_end() {
362 let mut mm = make_mm();
363 let page = mm.claim_page().expect("claim");
364 mm.write_at_raw(page, 65_532, &[1, 2, 3, 4])
365 .expect("write tail");
366
367 let value: MemoryResult<crate::RecordAddress> = mm.read_at(page, 65_532);
369 assert!(matches!(value, Err(MemoryError::SegmentationFault { .. })));
370 }
371
372 #[test]
373 fn test_should_init_memory_manager() {
374 let mm = make_mm();
375 assert_eq!(mm.last_page(), Some(1));
376 }
377
378 #[test]
379 fn test_should_get_last_page() {
380 let mm = make_mm();
381 let last_page = mm.last_page();
382 assert_eq!(last_page, Some(1)); }
384
385 #[test]
386 fn test_reserved_layout_is_schema_then_unclaimed_pages() {
387 let mm = make_mm();
388 assert_eq!(RESERVED_PAGES, 2);
389 assert_eq!(mm.schema_page(), 0);
390 assert_eq!(mm.unclaimed_pages_page(), 1);
391 }
392
393 #[test]
394 fn test_should_get_memory_page_size() {
395 let mm = make_mm();
396 let page_size = mm.page_size();
397 assert_eq!(page_size, HeapMemoryProvider::PAGE_SIZE);
398 }
399
400 #[test]
401 fn test_should_write_and_read_fixed_data_size() {
402 let mut mm = make_mm();
403 let data_to_write = FixedSizeData { a: 42, b: 1337 };
404 mm.write_at(SCRATCH_PAGE, 0, &data_to_write)
405 .expect("Failed to write data to scratch page");
406
407 let out: FixedSizeData = mm
408 .read_at(SCRATCH_PAGE, 0)
409 .expect("Failed to read data from scratch page");
410
411 assert_eq!(out, data_to_write);
412 }
413
414 #[test]
415 fn test_write_should_zero_padding() {
416 let mut mm = make_mm();
417 let data_to_write = Text("very_long_string".to_string());
418 mm.write_at(SCRATCH_PAGE, 0, &data_to_write)
419 .expect("Failed to write data to scratch page");
420
421 let mut buffer = vec![0; 32];
422 mm.read_at_raw(SCRATCH_PAGE, 0, &mut buffer)
423 .expect("Failed to read data from scratch page");
424
425 let non_zero_count = buffer.iter().filter(|&&b| b != 0).count();
426 assert_eq!(non_zero_count, data_to_write.size() as usize - 1);
427
428 let data_to_write_short = Text("short".to_string());
429 mm.write_at(SCRATCH_PAGE, 0, &data_to_write_short)
430 .expect("Failed to write data to scratch page");
431
432 let mut buffer = vec![0; 32];
433 mm.read_at_raw(SCRATCH_PAGE, 0, &mut buffer)
434 .expect("Failed to read data from scratch page");
435
436 let non_zero_count = buffer.iter().filter(|&&b| b != 0).count();
437 assert_eq!(non_zero_count, data_to_write_short.size() as usize - 1);
438 }
439
440 #[test]
441 fn test_should_zero_data() {
442 let mut mm = make_mm();
443 let data_to_write = FixedSizeData { a: 100, b: 200 };
444 mm.write_at(SCRATCH_PAGE, 48, &data_to_write)
445 .expect("Failed to write data to scratch page");
446
447 mm.zero(SCRATCH_PAGE, 48, &data_to_write)
448 .expect("Failed to zero data on scratch page");
449
450 let mut buffer = vec![0; 50];
451 mm.read_at_raw(SCRATCH_PAGE, 48, &mut buffer)
452 .expect("Failed to read data from scratch page");
453
454 assert!(buffer.iter().all(|&b| b == 0));
455 }
456
457 #[test]
458 fn test_should_zero_with_alignment() {
459 let mut mm = make_mm();
460 let data_to_write = FixedSizeData { a: 100, b: 200 };
461 mm.write_at(SCRATCH_PAGE, 0, &data_to_write)
462 .expect("Failed to write data to scratch page");
463 let data_to_write = FixedSizeData { a: 100, b: 200 };
464 mm.write_at(SCRATCH_PAGE, 6, &data_to_write)
465 .expect("Failed to write data to scratch page");
466
467 let data_with_alignment = DataWithAlignment { a: 100, b: 200 };
468 mm.zero(SCRATCH_PAGE, 0, &data_with_alignment)
469 .expect("Failed to zero data on scratch page");
470
471 let mut buffer = vec![0; 32];
472 mm.read_at_raw(SCRATCH_PAGE, 0, &mut buffer)
473 .expect("Failed to read data from scratch page");
474 assert!(
475 buffer.iter().all(|&b| b == 0),
476 "First 32 bytes are not zeroed"
477 );
478 }
479
480 #[test]
481 fn test_should_check_whether_write_is_aligned() {
482 let mut mm = make_mm();
483 let data_to_write = FixedSizeData { a: 100, b: 200 };
484 let res = mm.write_at(SCRATCH_PAGE, 2, &data_to_write);
485 assert!(matches!(res, Err(MemoryError::OffsetNotAligned { .. })));
486 }
487
488 #[test]
489 fn test_should_check_whether_read_is_aligned() {
490 let mut mm = make_mm();
491 let result: MemoryResult<FixedSizeData> = mm.read_at(SCRATCH_PAGE, 3);
492 assert!(matches!(result, Err(MemoryError::OffsetNotAligned { .. })));
493 }
494
495 #[test]
496 fn test_should_check_whether_zero_is_aligned() {
497 let mut mm = make_mm();
498 let data_to_zero = FixedSizeData { a: 1, b: 2 };
499 let result = mm.zero(SCRATCH_PAGE, 5, &data_to_zero);
500 assert!(matches!(result, Err(MemoryError::OffsetNotAligned { .. })));
501 }
502
503 #[test]
504 fn test_should_not_zero_unallocated_page() {
505 let mut mm = make_mm();
506 let data_to_zero = FixedSizeData { a: 1, b: 2 };
507 let result = mm.zero(10, 0, &data_to_zero);
508 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
509 }
510
511 #[test]
512 fn test_should_not_zero_out_of_bounds() {
513 let mut mm = make_mm();
514 let data_to_zero = FixedSizeData { a: 1, b: 2 };
515 let result = mm.zero(
516 SCRATCH_PAGE,
517 (HeapMemoryProvider::PAGE_SIZE - 4) as PageOffset,
518 &data_to_zero,
519 );
520 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
521 }
522
523 #[test]
524 fn test_should_read_raw() {
525 let mut mm = make_mm();
526 let data_to_write = vec![1u8, 2, 3, 4, 5];
527 mm.write_at_raw(SCRATCH_PAGE, 20, &data_to_write)
528 .expect("Failed to write raw data to scratch page");
529
530 let mut buf = vec![0u8; 5];
531 mm.read_at_raw(SCRATCH_PAGE, 20, &mut buf)
532 .expect("Failed to read raw data from scratch page");
533
534 assert_eq!(buf, data_to_write);
535 }
536
537 #[test]
538 fn test_should_fail_out_of_bounds_access() {
539 let mut mm = make_mm();
540 let data_to_write = FixedSizeData { a: 1, b: 2 };
541 let result = mm.write_at(
542 SCRATCH_PAGE,
543 (HeapMemoryProvider::PAGE_SIZE - 4) as PageOffset,
544 &data_to_write,
545 );
546 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
547
548 let result: MemoryResult<FixedSizeData> = mm.read_at(10, 0);
549 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
550 let result = mm.write_at(10, 0, &data_to_write);
551 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
552 }
553
554 #[test]
555 fn test_rejected_padded_write_does_not_touch_the_next_page() {
556 let mut mm = make_mm();
557 let first = mm.claim_page().expect("claim first");
558 let next = mm.claim_page().expect("claim next");
559 mm.write_at_raw(next, 0, &[0xAA; 4])
560 .expect("seed next page");
561
562 let result = mm.write_at(first, 65_530, &TenAlignedData([3; 6]));
564 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
565
566 let mut buf = [0u8; 4];
567 mm.read_at_raw(next, 0, &mut buf).expect("read next page");
568 assert_eq!(buf, [0xAA; 4]);
569 }
570
571 #[test]
572 fn test_should_claim_new_page_by_growing() {
573 let mut mm = make_mm();
574 let initial_last_page = mm.last_page().unwrap();
575 let new_page = mm.claim_page().expect("Failed to claim new page");
576 assert_eq!(new_page, initial_last_page + 1);
577 let updated_last_page = mm.last_page().unwrap();
578 assert_eq!(updated_last_page, new_page);
579 }
580
581 #[test]
582 fn test_unclaim_then_claim_returns_same_page() {
583 let mut mm = make_mm();
584 let first = mm.claim_page().expect("claim");
585 mm.write_at_raw(first, 0, &[0xAB, 0xCD, 0xEF, 0x42])
587 .expect("write");
588
589 mm.unclaim_page(first).expect("unclaim");
590
591 let reused = mm.claim_page().expect("claim again");
593 assert_eq!(reused, first);
594
595 let mut buf = [0u8; 4];
597 mm.read_at_raw(reused, 0, &mut buf).expect("read");
598 assert_eq!(buf, [0u8; 4]);
599 }
600
601 #[test]
602 fn test_claim_after_exhausting_unclaimed_grows_memory() {
603 let mut mm = make_mm();
604 let page_a = mm.claim_page().expect("claim a");
605 let page_b = mm.claim_page().expect("claim b");
606
607 mm.unclaim_page(page_a).expect("unclaim a");
608 mm.unclaim_page(page_b).expect("unclaim b");
609
610 let _ = mm.claim_page().expect("reuse 1");
612 let _ = mm.claim_page().expect("reuse 2");
613 let high_water_before = mm.last_page().unwrap();
614 let grown = mm.claim_page().expect("grow");
615 assert_eq!(grown, high_water_before + 1);
616 }
617
618 #[test]
619 fn test_unclaim_persists_across_reload() {
620 let mut provider = HeapMemoryProvider::default();
621 {
622 let mut mm = MemoryManager::init(provider);
623 let first = mm.claim_page().expect("claim");
624 mm.unclaim_page(first).expect("unclaim");
625 provider = mm.into_provider();
626 }
627 let mut mm = MemoryManager::init(provider);
628 let reused = mm.claim_page().expect("claim after reload");
629 assert_eq!(reused, RESERVED_PAGES as Page);
630 }
631
632 #[test]
633 fn test_zero_page_zeros_full_page_contents() {
634 let mut mm = make_mm();
635 let page = mm.claim_page().expect("claim");
636 mm.write_at_raw(page, 0, &[1, 2, 3, 4]).expect("write 0");
638 mm.write_at_raw(page, 30_000, &[9, 9]).expect("write 30k");
639
640 mm.zero_page(page).expect("zero_page");
641
642 let mut buf = [0u8; 4];
643 mm.read_at_raw(page, 0, &mut buf).expect("read");
644 assert_eq!(buf, [0u8; 4]);
645 let mut buf = [0u8; 2];
646 mm.read_at_raw(page, 30_000, &mut buf).expect("read");
647 assert_eq!(buf, [0u8; 2]);
648 }
649
650 #[test]
651 fn test_zero_page_rejects_unallocated_page() {
652 let mut mm = make_mm();
653 let result = mm.zero_page(99);
654 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
655 }
656
657 #[test]
658 fn test_should_check_unallocated_page() {
659 let mm = make_mm();
660 let result = mm.check_unallocated_page(100, 0, 10);
661 assert!(matches!(result, Err(MemoryError::SegmentationFault { .. })));
662
663 let last_page = mm.last_page().unwrap();
664 let result = mm.check_unallocated_page(last_page, 0, 10);
665 assert!(result.is_ok());
666 }
667
668 #[test]
669 fn test_should_compute_padding() {
670 assert_eq!(align_up::<DataWithAlignment>(0), 0);
671 assert_eq!(align_up::<DataWithAlignment>(1), 32);
672 assert_eq!(align_up::<DataWithAlignment>(2), 32);
673 assert_eq!(align_up::<DataWithAlignment>(3), 32);
674 assert_eq!(align_up::<DataWithAlignment>(31), 32);
675 assert_eq!(align_up::<DataWithAlignment>(32), 32);
676 assert_eq!(align_up::<DataWithAlignment>(48), 64);
677 assert_eq!(align_up::<DataWithAlignment>(147), 160);
678 }
679
680 #[derive(Debug, Clone, PartialEq)]
681 struct FixedSizeData {
682 a: u16,
683 b: u32,
684 }
685
686 impl Encode for FixedSizeData {
687 const SIZE: DataSize = DataSize::Fixed(6);
688 const ALIGNMENT: PageOffset = 6;
689
690 fn encode(&'_ self) -> Cow<'_, [u8]> {
691 let mut buf = vec![0u8; self.size() as usize];
692 buf[0..2].copy_from_slice(&self.a.to_le_bytes());
693 buf[2..6].copy_from_slice(&self.b.to_le_bytes());
694 Cow::Owned(buf)
695 }
696
697 fn decode(data: Cow<[u8]>) -> MemoryResult<Self>
698 where
699 Self: Sized,
700 {
701 let a = u16::from_le_bytes([data[0], data[1]]);
702 let b = u32::from_le_bytes([data[2], data[3], data[4], data[5]]);
703 Ok(FixedSizeData { a, b })
704 }
705
706 fn size(&self) -> MSize {
707 6
708 }
709 }
710
711 #[derive(Debug, Clone, PartialEq)]
712 struct DataWithAlignment {
713 a: u16,
714 b: u32,
715 }
716
717 impl Encode for DataWithAlignment {
718 const SIZE: DataSize = DataSize::Dynamic;
719 const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
720
721 fn encode(&'_ self) -> Cow<'_, [u8]> {
722 let mut buf = vec![0u8; self.size() as usize];
723 buf[0..2].copy_from_slice(&self.a.to_le_bytes());
724 buf[2..6].copy_from_slice(&self.b.to_le_bytes());
725 Cow::Owned(buf)
726 }
727
728 fn decode(data: Cow<[u8]>) -> MemoryResult<Self>
729 where
730 Self: Sized,
731 {
732 let a = u16::from_le_bytes([data[0], data[1]]);
733 let b = u32::from_le_bytes([data[2], data[3], data[4], data[5]]);
734 Ok(DataWithAlignment { a, b })
735 }
736
737 fn size(&self) -> MSize {
738 6
739 }
740 }
741
742 #[derive(Debug, Clone, PartialEq)]
745 struct TenAlignedData([u8; 6]);
746
747 impl Encode for TenAlignedData {
748 const SIZE: DataSize = DataSize::Dynamic;
749 const ALIGNMENT: PageOffset = 10;
750
751 fn encode(&'_ self) -> Cow<'_, [u8]> {
752 Cow::Owned(self.0.to_vec())
753 }
754
755 fn decode(data: Cow<[u8]>) -> MemoryResult<Self>
756 where
757 Self: Sized,
758 {
759 Ok(Self(data[0..6].try_into()?))
760 }
761
762 fn size(&self) -> MSize {
763 6
764 }
765 }
766}