feophantlib/engine/io/page_formats/
page_header.rs1use crate::engine::io::{format_traits::Parseable, ConstEncodedSize};
4
5use super::{ItemIdData, UInt12, UInt12Error};
6use bytes::{Buf, BufMut};
7use std::convert::TryFrom;
8use thiserror::Error;
9
10#[derive(Debug, PartialEq)]
11pub struct PageHeader {
12 pd_lower: UInt12,
13 pd_upper: UInt12,
14}
15
16impl PageHeader {
17 pub fn new() -> PageHeader {
18 PageHeader {
19 pd_lower: UInt12::new((PageHeader::encoded_size()) as u16).unwrap(),
20 pd_upper: UInt12::max(),
21 }
22 }
23
24 pub fn get_item_count(&self) -> usize {
25 let lower: usize = self.pd_lower.to_u16().into();
26 (lower - PageHeader::encoded_size()) / ItemIdData::encoded_size()
27 }
28
29 pub fn get_free_space(&self) -> usize {
30 if self.pd_upper < self.pd_lower {
32 return 0;
33 }
34 (self.pd_upper - self.pd_lower).to_u16() as usize + 1
35 }
36
37 pub fn can_fit(&self, row_size: usize) -> bool {
38 let needed = row_size + ItemIdData::encoded_size();
39 let have = self.get_free_space();
40 have >= needed
41 }
42
43 pub fn add_item(&mut self, row_size: usize) -> Result<ItemIdData, PageHeaderError> {
44 if !self.can_fit(row_size) {
45 return Err(PageHeaderError::InsufficentFreeSpace());
46 }
47
48 let row_u12 = UInt12::try_from(row_size)?;
49
50 self.pd_lower += UInt12::try_from(ItemIdData::encoded_size())?;
51 self.pd_upper -= row_u12;
52
53 let item_offset = self.pd_upper + UInt12::new(1).unwrap();
55
56 Ok(ItemIdData::new(item_offset, row_u12))
57 }
58
59 pub fn serialize(&self, buffer: &mut impl BufMut) {
60 UInt12::serialize_packed(buffer, &[self.pd_lower, self.pd_upper]);
61 }
62}
63
64impl Default for PageHeader {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl ConstEncodedSize for PageHeader {
71 fn encoded_size() -> usize {
72 3
73 }
74}
75
76impl Parseable<PageHeaderError> for PageHeader {
77 type Output = Self;
78 fn parse(buffer: &mut impl Buf) -> Result<Self, PageHeaderError> {
79 let items = UInt12::parse_packed(buffer, 2)?;
80 Ok(PageHeader {
81 pd_lower: items[0],
82 pd_upper: items[1],
83 })
84 }
85}
86
87#[derive(Debug, Error)]
88pub enum PageHeaderError {
89 #[error("Not enough space to add")]
90 InsufficentFreeSpace(),
91 #[error("Value for u12 too large")]
92 TooLarge(#[from] UInt12Error),
93 #[error("Not enough data has {0} bytes")]
94 InsufficentData(usize),
95 #[error("Lower offset is too large")]
96 LowerOffsetTooLarge(),
97 #[error("Upper offset is too large")]
98 UpperOffsetTooLarge(),
99}
100
101#[cfg(test)]
102mod tests {
103 use bytes::BytesMut;
104
105 use crate::constants::PAGE_SIZE;
106
107 use super::*;
108
109 #[test]
110 fn sizes_match() -> Result<(), Box<dyn std::error::Error>> {
111 let mut test = PageHeader::new();
112 let calc_len = PageHeader::encoded_size();
113
114 let mut buffer = BytesMut::new();
115 test.serialize(&mut buffer);
116
117 assert_eq!(calc_len, buffer.freeze().len());
118 Ok(())
119 }
120
121 #[test]
122 fn test_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
123 let test = PageHeader::new();
124 let mut buffer = BytesMut::new();
125 test.serialize(&mut buffer);
126 let test_rt = PageHeader::parse(&mut buffer)?;
127
128 let test_new = PageHeader::new();
129 assert_eq!(test_rt, test_new);
130
131 Ok(())
132 }
133
134 #[test]
135 fn test_initial_freespace() {
136 let test = PageHeader::new();
137
138 let default_free_space: usize = PAGE_SIZE as usize - PageHeader::encoded_size();
139 let found_free_space = test.get_free_space();
140 assert_eq!(found_free_space, default_free_space);
141 }
142
143 #[test]
144 fn test_item_count() {
145 let mut test = PageHeader::new();
146
147 test.add_item(5).unwrap();
148 test.add_item(5).unwrap();
149
150 assert_eq!(test.get_item_count(), 2);
151
152 let remain_free = PAGE_SIZE as usize - PageHeader::encoded_size() - (ItemIdData::encoded_size() * 2) - 10; assert_eq!(test.get_free_space(), remain_free)
157 }
158
159 #[test]
160 fn test_too_big() -> Result<(), Box<dyn std::error::Error>> {
161 let mut test = PageHeader::new();
162
163 let needed = PAGE_SIZE as usize - PageHeader::encoded_size() - ItemIdData::encoded_size();
164 test.add_item(needed)?; assert_eq!(test.get_item_count(), 1); assert_eq!(test.get_free_space(), 0); assert!(!test.can_fit(1)); assert!(test.add_item(0).is_err()); Ok(())
172 }
173}