1#![doc = include_str!("../README.md")]
2#![cfg_attr(target_family = "wasm", feature(wasi_ext))]
3
4mod file_ext;
5pub mod page_no;
6
7use file_ext::FileExt;
8use std::fmt;
9use std::fs::File;
10use std::io::{ErrorKind, Result};
11use std::sync::atomic::{AtomicU64, Ordering};
12
13pub struct Pages<const SIZE: usize> {
14 len: AtomicU64,
16 pub file: File,
17}
18
19impl<const SIZE: usize> Pages<SIZE> {
20 pub fn open(file: File) -> Result<Self> {
22 let block_size = SIZE as u64;
23 let file_len = file.metadata()?.len();
24 if file_len % block_size != 0 {
26 return Err(ErrorKind::InvalidData.into());
27 }
28 Ok(Self {
29 file,
30 len: (file_len / block_size).into(),
31 })
32 }
33
34 pub fn read(&self, num: u64) -> Result<[u8; SIZE]> {
36 debug_assert!(num < self.len() as u64);
37 let mut buf = [0; SIZE];
38 self.file.read_exact_at(&mut buf, SIZE as u64 * num)?;
39 Ok(buf)
40 }
41
42 #[inline]
43 pub fn write(&self, num: u64, buf: [u8; SIZE]) -> Result<()> {
44 debug_assert!(num < self.len() as u64);
45 self.file.write_all_at(&buf, SIZE as u64 * num)
46 }
47
48 pub fn alloc(&self, count: u64) -> Result<u64> {
50 let old_len = self.len.fetch_add(count, Ordering::SeqCst);
51 self.file.set_len(SIZE as u64 * (old_len + count))?;
52 Ok(old_len)
53 }
54
55 pub fn create(&self, buf: [u8; SIZE]) -> Result<u64> {
56 let num = self.len.fetch_add(1, Ordering::SeqCst);
57 self.write(num, buf)?;
58 Ok(num)
59 }
60
61 #[inline]
62 #[allow(clippy::len_without_is_empty)]
63 pub fn len(&self) -> u64 {
64 self.len.load(Ordering::SeqCst)
65 }
66
67 #[inline]
68 pub fn set_len(&self, len: u64) -> Result<()> {
69 self.len.store(len, Ordering::SeqCst);
70 self.file.set_len(SIZE as u64 * len)
71 }
72}
73
74impl<const SIZE: usize> fmt::Debug for Pages<SIZE> {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("Pages")
77 .field("len", &self.len)
78 .field("block size", &SIZE)
79 .finish()
80 }
81}