vkobject_rs/
common.rs

1
2use std::{
3	any::Any,
4	collections::hash_map::DefaultHasher,
5	env,
6	ffi::CString,
7	fmt::Debug,
8	fs::{read, write},
9	hash::{Hash, Hasher},
10	io,
11	iter::FromIterator,
12	mem::{forget, size_of, size_of_val},
13	ops::{Deref, DerefMut},
14	slice,
15	path::PathBuf,
16	thread::sleep,
17	time::Duration,
18	vec::IntoIter,
19};
20use rand::prelude::*;
21
22/// A structure to hold owned C-type strings for ffi usages.
23#[derive(Debug)]
24pub struct CStringArray {
25	strings: Vec<CString>,
26	cstrarr: Vec<*const i8>,
27}
28
29impl CStringArray {
30	/// Create a new `CStringArray` from an array of `&str` or `String`.
31	pub fn new<T>(input: &[T]) -> Self
32	where
33		T: Clone + Into<Vec<u8>> {
34		let strings: Vec<CString> = input.iter().map(|s|CString::new(s.clone()).unwrap()).collect();
35		let cstrarr: Vec<*const i8> = strings.iter().map(|s|s.as_ptr()).collect();
36		Self {
37			strings,
38			cstrarr,
39		}
40	}
41
42	/// Get the number of the strings
43	pub fn len(&self) -> usize {
44		self.strings.len()
45	}
46
47	/// Get is empty
48	pub fn is_empty(&self) -> bool {
49		self.strings.is_empty()
50	}
51
52	/// Get the pointer to the string list.
53	pub fn as_ptr(&self) -> *const *const i8 {
54		self.cstrarr.as_ptr()
55	}
56}
57
58impl<'a> FromIterator<&'a String> for CStringArray {
59	fn from_iter<T>(input: T) -> Self
60	where
61		T: IntoIterator<Item = &'a String> {
62		let strings: Vec<CString> = input.into_iter().map(|s|CString::new(s.clone()).unwrap()).collect();
63		let cstrarr: Vec<*const i8> = strings.iter().map(|s|s.as_ptr()).collect();
64		Self {
65			strings,
66			cstrarr,
67		}
68	}
69}
70
71impl Clone for CStringArray {
72	fn clone(&self) -> Self {
73		let strings = self.strings.clone();
74		let cstrarr: Vec<*const i8> = strings.iter().map(|s|s.as_ptr()).collect();
75		Self {
76			strings,
77			cstrarr,
78		}
79	}
80}
81
82/// Sleep random nanoseconds, the nanosecond value could be bigger than a second.
83pub fn random_sleep<R: Rng>(max_nanos: u64, rng: &mut R) {
84	let sleep_nanos = rng.random_range(0..max_nanos);
85	let secs = sleep_nanos / 1000000000;
86	let nanos = (sleep_nanos % 1000000000) as u32;
87	sleep(Duration::new(secs, nanos));
88}
89
90/// The error returned from the spin function
91#[derive(Debug)]
92pub enum SpinError<E: Debug> {
93	/// The spin function could not acquire the lock
94	SpinFail,
95
96	/// The spin function failed for other reasons
97	OtherError(E)
98}
99
100pub fn spin_work_with_exp_backoff<T, E: Debug, W: FnMut() -> Result<T, SpinError<E>>>(mut spin_func: W, max_sleep_nanos: u64) -> Result<T, E> {
101	let mut sleep_nanos = 1000;
102	let mut rng = SmallRng::from_os_rng();
103	loop {
104		match spin_func() {
105			Ok(r) => return Ok(r),
106			Err(e) => match e {
107				SpinError::SpinFail => {}
108				SpinError::OtherError(e) => return Err(e),
109			}
110		}
111		random_sleep(sleep_nanos, &mut rng);
112		if sleep_nanos < max_sleep_nanos {
113			sleep_nanos = (sleep_nanos * 3) >> 1;
114		}
115	}
116}
117
118/// The resource guard ensures no resource is leaking. On `drop()`, the `destroyer` is used to free the `resource`.
119#[derive(Debug)]
120pub struct ResourceGuard<R, D: Fn(&R)> {
121	resource: Option<R>,
122	destroyer: D,
123}
124
125impl<R, D: Fn(&R)> ResourceGuard<R, D> {
126	/// Create the `ResourceGuard`, `resource` is the resource you want to handle, and `destroyer` is the closure you write to free the resource.
127	pub fn new(resource: R, destroyer: D) -> Self {
128		Self {
129			resource: Some(resource),
130			destroyer,
131		}
132	}
133
134	/// Release the resource from this structure, so it won't be freed when the guard is `drop()`ing.
135	pub fn release(mut self) -> R {
136		self.resource.take().unwrap()
137	}
138}
139
140impl<R, D: Fn(&R)> Deref for ResourceGuard<R, D> {
141	type Target = R;
142	fn deref(&self) -> &R {
143		self.resource.as_ref().unwrap()
144	}
145}
146
147impl<R, D: Fn(&R)> DerefMut for ResourceGuard<R, D> {
148	fn deref_mut(&mut self) -> &mut R {
149		self.resource.as_mut().unwrap()
150	}
151}
152
153impl<R, D: Fn(&R)> Drop for ResourceGuard<R, D> {
154	fn drop(&mut self) {
155		if let Some(resource) = &self.resource {
156			(self.destroyer)(resource);
157		}
158	}
159}
160
161/// Generate a temp file path for cache, the filename is hashed
162pub fn get_hashed_cache_file_path(cache_usage: &str, extension: Option<&str>) -> PathBuf {
163	let exe_path = env::current_exe().unwrap_or(PathBuf::from(env!("CARGO_PKG_NAME")));
164	let mut hasher = DefaultHasher::new();
165	exe_path.to_string_lossy().to_string().hash(&mut hasher);
166	cache_usage.hash(&mut hasher);
167	let hash = hasher.finish();
168	let mut path = std::env::temp_dir();
169	path.push(format!("{hash}"));
170	path.set_extension(extension.unwrap_or("tmp"));
171	path
172}
173
174pub fn load_cache<T: Clone + Copy + Sized>(cache_usage: &str, extension: Option<&str>) -> io::Result<Vec<T>> {
175	let path = get_hashed_cache_file_path(cache_usage, extension);
176	let mut data = read(&path)?;
177	let item_size = size_of::<T>();
178	let ptr = data.as_mut_ptr();
179	let len = data.len() / item_size;
180	let capacity = data.capacity() / item_size;
181	unsafe {
182		forget(data);
183		Ok(Vec::from_raw_parts(ptr as *mut T, len, capacity))
184	}
185}
186
187pub fn save_cache<T: Clone + Copy + Sized>(cache_usage: &str, extension: Option<&str>, data: &[T]) -> io::Result<()> {
188	let path = get_hashed_cache_file_path(cache_usage, extension);
189	let ptr = data.as_ptr() as *const u8;
190	let len = size_of_val(data);
191	let data: &[u8] = unsafe {slice::from_raw_parts(ptr, len)};
192	write(&path, data)
193}
194
195/// A helper trait for `struct_iterable` crate, give a struct that derives `Iterable` the ability to iterate the member's offset and size
196pub trait IterableDataAttrib {
197	/// A member that allows to get the iterator, this function is to be implemented
198	fn iter_members(&self) -> IntoIter<(&'static str, &dyn Any)>;
199
200	/// Get an iterator that could give you the name, offset, and size of the struct members
201	fn iter_members_data_attribs(&self) -> IntoIter<(&'static str, usize, usize)> {
202		let mut instance_ptr = None;
203		let data_attribs: Vec<_> = self.iter_members().map(|(name, value)|{
204			let value_ptr = value as *const dyn Any as *const u8;
205			if instance_ptr.is_none() {
206				instance_ptr = Some(value_ptr)
207			}
208			let offset = unsafe { value_ptr.offset_from(instance_ptr.unwrap()) } as usize;
209			(name, offset, size_of_val(value))
210		}).collect();
211		data_attribs.into_iter()
212	}
213}