#![no_std]
use core::{mem, num::*, slice};
use zerocopy::AsBytes;
use __private::is_default;
#[doc(hidden)]
pub unsafe trait NonZero {
#[allow(private_bounds)]
type Primitive: ConstDefaultRef + AsBytes;
}
macro_rules! impl_nonzero {
($($primitive_ty:ty: $nonzero_ty:ty);* $(;)?) => {
$(
unsafe impl NonZero for $nonzero_ty {
type Primitive = $primitive_ty;
}
impl ConstDefaultRef for $primitive_ty {
const DEFAULT_REF: &'static Self = &0;
}
)*
};
}
impl_nonzero! {
i8: NonZeroI8;
i16: NonZeroI16;
i32: NonZeroI32;
i64: NonZeroI64;
i128: NonZeroI128;
isize: NonZeroIsize;
u8: NonZeroU8;
u16: NonZeroU16;
u32: NonZeroU32;
u64: NonZeroU64;
u128: NonZeroU128;
usize: NonZeroUsize;
}
#[macro_export]
macro_rules! nonzero {
($expr:expr) => {{
const COMPILE_TIME_CHECK_MUST_BE_CONST_EVALUABLE: () = {
if $crate::__private::is_default(&$expr) {
$crate::__private::core::panic!("`src` may not be zero")
}
};
$crate::nonzero(&$expr)
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! non0 {
($expr:expr) => {
$crate::nonzero!($expr)
};
}
#[doc(hidden)]
#[track_caller]
pub const fn nonzero<T: NonZero>(src: &T::Primitive) -> T {
match is_default(src) {
true => panic!("`src` may not be zero"),
false => unsafe { mem::transmute_copy(src) },
}
}
trait ConstDefaultRef: 'static {
const DEFAULT_REF: &'static Self;
}
const fn as_bytes<T: AsBytes>(src: &T) -> &[u8] {
unsafe { slice::from_raw_parts(src as *const T as *const u8, mem::size_of::<T>()) }
}
const fn slice_eq(left: &[u8], right: &[u8]) -> bool {
if left.len() != right.len() {
return false;
}
let mut ix = right.len();
while let Some(nix) = ix.checked_sub(1) {
if left[nix] != right[nix] {
return false;
}
ix = nix
}
true
}
#[doc(hidden)]
pub mod __private {
use super::*;
pub extern crate core;
#[allow(private_bounds)]
pub const fn is_default<T: AsBytes + ConstDefaultRef>(src: &T) -> bool {
let src = as_bytes(src);
let zero = as_bytes(T::DEFAULT_REF);
slice_eq(src, zero)
}
}