use core::{fmt::Display, marker::PhantomData};
use zerocopy::{
ByteEq, ByteHash, ByteOrder, FromBytes, Immutable, IntoBytes, KnownLayout, Order, Unaligned,
};
use crate::u24;
#[derive(
Debug, FromBytes, IntoBytes, Immutable, KnownLayout, Unaligned, Clone, Copy, ByteEq, ByteHash,
)]
#[repr(C)]
pub struct U24<O>([u8; 3], PhantomData<O>);
impl<O> U24<O> {
pub const ZERO: Self = Self([0, 0, 0], PhantomData);
pub const MAX: Self = Self([0xFF, 0xFF, 0xFF], PhantomData);
#[inline]
pub const fn from_bytes(bytes: [u8; 3]) -> Self {
Self(bytes, PhantomData)
}
#[inline]
pub const fn to_bytes(&self) -> [u8; 3] {
self.0
}
}
impl<O> Default for U24<O> {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl<O: ByteOrder> U24<O> {
#[inline]
pub const fn new(value: u24) -> Self {
let bytes = match O::ORDER {
Order::BigEndian => value.to_be_bytes(),
Order::LittleEndian => value.to_le_bytes(),
};
Self(bytes, PhantomData)
}
#[inline]
pub const fn get(&self) -> u24 {
match O::ORDER {
Order::BigEndian => u24::from_be_bytes(self.0),
Order::LittleEndian => u24::from_le_bytes(self.0),
}
}
#[inline]
pub const fn set(&mut self, n: u24) {
match O::ORDER {
Order::BigEndian => self.0 = n.to_be_bytes(),
Order::LittleEndian => self.0 = n.to_le_bytes(),
}
}
}
impl<O: ByteOrder> From<u24> for U24<O> {
#[inline]
fn from(value: u24) -> Self {
Self::new(value)
}
}
impl<O: ByteOrder> From<U24<O>> for u24 {
#[inline]
fn from(value: U24<O>) -> Self {
value.get()
}
}
impl<O: ByteOrder> Display for U24<O> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Display::fmt(&self.get(), f)
}
}
impl<O: ByteOrder> PartialOrd for U24<O> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<O: ByteOrder> Ord for U24<O> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.get().cmp(&other.get())
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::format;
use zerocopy::{BE, LE};
use super::*;
#[test]
fn test_constants() {
let zero_be: U24<BE> = U24::ZERO;
let zero_le: U24<LE> = U24::ZERO;
assert_eq!(zero_be.to_bytes(), [0, 0, 0]);
assert_eq!(zero_le.to_bytes(), [0, 0, 0]);
let max_be: U24<BE> = U24::MAX;
let max_le: U24<LE> = U24::MAX;
assert_eq!(max_be.to_bytes(), [0xFF, 0xFF, 0xFF]);
assert_eq!(max_le.to_bytes(), [0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_from_bytes() {
let be_val = U24::<BE>::from_bytes([0x12, 0x34, 0x56]);
assert_eq!(be_val.to_bytes(), [0x12, 0x34, 0x56]);
let le_val = U24::<LE>::from_bytes([0x56, 0x34, 0x12]);
assert_eq!(le_val.to_bytes(), [0x56, 0x34, 0x12]);
}
#[test]
fn test_new_and_get_big_endian() {
let value = u24!(0x123456);
let be_val = U24::<BE>::new(value);
assert_eq!(be_val.to_bytes(), [0x12, 0x34, 0x56]);
assert_eq!(be_val.get(), value);
}
#[test]
fn test_new_and_get_little_endian() {
let value = u24!(0x123456);
let le_val = U24::<LE>::new(value);
assert_eq!(le_val.to_bytes(), [0x56, 0x34, 0x12]);
assert_eq!(le_val.get(), value);
}
#[test]
fn test_endianness_conversion() {
let value = u24!(0x123456);
let be_val = U24::<BE>::new(value);
let le_val = U24::<LE>::new(value);
assert_eq!(be_val.get(), le_val.get());
assert_ne!(be_val.to_bytes(), le_val.to_bytes());
assert_eq!(be_val.to_bytes(), [0x12, 0x34, 0x56]);
assert_eq!(le_val.to_bytes(), [0x56, 0x34, 0x12]);
}
#[test]
fn test_set_method() {
let mut be_val = U24::<BE>::new(u24!(0x111111));
let mut le_val = U24::<LE>::new(u24!(0x111111));
let new_value = u24!(0x123456);
be_val.set(new_value);
le_val.set(new_value);
assert_eq!(be_val.get(), new_value);
assert_eq!(le_val.get(), new_value);
assert_eq!(be_val.to_bytes(), [0x12, 0x34, 0x56]);
assert_eq!(le_val.to_bytes(), [0x56, 0x34, 0x12]);
}
#[test]
fn test_conversions() {
let original = u24!(0x123456);
let be_val: U24<BE> = U24::from(original);
let le_val: U24<LE> = U24::from(original);
assert_eq!(be_val.get(), original);
assert_eq!(le_val.get(), original);
let be_back: u24 = be_val.into();
let le_back: u24 = le_val.into();
assert_eq!(be_back, original);
assert_eq!(le_back, original);
}
#[test]
fn test_edge_values() {
let zero = u24!(0);
let be_zero = U24::<BE>::new(zero);
let le_zero = U24::<LE>::new(zero);
assert_eq!(be_zero, U24::ZERO);
assert_eq!(le_zero, U24::ZERO);
assert_eq!(be_zero.get(), zero);
assert_eq!(le_zero.get(), zero);
let max = u24!(0xFFFFFF);
let be_max = U24::<BE>::new(max);
let le_max = U24::<LE>::new(max);
assert_eq!(be_max, U24::MAX);
assert_eq!(le_max, U24::MAX);
assert_eq!(be_max.get(), max);
assert_eq!(le_max.get(), max);
}
#[test]
fn test_display() {
let value = u24!(0x123456);
let be_val = U24::<BE>::new(value);
let le_val = U24::<LE>::new(value);
assert_eq!(format!("{}", be_val), format!("{}", value));
assert_eq!(format!("{}", le_val), format!("{}", value));
}
#[test]
fn test_default() {
let be_default: U24<BE> = U24::default();
let le_default: U24<LE> = U24::default();
assert_eq!(be_default.to_bytes(), [0, 0, 0]);
assert_eq!(le_default.to_bytes(), [0, 0, 0]);
assert_eq!(be_default.get(), u24!(0));
assert_eq!(le_default.get(), u24!(0));
}
#[test]
fn test_little_endian_ordering_uses_numeric_value() {
let one = U24::<LE>::new(u24!(1));
let two_fifty_six = U24::<LE>::new(u24!(256));
assert!(one < two_fifty_six);
}
}