linkstore/
store.rs

1#[doc(hidden)]
2pub mod private {
3	use crate::TryDecodeLinkstore;
4	use core::{cell::UnsafeCell, mem::size_of};
5
6	pub use crate::embed::encode::MAGIC;
7
8	#[repr(transparent)]
9	pub struct VolatileWrapper<T: Sized>(UnsafeCell<T>);
10	impl<T: Sized> VolatileWrapper<T> {
11		#[doc(hidden)]
12		pub const fn new(val: T) -> Self {
13			Self(UnsafeCell::new(val))
14		}
15
16		#[doc(hidden)]
17		pub fn get(&'static self) -> &'static T {
18			unsafe {
19				core::mem::forget(core::ptr::read_volatile(self.0.get()));
20				&*self.0.get()
21			}
22		}
23	}
24	unsafe impl<T: TryDecodeLinkstore + Sized> Sync for VolatileWrapper<T> {}
25
26	pub const fn calc_padding<Container, T>(name: &'static str) -> usize
27	where
28		Container: Sized,
29		T: Sized,
30	{
31		size_of::<Container>() - (name.len() + 1 + 1) - (size_of::<usize>() * 2) - size_of::<T>()
32	}
33}
34
35/// Defines linkstores in the current binary.
36///
37/// ```no_run
38/// #[macro_use] extern crate linkstore;
39///
40/// linkstore! {
41///     pub static LINKSTORE_TEST: u64 = 0xDEADBEEF;
42///     pub static LINKSTORE_YEAH: u32 = 0xDEADBEEF;
43///     pub static LINKSTORE_BYTES: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
44///     pub static LINKSTORE_SHORTS: [u16; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
45///     pub static LINKSTORE_BIG: u128 = 0xDEADBEEF;
46/// }
47///
48/// fn main() {
49///     unsafe {
50///         println!("LINKSTORE_TEST = {:x}", LINKSTORE_TEST::get());
51///         println!("LINKSTORE_YEAH = {:x}", LINKSTORE_YEAH::get());
52///         println!("LINKSTORE_BYTES = {:?}", LINKSTORE_BYTES::get());
53///         println!("LINKSTORE_SHORTS = {:?}", LINKSTORE_SHORTS::get());
54///         println!("LINKSTORE_BIG = {:b}", LINKSTORE_BIG::get());
55///     }
56/// }
57/// ```
58#[cfg_attr(docsrs, doc(cfg(feature = "store")))]
59#[macro_export]
60macro_rules! linkstore {
61	{$($vis:vis static $name:ident: $ty:ty = $init:expr;)+} => {$(
62		#[allow(non_snake_case)]
63		$vis mod $name {
64			use ::core::mem::{size_of, align_of};
65			use $crate::__private::*;
66
67			const NAME: &'static str = stringify!($name);
68
69			#[repr(C)]
70			pub struct LinkStoreContainer<T: $crate::EncodeLinkstore> {
71				name: [u8; NAME.len() + 1 + 1],
72				size: [u8; size_of::<usize>()],
73				padding: [u8; size_of::<usize>()],
74				pub value: VolatileWrapper<T>
75			}
76
77			#[cfg_attr(target_os = "macos", link_section = "__TEXT,.lnkstre")]
78			#[cfg_attr(not(target_os = "macos"), link_section = ".lnkstre")]
79			#[used]
80			static $name: LinkStoreContainer<$ty> = LinkStoreContainer {
81				name: {
82					let mut static_bytes = [0u8; NAME.len() + 1 + 1];
83					static_bytes[0] = MAGIC;
84
85					let bytes = NAME.as_bytes();
86					let mut i = 0;
87					while i < NAME.len() {
88						static_bytes[i + 1] = bytes[i];
89						i += 1;
90					}
91
92					static_bytes
93				},
94
95				size: size_of::<$ty>().to_le_bytes(),
96				padding: calc_padding::<LinkStoreContainer<$ty>, $ty>(NAME).to_le_bytes(),
97
98				value: VolatileWrapper::new($init)
99			};
100
101			/// Gets a the value contained in the linkstore.
102			///
103			/// ## Safety
104			///
105			/// This function is unsafe because malformed, corrupted or otherwise invalid data in the binary or unsound decoding implementations may cause undefined behavior.
106			pub unsafe fn get() -> &'static $ty {
107				debug_assert_eq!(align_of::<LinkStoreContainer<$ty>>(), align_of::<$ty>(), "Alignment error - this is a bug with linkstore!");
108				$name.value.get()
109			}
110		}
111	)+};
112}