memory_pager/
lib.rs

1#![cfg_attr(feature = "nightly", deny(missing_docs))]
2#![cfg_attr(feature = "nightly", feature(external_doc))]
3#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
4#![cfg_attr(test, deny(warnings))]
5
6mod iter;
7mod page;
8
9pub use crate::iter::Iter;
10pub use crate::page::Page;
11
12use std::fs::File;
13use std::io;
14use std::io::Read;
15
16/// Memory pager instance. Manages [`Page`] instances.
17///
18/// [`Page`]: struct.Page.html
19#[derive(Debug)]
20pub struct Pager {
21  pages: Vec<Option<Page>>,
22  page_size: usize,
23}
24
25impl Pager {
26  /// Create a new [`Pager`] instance with a [`page_size`].
27  ///
28  /// [`Pager`]: struct.Pager.html
29  /// [`page_size`]: struct.Pager.html#structfield.page_size
30  #[inline]
31  pub fn new(page_size: usize) -> Self {
32    Pager {
33      page_size,
34      pages: Vec::new(),
35    }
36  }
37
38  /// Create a new instance from a file.
39  ///
40  /// This is particularly useful when restoring the `memory-pager` from disk,
41  /// as it's possible to open a file, and directly convert it into a pager
42  /// instance.
43  ///
44  /// # Options
45  ///
46  /// The third argument is an optional `offset` of `usize`. This is useful to
47  /// ignore the first few bytes if the file has a header that isn't part of the
48  /// bitfield's body.
49  ///
50  /// # Errors
51  ///
52  /// This method will return an error if the `File` length is not a multiple of
53  /// `page_size`. It can also fail if it's unable to read the file's metadata.
54  ///
55  /// # Examples
56  ///
57  /// ```rust
58  /// # extern crate memory_pager as pager;
59  /// # extern crate failure;
60  /// use failure::Error;
61  /// use pager::Pager;
62  /// use std::fs;
63  ///
64  /// fn main () -> Result<(), Error> {
65  ///   let mut file = fs::File::open("tests/fixtures/40_empty.bin")?;
66  ///   let page_size = 10;
67  ///   let _pager = Pager::from_file(&mut file, page_size, None)?;
68  ///   Ok(())
69  /// }
70  /// ```
71  #[inline]
72  pub fn from_file(
73    file: &mut File,
74    page_size: usize,
75    offset: Option<usize>,
76  ) -> Result<Self, io::Error> {
77    let offset = offset.unwrap_or(0);
78    let len = file.metadata()?.len() as usize - offset;
79
80    if len % page_size != 0 {
81      return Err(io::Error::new(
82        io::ErrorKind::Other,
83        format!(
84          "<memory-pager>: Reader len ({}) is not a multiple of {}",
85          len, page_size
86        ),
87      ));
88    }
89
90    let page_count = len / page_size;
91    let mut pages = Vec::with_capacity(page_count);
92    let mut buf = vec![0; page_size];
93
94    for index in 0..page_count {
95      let bytes_read = file.read(&mut buf)?;
96
97      // This should already be guarded for, but making extra extra sure.
98      if bytes_read < page_size {
99        break;
100      }
101
102      // The buffer is reused if it only contains zeroes.
103      if is_zeroed(&buf) {
104        pages.push(None);
105      } else {
106        pages.push(Some(Page::new(index, buf)));
107        buf = vec![0; page_size];
108      }
109    }
110
111    Ok(Self { pages, page_size })
112  }
113
114  /// Get a [`Page`] mutably. The page will be allocated on first access.
115  ///
116  /// [`Page`]: struct.Page.html
117  #[inline]
118  pub fn get_mut_or_alloc(&mut self, page_num: usize) -> &mut Page {
119    if page_num >= self.pages.len() {
120      self.grow_pages(page_num);
121    }
122
123    // This should never be out of bounds.
124    if self.pages[page_num].is_none() {
125      let buf = vec![0; self.page_size];
126      let page = Page::new(page_num, buf);
127      self.pages[page_num] = Some(page);
128    }
129
130    self.pages[page_num].as_mut().unwrap()
131  }
132
133  /// Get a [`Page`] wrapped in an `Option` enum. Does not allocate on access.
134  ///
135  /// [`Page`]: struct.Page.html
136  #[inline]
137  pub fn get(&self, page_num: usize) -> Option<&Page> {
138    self.pages.get(page_num).and_then(|page| page.as_ref())
139  }
140
141  /// Get a mutable [`Page`] wrapped in an `Option` enum. Does not allocate on
142  /// access.
143  ///
144  /// [`Page`]: struct.Page.html
145  #[inline]
146  pub fn get_mut(&mut self, page_num: usize) -> Option<&mut Page> {
147    self.pages.get_mut(page_num).and_then(|page| page.as_mut())
148  }
149
150  /// Grow the page buffer capacity to accommodate more elements.
151  #[inline]
152  fn grow_pages(&mut self, index: usize) {
153    self.pages.resize(index + 1, None);
154  }
155
156  /// The number of pages held by `memory-pager`. Doesn't account for empty
157  /// entries. Comparable to `vec.len()` in usage.
158  #[inline]
159  pub fn len(&self) -> usize {
160    self.pages.len()
161  }
162
163  /// check whether the `length` is zero.
164  #[inline]
165  pub fn is_empty(&self) -> bool {
166    self.pages.is_empty()
167  }
168
169  /// Get the memory page size in bytes.
170  #[inline]
171  pub fn page_size(&self) -> usize {
172    self.page_size
173  }
174
175  /// Iterate over `&Pages`.
176  #[inline]
177  pub fn iter(&self) -> Iter<'_> {
178    Iter::new(self)
179  }
180}
181
182/// Create a new [`Pager`] instance with a [`page_size`] of `1024`.
183///
184/// [`Pager`]: struct.Pager.html
185/// [`page_size`]: struct.Pager.html#structfield.page_size
186impl Default for Pager {
187  fn default() -> Self {
188    Pager::new(1024)
189  }
190}
191
192#[inline]
193fn is_zeroed(vec: &[u8]) -> bool {
194  for byte in vec {
195    if *byte != 0 {
196      return false;
197    }
198  }
199  true
200}