use crate::util::impl_fmt_with;
#[derive(Copy, Hash)]
#[derive_const(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct u24(u32);
impl u24 {
pub const BITS: u32 = 24;
pub const MIN: Self = Self(0x000000);
pub const MAX: Self = Self(0xFFFFFF);
pub const fn new(num: u32) -> Option<Self> {
if num <= Self::MAX.0 { Some(Self(num)) } else { None }
}
pub const fn new_saturating(num: u32) -> Self {
Self(num.min(Self::MAX.0))
}
pub const fn new_truncating(num: u32) -> Self {
Self(num & Self::MAX.0)
}
pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
let [a, b, c] = bytes;
Self(u32::from_be_bytes([0, a, b, c]))
}
pub const fn to_be_bytes(self) -> [u8; 3] {
*self.0.to_be_bytes().last_chunk().unwrap()
}
pub const fn get(self) -> u32 {
self.0
}
pub const fn wrapping_neg(self) -> Self {
Self(self.0.wrapping_neg() & Self::MAX.0)
}
pub const fn wrapping_add(self, rhs: Self) -> Self {
Self(self.0.wrapping_add(rhs.0) & Self::MAX.0)
}
pub const fn wrapping_sub(self, rhs: Self) -> Self {
Self(self.0.wrapping_sub(rhs.0) & Self::MAX.0)
}
pub const fn wrapping_mul(self, rhs: Self) -> Self {
Self(self.0.wrapping_mul(rhs.0) & Self::MAX.0)
}
pub const fn wrapping_div(self, rhs: Self) -> Self {
Self(self.0.wrapping_div(rhs.0))
}
pub const fn wrapping_rem(self, rhs: Self) -> Self {
Self(self.0.wrapping_rem(rhs.0))
}
pub const fn saturating_add(self, rhs: Self) -> Self {
Self(self.0.wrapping_add(rhs.0).min(Self::MAX.0))
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self(self.0.saturating_sub(rhs.0))
}
pub const fn saturating_mul(self, rhs: Self) -> Self {
Self(self.0.widening_mul(rhs.0).min(Self::MAX.0 as u64) as u32)
}
pub const fn saturating_div(self, rhs: Self) -> Self {
Self(self.0.saturating_div(rhs.0))
}
pub const fn checked_neg(self) -> Option<Self> {
self.0.checked_neg().map(Self)
}
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.wrapping_add(rhs.0).try_into().ok()
}
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
self.0.widening_mul(rhs.0).try_into().ok()
}
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
self.0.checked_div(rhs.0).map(Self)
}
pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
self.0.checked_rem(rhs.0).map(Self)
}
}
const impl std::ops::Add for u24 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
#[cfg(debug_assertions)]
return self.checked_add(rhs).expect("attempt to add with overflow");
#[cfg(not(debug_assertions))]
return self.wrapping_add(rhs);
}
}
const impl std::ops::Sub for u24 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
#[cfg(debug_assertions)]
return self.checked_sub(rhs).expect("attempt to subtract with overflow");
#[cfg(not(debug_assertions))]
return self.wrapping_sub(rhs);
}
}
const impl std::ops::Mul for u24 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
#[cfg(debug_assertions)]
return self.checked_mul(rhs).expect("attempt to multiply with overflow");
#[cfg(not(debug_assertions))]
return self.wrapping_mul(rhs);
}
}
const impl std::ops::Div for u24 {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
#[cfg(debug_assertions)]
return self.checked_div(rhs).expect("attempt to divide by zero");
#[cfg(not(debug_assertions))]
return self.wrapping_div(rhs);
}
}
const impl std::ops::Rem for u24 {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
#[cfg(debug_assertions)]
return self
.checked_rem(rhs)
.expect("attempt to calculate the remainder with a divisor of zero");
#[cfg(not(debug_assertions))]
return self.wrapping_rem(rhs);
}
}
impl_fmt_with! {
Debug, Display, Binary, Octal, LowerHex, UpperHex, LowerExp, UpperExp:
|this: &u24| this.get()
}
const impl PartialEq<u32> for u24 {
fn eq(&self, other: &u32) -> bool {
self.get().eq(other)
}
}
const impl PartialOrd<u32> for u24 {
fn partial_cmp(&self, other: &u32) -> Option<std::cmp::Ordering> {
Some(self.get().cmp(other))
}
}
const impl std::str::FromStr for u24 {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
u32::from_str(s).or(Err(())).and_then(Self::try_from)
}
}
const impl TryFrom<usize> for u24 {
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
value.try_into().ok().and_then(Self::new).ok_or(())
}
}
const impl TryFrom<u64> for u24 {
type Error = ();
fn try_from(value: u64) -> Result<Self, Self::Error> {
value.try_into().ok().and_then(Self::new).ok_or(())
}
}
const impl TryFrom<u32> for u24 {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::new(value).ok_or(())
}
}
const impl From<u16> for u24 {
fn from(value: u16) -> Self {
Self(value as u32)
}
}
const impl From<u8> for u24 {
fn from(value: u8) -> Self {
Self(value as u32)
}
}
const impl TryFrom<u24> for usize {
type Error = ();
fn try_from(value: u24) -> Result<Self, Self::Error> {
value.get().try_into().ok().ok_or(())
}
}
const impl From<u24> for u64 {
fn from(value: u24) -> Self {
value.get() as u64
}
}
const impl From<u24> for u32 {
fn from(value: u24) -> Self {
value.get()
}
}
const impl TryFrom<u24> for u16 {
type Error = ();
fn try_from(value: u24) -> Result<Self, Self::Error> {
value.get().try_into().ok().ok_or(())
}
}
const impl TryFrom<u24> for u8 {
type Error = ();
fn try_from(value: u24) -> Result<Self, Self::Error> {
value.get().try_into().ok().ok_or(())
}
}