use crate::format::{MAX_PSIZE, P_BLEAF, P_INVALID, PAGE_HEADER_SIZE, align_entry};
#[derive(Clone, Copy)]
pub(crate) struct PageBufRef<'a> {
bytes: &'a [u8],
}
impl<'a> PageBufRef<'a> {
pub(crate) const fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
pub(crate) fn prev_pg(&self) -> u32 {
read_u32(self.bytes, 4)
}
pub(crate) fn next_pg(&self) -> u32 {
read_u32(self.bytes, 8)
}
pub(crate) fn flags(&self) -> u32 {
read_u32(self.bytes, 12)
}
pub(crate) fn lower(&self) -> usize {
read_u16(self.bytes, 16) as usize
}
pub(crate) fn upper(&self) -> usize {
read_u16(self.bytes, 18) as usize
}
pub(crate) fn nentries(&self) -> usize {
(self.lower() - PAGE_HEADER_SIZE) / 2
}
pub(crate) fn free_space(&self) -> usize {
self.upper() - self.lower()
}
pub(crate) fn is_leaf(&self) -> bool {
self.flags() & P_BLEAF != 0
}
pub(crate) fn is_rightmost(&self) -> bool {
self.next_pg() == P_INVALID
}
pub(crate) fn linp(&self, i: usize) -> u16 {
read_u16(self.bytes, PAGE_HEADER_SIZE + i * 2)
}
pub(crate) const fn bytes(&self) -> &'a [u8] {
self.bytes
}
}
pub(crate) struct PageBufMut<'a> {
bytes: &'a mut [u8],
}
impl<'a> PageBufMut<'a> {
pub(crate) const fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
pub(crate) const fn as_ref(&self) -> PageBufRef<'_> {
PageBufRef::new(self.bytes)
}
pub(crate) fn set_pgno(&mut self, v: u32) {
write_u32(self.bytes, 0, v);
}
pub(crate) fn set_prev_pg(&mut self, v: u32) {
write_u32(self.bytes, 4, v);
}
pub(crate) fn set_next_pg(&mut self, v: u32) {
write_u32(self.bytes, 8, v);
}
pub(crate) fn set_flags(&mut self, v: u32) {
write_u32(self.bytes, 12, v);
}
pub(crate) fn set_lower(&mut self, v: usize) {
assert!(v <= MAX_PSIZE, "lower {v} exceeds MAX_PSIZE {MAX_PSIZE}");
write_u16(self.bytes, 16, page_offset_as_u16(v));
}
pub(crate) fn set_upper(&mut self, v: usize) {
assert!(v <= MAX_PSIZE, "upper {v} exceeds MAX_PSIZE {MAX_PSIZE}");
write_u16(self.bytes, 18, page_offset_as_u16(v));
}
pub(crate) fn set_linp(&mut self, i: usize, v: usize) {
assert!(
v < MAX_PSIZE,
"linp[{i}] = {v} must be < MAX_PSIZE {MAX_PSIZE}"
);
write_u16(self.bytes, PAGE_HEADER_SIZE + i * 2, page_offset_as_u16(v));
}
pub(crate) fn flags(&self) -> u32 {
self.as_ref().flags()
}
pub(crate) fn lower(&self) -> usize {
self.as_ref().lower()
}
pub(crate) fn upper(&self) -> usize {
self.as_ref().upper()
}
pub(crate) fn nentries(&self) -> usize {
self.as_ref().nentries()
}
pub(crate) fn free_space(&self) -> usize {
self.as_ref().free_space()
}
pub(crate) fn linp(&self, i: usize) -> u16 {
self.as_ref().linp(i)
}
pub(crate) const fn bytes(&self) -> &[u8] {
self.bytes
}
pub(crate) const fn bytes_mut(&mut self) -> &mut [u8] {
self.bytes
}
pub(crate) fn reserve_slot(&mut self, idx: usize, nbytes: usize) -> usize {
let n = self.nentries();
assert!(idx <= n, "reserve_slot idx {idx} past nentries {n}");
assert_eq!(
nbytes,
align_entry(nbytes),
"entry size {nbytes} not aligned"
);
assert!(
self.free_space() >= nbytes + 2,
"no room to reserve slot: free_space {free} < {needed}",
free = self.free_space(),
needed = nbytes + 2,
);
let lower = self.lower();
let upper = self.upper();
if idx < n {
let start = PAGE_HEADER_SIZE + idx * 2;
let end = PAGE_HEADER_SIZE + n * 2;
self.bytes.copy_within(start..end, start + 2);
}
let new_upper = upper - nbytes;
self.set_linp(idx, new_upper);
self.set_lower(lower + 2);
self.set_upper(new_upper);
new_upper
}
}
#[inline]
fn read_u16(buf: &[u8], off: usize) -> u16 {
u16::from_ne_bytes([buf[off], buf[off + 1]])
}
#[inline]
fn read_u32(buf: &[u8], off: usize) -> u32 {
u32::from_ne_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]])
}
#[inline]
fn write_u16(buf: &mut [u8], off: usize, v: u16) {
buf[off..off + 2].copy_from_slice(&v.to_ne_bytes());
}
#[inline]
fn write_u32(buf: &mut [u8], off: usize, v: u32) {
buf[off..off + 4].copy_from_slice(&v.to_ne_bytes());
}
#[inline]
fn page_offset_as_u16(off: usize) -> u16 {
debug_assert!(off <= MAX_PSIZE);
u16::try_from(off).expect("page offset within MAX_PSIZE")
}