1#[cfg(test)]
2pub mod tests;
3
4#[cfg(test)]
5use crate::test_util::journal::TestTransaction as Transaction;
6#[cfg(test)]
7use crate::test_util::journal::TestWriteJournal as WriteJournal;
8use crate::util::div_mod_pow2;
9use crate::util::div_pow2;
10use crate::util::floor_pow2;
11use crate::util::mod_pow2;
12use num_derive::FromPrimitive;
13use num_traits::FromPrimitive;
14use off64::int::create_u64_be;
15use off64::int::Off64ReadInt;
16use off64::int::Off64WriteMutInt;
17use off64::usz;
18use std::sync::Arc;
19use tracing::info;
20#[cfg(not(test))]
21use write_journal::Transaction;
22#[cfg(not(test))]
23use write_journal::WriteJournal;
24
25pub(crate) const MIN_PAGE_SIZE_POW2: u8 = 8;
26pub(crate) const MAX_PAGE_SIZE_POW2: u8 = 32;
27
28const PAGE_HEADER_CAP_POW2: u8 = 4;
30pub(crate) const PAGE_HEADER_CAP: u64 = 1 << PAGE_HEADER_CAP_POW2;
31
32const FREE_PAGE_OFFSETOF_PREV: u64 = 0;
33const FREE_PAGE_OFFSETOF_NEXT: u64 = FREE_PAGE_OFFSETOF_PREV + 5;
34
35const OBJECT_OFFSETOF_PREV: u64 = 0;
36const OBJECT_OFFSETOF_NEXT: u64 = OBJECT_OFFSETOF_PREV + 5;
37const OBJECT_OFFSETOF_DELETED_SEC: u64 = OBJECT_OFFSETOF_NEXT + 5;
38const OBJECT_OFFSETOF_META_SIZE_AND_STATE: u64 = OBJECT_OFFSETOF_DELETED_SEC + 5;
39
40pub(crate) trait PageHeader {
41 fn serialize(&self, out: &mut [u8]);
43 fn deserialize(raw: &[u8]) -> Self;
44}
45
46pub(crate) struct FreePagePageHeader {
47 pub prev: u64,
48 pub next: u64,
49}
50
51impl PageHeader for FreePagePageHeader {
52 fn serialize(&self, out: &mut [u8]) {
53 out.write_u40_be_at(FREE_PAGE_OFFSETOF_PREV, self.prev >> MIN_PAGE_SIZE_POW2);
54 out.write_u40_be_at(FREE_PAGE_OFFSETOF_NEXT, self.next >> MIN_PAGE_SIZE_POW2);
55 }
56
57 fn deserialize(raw: &[u8]) -> Self {
58 Self {
59 prev: raw.read_u40_be_at(FREE_PAGE_OFFSETOF_PREV) << MIN_PAGE_SIZE_POW2,
60 next: raw.read_u40_be_at(FREE_PAGE_OFFSETOF_NEXT) << MIN_PAGE_SIZE_POW2,
61 }
62 }
63}
64
65#[derive(PartialEq, Eq, Clone, Copy, Debug, FromPrimitive)]
66#[repr(u8)]
67pub(crate) enum ObjectState {
68 Incomplete = 1,
70 Committed,
71 Deleted,
72}
73
74pub(crate) struct ObjectPageHeader {
75 pub prev: u64,
77 pub next: u64,
78 pub deleted_sec: Option<u64>,
80 pub state: ObjectState,
81 pub metadata_size_pow2: u8,
82}
83
84impl PageHeader for ObjectPageHeader {
85 fn serialize(&self, out: &mut [u8]) {
86 out.write_u40_be_at(OBJECT_OFFSETOF_PREV, self.prev >> MIN_PAGE_SIZE_POW2);
87 out.write_u40_be_at(OBJECT_OFFSETOF_NEXT, self.next >> MIN_PAGE_SIZE_POW2);
88 out.write_u40_be_at(OBJECT_OFFSETOF_DELETED_SEC, self.deleted_sec.unwrap_or(0));
89 out[usz!(OBJECT_OFFSETOF_META_SIZE_AND_STATE)] =
90 (self.metadata_size_pow2 << 3) | (self.state as u8);
91 }
92
93 fn deserialize(raw: &[u8]) -> Self {
94 let b = raw[usz!(OBJECT_OFFSETOF_META_SIZE_AND_STATE)];
95 Self {
96 prev: raw.read_u40_be_at(OBJECT_OFFSETOF_PREV) << MIN_PAGE_SIZE_POW2,
97 next: raw.read_u40_be_at(OBJECT_OFFSETOF_NEXT) << MIN_PAGE_SIZE_POW2,
98 deleted_sec: Some(raw.read_u40_be_at(OBJECT_OFFSETOF_DELETED_SEC)).filter(|ts| *ts != 0),
99 state: ObjectState::from_u8(b & 0b111).unwrap(),
100 metadata_size_pow2: b >> 3,
101 }
102 }
103}
104
105pub(crate) struct Pages {
106 journal: Arc<WriteJournal>,
108 heap_dev_offset: u64,
109 block_size_pow2: u8,
110 pages_per_lpage_pow2: u8,
111 pub block_size: u64,
113 pub lpage_size_pow2: u8,
115 pub spage_size_pow2: u8,
117}
118
119impl Pages {
120 pub fn new(
121 journal: Arc<WriteJournal>,
122 heap_dev_offset: u64,
123 spage_size_pow2: u8,
124 lpage_size_pow2: u8,
125 ) -> Pages {
126 assert!(spage_size_pow2 >= MIN_PAGE_SIZE_POW2);
127 assert!(lpage_size_pow2 >= spage_size_pow2 && lpage_size_pow2 <= MAX_PAGE_SIZE_POW2);
128 assert_eq!(mod_pow2(heap_dev_offset, lpage_size_pow2), 0);
130 let pages_per_lpage_pow2 = 1 + lpage_size_pow2 - spage_size_pow2;
134 let lpages_per_block_pow2 = lpage_size_pow2 + 3 - pages_per_lpage_pow2;
136 let block_size_pow2 = lpages_per_block_pow2 + lpage_size_pow2;
138 let block_size = 1 << block_size_pow2;
139 info!(block_size, "page config");
140 Pages {
141 block_size_pow2,
142 block_size,
143 heap_dev_offset,
144 journal,
145 lpage_size_pow2,
146 pages_per_lpage_pow2,
147 spage_size_pow2,
148 }
149 }
150
151 #[allow(unused)]
152 pub fn spage_size(&self) -> u64 {
153 1 << self.spage_size_pow2
154 }
155
156 #[allow(unused)]
157 pub fn lpage_size(&self) -> u64 {
158 1 << self.lpage_size_pow2
159 }
160
161 fn assert_valid_page_dev_offset(&self, page_dev_offset: u64) {
162 assert!(
164 page_dev_offset >= self.heap_dev_offset,
165 "page dev offset {page_dev_offset} is not in the heap"
166 );
167 assert!(
169 mod_pow2(page_dev_offset - self.heap_dev_offset, self.block_size_pow2) >= self.lpage_size(),
170 "page dev offset {page_dev_offset} is in a metadata lpage"
171 );
172 assert_eq!(
174 mod_pow2(page_dev_offset, self.spage_size_pow2),
175 0,
176 "page dev offset {page_dev_offset} is not a multiple of spage"
177 );
178 }
179
180 fn get_page_free_bit_offset(&self, page_dev_offset: u64, page_size_pow2: u8) -> (u64, u64) {
181 self.assert_valid_page_dev_offset(page_dev_offset);
182 let block_dev_offset = self.heap_dev_offset
184 + floor_pow2(page_dev_offset - self.heap_dev_offset, self.block_size_pow2);
185 let (lpage_n, offset_within_lpage) =
210 div_mod_pow2(page_dev_offset - block_dev_offset, self.lpage_size_pow2);
211 let lpage_dev_offset = page_dev_offset - offset_within_lpage;
212 let n = div_pow2(page_dev_offset - lpage_dev_offset, page_size_pow2);
213 let i = (lpage_n * (1 << self.pages_per_lpage_pow2))
214 | (1 << (self.lpage_size_pow2 - page_size_pow2))
215 | n;
216 let (elem, bit) = div_mod_pow2(i - 1, 6);
217 let elem_dev_offset = block_dev_offset + (elem * 8);
218
219 (elem_dev_offset, bit)
220 }
221
222 pub async fn is_page_free(&self, page_dev_offset: u64, page_size_pow2: u8) -> bool {
223 let (elem_dev_offset, bit) = self.get_page_free_bit_offset(page_dev_offset, page_size_pow2);
224 let bitmap = self
225 .journal
226 .read_with_overlay(elem_dev_offset, 8)
227 .await
228 .read_u64_be_at(0);
229 (bitmap & (1 << bit)) != 0
230 }
231
232 pub async fn mark_page_as_free(
233 &self,
234 txn: &mut Transaction,
235 page_dev_offset: u64,
236 page_size_pow2: u8,
237 ) {
238 let (elem_dev_offset, bit) = self.get_page_free_bit_offset(page_dev_offset, page_size_pow2);
239 let bitmap = self
240 .journal
241 .read_with_overlay(elem_dev_offset, 8)
242 .await
243 .read_u64_be_at(0);
244 txn.write_with_overlay(elem_dev_offset, create_u64_be(bitmap | (1 << bit)));
245 }
246
247 pub async fn mark_page_as_not_free(
248 &self,
249 txn: &mut Transaction,
250 page_dev_offset: u64,
251 page_size_pow2: u8,
252 ) {
253 let (elem_dev_offset, bit) = self.get_page_free_bit_offset(page_dev_offset, page_size_pow2);
254 let bitmap = self
255 .journal
256 .read_with_overlay(elem_dev_offset, 8)
257 .await
258 .read_u64_be_at(0);
259 txn.write_with_overlay(elem_dev_offset, create_u64_be(bitmap & !(1 << bit)));
260 }
261
262 pub async fn read_page_header<H: PageHeader>(&self, page_dev_offset: u64) -> H {
263 self.assert_valid_page_dev_offset(page_dev_offset);
264 let raw = self
265 .journal
266 .read_with_overlay(page_dev_offset, PAGE_HEADER_CAP)
267 .await;
268 H::deserialize(&raw)
269 }
270
271 pub fn write_page_header<H: PageHeader>(
272 &self,
273 txn: &mut Transaction,
274 page_dev_offset: u64,
275 h: H,
276 ) {
277 self.assert_valid_page_dev_offset(page_dev_offset);
278 let mut out = vec![0u8; usz!(PAGE_HEADER_CAP)];
279 h.serialize(&mut out);
280 txn.write_with_overlay(page_dev_offset, out);
281 }
282
283 pub async fn update_page_header<H: PageHeader>(
284 &self,
285 txn: &mut Transaction,
286 page_dev_offset: u64,
287 f: impl FnOnce(&mut H) -> (),
288 ) {
289 let mut hdr = self.read_page_header(page_dev_offset).await;
290 f(&mut hdr);
291 self.write_page_header(txn, page_dev_offset, hdr);
292 }
293}