feophantlib/engine/io/page_formats/
page_offset.rs1use crate::{
2 constants::{PAGES_PER_FILE, PAGE_SIZE},
3 engine::io::{
4 format_traits::{Parseable, Serializable},
5 ConstEncodedSize,
6 },
7};
8use bytes::{Buf, BufMut};
9use std::{
10 convert::TryFrom,
11 fmt,
12 mem::size_of,
13 num::TryFromIntError,
14 ops::{Add, AddAssign, Mul},
15};
16use thiserror::Error;
17
18#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct PageOffset(pub usize);
20
21impl PageOffset {
22 pub fn calculate_page_offset(file_number: usize, offset_in_file: usize) -> PageOffset {
28 let offset = file_number * PAGES_PER_FILE + (offset_in_file / PAGE_SIZE as usize);
29 PageOffset(offset)
30 }
31
32 pub fn get_file_chunk_size(&self) -> usize {
34 ((self.0 % PAGES_PER_FILE) + 1) * PAGE_SIZE as usize
35 }
36 pub fn get_file_number(&self) -> usize {
38 self.0 / PAGES_PER_FILE
39 }
40
41 pub fn get_file_seek(&self) -> usize {
43 self.get_file_chunk_size() - PAGE_SIZE as usize
44 }
45
46 pub fn get_bitmask_offset(&self) -> (PageOffset, usize) {
53 let offset = self.0 / (PAGE_SIZE as usize * 8);
54 let inside_offset = self.0 % (PAGE_SIZE as usize * 8);
55 (PageOffset(offset), inside_offset)
56 }
57
58 pub fn is_same_file(&self, rhs: &PageOffset) -> bool {
60 let diff;
61 if self.0 > rhs.0 {
62 diff = self.0 - rhs.0;
63 } else {
64 diff = rhs.0 - self.0;
65 }
66 PAGES_PER_FILE > diff
67 }
68
69 pub fn next(&self) -> PageOffset {
71 PageOffset(self.0 + 1)
72 }
73}
74
75impl Add for PageOffset {
76 type Output = PageOffset;
77
78 fn add(self, rhs: Self) -> Self::Output {
79 PageOffset(self.0 + rhs.0)
80 }
81}
82
83impl AddAssign for PageOffset {
84 fn add_assign(&mut self, other: Self) {
85 self.0.add_assign(other.0);
86 }
87}
88
89impl ConstEncodedSize for PageOffset {
90 fn encoded_size() -> usize {
91 size_of::<usize>()
92 }
93}
94
95impl fmt::Display for PageOffset {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 self.0.fmt(f)
98 }
99}
100
101impl Parseable<PageOffsetError> for PageOffset {
102 type Output = Self;
103 fn parse(buffer: &mut impl Buf) -> Result<Self, PageOffsetError> {
104 if buffer.remaining() < size_of::<usize>() {
105 return Err(PageOffsetError::BufferTooShort(
106 size_of::<usize>(),
107 buffer.remaining(),
108 ));
109 }
110
111 let value = buffer.get_uint_le(size_of::<usize>());
112 let page = usize::try_from(value)?;
113
114 Ok(PageOffset(page))
115 }
116}
117
118impl Mul for PageOffset {
119 type Output = PageOffset;
120
121 fn mul(self, rhs: Self) -> Self::Output {
122 PageOffset(self.0 * rhs.0)
123 }
124}
125
126impl Serializable for PageOffset {
127 fn serialize(&self, buffer: &mut impl BufMut) {
128 buffer.put_uint_le(self.0 as u64, size_of::<usize>());
131 }
132}
133
134#[derive(Debug, Error, PartialEq)]
135pub enum PageOffsetError {
136 #[error("Not enough space to parse usize need {0} got {1}")]
137 BufferTooShort(usize, usize),
138 #[error(transparent)]
139 TryFromIntError(#[from] TryFromIntError),
140}
141
142#[cfg(test)]
143mod tests {
144 use std::collections::HashMap;
145
146 use uuid::Uuid;
147
148 use super::*;
149
150 #[test]
151 fn test_add() -> Result<(), Box<dyn std::error::Error>> {
152 assert_eq!(PageOffset(1) + PageOffset(2), PageOffset(3));
153 Ok(())
154 }
155
156 #[test]
157 fn test_add_assign() -> Result<(), Box<dyn std::error::Error>> {
158 let mut test = PageOffset(1);
159 test += PageOffset(2);
160 assert_eq!(test, PageOffset(3));
161 Ok(())
162 }
163
164 #[test]
165 fn test_mul() -> Result<(), Box<dyn std::error::Error>> {
166 assert_eq!(PageOffset(2) * PageOffset(3), PageOffset(6));
167 Ok(())
168 }
169
170 #[test]
171 fn test_calculate_page_offset() -> Result<(), Box<dyn std::error::Error>> {
172 assert_eq!(PageOffset::calculate_page_offset(0, 0), PageOffset(0));
173 assert_eq!(
174 PageOffset::calculate_page_offset(0, PAGE_SIZE as usize),
175 PageOffset(1)
176 );
177
178 assert_eq!(
179 PageOffset::calculate_page_offset(1, PAGE_SIZE as usize),
180 PageOffset(PAGES_PER_FILE + 1)
181 );
182
183 Ok(())
184 }
185
186 #[test]
187 fn test_get_file_chunk_size() -> Result<(), Box<dyn std::error::Error>> {
188 assert_eq!(PageOffset(0).get_file_chunk_size(), PAGE_SIZE as usize);
189 assert_eq!(PageOffset(1).get_file_chunk_size(), PAGE_SIZE as usize * 2);
190 assert_eq!(
191 PageOffset(PAGES_PER_FILE).get_file_chunk_size(),
192 PAGE_SIZE as usize
193 );
194 assert_eq!(
195 PageOffset(PAGES_PER_FILE - 1).get_file_chunk_size(),
196 PAGE_SIZE as usize * PAGES_PER_FILE
197 );
198 assert_eq!(
199 PageOffset(PAGES_PER_FILE + 1).get_file_chunk_size(),
200 2 * PAGE_SIZE as usize
201 );
202
203 Ok(())
204 }
205
206 #[test]
207 fn test_get_file_number() -> Result<(), Box<dyn std::error::Error>> {
208 assert_eq!(PageOffset(0).get_file_number(), 0);
209 assert_eq!(PageOffset(PAGES_PER_FILE).get_file_number(), 1);
210 assert_eq!(PageOffset(PAGES_PER_FILE - 1).get_file_number(), 0);
211 assert_eq!(PageOffset(PAGES_PER_FILE + 1).get_file_number(), 1);
212
213 Ok(())
214 }
215
216 #[test]
217 fn test_get_file_seek() -> Result<(), Box<dyn std::error::Error>> {
218 assert_eq!(PageOffset(0).get_file_seek(), 0);
219 assert_eq!(PageOffset(PAGES_PER_FILE).get_file_seek(), 0);
220 assert_eq!(
221 PageOffset(PAGES_PER_FILE - 1).get_file_seek(),
222 (PAGES_PER_FILE - 1) * PAGE_SIZE as usize
223 );
224 assert_eq!(
225 PageOffset(PAGES_PER_FILE + 1).get_file_seek(),
226 PAGE_SIZE as usize
227 );
228 assert_eq!(
229 PageOffset(PAGES_PER_FILE + 2).get_file_seek(),
230 2 * PAGE_SIZE as usize
231 );
232
233 Ok(())
234 }
235
236 #[test]
237 fn test_is_same_file() -> Result<(), Box<dyn std::error::Error>> {
238 assert!(!PageOffset(0).is_same_file(&PageOffset(PAGES_PER_FILE)));
239 assert!(PageOffset(0).is_same_file(&PageOffset(0)));
240 assert!(PageOffset(0).is_same_file(&PageOffset(PAGES_PER_FILE - 1)));
241 assert!(!PageOffset(PAGES_PER_FILE).is_same_file(&PageOffset(0)));
242 assert!(PageOffset(PAGES_PER_FILE - 1).is_same_file(&PageOffset(0)));
243
244 Ok(())
245 }
246
247 #[test]
248 fn test_increment_and_hash_map() -> Result<(), Box<dyn std::error::Error>> {
249 let test = PageOffset(0);
250 assert_eq!(test.next(), PageOffset(1));
251
252 let test_uuid = Uuid::new_v4();
253
254 let mut resource_lookup: HashMap<Uuid, PageOffset> = HashMap::new();
255 resource_lookup.insert(test_uuid, PageOffset(0));
256 let test0 = resource_lookup.remove(&test_uuid).unwrap();
257 resource_lookup.insert(test_uuid, test0.next());
258 let test1 = resource_lookup.get(&test_uuid).unwrap();
259
260 assert_eq!(*test1, PageOffset(1));
261 Ok(())
262 }
263}