use crate::traits::NonNullable;
use core::mem::{ManuallyDrop, MaybeUninit};
#[derive(Debug)]
pub struct MaybeNull<T: NonNullable> {
inner: MaybeUninit<T>,
}
impl<T: NonNullable> MaybeNull<T> {
#[inline(always)]
#[must_use]
pub const fn null() -> Self {
Self {
inner: MaybeUninit::zeroed(),
}
}
#[inline(always)]
#[must_use]
pub const fn new(val: T) -> Self {
Self {
inner: MaybeUninit::new(val),
}
}
#[inline(always)]
pub const fn is_null(&self) -> bool {
let ptr = self.inner.as_ptr().cast::<u8>();
let mut i = 0;
while i < size_of::<T>() {
if unsafe { ptr.add(i).read() } != 0 {
return false;
}
i += 1;
}
true
}
#[inline(always)]
pub const fn is_init(&self) -> bool {
let ptr = self.inner.as_ptr().cast::<u8>();
let mut i = 0;
while i < size_of::<T>() {
if unsafe { ptr.add(i).read() } == 0 {
return false;
}
i += 1;
}
true
}
#[inline(always)]
pub fn into_inner(self) -> Option<T> {
if self.is_init() {
unsafe { Some(self.into_inner_unchecked()) }
} else {
None
}
}
#[inline(always)]
pub unsafe fn into_inner_unchecked(self) -> T {
unsafe { ManuallyDrop::new(self).inner.assume_init_read() }
}
#[inline(always)]
pub const fn get(&self) -> Option<&T> {
if self.is_init() {
unsafe { Some(self.get_unchecked()) }
} else {
None
}
}
#[inline(always)]
pub const fn get_mut(&mut self) -> Option<&mut T> {
if self.is_init() {
unsafe { Some(self.get_mut_unchecked()) }
} else {
None
}
}
#[inline(always)]
pub const unsafe fn get_unchecked(&self) -> &T {
unsafe { self.inner.assume_init_ref() }
}
#[inline(always)]
pub const unsafe fn get_mut_unchecked(&mut self) -> &mut T {
unsafe { self.inner.assume_init_mut() }
}
#[inline(always)]
pub const fn as_ptr(&self) -> *const T {
self.inner.as_ptr()
}
#[inline(always)]
pub const fn as_mut_ptr(&mut self) -> *mut T {
self.inner.as_mut_ptr()
}
#[inline(always)]
pub fn set(&mut self, val: T) -> bool {
if self.is_null() {
unsafe {
self.inner.as_mut_ptr().write(val);
}
true
} else {
false
}
}
#[inline(always)]
pub fn force_set(&mut self, val: T) {
if self.is_init() {
unsafe {
self.drop_unchecked();
}
}
unsafe {
self.inner.as_mut_ptr().write(val);
}
}
#[inline(always)]
pub fn nullify(&mut self) {
if self.is_init() {
unsafe {
self.drop_unchecked();
}
unsafe {
core::ptr::write_bytes(self.inner.as_mut_ptr().cast::<u8>(), 0, size_of::<T>());
}
}
}
#[inline(always)]
pub const unsafe fn nullify_unchecked(&mut self) {
unsafe {
core::ptr::write_bytes(self.inner.as_mut_ptr().cast::<u8>(), 0, size_of::<T>());
}
}
#[inline(always)]
pub unsafe fn drop_unchecked(&mut self) {
unsafe {
self.inner.assume_init_drop();
}
}
pub fn match_null_ref<R>(
&self,
if_init: impl FnOnce(&T) -> R,
if_null: impl FnOnce() -> R,
) -> R {
if self.is_init() {
unsafe { if_init(self.get_unchecked()) }
} else {
if_null()
}
}
pub fn match_null_mut<R>(
&mut self,
if_init: impl FnOnce(&mut T) -> R,
if_null: impl FnOnce() -> R,
) -> R {
if self.is_init() {
unsafe { if_init(self.get_mut_unchecked()) }
} else {
if_null()
}
}
pub fn match_null<R>(self, if_init: impl FnOnce(T) -> R, if_null: impl FnOnce() -> R) -> R {
if self.is_init() {
unsafe { if_init(self.into_inner_unchecked()) }
} else {
if_null()
}
}
}
impl<T: NonNullable> Drop for MaybeNull<T> {
#[inline(always)]
fn drop(&mut self) {
if self.is_init() {
unsafe {
self.drop_unchecked();
}
}
}
}
#[macro_export]
macro_rules! match_null {
(
match $maybe_null:ident {
Init($init_val:ident) => $if_init:block
Null => $if_null:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.into_inner_unchecked() };
$if_init
} else $if_null
};
(
match $maybe_null:ident {
Null => $if_null:block
Init($init_val:ident) => $if_init:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.into_inner_unchecked() };
$if_init
} else $if_null
};
(
match &$maybe_null:ident {
Init($init_val:ident) => $if_init:block
Null => $if_null:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.get_unchecked() };
$if_init
} else $if_null
};
(
match &$maybe_null:ident {
Null => $if_null:block
Init($init_val:ident) => $if_init:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.get_unchecked() };
$if_init
} else $if_null
};
(
match &mut $maybe_null:ident {
Init($init_val:ident) => $if_init:block
Null => $if_null:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.get_mut_unchecked() };
$if_init
} else $if_null
};
(
match &mut $maybe_null:ident {
Null => $if_null:block
Init($init_val:ident) => $if_init:block
}
) => {
if $maybe_null.is_init() {
let $init_val = unsafe { $maybe_null.get_mut_unchecked() };
$if_init
} else $if_null
};
}