1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{marker, mem};
use std::ffi::CString;
use std::os::raw::{c_char, c_void};

pub struct Weak<F> {
	name: &'static str,
	addr: AtomicUsize,
	_marker: marker::PhantomData<F>,
}

impl<F> Weak<F> {
	pub const fn new(name: &'static str) -> Weak<F> {
		Weak {
			name,
			addr: AtomicUsize::new(1),
			_marker: marker::PhantomData,
		}
	}
	
	pub fn get(&self) -> Option<&F> {
		assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
		unsafe {
			if self.addr.load(Ordering::SeqCst) == 1 {
				self.addr.store(fetch(self.name), Ordering::SeqCst);
			}
			if self.addr.load(Ordering::SeqCst) == 0 {
				None
			} else {
				mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
			}
		}
	}
}

unsafe fn fetch(name: &str) -> usize {
	let name = match CString::new(name) {
		Ok(cstr) => cstr,
		Err(..) => return 0,
	};
	find_symbol(name.as_ptr())
}

#[cfg(unix)]
unsafe fn find_symbol(name: *const c_char) -> usize {
	extern "C" {
		pub fn dlsym(
			handle: *mut c_void,
			symbol: *const c_char,
		) -> *mut c_void;
	}
	pub const RTLD_DEFAULT: *mut c_void = 0i64 as *mut c_void;
	
	dlsym(RTLD_DEFAULT, name) as usize
}

#[cfg(windows)]
unsafe fn find_symbol(name: *const c_char) -> usize {
	use std::ptr::null;
	use kernel32::{GetModuleHandle, GetProcAddress};
	GetProcAddress(GetModuleHandleA(null()), name) as usize
}