#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct LE<const N: usize>(pub [u8; N]);
impl<const N: usize> Ord for LE<N> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.iter().rev().cmp(other.0.iter().rev())
}
}
impl<const N: usize> PartialOrd for LE<N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> From<[u8; N]> for LE<N> {
#[inline]
fn from(bytes: [u8; N]) -> Self {
Self(bytes)
}
}
impl<const N: usize> From<LE<N>> for [u8; N] {
#[inline]
fn from(bytes: LE<N>) -> Self {
bytes.0
}
}
impl<const N: usize> AsRef<[u8; N]> for LE<N> {
#[inline]
fn as_ref(&self) -> &[u8; N] {
&self.0
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct B2<const N: usize>(pub [u8; N]);
impl<const N: usize> Ord for B2<N> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
if N == 0 {
return core::cmp::Ordering::Equal;
}
(self.0[0] ^ 0x80)
.cmp(&(other.0[0] ^ 0x80))
.then_with(|| self.0[1..].cmp(&other.0[1..]))
}
}
impl<const N: usize> PartialOrd for B2<N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> From<[u8; N]> for B2<N> {
#[inline]
fn from(bytes: [u8; N]) -> Self {
Self(bytes)
}
}
impl<const N: usize> From<B2<N>> for [u8; N] {
#[inline]
fn from(bytes: B2<N>) -> Self {
bytes.0
}
}
impl<const N: usize> AsRef<[u8; N]> for B2<N> {
#[inline]
fn as_ref(&self) -> &[u8; N] {
&self.0
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct L2<const N: usize>(pub [u8; N]);
impl<const N: usize> Ord for L2<N> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let mut lhs = self.0.iter().rev();
let mut rhs = other.0.iter().rev();
let Some(&lhs_sign) = lhs.next() else {
return core::cmp::Ordering::Equal;
};
let rhs_sign = *rhs.next().expect("matching const length");
(lhs_sign ^ 0x80)
.cmp(&(rhs_sign ^ 0x80))
.then_with(|| lhs.cmp(rhs))
}
}
impl<const N: usize> PartialOrd for L2<N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> From<[u8; N]> for L2<N> {
#[inline]
fn from(bytes: [u8; N]) -> Self {
Self(bytes)
}
}
impl<const N: usize> From<L2<N>> for [u8; N] {
#[inline]
fn from(bytes: L2<N>) -> Self {
bytes.0
}
}
impl<const N: usize> AsRef<[u8; N]> for L2<N> {
#[inline]
fn as_ref(&self) -> &[u8; N] {
&self.0
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LX<const N: usize>(pub [u8; N]);
impl<const N: usize> From<[u8; N]> for LX<N> {
#[inline]
fn from(bytes: [u8; N]) -> Self {
Self(bytes)
}
}
impl<const N: usize> From<LX<N>> for [u8; N] {
#[inline]
fn from(bytes: LX<N>) -> Self {
bytes.0
}
}
impl<const N: usize> AsRef<[u8; N]> for LX<N> {
#[inline]
fn as_ref(&self) -> &[u8; N] {
&self.0
}
}
#[cfg(test)]
mod le_tests {
use super::{B2, L2, LE};
#[test]
fn le_order_compares_most_significant_byte_first() {
assert!(LE([1]) < LE([2]));
assert!(LE([1, 0]) < LE([2, 0]));
assert!(LE([255, 0]) < LE([0, 1]));
assert!(LE([255, 255, 0, 0]) < LE([0, 0, 1, 0]));
}
#[test]
fn le_equality_matches_inner_bytes() {
assert_eq!(LE([1, 2, 3, 4]), LE([1, 2, 3, 4]));
assert_ne!(LE([1, 2, 3, 4]), LE([1, 2, 3, 5]));
}
#[test]
fn b2_order_compares_as_signed_big_endian() {
assert!(B2([0x80]) < B2([0x00]));
assert!(B2([0xFF]) < B2([0x00]));
assert!(B2([0x80, 0x00]) < B2([0xFF, 0xFF]));
assert!(B2([0xFF, 0xFF]) < B2([0x00, 0x00]));
assert!(B2([0x00, 0x00]) < B2([0x7F, 0xFF]));
}
#[test]
fn b2_equality_matches_inner_bytes() {
assert_eq!(B2([1, 2, 3, 4]), B2([1, 2, 3, 4]));
assert_ne!(B2([1, 2, 3, 4]), B2([1, 2, 3, 5]));
}
#[test]
fn l2_order_compares_as_signed_little_endian() {
assert!(L2([0x80]) < L2([0x00]));
assert!(L2([0xFF]) < L2([0x00]));
assert!(L2([0x00, 0x80]) < L2([0xFF, 0xFF]));
assert!(L2([0xFF, 0xFF]) < L2([0x00, 0x00]));
assert!(L2([0x01, 0x00]) < L2([0x00, 0x01]));
assert!(L2([0x00, 0x00]) < L2([0xFF, 0x7F]));
}
#[test]
fn l2_equality_matches_inner_bytes() {
assert_eq!(L2([1, 2, 3, 4]), L2([1, 2, 3, 4]));
assert_ne!(L2([1, 2, 3, 4]), L2([1, 2, 3, 5]));
}
}
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! uint_uint {
($NAME:ident, $A:ty, $B:ty) => {
#[doc = concat!("`", stringify!($A), " → ", stringify!($B), "` saturating cast.")]
pub const $NAME: $crate::conn::Conn<$A, $B> = {
fn ceil(x: $A) -> $B {
x as $B
}
fn inner(x: $B) -> $A {
let cap = <$A>::MAX as $B;
let clipped = if x > cap { cap } else { x };
clipped as $A
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use uint_uint;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! int_uint {
($NAME:ident, $A:ty, $B:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($A),
" → ",
stringify!($B),
"` saturating cast: negatives clip to 0; `inner` saturates at source max."
)]
pub const $NAME: $crate::conn::Conn<$A, $B> = {
fn ceil(x: $A) -> $B {
if x < 0 { 0 } else { x as $B }
}
fn inner(x: $B) -> $A {
let cap = <$A>::MAX as $B;
let clipped = if x > cap { cap } else { x };
clipped as $A
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use int_uint;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! ext_int {
($NAME:ident, $A:ty, $B:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`Extended<",
stringify!($A),
"> → ",
stringify!($B),
"` left-Galois widening."
)]
pub const $NAME: $crate::conn::Conn<$crate::extended::Extended<$A>, $B> = {
const BELOW: $B = (<$A>::MIN as $B) - 1;
const ABOVE: $B = (<$A>::MAX as $B) + 1;
fn ceil(x: $crate::extended::Extended<$A>) -> $B {
match x {
$crate::extended::Extended::NegInf => <$B>::MIN,
$crate::extended::Extended::Finite(a) => a as $B,
$crate::extended::Extended::PosInf => ABOVE,
}
}
fn inner(x: $B) -> $crate::extended::Extended<$A> {
if x <= BELOW {
$crate::extended::Extended::NegInf
} else if x >= ABOVE {
$crate::extended::Extended::PosInf
} else {
$crate::extended::Extended::Finite(x as $A)
}
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use ext_int;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! int_int_narrow {
($NAME:ident, $A:ty, $B:ty) => {
#[doc = concat!("`", stringify!($A), " → ", stringify!($B), "` saturating narrow.")]
pub const $NAME: $crate::conn::Conn<$A, $B> = {
fn ceil(x: $A) -> $B {
if x > <$B>::MAX as $A {
<$B>::MAX
} else if x < <$B>::MIN as $A {
<$B>::MIN
} else {
x as $B
}
}
fn inner(x: $B) -> $A {
if x == <$B>::MAX { <$A>::MAX } else { x as $A }
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use int_int_narrow;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! uint_uint_narrow {
($NAME:ident, $A:ty, $B:ty) => {
#[doc = concat!("`", stringify!($A), " → ", stringify!($B), "` saturating narrow.")]
pub const $NAME: $crate::conn::Conn<$A, $B> = {
fn ceil(x: $A) -> $B {
if x > <$B>::MAX as $A {
<$B>::MAX
} else {
x as $B
}
}
fn inner(x: $B) -> $A {
if x == <$B>::MAX { <$A>::MAX } else { x as $A }
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use uint_uint_narrow;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! uint_int_sat {
($(#[$attr:meta])* $NAME:ident, $A:ty, $B:ty) => {
#[doc = concat!("`", stringify!($A), " → ", stringify!($B), "` saturating cast (right-Galois).")]
$(#[$attr])*
pub const $NAME: $crate::conn::Conn<$A, $B, $crate::conn::R> = {
fn floor(x: $A) -> $B {
if x > <$B>::MAX as $A {
<$B>::MAX
} else {
x as $B
}
}
fn inner(x: $B) -> $A {
if x < 0 { 0 } else { x as $A }
}
$crate::conn::Conn::new_r(inner, floor)
};
};
}
pub(crate) use uint_int_sat;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! nz_int_ext {
($NAME:ident, $A:ty, $NZ:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($A),
" ↔ ",
stringify!($NZ),
"` (signed; floor/ceil split 0 between -1 and +1)."
)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct $NAME;
impl $NAME {
const fn _ceil(v: $A) -> $NZ {
let r: $A = if v == 0 { 1 } else { v };
match <$NZ>::new(r) {
Some(nz) => nz,
None => panic!("nz_int_ext::ceil produced zero"),
}
}
const fn _floor(v: $A) -> $NZ {
let r: $A = if v == 0 { -1 } else { v };
match <$NZ>::new(r) {
Some(nz) => nz,
None => panic!("nz_int_ext::floor produced zero"),
}
}
const fn _inner(nz: $NZ) -> $A {
nz.get()
}
}
impl $NAME {
#[allow(dead_code)]
#[inline]
#[must_use]
pub const fn view_l(self) -> $crate::conn::Conn<$A, $NZ, $crate::conn::L> {
$crate::conn::Conn::new_l($NAME::_ceil, $NAME::_inner)
}
#[allow(dead_code)]
#[inline]
#[must_use]
pub const fn view_r(self) -> $crate::conn::Conn<$A, $NZ, $crate::conn::R> {
$crate::conn::Conn::new_r($NAME::_inner, $NAME::_floor)
}
#[allow(dead_code)]
#[inline]
#[must_use]
pub const fn swap_l(self) -> $crate::conn::Conn<$NZ, $A, $crate::conn::R> {
self.view_l().swap_l()
}
#[allow(dead_code)]
#[inline]
#[must_use]
pub const fn swap_r(self) -> $crate::conn::Conn<$NZ, $A, $crate::conn::L> {
self.view_r().swap_r()
}
}
impl $crate::conn::ConnL for $NAME {
type A = $A;
type B = $NZ;
#[inline]
fn swap_l(&self) -> $crate::conn::Conn<$NZ, $A, $crate::conn::R> {
$NAME::swap_l(*self)
}
}
impl $crate::conn::ConnR for $NAME {
type A = $A;
type B = $NZ;
#[inline]
fn swap_r(&self) -> $crate::conn::Conn<$NZ, $A, $crate::conn::L> {
$NAME::swap_r(*self)
}
}
};
}
pub(crate) use nz_int_ext;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! nz_uint_ext {
($NAME:ident, $A:ty, $NZ:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($A),
" ↔ ",
stringify!($NZ),
"` (unsigned; 0 saturates to NonZero(1); single-sided left-Galois)."
)]
pub const $NAME: $crate::conn::Conn<$A, $NZ> = {
fn ceil(v: $A) -> $NZ {
let r: $A = if v == 0 { 1 } else { v };
match <$NZ>::new(r) {
Some(nz) => nz,
None => panic!("nz_uint_ext::ceil produced zero"),
}
}
fn inner(nz: $NZ) -> $A {
nz.get()
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use nz_uint_ext;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! nz_uint_widen {
($NAME:ident, $NZN:ty, $UN:ty, $NZM:ty, $UM:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($NZN),
" → ",
stringify!($NZM),
"` lossless widening; `inner` saturates at `",
stringify!($NZN),
"::MAX`."
)]
pub const $NAME: $crate::conn::Conn<$NZN, $NZM> = {
fn ceil(n: $NZN) -> $NZM {
match <$NZM>::new(n.get() as $UM) {
Some(nz) => nz,
None => panic!("nz_uint_widen::ceil produced zero"),
}
}
fn inner(m: $NZM) -> $NZN {
let cap = <$UN>::MAX as $UM;
let val = m.get();
let clipped = if val > cap { cap } else { val };
match <$NZN>::new(clipped as $UN) {
Some(nz) => nz,
None => panic!("nz_uint_widen::inner produced zero"),
}
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use nz_uint_widen;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! int_uint_narrow {
($NAME:ident, $A:ty, $B:ty) => {
#[doc = concat!("`", stringify!($A), " → ", stringify!($B), "` saturating narrow.")]
pub const $NAME: $crate::conn::Conn<$A, $B> = {
fn ceil(x: $A) -> $B {
if x < 0 {
0
} else if x > <$B>::MAX as $A {
<$B>::MAX
} else {
x as $B
}
}
fn inner(x: $B) -> $A {
if x == <$B>::MAX { <$A>::MAX } else { x as $A }
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use int_uint_narrow;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! nz_int_narrow {
($NAME:ident, $NZN:ty, $IN:ty, $NZM:ty, $IM:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($NZN),
" → ",
stringify!($NZM),
"` saturating narrow; `inner` widens with FINE_MAX fixup at `",
stringify!($NZM),
"::MAX`."
)]
pub const $NAME: $crate::conn::Conn<$NZN, $NZM> = {
fn ceil(n: $NZN) -> $NZM {
let val = n.get();
let clipped = if val > <$IM>::MAX as $IN {
<$IM>::MAX
} else if val < <$IM>::MIN as $IN {
<$IM>::MIN
} else {
val as $IM
};
match <$NZM>::new(clipped) {
Some(nz) => nz,
None => panic!("nz_int_narrow::ceil produced zero"),
}
}
fn inner(m: $NZM) -> $NZN {
let v: $IN = if m.get() == <$IM>::MAX {
<$IN>::MAX
} else {
m.get() as $IN
};
match <$NZN>::new(v) {
Some(nz) => nz,
None => panic!("nz_int_narrow::inner produced zero"),
}
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use nz_int_narrow;
#[cfg_attr(feature = "macros", macro_export)]
macro_rules! nz_uint_narrow {
($NAME:ident, $NZN:ty, $UN:ty, $NZM:ty, $UM:ty) => {
#[rustfmt::skip]
#[doc = concat!(
"`",
stringify!($NZN),
" → ",
stringify!($NZM),
"` saturating narrow; `inner` widens with FINE_MAX fixup at `",
stringify!($NZM),
"::MAX`."
)]
pub const $NAME: $crate::conn::Conn<$NZN, $NZM> = {
fn ceil(n: $NZN) -> $NZM {
let val = n.get();
let clipped = if val > <$UM>::MAX as $UN {
<$UM>::MAX
} else {
val as $UM
};
match <$NZM>::new(clipped) {
Some(nz) => nz,
None => panic!("nz_uint_narrow::ceil produced zero"),
}
}
fn inner(m: $NZM) -> $NZN {
let v: $UN = if m.get() == <$UM>::MAX {
<$UN>::MAX
} else {
m.get() as $UN
};
match <$NZN>::new(v) {
Some(nz) => nz,
None => panic!("nz_uint_narrow::inner produced zero"),
}
}
$crate::conn::Conn::new_l(ceil, inner)
};
};
}
pub(crate) use nz_uint_narrow;
pub mod i008;
pub mod i016;
pub mod i032;
pub mod i064;
pub mod i128;
pub mod u008;
pub mod u016;
pub mod u032;
pub mod u064;
pub mod u128;
#[cfg(feature = "f16")]
pub mod f016;
pub mod f032;
pub mod f064;
pub mod bool;
pub mod char;
pub mod isize;
pub mod usize;