bbolt_rs/common/page/
mod.rs

1use crate::common::PgId;
2use bytemuck::{Pod, Zeroable};
3use freelist::FREE_LIST_PAGE_FLAG;
4use meta::META_PAGE_FLAG;
5use std::borrow::Cow;
6use std::cmp::Ordering;
7use std::marker::PhantomData;
8use std::mem;
9use std::ops::{Deref, DerefMut};
10use tree::branch::BRANCH_PAGE_FLAG;
11use tree::leaf::LEAF_PAGE_FLAG;
12
13pub mod freelist;
14pub mod meta;
15pub mod tree;
16
17pub const PAGE_HEADER_SIZE: usize = mem::size_of::<PageHeader>();
18
19//TODO: This needs to be cleaned up.
20/// Represents a page type that can be coerced or mutated from a [RefPage] or [MutPage]
21pub trait CoerciblePage {
22  /// The page flag discriminator
23  fn page_flag() -> u16;
24
25  /// Set the page flag
26  #[inline]
27  fn set_flag(page: &mut PageHeader) {
28    page.flags = Self::page_flag();
29  }
30
31  /// Take "ownership" of page pointer.
32  // TODO: Rename because we're not owning the pointer in the memory sense,
33  // but rather as a type
34  fn own(bytes: *mut u8) -> Self;
35
36  /// Const cast a [RefPage] into a specific page type
37  #[inline]
38  unsafe fn unchecked_ref<'a>(mapped_page: &'a RefPage<'_>) -> &'a Self
39  where
40    Self: Sized,
41  {
42    &*(mapped_page as *const RefPage as *const Self)
43  }
44
45  /// Mut cast a [MutPage] into a specific page type
46  #[inline]
47  unsafe fn unchecked_mut<'a>(mapped_page: &'a mut MutPage<'_>) -> &'a mut Self
48  where
49    Self: Sized,
50  {
51    &mut *(mapped_page as *mut MutPage<'_> as *mut Self)
52  }
53
54  /// Mutate a [MutPage] into a specific page type.
55  #[inline]
56  fn mut_into<'a>(mapped_page: &'a mut MutPage<'_>) -> &'a mut Self
57  where
58    Self: Sized,
59  {
60    Self::set_flag(mapped_page);
61    unsafe { Self::unchecked_mut(mapped_page) }
62  }
63
64  /// Const cast a [RefPage] into a specific page type if the type matches
65  #[inline]
66  fn coerce_ref<'a>(mapped_page: &'a RefPage<'_>) -> Option<&'a Self>
67  where
68    Self: Sized,
69  {
70    if mapped_page.flags == Self::page_flag() {
71      Some(unsafe { Self::unchecked_ref(mapped_page) })
72    } else {
73      None
74    }
75  }
76
77  /// Mut cast a [MutPage] into a specific page type if the type matches
78  #[inline]
79  fn coerce_mut<'a>(mapped_page: &'a mut MutPage<'_>) -> Option<&'a mut Self>
80  where
81    Self: Sized,
82  {
83    if mapped_page.flags == Self::page_flag() {
84      Some(unsafe { Self::unchecked_mut(mapped_page) })
85    } else {
86      None
87    }
88  }
89}
90
91/// A read-only view of page aligned, multiple of page-sized section of memory.
92/// Always begins with a [PageHeader]
93#[derive(Copy, Clone, Eq, PartialEq)]
94pub struct RefPage<'tx> {
95  bytes: *const u8,
96  phantom: PhantomData<&'tx [u8]>,
97}
98
99impl<'tx> RefPage<'tx> {
100  pub fn new(bytes: *const u8) -> RefPage<'tx> {
101    RefPage {
102      bytes,
103      phantom: PhantomData,
104    }
105  }
106}
107
108impl<'tx> Deref for RefPage<'tx> {
109  type Target = PageHeader;
110
111  fn deref(&self) -> &Self::Target {
112    unsafe { &*(self.bytes as *const PageHeader) }
113  }
114}
115
116/// A mutable view of page aligned, multiple of page-sized section of memory.
117/// Always begins with a [PageHeader]
118pub struct MutPage<'tx> {
119  bytes: *mut u8,
120  phantom: PhantomData<&'tx mut [u8]>,
121}
122
123impl<'tx> MutPage<'tx> {
124  pub fn new(bytes: *mut u8) -> MutPage<'tx> {
125    MutPage {
126      bytes,
127      phantom: PhantomData,
128    }
129  }
130}
131
132impl<'tx> AsRef<RefPage<'tx>> for MutPage<'tx> {
133  fn as_ref(&self) -> &RefPage<'tx> {
134    unsafe { &*(self as *const MutPage<'tx> as *const RefPage<'tx>) }
135  }
136}
137
138impl<'tx> Deref for MutPage<'tx> {
139  type Target = PageHeader;
140
141  fn deref(&self) -> &Self::Target {
142    unsafe { &*(self.bytes as *const PageHeader) }
143  }
144}
145
146impl<'tx> DerefMut for MutPage<'tx> {
147  fn deref_mut(&mut self) -> &mut Self::Target {
148    unsafe { &mut *(self.bytes as *mut PageHeader) }
149  }
150}
151
152/// `PageHeader` represents the on-file layout of a page header.
153///
154/// `page` in Go BBolt
155#[repr(C)]
156#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
157pub struct PageHeader {
158  /// This Page's ID
159  pub id: PgId,
160  /// Page's type. Branch(0x01), Leaf(0x02), Meta(0x04), or FreeList(0x10)
161  pub flags: u16,
162  /// Defines the number of items in the Branch, Leaf, and Freelist pages
163  pub count: u16,
164  //TODO: make setting this unsafe
165  /// How many additional meta.page_size pages are included in this page
166  pub overflow: u32,
167}
168
169impl PartialOrd for PageHeader {
170  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
171    Some(self.cmp(other))
172  }
173}
174
175impl Ord for PageHeader {
176  fn cmp(&self, other: &Self) -> Ordering {
177    self.id.cmp(&other.id)
178  }
179}
180
181impl PartialEq for PageHeader {
182  fn eq(&self, other: &Self) -> bool {
183    self.id == other.id
184  }
185}
186
187impl Eq for PageHeader {}
188
189impl PageHeader {
190  #[inline]
191  pub fn set_branch(&mut self) {
192    self.flags = BRANCH_PAGE_FLAG;
193  }
194
195  #[inline]
196  pub fn set_leaf(&mut self) {
197    self.flags = LEAF_PAGE_FLAG;
198  }
199
200  #[inline]
201  pub fn set_meta(&mut self) {
202    self.flags = META_PAGE_FLAG;
203  }
204
205  #[inline]
206  pub fn set_free_list(&mut self) {
207    self.flags = FREE_LIST_PAGE_FLAG;
208  }
209
210  pub fn fast_check(&self, id: PgId) {
211    assert_eq!(
212      self.id, id,
213      "Page expected to be {}, but self identifies as {}",
214      id, self.id
215    );
216    assert!(
217      self.flags == BRANCH_PAGE_FLAG
218        || self.flags == LEAF_PAGE_FLAG
219        || self.flags == META_PAGE_FLAG
220        || self.flags == FREE_LIST_PAGE_FLAG,
221      "page {}: has unexpected type/flags {}",
222      self.id,
223      self.flags
224    );
225  }
226
227  #[inline]
228  pub fn is_branch(&self) -> bool {
229    self.flags & BRANCH_PAGE_FLAG != 0
230  }
231
232  #[inline]
233  pub fn is_leaf(&self) -> bool {
234    self.flags & LEAF_PAGE_FLAG != 0
235  }
236
237  #[inline]
238  pub fn is_meta(&self) -> bool {
239    self.flags & META_PAGE_FLAG != 0
240  }
241
242  #[inline]
243  pub fn is_free_list(&self) -> bool {
244    self.flags & FREE_LIST_PAGE_FLAG != 0
245  }
246
247  /// page_type returns a human readable page type string used for debugging.
248  pub fn page_type(&self) -> Cow<'static, str> {
249    if self.is_branch() {
250      Cow::Borrowed("branch")
251    } else if self.is_leaf() {
252      Cow::Borrowed("leaf")
253    } else if self.is_meta() {
254      Cow::Borrowed("meta")
255    } else if self.is_free_list() {
256      Cow::Borrowed("freelist")
257    } else {
258      Cow::Owned(format!("unknown<{:#02x}>", self.flags))
259    }
260  }
261}
262
263/// PageInfo represents human-readable information about a page.
264#[derive(Debug, Eq, PartialEq)]
265pub struct PageInfo {
266  pub id: u64,
267  pub t: Cow<'static, str>,
268  pub count: u64,
269  pub overflow_count: u64,
270}