use core::cmp::Ordering;
use core::fmt::Debug;
use core::hash::Hash;
use core::ops::{AddAssign, Sub};
#[cfg(feature = "get-size2")]
use get_size2::GetSize;
#[cfg(feature = "postcard-schema")]
use postcard_schema::Schema;
#[cfg(feature = "postcard-schema")]
use postcard_schema::schema::{DataModelType, NamedType};
#[cfg(feature = "serde")]
use serde::de::{self, Unexpected};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub trait Index: Debug + Copy + Eq + Ord + Hash + AddAssign + Sub<Output = Self> {
const ZERO: Self;
const MAX: Self;
fn from_usize(i: usize) -> Self;
fn to_usize(&self) -> usize;
fn incr(&mut self);
fn strict_add(self, other: Self) -> Self;
}
impl Index for u16 {
const ZERO: Self = 0;
const MAX: Self = u16::MAX;
fn from_usize(i: usize) -> Self {
i as u16
}
fn to_usize(&self) -> usize {
*self as usize
}
fn incr(&mut self) {
*self += 1;
}
fn strict_add(self, other: Self) -> Self {
self.strict_add(other)
}
}
impl Index for u32 {
const ZERO: Self = 0;
const MAX: Self = u32::MAX;
fn from_usize(i: usize) -> Self {
i as u32
}
fn to_usize(&self) -> usize {
*self as usize
}
fn incr(&mut self) {
*self += 1;
}
fn strict_add(self, other: Self) -> Self {
self.strict_add(other)
}
}
impl Index for u64 {
const ZERO: Self = 0;
const MAX: Self = u64::MAX;
fn from_usize(i: usize) -> Self {
i as u64
}
fn to_usize(&self) -> usize {
*self as usize
}
fn incr(&mut self) {
*self += 1;
}
fn strict_add(self, other: Self) -> Self {
self.strict_add(other)
}
}
macro_rules! impl_index {
( $typ:ty, $uint:ty, $bytes:expr ) => {
impl Debug for $typ {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.to_uint().fmt(f)
}
}
impl PartialOrd for $typ {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $typ {
fn cmp(&self, other: &Self) -> Ordering {
self.to_uint().cmp(&other.to_uint())
}
}
impl Sub for $typ {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::from_uint(self.to_uint() - other.to_uint())
}
}
impl AddAssign for $typ {
fn add_assign(&mut self, other: Self) {
*self = Self::from_uint(self.to_uint() + other.to_uint())
}
}
impl Index for $typ {
const ZERO: Self = Self([0; $bytes]);
const MAX: Self = Self([u8::MAX; $bytes]);
fn from_usize(i: usize) -> Self {
Self::from_uint(i as $uint)
}
fn to_usize(&self) -> usize {
self.to_uint() as usize
}
fn incr(&mut self) {
*self = Self::from_uint(self.to_uint() + 1);
}
fn strict_add(self, other: Self) -> Self {
Self::from_uint(self.to_uint() + other.to_uint())
}
}
#[cfg(feature = "serde")]
impl Serialize for $typ {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_uint().serialize(serializer)
}
}
#[cfg(feature = "postcard-schema")]
impl Schema for $typ {
const SCHEMA: &'static NamedType = &NamedType {
name: stringify!($typ),
ty: &DataModelType::Usize,
};
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for $typ {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let i = Deserialize::deserialize(deserializer)?;
Self::try_from_uint(i).ok_or_else(|| {
de::Error::invalid_value(Unexpected::Unsigned(i.into()), &Self::EXPECTED)
})
}
}
};
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U16([u8; 2]);
impl U16 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 16-bit integer";
#[inline(always)]
fn from_uint(x: u16) -> Self {
Self(x.to_ne_bytes())
}
#[cfg(feature = "serde")]
#[inline(always)]
fn try_from_uint(x: u16) -> Option<Self> {
Some(Self(x.to_ne_bytes()))
}
#[inline(always)]
fn to_uint(self) -> u16 {
u16::from_ne_bytes(self.0)
}
}
impl_index!(U16, u16, 2);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U24([u8; 3]);
impl U24 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 24-bit integer";
#[inline(always)]
fn from_uint(x: u32) -> Self {
Self::try_from_uint(x).expect("Integer overflow while converting u32 to u24")
}
#[inline(always)]
fn try_from_uint(x: u32) -> Option<Self> {
if x >= 1 << 24 {
return None;
}
if cfg!(target_endian = "big") {
let [_, a, b, c] = x.to_ne_bytes();
Some(Self([a, b, c]))
} else {
let [a, b, c, _] = x.to_ne_bytes();
Some(Self([a, b, c]))
}
}
#[inline(always)]
fn to_uint(self) -> u32 {
let [a, b, c] = self.0;
if cfg!(target_endian = "big") {
u32::from_ne_bytes([0, a, b, c])
} else {
u32::from_ne_bytes([a, b, c, 0])
}
}
}
impl_index!(U24, u32, 3);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U32([u8; 4]);
impl U32 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 32-bit integer";
#[inline(always)]
fn from_uint(x: u32) -> Self {
Self(x.to_ne_bytes())
}
#[cfg(feature = "serde")]
#[inline(always)]
fn try_from_uint(x: u32) -> Option<Self> {
Some(Self(x.to_ne_bytes()))
}
#[inline(always)]
fn to_uint(self) -> u32 {
u32::from_ne_bytes(self.0)
}
}
impl_index!(U32, u32, 4);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U40([u8; 5]);
impl U40 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 40-bit integer";
#[inline(always)]
fn from_uint(x: u64) -> Self {
Self::try_from_uint(x).expect("Integer overflow while converting u64 to u40")
}
#[inline(always)]
fn try_from_uint(x: u64) -> Option<Self> {
if x >= 1 << 40 {
return None;
}
if cfg!(target_endian = "big") {
let [_, _, _, a, b, c, d, e] = x.to_ne_bytes();
Some(Self([a, b, c, d, e]))
} else {
let [a, b, c, d, e, _, _, _] = x.to_ne_bytes();
Some(Self([a, b, c, d, e]))
}
}
#[inline(always)]
fn to_uint(self) -> u64 {
let [a, b, c, d, e] = self.0;
if cfg!(target_endian = "big") {
u64::from_ne_bytes([0, 0, 0, a, b, c, d, e])
} else {
u64::from_ne_bytes([a, b, c, d, e, 0, 0, 0])
}
}
}
impl_index!(U40, u64, 5);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U48([u8; 6]);
impl U48 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 48-bit integer";
#[inline(always)]
fn from_uint(x: u64) -> Self {
Self::try_from_uint(x).expect("Integer overflow while converting u64 to u48")
}
#[inline(always)]
fn try_from_uint(x: u64) -> Option<Self> {
if x >= 1 << 48 {
return None;
}
if cfg!(target_endian = "big") {
let [_, _, a, b, c, d, e, f] = x.to_ne_bytes();
Some(Self([a, b, c, d, e, f]))
} else {
let [a, b, c, d, e, f, _, _] = x.to_ne_bytes();
Some(Self([a, b, c, d, e, f]))
}
}
#[inline(always)]
fn to_uint(self) -> u64 {
let [a, b, c, d, e, f] = self.0;
if cfg!(target_endian = "big") {
u64::from_ne_bytes([0, 0, a, b, c, d, e, f])
} else {
u64::from_ne_bytes([a, b, c, d, e, f, 0, 0])
}
}
}
impl_index!(U48, u64, 6);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U56([u8; 7]);
impl U56 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 56-bit integer";
#[inline(always)]
fn from_uint(x: u64) -> Self {
Self::try_from_uint(x).expect("Integer overflow while converting u64 to u56")
}
#[inline(always)]
fn try_from_uint(x: u64) -> Option<Self> {
if x >= 1 << 56 {
return None;
}
if cfg!(target_endian = "big") {
let [_, a, b, c, d, e, f, g] = x.to_ne_bytes();
Some(Self([a, b, c, d, e, f, g]))
} else {
let [a, b, c, d, e, f, g, _] = x.to_ne_bytes();
Some(Self([a, b, c, d, e, f, g]))
}
}
#[inline(always)]
fn to_uint(self) -> u64 {
let [a, b, c, d, e, f, g] = self.0;
if cfg!(target_endian = "big") {
u64::from_ne_bytes([0, a, b, c, d, e, f, g])
} else {
u64::from_ne_bytes([a, b, c, d, e, f, g, 0])
}
}
}
impl_index!(U56, u64, 7);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "get-size2", derive(GetSize))]
pub struct U64([u8; 8]);
impl U64 {
#[cfg(feature = "serde")]
const EXPECTED: &str = "a 64-bit integer";
#[inline(always)]
fn from_uint(x: u64) -> Self {
Self(x.to_ne_bytes())
}
#[cfg(feature = "serde")]
#[inline(always)]
fn try_from_uint(x: u64) -> Option<Self> {
Some(Self(x.to_ne_bytes()))
}
#[inline(always)]
fn to_uint(self) -> u64 {
u64::from_ne_bytes(self.0)
}
}
impl_index!(U64, u64, 8);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_alignment() {
assert_eq!(core::mem::align_of::<u8>(), 1);
assert_eq!(core::mem::align_of::<U16>(), 1);
assert_eq!(core::mem::align_of::<U24>(), 1);
assert_eq!(core::mem::align_of::<U32>(), 1);
assert_eq!(core::mem::align_of::<U40>(), 1);
assert_eq!(core::mem::align_of::<U48>(), 1);
assert_eq!(core::mem::align_of::<U56>(), 1);
assert_eq!(core::mem::align_of::<U64>(), 1);
}
#[cfg(feature = "postcard-schema")]
#[test]
fn test_schemas() {
assert_eq!(
U16::SCHEMA,
&NamedType {
name: "U16",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U24::SCHEMA,
&NamedType {
name: "U24",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U32::SCHEMA,
&NamedType {
name: "U32",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U40::SCHEMA,
&NamedType {
name: "U40",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U48::SCHEMA,
&NamedType {
name: "U48",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U56::SCHEMA,
&NamedType {
name: "U56",
ty: &DataModelType::Usize,
}
);
assert_eq!(
U64::SCHEMA,
&NamedType {
name: "U64",
ty: &DataModelType::Usize,
}
);
}
#[cfg(feature = "postcard-schema")]
#[derive(Schema)]
#[expect(dead_code)]
struct Foo(u32);
#[cfg(feature = "postcard-schema")]
#[test]
fn test_baseline_schemas() {
assert_eq!(
u32::SCHEMA,
&NamedType {
name: "u32",
ty: &DataModelType::U32,
}
);
assert_eq!(
Foo::SCHEMA,
&NamedType {
name: "Foo",
ty: &DataModelType::NewtypeStruct(&NamedType {
name: "u32",
ty: &DataModelType::U32,
}),
}
);
}
}