Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-config/src/any.rs')
| -rw-r--r-- | helix-config/src/any.rs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/helix-config/src/any.rs b/helix-config/src/any.rs new file mode 100644 index 00000000..891d9e8c --- /dev/null +++ b/helix-config/src/any.rs @@ -0,0 +1,76 @@ +/// this is a reimplementation of dynamic dispatch that only stores the +/// information we need and stores everythin inline. Values that are smaller or +/// the same size as a slice (2 usize) are also stored inline. This avoids +/// significant overallocation when setting lots of simple config +/// options (integers, strings, lists, enums) +use std::any::{Any, TypeId}; +use std::mem::{align_of, size_of, MaybeUninit}; + +pub struct ConfigData { + data: MaybeUninit<[usize; 2]>, + ty: TypeId, + drop_fn: unsafe fn(MaybeUninit<[usize; 2]>), +} + +const fn store_inline<T>() -> bool { + size_of::<T>() <= size_of::<[usize; 2]>() && align_of::<T>() <= align_of::<[usize; 2]>() +} + +impl ConfigData { + unsafe fn drop_impl<T: Any>(mut data: MaybeUninit<[usize; 2]>) { + if store_inline::<T>() { + data.as_mut_ptr().cast::<T>().drop_in_place(); + } else { + let ptr = data.as_mut_ptr().cast::<*mut T>().read(); + drop(Box::from_raw(ptr)); + } + } + + pub fn get<T: Any>(&self) -> &T { + assert_eq!(TypeId::of::<T>(), self.ty); + unsafe { + if store_inline::<T>() { + return &*self.data.as_ptr().cast(); + } + let data: *const T = self.data.as_ptr().cast::<*const T>().read(); + &*data + } + } + pub fn new<T: Any>(val: T) -> Self { + let mut data = MaybeUninit::uninit(); + if store_inline::<T>() { + let data: *mut T = data.as_mut_ptr() as _; + unsafe { + data.write(val); + } + } else { + assert!(store_inline::<*const T>()); + let data: *mut *const T = data.as_mut_ptr() as _; + unsafe { + data.write(Box::into_raw(Box::new(val))); + } + }; + Self { + data, + ty: TypeId::of::<T>(), + drop_fn: ConfigData::drop_impl::<T>, + } + } +} + +impl Drop for ConfigData { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.data); + } + } +} + +impl std::fmt::Debug for ConfigData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConfigData").finish_non_exhaustive() + } +} + +unsafe impl Send for ConfigData {} +unsafe impl Sync for ConfigData {} |