#[repr(align(4096))]
#[derive(Clone, Copy)]
pub struct Page(#[allow(unused)] [u8; Buffer::PAGE]);
const _: () = {
assert!(size_of::<Page>() == Buffer::PAGE);
assert!(align_of::<Page>() == Buffer::PAGE);
};
pub struct Buffer {
pages: Vec<Page>,
len: usize,
}
impl Buffer {
const PAGE: usize = 4096;
#[must_use]
pub fn new(len: usize) -> Self {
let pages = len.div_ceil(Self::PAGE).max(1);
Self {
pages: vec![Page([0; Self::PAGE]); pages],
len,
}
}
fn as_bytes(pages: &[Page]) -> &[u8] {
unsafe {
std::slice::from_raw_parts(pages.as_ptr().cast::<u8>(), std::mem::size_of_val(pages))
}
}
fn as_bytes_mut(pages: &mut [Page]) -> &mut [u8] {
let len = std::mem::size_of_val(pages);
unsafe { std::slice::from_raw_parts_mut(pages.as_mut_ptr().cast::<u8>(), len) }
}
}
impl std::ops::Deref for Buffer {
type Target = [u8];
fn deref(&self) -> &[u8] {
&Self::as_bytes(&self.pages)[..self.len]
}
}
impl std::ops::DerefMut for Buffer {
fn deref_mut(&mut self) -> &mut [u8] {
&mut Self::as_bytes_mut(&mut self.pages)[..self.len]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_page_aligned() {
for len in [1, 4095, 4096, 4097, 256 * 1024] {
let buf = Buffer::new(len);
assert_eq!(buf.as_ptr() as usize % Buffer::PAGE, 0, "len {len}");
assert_eq!(buf.len(), len, "len {len}");
}
}
#[test]
fn is_zeroed_and_writable() {
let mut buf = Buffer::new(8192);
assert!(buf.iter().all(|&b| b == 0));
buf[8191] = 7;
assert_eq!(buf[8191], 7);
}
}