mun_memory 0.4.0

Memory management functionality for Mun
Documentation
#![allow(clippy::mutable_key_type)]

use crate::{HasStaticType, Type};
use lazy_static::lazy_static;
use std::{collections::HashMap, ptr::NonNull};

type CastFn = fn(NonNull<u8>, NonNull<u8>);

macro_rules! insert_cast_fn {
    { $table:ident, $A:ty, $B:ty } => {
        $table.insert(
            (<$A>::type_info().clone(), <$B>::type_info().clone()),
            cast_from_to::<$A, $B> as CastFn,
        )
    }
}

lazy_static! {
    static ref CAST_FN_TABLE: HashMap<(Type, Type), CastFn> = {
        let mut table = HashMap::new();
        insert_cast_fn!(table, f32, f64);
        insert_cast_fn!(table, i8, i16);
        insert_cast_fn!(table, i8, i32);
        insert_cast_fn!(table, i8, i64);
        insert_cast_fn!(table, i8, i128);
        insert_cast_fn!(table, i16, i32);
        insert_cast_fn!(table, i16, i64);
        insert_cast_fn!(table, i16, i128);
        insert_cast_fn!(table, i32, i64);
        insert_cast_fn!(table, i32, i128);
        insert_cast_fn!(table, i64, i128);
        insert_cast_fn!(table, u8, i16);
        insert_cast_fn!(table, u8, u16);
        insert_cast_fn!(table, u8, i32);
        insert_cast_fn!(table, u8, u32);
        insert_cast_fn!(table, u8, i64);
        insert_cast_fn!(table, u8, u64);
        insert_cast_fn!(table, u8, i128);
        insert_cast_fn!(table, u8, u128);
        insert_cast_fn!(table, u16, i32);
        insert_cast_fn!(table, u16, u32);
        insert_cast_fn!(table, u16, i64);
        insert_cast_fn!(table, u16, u64);
        insert_cast_fn!(table, u16, i128);
        insert_cast_fn!(table, u16, u128);
        insert_cast_fn!(table, u32, i64);
        insert_cast_fn!(table, u32, u64);
        insert_cast_fn!(table, u32, i128);
        insert_cast_fn!(table, u32, u128);
        insert_cast_fn!(table, u64, i128);
        insert_cast_fn!(table, u64, u128);
        table
    };
}

fn cast_from_to<A, B>(src: NonNull<u8>, dest: NonNull<u8>)
where
    A: Copy + Into<B>,
{
    let value = unsafe { *src.cast::<A>().as_ref() };
    unsafe { *dest.cast::<B>().as_mut() = value.into() };
}

pub fn try_cast_from_to(old_id: Type, new_id: Type, src: NonNull<u8>, dest: NonNull<u8>) -> bool {
    if let Some(cast_fn) = CAST_FN_TABLE.get(&(old_id, new_id)) {
        cast_fn(src, dest);
        true
    } else {
        false
    }
}

#[cfg(test)]
mod tests {
    use super::try_cast_from_to;
    use crate::HasStaticType;
    use std::ptr::NonNull;

    fn assert_cast<A, B>(a: A, mut b: B)
    where
        A: Copy + Into<B> + HasStaticType,
        B: PartialEq + std::fmt::Debug + HasStaticType,
    {
        assert!(try_cast_from_to(
            A::type_info().clone(),
            B::type_info().clone(),
            unsafe { NonNull::new_unchecked(&a as *const _ as *mut _) },
            unsafe { NonNull::new_unchecked(&mut b as *mut _) }.cast::<u8>(),
        ));
        assert_eq!(b, a.into());
    }

    #[test]
    fn cast_f32_to_f64() {
        assert_cast(std::f32::consts::PI, 0f64);
    }

    #[test]
    fn cast_i8_to_i16() {
        assert_cast(-5i8, 0i16);
    }

    #[test]
    fn cast_i8_to_i32() {
        assert_cast(-5i8, 0i32);
    }

    #[test]
    fn cast_i8_to_i64() {
        assert_cast(-5i8, 0i64);
    }

    #[test]
    fn cast_i8_to_i128() {
        assert_cast(-5i8, 0i128);
    }

    #[test]
    fn cast_i16_to_i32() {
        assert_cast(-5i16, 0i32);
    }

    #[test]
    fn cast_i16_to_i64() {
        assert_cast(-5i16, 0i64);
    }

    #[test]
    fn cast_i16_to_i128() {
        assert_cast(-5i16, 0i128);
    }

    #[test]
    fn cast_i32_to_i64() {
        assert_cast(-5i32, 0i64);
    }

    #[test]
    fn cast_i32_to_i128() {
        assert_cast(-5i32, 0i128);
    }

    #[test]
    fn cast_i64_to_i128() {
        assert_cast(-5i64, 0i128);
    }

    #[test]
    fn cast_u8_to_i16() {
        assert_cast(5u8, 0i16);
    }

    #[test]
    fn cast_u8_to_u16() {
        assert_cast(5u8, 0u16);
    }

    #[test]
    fn cast_u8_to_i32() {
        assert_cast(5u8, 0i32);
    }

    #[test]
    fn cast_u8_to_u32() {
        assert_cast(5u8, 0u32);
    }

    #[test]
    fn cast_u8_to_i64() {
        assert_cast(5u8, 0i64);
    }

    #[test]
    fn cast_u8_to_u64() {
        assert_cast(5u8, 0u64);
    }

    #[test]
    fn cast_u8_to_i128() {
        assert_cast(5u8, 0i128);
    }

    #[test]
    fn cast_u8_to_u128() {
        assert_cast(5u8, 0u128);
    }

    #[test]
    fn cast_u16_to_i32() {
        assert_cast(5u16, 0i32);
    }

    #[test]
    fn cast_u16_to_u32() {
        assert_cast(5u16, 0u32);
    }

    #[test]
    fn cast_u16_to_i64() {
        assert_cast(5u16, 0i64);
    }

    #[test]
    fn cast_u16_to_u64() {
        assert_cast(5u16, 0u64);
    }

    #[test]
    fn cast_u16_to_i128() {
        assert_cast(5u16, 0i128);
    }

    #[test]
    fn cast_u16_to_u128() {
        assert_cast(5u16, 0u128);
    }

    #[test]
    fn cast_u32_to_i64() {
        assert_cast(5u32, 0i64);
    }

    #[test]
    fn cast_u32_to_u64() {
        assert_cast(5u32, 0u64);
    }

    #[test]
    fn cast_u32_to_i128() {
        assert_cast(5u32, 0i128);
    }

    #[test]
    fn cast_u32_to_u128() {
        assert_cast(5u32, 0u128);
    }

    #[test]
    fn cast_u64_to_i128() {
        assert_cast(5u64, 0i128);
    }

    #[test]
    fn cast_u64_to_u128() {
        assert_cast(5u64, 0u128);
    }
}