use crate::{block::MpmcBlock as Block, page::page_size};
use core::ptr;
#[derive(Clone, Copy, Debug)]
pub(crate) struct HeadCodec {
block_length: usize,
pointer_mask: usize,
index_mask: usize,
has_next_mask: usize,
}
impl HeadCodec {
pub(crate) fn new<T>() -> Self {
let page_size = page_size();
let block_length = Block::<T>::capacity();
let page_bits = page_size.trailing_zeros();
let index_bits = usize::BITS - block_length.leading_zeros();
assert!(
index_bits <= page_bits,
"the block index does not fit in the page-aligned pointer tag"
);
let index_mask = usize::MAX >> (usize::BITS - index_bits);
let has_next_mask = if index_bits < page_bits {
1usize << index_bits
} else {
0
};
Self {
block_length,
pointer_mask: !(page_size - 1),
index_mask,
has_next_mask,
}
}
#[inline]
pub(crate) const fn block_length(self) -> usize {
self.block_length
}
#[cfg(test)]
const fn has_next_enabled(self) -> bool {
self.has_next_mask != 0
}
}
pub(crate) struct Head<T> {
pub(crate) block: *mut Block<T>,
pub(crate) index: usize,
pub(crate) has_next: bool,
}
impl<T> Head<T> {
pub(crate) const ZERO: Self = Self {
block: ptr::null_mut(),
index: 0,
has_next: false,
};
pub(crate) const fn from_ptr(block: *mut Block<T>) -> Self {
Self {
block,
..Self::ZERO
}
}
#[inline]
pub(crate) fn from_usize(value: usize, codec: HeadCodec) -> Self {
Self {
block: ptr::with_exposed_provenance_mut(value & codec.pointer_mask),
index: value & codec.index_mask,
has_next: codec.has_next_mask != 0 && value & codec.has_next_mask != 0,
}
}
#[inline]
pub(crate) fn to_usize(self, codec: HeadCodec) -> usize {
debug_assert_eq!(self.block.addr() & !codec.pointer_mask, 0);
debug_assert!(self.index <= codec.block_length);
self.block.expose_provenance()
| (self.index & codec.index_mask)
| (if self.has_next {
codec.has_next_mask
} else {
0
})
}
pub(crate) const fn is_zero(&self) -> bool {
self.block.is_null() && self.index == 0 && !self.has_next
}
}
impl<T> Copy for Head<T> {}
impl<T> Clone for Head<T> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(test)]
mod tests {
use super::{Head, HeadCodec};
use crate::block::MpmcBlock as Block;
#[test]
fn codec_round_trips_the_page_aligned_block_and_index() {
let codec = HeadCodec::new::<u64>();
let length = codec.block_length();
assert!(codec.has_next_enabled());
let block = Block::<u64>::new();
let head = Head {
block,
index: length,
has_next: true,
};
let decoded = Head::from_usize(head.to_usize(codec), codec);
assert_eq!(decoded.block, block);
assert_eq!(decoded.index, length);
assert!(decoded.has_next);
Block::free(block);
}
}