#![no_std]
#![feature(const_fn, const_generics)]
#![allow(incomplete_features)]
#![forbid(missing_docs)]
#![forbid(missing_debug_implementations)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::needless_doctest_main)]
use core::{cmp::Ordering, iter::FusedIterator, marker::PhantomData, num::NonZeroUsize};
pub mod read_only;
pub mod write_only;
#[repr(transparent)]
pub struct VolAddress<T> {
address: NonZeroUsize,
marker: PhantomData<*mut T>,
}
impl<T> Clone for VolAddress<T> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for VolAddress<T> {}
impl<T> PartialEq for VolAddress<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.address == other.address
}
}
impl<T> Eq for VolAddress<T> {}
impl<T> PartialOrd for VolAddress<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.address.cmp(&other.address))
}
}
impl<T> Ord for VolAddress<T> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.address.cmp(&other.address)
}
}
impl<T> core::fmt::Debug for VolAddress<T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "VolAddress({:p})", *self)
}
}
impl<T> core::fmt::Pointer for VolAddress<T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{:p}", self.address.get() as *mut T)
}
}
impl<T> VolAddress<T> {
#[inline(always)]
pub const unsafe fn new(address: usize) -> Self {
Self {
address: NonZeroUsize::new_unchecked(address),
marker: PhantomData,
}
}
#[inline(always)]
pub const unsafe fn cast<Z>(self) -> VolAddress<Z> {
VolAddress {
address: self.address,
marker: PhantomData,
}
}
#[inline(always)]
pub const unsafe fn offset(self, offset: isize) -> Self {
Self {
address: NonZeroUsize::new_unchecked(self.address.get().wrapping_add(offset as usize * core::mem::size_of::<T>())),
marker: PhantomData,
}
}
#[inline(always)]
pub const fn is_aligned(self) -> bool {
self.address.get() % core::mem::align_of::<T>() == 0
}
#[inline(always)]
pub const fn to_usize(self) -> usize {
self.address.get()
}
#[inline(always)]
pub const unsafe fn iter_slots(self, slots: usize) -> VolIter<T> {
VolIter {
vol_address: self,
slots_remaining: slots,
}
}
#[inline(always)]
pub fn read(self) -> T
where
T: Copy,
{
unsafe { (self.address.get() as *mut T).read_volatile() }
}
#[inline(always)]
pub unsafe fn read_non_copy(self) -> T {
(self.address.get() as *mut T).read_volatile()
}
#[inline(always)]
pub fn write(self, val: T) {
unsafe { (self.address.get() as *mut T).write_volatile(val) }
}
}
pub struct VolBlock<T, const COUNT: usize> {
vol_address: VolAddress<T>,
}
impl<T, const COUNT: usize> Clone for VolBlock<T, COUNT> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<T, const COUNT: usize> Copy for VolBlock<T, COUNT> {}
impl<T, const COUNT: usize> PartialEq for VolBlock<T, COUNT> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.vol_address == other.vol_address
}
}
impl<T, const COUNT: usize> Eq for VolBlock<T, COUNT> {}
impl<T, const COUNT: usize> core::fmt::Debug for VolBlock<T, COUNT> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "VolBlock({:p}, count={})", self.vol_address.address.get() as *mut T, COUNT)
}
}
impl<T, const COUNT: usize> VolBlock<T, COUNT> {
#[inline(always)]
pub const unsafe fn new(address: usize) -> Self {
Self {
vol_address: VolAddress::new(address),
}
}
#[inline(always)]
pub const fn len(self) -> usize {
COUNT
}
#[inline(always)]
pub const fn iter(self) -> VolIter<T> {
VolIter {
vol_address: self.vol_address,
slots_remaining: COUNT,
}
}
#[inline(always)]
pub const unsafe fn index_unchecked(self, slot: usize) -> VolAddress<T> {
self.vol_address.offset(slot as isize)
}
#[inline(always)]
pub fn index(self, slot: usize) -> VolAddress<T> {
if slot < COUNT {
unsafe { self.index_unchecked(slot) }
} else {
panic!("Index Requested: {} >= Slot Count: {}", slot, COUNT)
}
}
#[inline(always)]
pub fn get(self, slot: usize) -> Option<VolAddress<T>> {
if slot < COUNT {
unsafe { Some(self.index_unchecked(slot)) }
} else {
None
}
}
}
pub struct VolSeries<T, const COUNT: usize, const STRIDE: usize> {
vol_address: VolAddress<T>,
}
impl<T, const COUNT: usize, const STRIDE: usize> Clone for VolSeries<T, COUNT, STRIDE> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<T, const COUNT: usize, const STRIDE: usize> Copy for VolSeries<T, COUNT, STRIDE> {}
impl<T, const COUNT: usize, const STRIDE: usize> PartialEq for VolSeries<T, COUNT, STRIDE> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.vol_address == other.vol_address
}
}
impl<T, const COUNT: usize, const STRIDE: usize> Eq for VolSeries<T, COUNT, STRIDE> {}
impl<T, const COUNT: usize, const STRIDE: usize> core::fmt::Debug for VolSeries<T, COUNT, STRIDE> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"VolSeries({:p}, count={}, series={})",
self.vol_address.address.get() as *mut T,
COUNT,
STRIDE
)
}
}
impl<T, const COUNT: usize, const STRIDE: usize> VolSeries<T, COUNT, STRIDE> {
#[inline(always)]
pub const unsafe fn new(address: usize) -> Self {
Self {
vol_address: VolAddress::new(address),
}
}
#[inline(always)]
pub const fn len(self) -> usize {
COUNT
}
#[inline(always)]
pub const fn iter(self) -> VolStridingIter<T, STRIDE> {
VolStridingIter {
vol_address: self.vol_address,
slots_remaining: COUNT,
}
}
#[inline(always)]
pub const unsafe fn index_unchecked(self, slot: usize) -> VolAddress<T> {
self.vol_address.cast::<u8>().offset((STRIDE * slot) as isize).cast::<T>()
}
#[inline(always)]
pub fn index(self, slot: usize) -> VolAddress<T> {
if slot < COUNT {
unsafe { self.index_unchecked(slot) }
} else {
panic!("Index Requested: {} >= Slot Count: {}", slot, COUNT)
}
}
#[inline(always)]
pub fn get(self, slot: usize) -> Option<VolAddress<T>> {
if slot < COUNT {
unsafe { Some(self.index_unchecked(slot)) }
} else {
None
}
}
}
pub struct VolIter<T> {
vol_address: VolAddress<T>,
slots_remaining: usize,
}
impl<T> Clone for VolIter<T> {
#[inline(always)]
fn clone(&self) -> Self {
Self {
vol_address: self.vol_address,
slots_remaining: self.slots_remaining,
}
}
}
impl<T> PartialEq for VolIter<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.vol_address == other.vol_address && self.slots_remaining == other.slots_remaining
}
}
impl<T> Eq for VolIter<T> {}
impl<T> Iterator for VolIter<T> {
type Item = VolAddress<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.slots_remaining > 0 {
let out = self.vol_address;
unsafe {
self.slots_remaining -= 1;
self.vol_address = self.vol_address.offset(1);
}
Some(out)
} else {
None
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.slots_remaining, Some(self.slots_remaining))
}
#[inline(always)]
fn count(self) -> usize {
self.slots_remaining
}
#[inline(always)]
fn last(self) -> Option<Self::Item> {
if self.slots_remaining > 0 {
Some(unsafe { self.vol_address.offset(self.slots_remaining as isize) })
} else {
None
}
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.slots_remaining > n {
unsafe {
let out = self.vol_address.offset(n as isize);
let jump = n + 1;
self.slots_remaining -= jump;
self.vol_address = self.vol_address.offset(jump as isize);
Some(out)
}
} else {
self.slots_remaining = 0;
None
}
}
#[inline(always)]
fn max(self) -> Option<Self::Item> {
self.last()
}
#[inline(always)]
fn min(mut self) -> Option<Self::Item> {
self.nth(0)
}
}
impl<T> FusedIterator for VolIter<T> {}
impl<T> core::fmt::Debug for VolIter<T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"VolIter({:p}, remaining={})",
self.vol_address.address.get() as *mut T,
self.slots_remaining
)
}
}
pub struct VolStridingIter<T, const STRIDE: usize> {
vol_address: VolAddress<T>,
slots_remaining: usize,
}
impl<T, const STRIDE: usize> Clone for VolStridingIter<T, STRIDE> {
#[inline(always)]
fn clone(&self) -> Self {
Self {
vol_address: self.vol_address,
slots_remaining: self.slots_remaining,
}
}
}
impl<T, const STRIDE: usize> PartialEq for VolStridingIter<T, STRIDE> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.vol_address == other.vol_address && self.slots_remaining == other.slots_remaining
}
}
impl<T, const STRIDE: usize> Eq for VolStridingIter<T, STRIDE> {}
impl<T, const STRIDE: usize> Iterator for VolStridingIter<T, STRIDE> {
type Item = VolAddress<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.slots_remaining > 0 {
let out = self.vol_address;
unsafe {
self.slots_remaining -= 1;
self.vol_address = self.vol_address.cast::<u8>().offset(STRIDE as isize).cast::<T>();
}
Some(out)
} else {
None
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.slots_remaining, Some(self.slots_remaining))
}
#[inline(always)]
fn count(self) -> usize {
self.slots_remaining
}
#[inline(always)]
fn last(self) -> Option<Self::Item> {
if self.slots_remaining > 0 {
Some(unsafe {
self
.vol_address
.cast::<u8>()
.offset((STRIDE * self.slots_remaining) as isize)
.cast::<T>()
})
} else {
None
}
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.slots_remaining > n {
unsafe {
let out = self.vol_address.cast::<u8>().offset((STRIDE * n) as isize).cast::<T>();
let jump = n + 1;
self.slots_remaining -= jump;
self.vol_address = self.vol_address.cast::<u8>().offset((STRIDE * jump) as isize).cast::<T>();
Some(out)
}
} else {
self.slots_remaining = 0;
None
}
}
#[inline(always)]
fn max(self) -> Option<Self::Item> {
self.last()
}
#[inline(always)]
fn min(mut self) -> Option<Self::Item> {
self.nth(0)
}
}
impl<T, const STRIDE: usize> FusedIterator for VolStridingIter<T, STRIDE> {}
impl<T, const STRIDE: usize> core::fmt::Debug for VolStridingIter<T, STRIDE> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"VolStridingIter({:p}, remaining={}, stride={})",
self.vol_address.address.get() as *mut T,
self.slots_remaining,
STRIDE
)
}
}