use std::{
alloc::{alloc_zeroed, Layout},
any::type_name,
fmt::Display,
mem::MaybeUninit,
};
#[derive(Clone, Copy)]
pub struct AnyValue {
pointer: Option<*mut u8>,
type_name: &'static str,
is_taken: bool,
}
impl Display for AnyValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AnyValue").field(&self.type_name).finish()
}
}
impl std::fmt::Debug for AnyValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AnyValue").field(&self.type_name).finish()
}
}
unsafe impl Send for AnyValue {}
unsafe impl Sync for AnyValue {}
impl AnyValue {
pub fn new_zero() -> Self {
AnyValue {
pointer: None,
type_name: "",
is_taken: false,
}
}
pub fn new<V>(v: V) -> Self {
let res = Self::new_zero();
res.replace(v);
res
}
pub fn new_volatile<V>(v: V) -> Self {
let res = Self::new_zero();
res.replace_volatile(v);
res
}
pub fn is_empty(&self) -> bool {
self.pointer.is_none()
}
pub fn replace<V>(&self, v: V) {
self.check_type::<V>();
self.replace_with_write(v, |dst, src| unsafe { std::ptr::write(dst, src) })
}
pub fn replace_volatile<V>(&self, v: V) {
self.check_type::<V>();
self.replace_with_write(v, |dst, src| unsafe { std::ptr::write_volatile(dst, src) })
}
#[allow(clippy::cast_ref_to_mut)]
pub fn replace_with_write<V, F>(&self, v: V, f: F)
where
F: Fn(*mut V, V),
{
let layout = Layout::new::<MaybeUninit<V>>();
let pointer = unsafe { alloc_zeroed(layout) };
f(pointer.cast::<V>(), v);
unsafe {
let ptr = &mut *(self as *const Self as *mut Self);
ptr.type_name = type_name::<V>();
ptr.pointer = Some(pointer);
ptr.is_taken = false;
}
}
#[allow(clippy::mut_from_ref)]
pub fn to_mut<V>(&self) -> &mut V {
self.check_taken();
self.check_type::<V>();
self.check_none();
unsafe { &mut *(self.pointer.unwrap() as *const V as *mut V) }
}
pub fn to_ref<V>(&self) -> &V {
self.check_taken();
self.check_type::<V>();
self.check_none();
unsafe { &*(self.pointer.unwrap() as *const V as *mut V) }
}
pub fn take<V>(&self) -> V {
self.check_taken();
self.check_type::<V>();
self.check_none();
self.take_with_read(|src| unsafe { std::ptr::read(src) })
}
pub fn take_volatile<V>(&self) -> V {
self.check_taken();
self.check_type::<V>();
self.check_none();
self.take_with_read(|src| unsafe { std::ptr::read_volatile(src) })
}
#[allow(clippy::cast_ref_to_mut)]
pub fn take_with_read<V, F>(&self, f: F) -> V
where
F: Fn(*const V) -> V,
{
let ptr = self.pointer.unwrap() as *const V;
let v = f(ptr);
unsafe { &mut *(self as *const AnyValue as *mut AnyValue) }.is_taken = true;
v
}
pub fn get_string(&self) -> String {
self.to_ref::<String>().to_string()
}
fn check_type<V>(&self) {
if self.type_name.is_empty() {
return;
}
let type_name = type_name::<V>();
if type_name != self.type_name {
panic!("类型不一致,期望:{},实际:{}", type_name, self.type_name);
}
}
fn check_taken(&self) {
if self.is_taken {
panic!("该值已取出:{}", self.type_name);
}
}
fn check_none(&self) {
if self.pointer.is_none() {
panic!("数据为空:{}", self.type_name);
}
}
}
#[cfg(test)]
mod tests {
use std::panic;
use crate::any::value::AnyValue;
#[test]
fn test() {
assert_eq!(AnyValue::new(1).take::<i32>(), 1);
assert_eq!(AnyValue::new("2").take::<&str>(), "2");
assert_ne!(AnyValue::new(1).take::<i32>(), 2);
assert_ne!(AnyValue::new("1").take::<&str>(), "2");
assert!(panic::catch_unwind(|| {
AnyValue::new("2").take::<i32>();
})
.is_err());
assert!(panic::catch_unwind(|| {
let v = AnyValue::new("abc");
assert_eq!(v.take::<&str>(), "abc");
assert_eq!(v.take::<&str>(), "abc");
})
.is_err());
}
}