use core::mem; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; /// Places a sized `T` into LVGL memory. /// /// This is useful for cases when we need to allocate memory on Rust side /// and handover the management of that memory to LVGL. May also be used in cases we /// want to use dynamic memory in the Rust side. pub(crate) struct Box(NonNull); impl Box { /// Allocate memory using LVGL memory API and place `T` in the LVGL tracked memory. pub fn new(value: T) -> Box { let size = mem::size_of::(); let inner = unsafe { let ptr = lvgl_sys::lv_mem_alloc(size as lvgl_sys::size_t) as *mut T; // LVGL should align the memory address for us! assert_eq!( ptr as usize % mem::align_of::(), 0, "Memory address not aligned!" ); NonNull::new(ptr) .map(|p| { p.as_ptr().write(value); p }) .unwrap_or_else(|| { panic!("Could not allocate memory {} bytes", size); }) }; Box(inner) } pub fn into_raw(self) -> *mut T { let b = mem::ManuallyDrop::new(self); b.0.as_ptr() } } impl Drop for Box { fn drop(&mut self) { unsafe { lvgl_sys::lv_mem_free(self.0.as_ptr() as *const cty::c_void); } } } impl DerefMut for Box { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } impl Deref for Box { type Target = T; fn deref(&self) -> &Self::Target { unsafe { self.0.as_ref() } } } impl AsMut for Box { fn as_mut(&mut self) -> &mut T { unsafe { self.0.as_mut() } } } #[cfg(test)] mod test { use super::*; use std::sync::Once; use std::vec::Vec; static INIT_LVGL: Once = Once::new(); fn init() { INIT_LVGL.call_once(|| { unsafe { lvgl_sys::lv_init(); }; }); } #[test] fn place_value_in_lv_mem() { init(); let v = Box::new(5); drop(v); let v = Box::new(10); drop(v); } #[test] fn place_complex_value_in_lv_mem() { init(); #[repr(C)] #[derive(Debug)] struct Point { x: u64, y: i8, t: i32, disp: i32, } let initial_mem_info = mem_info(); let mut keep = Vec::new(); for i in 0..100 { let p = Point { x: i, y: 42, t: 0, disp: -100, }; println!("{:?}", p); let mut b = Box::new(p); println!("memory address is {:p}", b.as_mut()); let point = b.as_mut(); if point.x != i { println!("{:?}", point); } assert_eq!(point.x, i); let info = mem_info(); println!("mem info: {:?}", &info); keep.push(b); } drop(keep); unsafe { lvgl_sys::lv_mem_defrag(); } let final_info = mem_info(); println!("mem info: {:?}", &final_info); // If this fails, we are leaking memory! BOOM! \o/ assert_eq!(initial_mem_info.free_size, final_info.free_size) } fn mem_info() -> lvgl_sys::lv_mem_monitor_t { let mut info = lvgl_sys::lv_mem_monitor_t { total_size: 0, free_cnt: 0, free_size: 0, free_biggest_size: 0, used_cnt: 0, max_used: 0, used_pct: 0, frag_pct: 0, }; unsafe { lvgl_sys::lv_mem_monitor(&mut info as *mut _); } info } }