diff --git a/Cargo.toml b/Cargo.toml index 7e7622f..6c289cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,25 @@ [package] name = "SafeManuallyDrop" -version = "0.1.2" -authors = ["Денис Котляров "] -edition = "2018" +version = "0.1.5" +authors = ["Denis Kotlyarov "] +edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [ + "always_check_in_case_debug_assertions", + "enable_deprecated_hook", + + "support_panic_trig", + "support_hook_trig" #, + #"support_count_trig" +] +always_check_in_case_debug_assertions = [] +always_safe_manuallydrop = [] + +support_hook_trig = [] +support_count_trig = [] +support_panic_trig = [] + +enable_deprecated_hook = [] [dependencies] diff --git a/examples/combo_drop.rs b/examples/combo_drop.rs index 1b27240..ff6f447 100644 --- a/examples/combo_drop.rs +++ b/examples/combo_drop.rs @@ -3,16 +3,18 @@ use SafeManuallyDrop::ManuallyDrop; use std::ops::Deref; fn main() { + assert_eq!(SafeManuallyDrop::core::flags::IS_SAFE_MODE, true); + let data = vec![1, 2, 3, 4]; let mut control_drop = ManuallyDrop::new(data); let _e = control_drop.deref(); { unsafe { - assert_eq!(control_drop.is_maybe_next_panic(), false); + assert_eq!(control_drop.is_next_trig(), false); ManuallyDrop::drop(&mut control_drop); - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC ManuallyDrop::drop(&mut control_drop); } diff --git a/src/beh/safe.rs b/src/beh/safe.rs new file mode 100644 index 0000000..a59ab30 --- /dev/null +++ b/src/beh/safe.rs @@ -0,0 +1,99 @@ + +use core::hash::Hash; +use crate::UnsafeStdManuallyDrop; +use crate::core::trig::TrigManuallyDrop; +use core::marker::PhantomData; + +/// A safe version of the insecure manual control of freeing memory. +// #[repr(transparent)] +//#[derive(/*Copy,*/ Clone, Debug)] +#[derive(/*Copy,*/ Clone, Debug/*, Default, PartialEq, Eq, PartialOrd, Ord, Hash*/)] +pub struct SafeManuallyDrop where T: ?Sized, Trig: TrigManuallyDrop { + pub (crate) state: StateManuallyDrop, + pub (crate) _pp: PhantomData, + pub (crate) value: UnsafeStdManuallyDrop, +} + +crate::__codegen! { + @use; + #SafeManuallyDrop [is_safe: true]; +} + +//impl Copy for ManuallyDrop where T: ?Sized + Copy {} TODO + +impl Default for SafeManuallyDrop where T: ?Sized + Default, Trig: TrigManuallyDrop { + #[inline(always)] + fn default() -> Self { + Self::new( + Default::default() + ) + } +} + +impl PartialEq for SafeManuallyDrop where T: ?Sized + PartialEq, Trig: TrigManuallyDrop { + #[inline] + fn eq(&self, a: &Rhs) -> bool { + let value: &T = self.value.deref(); + PartialEq::::eq(value, a) + } + + #[inline] + fn ne(&self, a: &Rhs) -> bool { + let value: &T = self.value.deref(); + PartialEq::::ne(value, a) + } +} + +impl Eq for SafeManuallyDrop where T: Eq + PartialEq>, Trig: TrigManuallyDrop { + #[inline] + fn assert_receiver_is_total_eq(&self) { + let value: &T = self.value.deref(); + Eq::assert_receiver_is_total_eq(value) + } +} + +impl Ord for SafeManuallyDrop where T: Ord + PartialOrd>, Trig: TrigManuallyDrop { + #[inline] + fn cmp(&self, a: &Self) -> core::cmp::Ordering { + let value: &T = self.value.deref(); + Ord::cmp(value, a) + } +} + +impl PartialOrd for SafeManuallyDrop where T: ?Sized + PartialOrd, Trig: TrigManuallyDrop { + #[inline] + fn partial_cmp(&self, a: &Rhs) -> Option { + let value: &T = self.value.deref(); + PartialOrd::partial_cmp(value, a) + } +} + +impl Hash for SafeManuallyDrop where T: ?Sized + Hash, Trig: TrigManuallyDrop { + #[inline] + fn hash(&self, a: &mut H) where H: core::hash::Hasher { + let value: &T = self.value.deref(); + Hash::hash(value, a) + } +} + +impl Drop for SafeManuallyDrop where T: ?Sized, Trig: TrigManuallyDrop { + #[inline] + fn drop(&mut self) { + let ref mut value = self.value; + let ref state = self.state; + + /*enum __HideTrig {} + impl TrigManuallyDrop for __HideTrig { + fn trig_next_invalid_beh<'a>(a: core::fmt::Arguments<'a>) -> ! { + Trig::trig_next_invalid_beh(a) + } + }*/ + + state.if_empty_then_run_trigfn::( + || unsafe { + // What for? - >> to ignore miri errors allocate. + UnsafeStdManuallyDrop::drop(value); + } + ); + } +} diff --git a/src/beh/unsafe.rs b/src/beh/unsafe.rs new file mode 100644 index 0000000..23a8a75 --- /dev/null +++ b/src/beh/unsafe.rs @@ -0,0 +1,17 @@ + +use core::marker::PhantomData; +use crate::UnsafeStdManuallyDrop; +use crate::core::trig::TrigManuallyDrop; + +/// Insecure standard implementation of manual memory management. +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct UnsafeManuallyDrop where T: ?Sized, Trig: TrigManuallyDrop { + _pp: PhantomData, + value: UnsafeStdManuallyDrop, +} + +crate::__codegen! { + @use; + #UnsafeManuallyDrop [is_safe: false]; +} diff --git a/src/core/flags.rs b/src/core/flags.rs new file mode 100644 index 0000000..4f15bd0 --- /dev/null +++ b/src/core/flags.rs @@ -0,0 +1,143 @@ + +#[macro_export] +#[doc(hidden)] +macro_rules! cfg_if_safemode { + [ #if_safe() { $($all:tt)* } $( else $($else_all:tt)* )? /*$($macros_data:tt)**/ ] => { + { + #[cfg( + any( + feature = "always_safe_manuallydrop", + all(feature = "always_check_in_case_debug_assertions", debug_assertions), + ) + )] { + $($all)* + } + + $( + #[cfg(not( + any( + feature = "always_safe_manuallydrop", + all(feature = "always_check_in_case_debug_assertions", debug_assertions), + ) + ))] { + $($else_all)* + } + )? + } + + /*$crate::cfg_if_safemode! { + $($macros_data)* + }*/ + }; + + [ + $(#[$($meta:tt)*])* + #if_safe ( $($all:tt)* ) $($macros_data:tt)* + ] => { + #[cfg( + any( + feature = "always_safe_manuallydrop", + all(feature = "always_check_in_case_debug_assertions", debug_assertions), + ) + )] + $(#[$($meta)*])* + $($all)* + + $crate::cfg_if_safemode! { + $($macros_data)* + } + }; + + [ + $(#[$($meta:tt)*])* + #if_not_safe ( $($all:tt)* ) $($macros_data:tt)* + ] => { + #[cfg( + not( + any( + feature = "always_safe_manuallydrop", + all(feature = "always_check_in_case_debug_assertions", debug_assertions), + ) + ) + )] + $(#[$($meta)*])* + $($all)* + + $crate::cfg_if_safemode! { + $($macros_data)* + } + }; + + [] => {}; + [ #if_safe { $($all:tt)* } ] => { + { + #[cfg( + any( + feature = "always_safe_manuallydrop", + all(feature = "always_check_in_case_debug_assertions", debug_assertions) + ) + )] { + $($all)* + } + } + }; +} + +crate::cfg_if_safemode! { + #if_not_safe (pub const IS_SAFE_MODE: bool = false;) + #if_safe (pub const IS_SAFE_MODE: bool = true;) +} + +#[cfg(feature = "support_panic_trig")] +pub const SUPPORT_PANIC_TRIG: bool = true; + +#[cfg(not(feature = "support_panic_trig"))] +pub const SUPPORT_PANIC_TRIG: bool = false; + +#[cfg(feature = "support_hook_trig")] +pub const SUPPORT_HOOK_TRIG: bool = true; + +#[cfg(not(feature = "support_hook_trig"))] +pub const SUPPORT_HOOK_TRIG: bool = false; + +#[cfg(feature = "support_count_trig")] +pub const SUPPORT_COUNT_TRIG: bool = true; + +#[cfg(not(feature = "support_count_trig"))] +pub const SUPPORT_COUNT_TRIG: bool = false; + +pub const SUPPORT_EMPTY_TRIG: bool = true; + +#[cfg(test)] +#[test] +fn test_flag_is_safe_mode() { + #[allow(unused_assignments)] + let mut is_checked_c = 0; + + #[cfg(feature = "always_safe_manuallydrop")] { + assert_eq!(IS_SAFE_MODE, true); + + //#[allow(unused_assignments)] // error[E0658]: attributes on expressions are experimental + is_checked_c = 1; + } + if is_checked_c != 1 {} // fix error[E0658]: attributes on expressions are experimental + + #[cfg( all(feature = "always_check_in_case_debug_assertions", debug_assertions) )] { + assert_eq!(IS_SAFE_MODE, true); + + is_checked_c = 1; + } + + #[cfg(not( + any( + all(feature = "always_check_in_case_debug_assertions", debug_assertions), + feature = "always_safe_manuallydrop" + ) + ))] { + assert_eq!(IS_SAFE_MODE, false); + + is_checked_c = is_checked_c + 1; + } + + assert_eq!(is_checked_c, 1); +} \ No newline at end of file diff --git a/src/panic.rs b/src/core/hook.rs similarity index 87% rename from src/panic.rs rename to src/core/hook.rs index f542537..44c71d3 100644 --- a/src/panic.rs +++ b/src/core/hook.rs @@ -8,18 +8,18 @@ static mut HOOK: HookFunction = |args| { }; // UNSAFE_MODE TODO!! -#[inline] +#[inline(always)] pub unsafe fn set_hook(function: HookFunction) { HOOK = function; } -#[inline] +#[inline(always)] pub fn take_hook() -> HookFunction { // TODO UNSAFE MODE unsafe { HOOK } } -#[inline] +#[inline(always)] pub fn run_hook(args: Arguments) -> ! { (take_hook())(args) } diff --git a/src/core/state.rs b/src/core/state.rs new file mode 100644 index 0000000..e6d570a --- /dev/null +++ b/src/core/state.rs @@ -0,0 +1,411 @@ + +#[cfg(feature = "support_panic_trig")] +use crate::core::trig::panic::PanicTrigManuallyDrop; +use crate::TrigManuallyDrop; +use core::fmt::Debug; +use core::sync::atomic::Ordering; +use core::sync::atomic::AtomicU8; + +const READ_ORDERING_METHOD: Ordering = Ordering::SeqCst; +const WRITE_ORDERING_METHOD: Ordering = Ordering::SeqCst; // + +#[repr(transparent)] +pub struct StateManuallyDrop { + state: AtomicU8, +} + +impl Clone for StateManuallyDrop { + #[inline] + fn clone(&self) -> Self { + Self { + state: AtomicU8::new(self.__read_byte()) + } + } +} + +impl Debug for StateManuallyDrop { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + f.debug_struct("StateManuallyDrop") + .field("state", &self.read()) + .finish() + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum StateManuallyDropData { + Empty = 1, + + // + TakeModeTrig = 5, + DropModeTrig = 15, + IntoInnerModeTrig = 25, + + IgnoreTrigWhenDrop = 30, +} + +impl From for StateManuallyDropData { + #[inline(always)] + fn from(a: u8) -> Self { + StateManuallyDropData::from_or_empty(a) + } +} + +impl Default for StateManuallyDropData { + #[inline(always)] + fn default() -> Self { + Self::empty() + } +} + +impl StateManuallyDropData { + #[inline(always)] + pub const fn into(self) -> u8 { + self as _ + } + + #[deprecated(since = "0.1.2", note = "Use `from_or_empty` instead")] + #[inline] + pub /*const*/ fn from_or_default(a: u8) -> Self { + Self::from_or_empty(a) + } + + #[inline] + pub /*const*/ fn from_or_empty(a: u8) -> Self { + Self::is_valid_byte_fn( + a, + || unsafe { + Self::force_from(a) + }, + || Self::empty() + ) + } + + #[inline] + pub /*const*/ fn from(a: u8) -> Option { + Self::is_valid_byte_fn( + a, + || { + let sself = unsafe { + Self::force_from(a) + }; + + Some(sself) + }, + || None + ) + } + + #[inline(always)] + const fn __empty() -> Self { + Self::Empty + } + + #[deprecated(since = "0.1.2", note = "Use `empty` instead")] + #[inline(always)] + pub const fn default() -> Self { + Self::empty() + } + + #[inline(always)] + pub const fn empty() -> Self { + Self::__empty() + } + + #[inline(always)] + pub const fn no_panic_state() -> Self { + Self::empty() + } + + #[inline(always)] + pub /*const*/ fn is_valid_byte_fn R, FE: FnOnce() -> R, R>(a: u8, next: F, errf: FE) -> R { + match a { + a if a == Self::Empty as _ || + + a == Self::TakeModeTrig as _ || + a == Self::DropModeTrig as _ || + a == Self::IntoInnerModeTrig as _ || + + a == Self::IgnoreTrigWhenDrop as _ => next(), + _ => errf() + } + } + + #[inline] + pub fn is_valid_byte(a: u8) -> bool { + Self::is_valid_byte_fn( + a, + || true, + || false, + ) + } + + #[inline(always)] + pub unsafe fn force_from(a: u8) -> Self { + debug_assert_eq!( + Self::is_valid_byte(a), + true + ); + + #[allow(unused_unsafe)] + let result: Self = unsafe { + core::mem::transmute(a as u8) + }; + result + } + + #[deprecated(since = "0.1.2", note = "Use `is_next_panic` instead")] + #[inline(always)] + pub const fn is_next_panic(&self) -> bool { + self.is_next_trig() + } + + #[inline(always)] + pub const fn is_next_trig(&self) -> bool { + match self { + StateManuallyDropData::Empty => false, + _ => true, + } + } + + #[deprecated(since = "0.1.2", note = "Use `is_empty` instead")] + #[inline(always)] + pub const fn is_default(&self) -> bool { + self.is_empty() + } + + #[inline(always)] + pub const fn is_empty(&self) -> bool { + match self { + StateManuallyDropData::Empty => true, + _ => false, + } + } +} + +impl StateManuallyDrop { + #[inline(always)] + pub /*const*/ fn empty() -> Self { + let sself = Self { + state: AtomicU8::new(StateManuallyDropData::empty() as _) + }; + debug_assert_eq!(sself.is_empty(), true); + debug_assert_eq!(sself.is_next_trig(), false); + + sself + } + + #[deprecated(since = "0.1.2", note = "Use `is_trig_mode` instead")] + #[inline(always)] + pub /*const*/ fn default() -> Self { + Self::empty() + } + + #[deprecated(since = "0.1.2", note = "Use `is_ignore_trig_mode` instead")] + #[inline(always)] + pub fn is_def_mode(&self) -> bool { + self.is_empty() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.read().is_empty() + } + + #[inline(always)] + fn __read_byte(&self) -> u8 { + self.state.load(READ_ORDERING_METHOD) + } + + #[inline(always)] + pub fn read(&self) -> StateManuallyDropData { + let byte = self.__read_byte(); + unsafe { + StateManuallyDropData::force_from(byte) + } + } + + #[inline(always)] + fn __force_write(&self, a: StateManuallyDropData) -> StateManuallyDropData { + let byte = self.state.swap(a as _, WRITE_ORDERING_METHOD); + unsafe { + StateManuallyDropData::force_from(byte) + } + } + + #[inline] + fn __safe_replace_mutstate(&self, new_state: StateManuallyDropData) { + debug_assert_eq!(new_state.is_next_trig(), true); + + let old_state = self.__force_write(new_state); + + // COMBO REPLACE STATE -> ERR + if old_state.is_next_trig() { + Trig::trig_next_invalid_beh( + format_args!( + "SafeManuallyDrop, undef_beh (combo_replace_state), SafeManuallyDrop::empty() != {:?}", + old_state + ) + ); + } + } + + #[deprecated(since = "0.1.2", note = "Use `to_dropmode_or_trig` instead")] + #[cfg(feature = "support_panic_trig")] + #[inline(always)] + pub fn to_dropmode_or_panic(&self) { + self.to_dropmode_or_trig::() + } + + #[inline(always)] + pub fn to_dropmode_or_trig(&self) { + self.__safe_replace_mutstate::( + StateManuallyDropData::DropModeTrig + ); + + debug_assert_eq!(self.is_next_trig(), true); + } + + #[deprecated(since = "0.1.2", note = "Use `to_takemode_or_trig` instead")] + #[cfg(feature = "support_panic_trig")] + #[inline(always)] + pub fn to_takemode_or_panic(&self) { + self.to_takemode_or_trig::() + } + + #[inline(always)] + pub fn to_takemode_or_trig(&self) { + self.__safe_replace_mutstate::( + StateManuallyDropData::TakeModeTrig + ); + + debug_assert_eq!(self.is_next_trig(), true); + } + + #[deprecated(since = "0.1.2", note = "Use `to_ignore_trig_when_drop` instead")] + #[cfg(feature = "support_panic_trig")] + #[inline(always)] + pub fn to_ignore_panic_when_drop(&self) { + self.to_ignore_trig_when_drop::() + } + + #[inline(always)] + pub fn to_ignore_trig_when_drop(&self) { + self.__safe_replace_mutstate::( + StateManuallyDropData::IgnoreTrigWhenDrop + ); + + debug_assert_eq!(self.is_next_trig(), true); + } + + #[deprecated(since = "0.1.2", note = "Use `to_intoinnermode_or_trig` instead")] + #[cfg(feature = "support_panic_trig")] + #[inline(always)] + pub fn to_intoinnermode_or_panic(&self) { + self.to_intoinnermode_or_trig::() + } + + #[inline(always)] + pub fn to_intoinnermode_or_trig(&self) { + self.__safe_replace_mutstate::( + StateManuallyDropData::IntoInnerModeTrig + ); + + debug_assert_eq!(self.is_next_trig(), true); + } + + #[deprecated(since = "0.1.2", note = "Use `deref_or_trig` instead")] + #[cfg(feature = "support_panic_trig")] + pub fn deref_or_panic(&self, fn_panic: F) { + type Trig = PanicTrigManuallyDrop; + let a_state = self.read(); + + if a_state.is_next_trig() { + fn_panic(); + + Trig::trig_next_invalid_beh( + format_args!( + "SafeManuallyDrop, undef_beh (deref_or_panic), SafeManuallyDrop::no_panic_state() != {:?}", + a_state + ) + ) + } + } + + pub fn deref_or_trig(&self) { + let a_state = self.read(); + + if a_state.is_next_trig() { + Trig::trig_next_invalid_beh( + format_args!( + "SafeManuallyDrop, undef_beh (deref_or_trig), SafeManuallyDrop::no_panic_state() != {:?}", + a_state + ) + ) + } + } + + #[deprecated(since = "0.1.2", note = "Use `if_empty_then_run_trigfn` instead")] + #[cfg(feature = "support_panic_trig")] + pub fn if_def_state_then_run_panicfn(&self, fn_panic: F) { + type Trig = PanicTrigManuallyDrop; + let a_state = self.read(); + + if a_state.is_empty() { + fn_panic(); + + Trig::trig_next_invalid_beh( + format_args!( + "SafeManuallyDrop, undef_beh (exp_def_state), SafeManuallyDrop::default\\empty() == {:?}", + a_state + ) + ) + } + } + + pub fn if_empty_then_run_trigfn(&self, fn_trig: F) { + let a_state = self.read(); + + if a_state.is_empty() { + fn_trig(); + + Trig::trig_next_invalid_beh( + format_args!( + "SafeManuallyDrop, undef_beh (exp_def_state), SafeManuallyDrop::empty() == {:?}", + a_state + ) + ) + } + } + + #[deprecated(since = "0.1.2", note = "Use `is_next_trig` instead")] + #[inline(always)] + pub fn is_next_panic(&self) -> bool { + self.is_next_trig() + } + + #[inline(always)] + pub fn is_next_trig(&self) -> bool { + self.read().is_next_trig() + } +} + +impl Default for StateManuallyDrop { + #[inline(always)] + fn default() -> Self { + StateManuallyDrop::empty() + } +} + +#[cfg(test)] +#[test] +fn test_state() { + use crate::core::trig::EmptyLoopTrigManuallyDrop; + + let state = StateManuallyDrop::empty(); + assert_eq!(state.is_empty(), true); + assert_eq!(state.is_next_trig(), false); + + state.deref_or_trig::(); // ok +} diff --git a/src/core/trig/counter.rs b/src/core/trig/counter.rs new file mode 100644 index 0000000..2fcbab2 --- /dev/null +++ b/src/core/trig/counter.rs @@ -0,0 +1,97 @@ + +use core::fmt::Arguments; +use crate::core::trig::TrigManuallyDrop; + +crate::cfg_if_safemode! { + #if_safe( use core::sync::atomic::AtomicU32; ) + #if_safe( use core::sync::atomic::Ordering; ) + + #if_safe( static COUNT_TRIG_SAFEMANUALLYDROP: AtomicU32 = AtomicU32::new(0); ) + #if_safe( const DEF_SETORDERING: Ordering = Ordering::SeqCst; ) + #if_safe( const DEF_GETORDERING: Ordering = Ordering::Relaxed; ) +} + +/// A protected version of SafeManuallyDrop with a function to count the amount of undefined behavior of the ManuallyDrop logic. +/// The undefined behavior of CounterManuallyDrop will be the same as when using the standard ManuallyDrop. +pub type CounterManuallyDrop = crate::beh::safe::SafeManuallyDrop; + +pub enum CounterTrigManuallyDrop {} + +impl TrigManuallyDrop for CounterTrigManuallyDrop { + #[inline(always)] + fn trig_next_invalid_beh<'a>(_a: Arguments<'a>) -> trig_manuallydrop_returntype!() { + crate::cfg_if_safemode! { + #if_safe() { + COUNT_TRIG_SAFEMANUALLYDROP.fetch_add(1, DEF_SETORDERING); + } + } + } +} + +#[inline] +pub fn get_count_trig_safemanuallydrop() -> u32 { + crate::cfg_if_safemode! { + #if_safe() { + let result: u32 = COUNT_TRIG_SAFEMANUALLYDROP.load(DEF_GETORDERING); + result + }else { + 0u32 + } + } +} + +#[inline] +pub fn get_optioncount_trig_safemanuallydrop() -> Option { + crate::cfg_if_safemode! { + #if_safe() { + Some( COUNT_TRIG_SAFEMANUALLYDROP.load(DEF_GETORDERING) ) + }else { + None + } + } +} + +impl CounterManuallyDrop<()> { + #[inline(always)] + pub fn get_count_trig_safemanuallydrop() -> u32 { + crate::core::trig::counter::get_count_trig_safemanuallydrop() + } + + #[inline(always)] + pub fn get_optioncount_trig_safemanuallydrop() -> Option { + crate::core::trig::counter::get_optioncount_trig_safemanuallydrop() + } +} + +#[cfg(test)] +#[test] +fn test_counter_trig_manuallydrop() { + static __TEST_COUNTER: AtomicU32 = AtomicU32::new(0); + struct __Test { } + impl Drop for __Test { + #[inline] + fn drop(&mut self) { + __TEST_COUNTER.fetch_add(1, DEF_SETORDERING); + } + } + + let mut check_data = CounterManuallyDrop::new(__Test { + + }); + + unsafe { // combo drop + CounterManuallyDrop::drop(&mut check_data); + assert_eq!(CounterManuallyDrop::get_count_trig_safemanuallydrop(), 0); + + CounterManuallyDrop::drop(&mut check_data); + assert_eq!(CounterManuallyDrop::get_count_trig_safemanuallydrop(), 1); + + CounterManuallyDrop::drop(&mut check_data); + assert_eq!(CounterManuallyDrop::get_count_trig_safemanuallydrop(), 2); + + assert_eq!( + __TEST_COUNTER.load(DEF_GETORDERING), + CounterManuallyDrop::get_count_trig_safemanuallydrop() + 1 + ); + } +} diff --git a/src/core/trig/hook.rs b/src/core/trig/hook.rs new file mode 100644 index 0000000..17747da --- /dev/null +++ b/src/core/trig/hook.rs @@ -0,0 +1,62 @@ + +use core::fmt::Arguments; +use crate::core::trig::TrigManuallyDrop; + +/// In case of undefined behavior of manual memory management, execute an external hook. +pub enum HookFnTrigManuallyDrop {} +/// Protected version of the SafeManuallyDrop with an execution function in case of undefined behavior of the ManuallyDrop logic. +pub type HookManuallyDrop = crate::beh::safe::SafeManuallyDrop; +/// The hook function to be executed in case of undefined behavior of the HookManuallyDrop. +pub type HookFunction = fn(Arguments) -> trig_manuallydrop_returntype!(); + +/// Hook is a function to be executed in case of undefined behavior of the ManuallyDrop logic. +static mut HOOK: HookFunction = |args| { + panic!("{}", args); +}; + +// UNSAFE_MODE TODO!! +/// Set a hook function to be executed in case of undefined behavior of ManuallyDrop logic. +#[inline(always)] +pub unsafe fn set_hook(function: HookFunction) { + HOOK = function; +} + +/// Get a hook function that will be executed in case of undefined behavior of the ManuallyDrop logic. +#[inline(always)] +pub fn take_hook() -> HookFunction { + // TODO UNSAFE MODE + unsafe { HOOK } +} + +/// Execute a hook function that is always executed in case of undefined behavior of the ManuallyDrop logic. +#[inline(always)] +pub fn run_hook<'a>(args: Arguments<'a>) -> trig_manuallydrop_returntype!() { + (take_hook())(args) +} + +impl TrigManuallyDrop for HookFnTrigManuallyDrop { + #[inline(always)] + fn trig_next_invalid_beh<'a>(a: Arguments<'a>) -> trig_manuallydrop_returntype!() { + crate::core::trig::hook::run_hook(a); + } +} + +impl HookManuallyDrop<()> { + /// Set a hook function to be executed in case of undefined behavior of ManuallyDrop logic. + #[inline(always)] + pub unsafe fn set_hook(function: HookFunction) { + crate::core::trig::hook::set_hook(function) + } + + /// Get a hook function that will be executed in case of undefined behavior of the ManuallyDrop logic. + #[inline(always)] + pub fn take_hook() -> HookFunction { + crate::core::trig::hook::take_hook() + } + + /// Execute a hook function that is always executed in case of undefined behavior of the ManuallyDrop logic. + #[inline(always)] + pub fn run_hook<'a>(args: Arguments<'a>) -> trig_manuallydrop_returntype!() { + crate::core::trig::hook::run_hook(args) + } +} diff --git a/src/core/trig/mod.rs b/src/core/trig/mod.rs new file mode 100644 index 0000000..f79c25e --- /dev/null +++ b/src/core/trig/mod.rs @@ -0,0 +1,52 @@ + +use core::fmt::Arguments; + +/// wait stable https://github.com/rust-lang/rust/issues/35121 +#[cfg(not(feature = "support_count_trig"))] +#[macro_export] +#[doc(hidden)] +macro_rules! trig_manuallydrop_returntype { + [] => { + ! + }; +} + +#[cfg(feature = "support_count_trig")] +#[macro_export] +#[doc(hidden)] +macro_rules! trig_manuallydrop_returntype { + [] => { + () + }; +} + +#[cfg(feature = "support_panic_trig")] +pub mod panic; +#[cfg(feature = "support_count_trig")] +pub mod counter; + +#[cfg(feature = "support_hook_trig")] +pub mod hook; + +/// Implementation of behavior in case of detection of undefined manual memory management. +pub trait TrigManuallyDrop { + fn trig_next_invalid_beh<'a>(a: Arguments<'a>) -> trig_manuallydrop_returntype!(); +} + +/// Trigger is the default function that will be executed in case of undefined behavior of protected ManuallyDrop. +#[cfg(feature = "support_hook_trig")] +pub type DefTrigManuallyDrop = crate::core::trig::hook::HookFnTrigManuallyDrop; + +/// Trigger is the default function that will be executed in case of undefined behavior of protected ManuallyDrop. +#[cfg(not(feature = "support_hook_trig"))] +pub type DefTrigManuallyDrop = EmptyLoopTrigManuallyDrop; + +pub enum EmptyLoopTrigManuallyDrop {} + +impl TrigManuallyDrop for EmptyLoopTrigManuallyDrop { + #[inline(always)] + fn trig_next_invalid_beh<'a>(_a: Arguments<'a>) -> trig_manuallydrop_returntype!() { + loop {} + } +} + diff --git a/src/core/trig/panic.rs b/src/core/trig/panic.rs new file mode 100644 index 0000000..709764c --- /dev/null +++ b/src/core/trig/panic.rs @@ -0,0 +1,16 @@ + +use core::fmt::Arguments; +use crate::core::trig::TrigManuallyDrop; + +/// A protected version of SafeManuallyDrop with a function to execute a panic in case of undefined behavior of the ManuallyDrop logic. +pub type PanicManuallyDrop = crate::beh::safe::SafeManuallyDrop; + +/// In case of undefined behavior of manual memory management, perform a normal panic. +pub enum PanicTrigManuallyDrop {} + +impl TrigManuallyDrop for PanicTrigManuallyDrop { + #[inline(always)] + fn trig_next_invalid_beh<'a>(a: Arguments<'a>) -> trig_manuallydrop_returntype!() { + panic!("{}", a); + } +} diff --git a/src/flags.rs b/src/flags.rs deleted file mode 100644 index 11d432e..0000000 --- a/src/flags.rs +++ /dev/null @@ -1,9 +0,0 @@ - -pub const IS_ASAFE_MODE: bool = { - #[cfg(debug_assertions)] { - true - } - #[cfg(not(debug_assertions))] { - false - } -}; diff --git a/src/lib.rs b/src/lib.rs index 39fcdb3..d0815fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//Copyright 2021 #UlinProject Denis Kotlyarov (Денис Котляров) +//Copyright 2022 #UlinProject Denis Kotlyarov (Денис Котляров) //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ //See the License for the specific language governing permissions and // limitations under the License. -//#Ulin Project 2021 +//#Ulin Project 2022 // @@ -20,275 +20,371 @@ #![no_std] +use crate::core::trig::DefTrigManuallyDrop; +use ::core as stdcore; -use crate::state::StateManuallyDropData; -use core::hash::Hash; -use crate::state::StateManuallyDrop; -use core::ops::DerefMut; -use core::ops::Deref; -pub use core::mem::ManuallyDrop as UnsafeManuallyDrop; +use crate::core::trig::TrigManuallyDrop; +pub use stdcore::mem::ManuallyDrop as UnsafeStdManuallyDrop; -pub type SafeManuallyDrop = ManuallyDrop; - -pub mod state; -pub mod flags; -pub mod panic; - -// Unsafe -#[cfg(not(debug_assertions))] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ManuallyDrop where T: ?Sized { - value: UnsafeManuallyDrop +/// The core of the library that defines the basic primitives. +pub mod core { + pub mod state; + #[macro_use] + pub mod flags; + + #[doc(hidden)] + #[deprecated(since = "0.1.2", note = "Use `crate::core::trig::hook` instead")] + #[cfg(feature = "enable_deprecated_hook")] + pub mod hook; + pub mod trig; } -// SafeTest -#[cfg(debug_assertions)] -#[derive(/*Copy,*/ Clone, Debug)] -pub struct ManuallyDrop where T: ?Sized { - state: StateManuallyDrop, - value: UnsafeManuallyDrop, +/// Safe and insecure implementations of manual memory management. +pub mod beh { + /*crate::cfg_if_safemode! { + /// Insecure standard implementation of manual memory management. + #if_not_safe(pub mod r#unsafe;) + + /// A safe version of the insecure manual control of freeing memory. + #if_safe(pub mod safe;) + }*/ + + /// Insecure standard implementation of manual memory management. + pub mod r#unsafe; + + /// A safe version of the insecure manual control of freeing memory. + pub mod safe; } -//impl Copy for ManuallyDrop where T: ?Sized + Copy {} TODO +// Unsafe +/// Unprotected version of ManuallyDrop with backwards compatibility for SafeManuallyDrop features. +pub type AlwaysUnsafeManuallyDrop = crate::beh::r#unsafe::UnsafeManuallyDrop; -#[cfg(debug_assertions)] -impl Default for ManuallyDrop where T: ?Sized + Default { - #[inline(always)] - fn default() -> Self { - Self::new(Default::default()) - } -} +// Safe +/// A protected version of SafeManuallyDrop with a function to execute a trigger function in case of undefined behavior of the ManuallyDrop logic. +pub type AlwaysSafeManuallyDrop = crate::beh::safe::SafeManuallyDrop; -#[cfg(debug_assertions)] -impl PartialEq for ManuallyDrop where T: ?Sized + PartialEq { - #[inline] - fn eq(&self, a: &Rhs) -> bool { - let value: &T = self.value.deref(); - PartialEq::::eq(value, a) - } - - #[inline] - fn ne(&self, a: &Rhs) -> bool { - let value: &T = self.value.deref(); - PartialEq::::ne(value, a) - } -} +/// A protected version of SafeManuallyDrop with a function to execute a panic in case of undefined behavior of the ManuallyDrop logic. +#[cfg(feature = "support_panic_trig")] +pub type PanicManuallyDrop = crate::core::trig::panic::PanicManuallyDrop; +/// Protected version of the SafeManuallyDrop with an execution function in case of undefined behavior of the ManuallyDrop logic. +#[cfg(feature = "support_hook_trig")] +pub type HookManuallyDrop = crate::core::trig::hook::HookManuallyDrop; -#[cfg(debug_assertions)] -impl Eq for ManuallyDrop where T: Eq + PartialEq> { - #[inline] - fn assert_receiver_is_total_eq(&self) { - let value: &T = self.value.deref(); - Eq::assert_receiver_is_total_eq(value) - } -} +/// A protected version of SafeManuallyDrop with a function to count the amount of undefined behavior of the ManuallyDrop logic. +/// The undefined behavior of CounterManuallyDrop will be the same as when using the standard ManuallyDrop. +#[cfg(feature = "support_count_trig")] +pub type CounterManuallyDrop = crate::core::trig::counter::CounterManuallyDrop; -#[cfg(debug_assertions)] -impl PartialOrd for ManuallyDrop where T: ?Sized + PartialOrd { - #[inline] - fn partial_cmp(&self, a: &Rhs) -> Option { - let value: &T = self.value.deref(); - PartialOrd::partial_cmp(value, a) - } +cfg_if_safemode! { + // Unsafe + /// Depending on the build flag, a protected version of ManuallyDrop or an unprotected version of ManuallyDrop with a default trigger. + #if_not_safe(pub type ManuallyDrop = crate::beh::r#unsafe::UnsafeManuallyDrop;) + //#if_not_safe(type CurrentManuallyDrop = crate::beh::r#unsafe::UnsafeManuallyDrop;) + //#if_not_safe(pub type AlwaysUnsafeManuallyDrop = crate::beh::r#unsafe::SafeManuallyDrop;) + + // Safe + /// Depending on the build flag, a protected version of ManuallyDrop or an unprotected version of ManuallyDrop with a default trigger. + #if_safe(pub type ManuallyDrop = crate::beh::safe::SafeManuallyDrop;) + //#if_safe(pub type AlwaysSafeManuallyDrop = crate::beh::safe::SafeManuallyDrop;) + //#if_safe(pub type PanicManuallyDrop = crate::beh::safe::SafeManuallyDrop;) + //#if_safe(pub type HookManuallyDrop = crate::beh::safe::SafeManuallyDrop;) + //#if_safe(type CurrentManuallyDrop = crate::beh::safe::SafeManuallyDrop;) } -#[cfg(debug_assertions)] -impl Ord for ManuallyDrop where T: Ord + PartialOrd> { - #[inline] - fn cmp(&self, a: &Self) -> core::cmp::Ordering { - let value: &T = self.value.deref(); - Ord::cmp(value, a) - } +#[doc(hidden)] +#[macro_export] +macro_rules! __if_codegen { + [ + if ( #true ) { + $($all:tt)* + } $(else { + $($else:tt)* + })* + ] => { + $($all)* + }; + [ + if ( #false ) { + $($all:tt)* + } $(else { + $($else:tt)* + })* + ] => { + $( $($else)* )* + }; } -#[cfg(debug_assertions)] -impl Hash for ManuallyDrop where T: ?Sized + Hash { - #[inline] - fn hash(&self, a: &mut H) where H: core::hash::Hasher { - let value: &T = self.value.deref(); - Hash::hash(value, a) - } -} -impl ManuallyDrop { - #[cfg(debug_assertions)] - #[inline(always)] - pub /*const*/ fn new(value: T) -> ManuallyDrop { - let value = UnsafeManuallyDrop::new(value); +#[doc(hidden)] +#[macro_export] +macro_rules! __codegen { + [ + @use; + $($($all:tt)+)? + ] => { + use ::core as stdcore; + use crate::core::state::StateManuallyDropData; + #[allow(unused_imports)] + use crate::core::state::StateManuallyDrop; + use stdcore::ops::DerefMut; + use stdcore::ops::Deref; - ManuallyDrop { - value, - state: StateManuallyDrop::default(), + $( + $crate::__codegen! { + $($all)+ + } + )? + }; + [ + #$current_type: tt [is_safe: $is_safe:tt]; + $($($all:tt)+)? + ] => { + impl $current_type where Trig: TrigManuallyDrop { + crate::__if_codegen! { + if (#$is_safe) { + // Safe + #[inline(always)] + pub /*const*/ fn new(value: T) -> Self { + let value = UnsafeStdManuallyDrop::new(value); + + Self { + value, + state: StateManuallyDrop::empty(), + _pp: PhantomData + } + } + }else { + // Unsafe + #[inline(always)] + pub /*const*/ fn new(value: T) -> Self { + let value = UnsafeStdManuallyDrop::new(value); + + Self { + value, + _pp: PhantomData + } + } + } + } + + #[inline(always)] + pub unsafe fn as_unsafestd_manuallydrop(&self) -> &UnsafeStdManuallyDrop { + &self.value + } + + #[inline(always)] + pub unsafe fn as_mut_unsafestd_manuallydrop(&mut self) -> &mut UnsafeStdManuallyDrop { + &mut self.value + } + + #[inline] + pub fn get_state(&self) -> Option { + crate::__if_codegen! { + if (#$is_safe) { + // Safe + Some(self.state.read()) + }else { + // Unsafe + None + } + } + } + + #[deprecated(since = "0.1.2", note = "Use `is_empty_state` instead")] + #[inline(always)] + pub fn is_def_state(&self) -> Option { + self.is_empty_state() + } + + #[inline] + pub fn is_empty_state(&self) -> Option { + crate::__if_codegen! { + if (#$is_safe) { + // Safe + Some(self.state.is_empty()) + }else { + // Unsafe + None + } + } + } + + #[inline(always)] + pub fn as_ptr(&self) -> *const T { + // TODO, VALID?, Exp: ManuallyDrop::as_ptr + &*(self.value.deref() as &T) + } + + #[inline(always)] + pub fn as_mut_ptr(&mut self) -> *mut T { + // TODO, VALID?, Exp: ManuallyDrop::as_mut_ptr + &mut *(self.value.deref_mut() as &mut T) + } + + #[inline(always)] + pub fn as_value(&self) -> &T { + &self.value + } + + #[inline(always)] + pub fn as_mut_value(&mut self) -> &mut T { + &mut self.value + } + + #[inline(always)] + pub /*const*/ fn into_inner(slot: $current_type) -> T { + let core_inner = Self::into_core_inner(slot); + + UnsafeStdManuallyDrop::into_inner(core_inner) + } + + crate::__if_codegen! { + if (#$is_safe) { + // Safe + #[inline(always)] + pub /*const*/ fn into_core_inner(slot: $current_type) -> UnsafeStdManuallyDrop { + slot.state.to_intoinnermode_or_trig::(); + + // analog UnsafeManuallyDrop::take + let result: UnsafeStdManuallyDrop = unsafe { + stdcore::ptr::read(&slot.value) + }; + + let _ignore_drop = UnsafeStdManuallyDrop::new(slot); + result + } + }else { + // Unsafe + #[inline(always)] + pub /*const*/ fn into_core_inner(slot: $current_type) -> UnsafeStdManuallyDrop { + slot.value + } + } + } + + #[inline(always)] + pub unsafe fn take(slot: &mut $current_type) -> T { + crate::__if_codegen! { + if (#$is_safe) { + slot.state.to_takemode_or_trig::(); + } + } + + UnsafeStdManuallyDrop::take(&mut slot.value) + } + + #[deprecated(since = "0.1.2", note = "Use `is_next_panic` instead")] + #[inline(always)] + pub fn is_maybe_next_panic(&self) -> bool { + self.is_next_trig() + } + + #[deprecated(since = "0.1.2", note = "Use `is_next_trig` instead")] + #[inline(always)] + pub fn is_next_panic(&self) -> bool { + self.is_next_trig() + } + + #[inline(always)] + pub fn is_next_trig(&self) -> bool { + crate::__if_codegen! { + if (#$is_safe) { + return self.state.is_next_trig(); + }else { + return false; + } + } + } + + #[deprecated(since = "0.1.2", note = "Use `is_next_trig_optionresult` instead")] + #[inline(always)] + pub fn is_next_panic_optionresult(&self) -> Option { + self.is_next_trig_optionresult() + } + + #[inline(always)] + pub fn is_next_trig_optionresult(&self) -> Option { + crate::__if_codegen! { + if (#$is_safe) { + return Some(self.state.is_next_trig()); + }else { + return None; + } + } + } + + #[inline(always)] + pub unsafe fn ignore_drop(&self) { + crate::__if_codegen! { + if (#$is_safe) { + self.state.to_ignore_trig_when_drop::(); + } + } + } } - } - - #[cfg(not(debug_assertions))] - #[inline(always)] - pub const fn new(value: T) -> ManuallyDrop { - let value = UnsafeManuallyDrop::new(value); - ManuallyDrop { - value, - } - } - - #[inline] - pub fn get_state(&self) -> Option { - #[cfg(not(debug_assertions))] { - return None; + impl $current_type where T: ?Sized, Trig: TrigManuallyDrop { + #[inline(always)] + pub unsafe fn drop(slot: &mut $current_type) { + crate::__if_codegen! { + if (#$is_safe) { + slot.state.to_dropmode_or_trig::(); + } + } + + UnsafeStdManuallyDrop::drop(&mut slot.value) + } } - #[cfg(debug_assertions)] { - return Some(self.state.read()); - } - } - - #[inline] - pub fn is_def_state(&self) -> Option { - #[cfg(not(debug_assertions))] { - return None; + impl $current_type<(), Trig> where Trig: TrigManuallyDrop { + #[inline(always)] + pub /*const*/ fn is_safe_mode() -> bool { + crate::core::flags::IS_SAFE_MODE + } } - #[cfg(debug_assertions)] { - return Some(self.state.is_def_mode()); - } - } - - #[inline(always)] - pub fn as_ptr(&self) -> *const T { - // TODO, VALID?, Exp: ManuallyDrop::as_ptr - &*(self.value.deref() as &T) - } - - #[inline(always)] - pub fn as_mut_ptr(&mut self) -> *mut T { - // TODO, VALID?, Exp: ManuallyDrop::as_mut_ptr - &mut *(self.value.deref_mut() as &mut T) - } - - #[inline(always)] - pub fn as_value(&self) -> &T { - &self.value - } - - #[inline(always)] - pub fn as_mut_value(&mut self) -> &mut T { - &mut self.value - } - - #[inline(always)] - pub /*const*/ fn into_inner(slot: ManuallyDrop) -> T { - let core_inner = Self::into_core_inner(slot); + impl Deref for $current_type where T: ?Sized, Trig: TrigManuallyDrop { + type Target = T; - UnsafeManuallyDrop::into_inner(core_inner) - } - - #[cfg(not(debug_assertions))] - #[inline(always)] - pub const fn into_core_inner(slot: ManuallyDrop) -> UnsafeManuallyDrop { - slot.value - } - - #[cfg(debug_assertions)] - #[inline(always)] - pub /*const*/ fn into_core_inner(slot: ManuallyDrop) -> UnsafeManuallyDrop { - slot.state.to_intoinnermode_or_panic(); - - // analog UnsafeManuallyDrop::take - let result: UnsafeManuallyDrop = unsafe { - core::ptr::read(&slot.value) - }; - - let _ignore_drop = UnsafeManuallyDrop::new(slot); - result - } - - #[inline(always)] - pub unsafe fn take(slot: &mut ManuallyDrop) -> T { - #[cfg(debug_assertions)] { - slot.state.to_takemode_or_panic(); + #[inline(always)] + fn deref(&self) -> &T { + crate::__if_codegen! { + if (#$is_safe) { + self.state.deref_or_trig::(); + } + } + + &self.value + } } - - UnsafeManuallyDrop::take(&mut slot.value) - } - - #[inline(always)] - pub fn is_maybe_next_panic(&self) -> bool { - #[cfg(not(debug_assertions))] { - return false; + + impl DerefMut for $current_type where T: ?Sized, Trig: TrigManuallyDrop { + #[inline(always)] + fn deref_mut(&mut self) -> &mut T { + crate::__if_codegen! { + if (#$is_safe) { + self.state.deref_or_trig::(); + } + } + + &mut self.value + } } - #[cfg(debug_assertions)] { - return !self.state.is_def_mode(); - } - } + $( + $crate::__codegen! { + $($all)+ + } + )? + }; - #[inline(always)] - pub unsafe fn ignore_drop(&self) { - #[cfg(debug_assertions)] { - self.state.to_ignore_panic_when_drop(); - } - } + //[] => {} } -impl ManuallyDrop where T: ?Sized { - #[inline(always)] - pub unsafe fn drop(slot: &mut ManuallyDrop) { - #[cfg(debug_assertions)] { - slot.state.to_dropmode_or_panic(); - } - - UnsafeManuallyDrop::drop(&mut slot.value) - } -} -impl ManuallyDrop<()> { - #[inline(always)] - pub const fn is_safe_mode() -> bool { - crate::flags::IS_ASAFE_MODE - } -} -impl Deref for ManuallyDrop where T: ?Sized { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &T { - #[cfg(debug_assertions)] { - self.state.deref_or_panic(|| {}); - } - - &self.value - } -} - -impl DerefMut for ManuallyDrop where T: ?Sized { - #[inline(always)] - fn deref_mut(&mut self) -> &mut T { - #[cfg(debug_assertions)] { - self.state.deref_or_panic(|| {}); - } - - &mut self.value - } -} - -#[cfg(debug_assertions)] -impl Drop for ManuallyDrop where T: ?Sized { - fn drop(&mut self) { - let ref mut value = self.value; - let ref state = self.state; - - // What for? - >> to ignore miri errors allocate. - state.exp_def_state_and_panic( - || unsafe { - UnsafeManuallyDrop::drop(value); - } - ); - } -} diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 0f6fa97..0000000 --- a/src/state.rs +++ /dev/null @@ -1,248 +0,0 @@ - -use core::fmt::Debug; -use core::sync::atomic::Ordering; -use core::sync::atomic::AtomicU8; - -const READ_ORDERING_METHOD: Ordering = Ordering::SeqCst; -const WRITE_ORDERING_METHOD: Ordering = Ordering::SeqCst; // - -#[repr(transparent)] -pub struct StateManuallyDrop { - state: AtomicU8, -} - -impl Clone for StateManuallyDrop { - #[inline] - fn clone(&self) -> Self { - Self { - state: AtomicU8::new(self.__read_byte()) - } - } -} - -impl Debug for StateManuallyDrop { - #[inline] - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - f.debug_struct("StateManuallyDrop") - .field("state", &self.read()) - .finish() - } -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum StateManuallyDropData { - DefState = 1, - - TakeMode = 5, - DropMode = 15, - IntoInnerMode = 25, - - IgnorePanicWhenDrop = 30, -} - -impl From for StateManuallyDropData { - #[inline] - fn from(a: u8) -> Self { - StateManuallyDropData::from_or_default(a) - } -} - -impl Default for StateManuallyDropData { - #[inline(always)] - fn default() -> Self { - StateManuallyDropData::default() - } -} - -impl StateManuallyDropData { - #[inline(always)] - pub const fn into(self) -> u8 { - self as _ - } - - #[inline] - pub /*const*/ fn from_or_default(a: u8) -> Self { - Self::is_valid_byte_fn( - a, - || unsafe { - Self::force_from(a) - }, - || Self::default() - ) - } - - #[inline] - pub /*const*/ fn from(a: u8) -> Option { - Self::is_valid_byte_fn( - a, - || Some(unsafe { - Self::force_from(a) - }), - || None - ) - } - - #[inline(always)] - pub const fn default() -> Self { - Self::DefState - } - - #[inline(always)] - pub /*const*/ fn is_valid_byte_fn R, FE: FnOnce() -> R, R>(a: u8, next: F, errf: FE) -> R { - match a { - a if a == Self::DefState as _ || - - a == Self::TakeMode as _ || - a == Self::DropMode as _ || - a == Self::IntoInnerMode as _ || - - a == Self::IgnorePanicWhenDrop as _ => next(), - _ => errf() - } - } - - #[inline] - pub fn is_valid_byte(a: u8) -> bool { - Self::is_valid_byte_fn( - a, - || true, - || false, - ) - } - - #[inline(always)] - pub unsafe fn force_from(a: u8) -> Self { - debug_assert_eq!( - Self::is_valid_byte(a), - true - ); - - #[allow(unused_unsafe)] - let result: Self = unsafe { - core::mem::transmute(a as u8) - }; - result - } -} - -impl StateManuallyDrop { - #[inline(always)] - pub const fn default() -> Self { - Self { - state: AtomicU8::new(StateManuallyDropData::default() as _) - } - } - - #[inline(always)] - pub fn is_def_mode(&self) -> bool { - self.read() == StateManuallyDropData::default() as _ - } - - #[inline(always)] - fn __read_byte(&self) -> u8 { - self.state.load(READ_ORDERING_METHOD) - } - - #[inline(always)] - pub fn read(&self) -> StateManuallyDropData { - let byte = self.__read_byte(); - unsafe { - StateManuallyDropData::force_from(byte) - } - } - - #[inline(always)] - fn __write(&self, a: StateManuallyDropData) -> StateManuallyDropData { - let byte = self.state.swap(a as _, WRITE_ORDERING_METHOD); - unsafe { - StateManuallyDropData::force_from(byte) - } - } - - #[inline] - fn __safe_replace_mutstate(&self, new_state: StateManuallyDropData) { - let a_state = self.__write(new_state); - - let def_state = StateManuallyDropData::default(); - match a_state == def_state { - true => {}, - false => crate::panic::run_hook( - format_args!( - "SafeManuallyDrop, undef_beh, {:?} != {:?}", - def_state, - a_state - ) - ), - } - } - - #[inline(always)] - pub fn to_dropmode_or_panic(&self) { - self.__safe_replace_mutstate( - StateManuallyDropData::DropMode - ); - } - - #[inline(always)] - pub fn to_takemode_or_panic(&self) { - self.__safe_replace_mutstate( - StateManuallyDropData::TakeMode - ); - } - - #[inline(always)] - pub fn to_ignore_panic_when_drop(&self) { - self.__safe_replace_mutstate( - StateManuallyDropData::IgnorePanicWhenDrop - ); - } - - #[inline(always)] - pub fn to_intoinnermode_or_panic(&self) { - self.__safe_replace_mutstate( - StateManuallyDropData::IntoInnerMode - ); - } - - pub fn deref_or_panic(&self, fn_panic: F) { - let a_state = self.read(); - - let def_state = StateManuallyDropData::default(); - if a_state != def_state { - fn_panic(); - - crate::panic::run_hook( - format_args!( - "SafeManuallyDrop, undef_beh (deref_or_panic), {:?} == {:?}", - def_state, - a_state - ) - ) - } - } - - pub fn exp_def_state_and_panic(&self, fn_panic: F) { - let a_state = self.read(); - - let def_state = StateManuallyDropData::default(); - if a_state == def_state { - fn_panic(); - - crate::panic::run_hook( - format_args!( - "SafeManuallyDrop, undef_beh (exp_def_state), {:?} == {:?}", - def_state, - a_state - ) - ) - } - } -} - -impl Default for StateManuallyDrop { - #[inline(always)] - fn default() -> Self { - StateManuallyDrop::default() - } -} diff --git a/tests/all.rs b/tests/all.rs index 4d335d2..8a73555 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -4,7 +4,7 @@ use SafeManuallyDrop::ManuallyDrop; /// Build test data #[inline(never)] fn build_new_test_vec() -> Vec { - let mut vec = Vec::new(); + let mut vec = Vec::with_capacity(4); vec.push("test".into()); vec.push("test2".into()); vec.push("test3".into()); @@ -29,7 +29,7 @@ mod panic_test_methods { unsafe { ManuallyDrop::drop(&mut control_drop); - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC ManuallyDrop::drop(&mut control_drop); } @@ -50,7 +50,7 @@ mod panic_test_methods { } } - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC let _e = control_drop.deref(); } @@ -71,7 +71,7 @@ mod panic_test_methods { drop(data); } - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC let _e = control_drop.deref(); } @@ -92,7 +92,7 @@ mod panic_test_methods { drop(data); } - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC unsafe { ManuallyDrop::drop(&mut control_drop); @@ -113,7 +113,7 @@ mod panic_test_methods { } } - assert_eq!(control_drop.is_maybe_next_panic(), true); + assert_eq!(control_drop.is_next_trig(), true); // <<-- PANIC let _data = ManuallyDrop::into_inner(control_drop); }