use crate::aligned_bytes::{Alignment, Misaligned, A1, A2, A4, A8};
use core::{borrow::Borrow, convert::TryFrom, fmt::Display, marker::PhantomData};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct AlignedOffset<A: Alignment>(usize, PhantomData<A>)
where
Self: core::ops::BitOr,
<Self as core::ops::BitOr>::Output: Into<Self>;
impl<A: Alignment> AlignedOffset<A> {
pub fn to_usize(&self) -> usize {
self.0
}
pub fn try_new(value: usize) -> Result<Self, Misaligned> {
if value % A::ALIGNMENT == 0 {
Ok(Self(value, PhantomData::<A> {}))
} else {
Err(Misaligned {})
}
}
}
pub fn align_offset<A: Alignment>(idx: usize) -> AlignedOffset<A> {
AlignedOffset::<A>(
(idx + A::ALIGNMENT - 1) & !(A::ALIGNMENT - 1),
PhantomData::<A> {},
)
}
impl<A: Alignment> Display for AlignedOffset<A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<A: Alignment> PartialEq<usize> for AlignedOffset<A> {
fn eq(&self, other: &usize) -> bool {
*other == self.0
}
}
impl<A: Alignment> PartialOrd<usize> for AlignedOffset<A> {
fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl<A: Alignment> PartialEq<AlignedOffset<A>> for usize {
fn eq(&self, other: &AlignedOffset<A>) -> bool {
*self == other.0
}
}
impl<A: Alignment> PartialOrd<AlignedOffset<A>> for usize {
fn partial_cmp(&self, other: &AlignedOffset<A>) -> Option<core::cmp::Ordering> {
self.partial_cmp(&other.0)
}
}
impl<A: Alignment> TryFrom<usize> for AlignedOffset<A> {
type Error = Misaligned;
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::try_new(value)
}
}
impl<A: Alignment> Borrow<usize> for AlignedOffset<A> {
fn borrow(&self) -> &usize {
&self.0
}
}
impl<A: Alignment> From<AlignedOffset<A>> for usize {
fn from(value: AlignedOffset<A>) -> Self {
value.0
}
}
impl<A: Alignment> core::ops::BitOr for AlignedOffset<A> {
type Output = AlignedOffset<A>;
fn bitor(self, rhs: Self) -> Self::Output {
AlignedOffset::<A>(self.0 | rhs.0, PhantomData::<A> {})
}
}
impl<A: Alignment> core::ops::Add for AlignedOffset<A> {
type Output = AlignedOffset<A>;
fn add(self, rhs: Self) -> Self::Output {
AlignedOffset::<A>(self.0 + rhs.0, PhantomData::<A> {})
}
}
macro_rules! impl_bitor {
($x:ty, $y:ty, $min:ty) => {
impl core::ops::BitOr<AlignedOffset<$x>> for AlignedOffset<$y> {
type Output = AlignedOffset<$min>;
fn bitor(self, rhs: AlignedOffset<$x>) -> AlignedOffset<$min> {
AlignedOffset::<$min>(self.0 | rhs.0, PhantomData::<$min> {})
}
}
};
}
macro_rules! narrowing {
($big:ty, $small:ty) => {
impl_bitor!($big, $small, $small);
impl_bitor!($small, $big, $small);
impl From<AlignedOffset<$big>> for AlignedOffset<$small> {
fn from(v: AlignedOffset<$big>) -> Self {
AlignedOffset::<$small>(v.0, PhantomData)
}
}
};
}
narrowing!(A2, A1);
narrowing!(A4, A1);
narrowing!(A8, A1);
narrowing!(A4, A2);
narrowing!(A8, A2);
narrowing!(A8, A4);
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::aligned_bytes::{A2, A4};
use core::convert::TryInto;
#[test]
fn test() {
let x: Result<AlignedOffset<A2>, Misaligned> = 3usize.try_into();
assert!(x.is_err());
let x: AlignedOffset<A2> = 0usize.try_into().unwrap();
assert_eq!(x.to_usize(), 0usize);
let x: AlignedOffset<A4> = 8usize.try_into().unwrap();
assert_eq!(x.to_usize(), 8usize);
assert_eq!(align_offset::<A4>(0).to_usize(), 0);
assert_eq!(align_offset::<A4>(1).to_usize(), 4);
assert_eq!(align_offset::<A4>(4).to_usize(), 4);
assert_eq!(align_offset::<A4>(6).to_usize(), 8);
assert_eq!(align_offset::<A4>(7).to_usize(), 8);
assert_eq!(align_offset::<A4>(8).to_usize(), 8);
assert_eq!(align_offset::<A4>(9).to_usize(), 12);
}
}