use std::ffi::{CStr, c_void};
use crate::os::{Lookup, imp};
use crate::{Error, Library, Result, Symbol, SymbolType};
pub struct Symbols<const N: usize>(pub [*mut c_void; N]);
unsafe impl<const N: usize> Send for Symbols<N> {}
unsafe impl<const N: usize> Sync for Symbols<N> {}
pub fn resolve(lib: &Library, name: &CStr, optional: bool, function: bool) -> Result<*mut c_void> {
match imp::lookup(lib.handle(), name, function) {
Lookup::Found(addr) if !addr.is_null() => Ok(addr),
_ if optional => Ok(std::ptr::null_mut()),
Lookup::Found(_) => Err(Error::NullSymbol { name: name.into() }),
Lookup::NotFound(message) => Err(Error::SymbolNotFound {
name: name.into(),
message,
}),
}
}
pub fn symbol<O: ?Sized, F: SymbolType>(_owner: &O, addr: *mut c_void) -> Symbol<'_, F> {
Symbol::new(F::from_address(addr).expect("dlsym: null symbol"))
}
pub const fn sort_order<const N: usize>(names: &[&str]) -> [(u64, usize); N] {
assert!(names.len() == N);
let mut order = [(0u64, 0usize); N];
let mut i = 0;
while i < N {
order[i] = (fnv1a(names[i]), i);
i += 1;
}
let mut start = N / 2;
while start > 0 {
start -= 1;
sift_down(&mut order, start, N);
}
let mut end = N;
while end > 1 {
end -= 1;
let tmp = order[0];
order[0] = order[end];
order[end] = tmp;
sift_down(&mut order, 0, end);
}
order
}
const fn sift_down(order: &mut [(u64, usize)], mut root: usize, end: usize) {
loop {
let mut child = 2 * root + 1;
if child >= end {
return;
}
if child + 1 < end && order[child].0 < order[child + 1].0 {
child += 1;
}
if order[root].0 >= order[child].0 {
return;
}
let tmp = order[root];
order[root] = order[child];
order[child] = tmp;
root = child;
}
}
pub const fn index_of(names: &[&str], order: &[(u64, usize)], name: &str) -> usize {
let hash = fnv1a(name);
let (mut lo, mut hi) = (0, order.len());
while lo < hi {
let mid = lo + (hi - lo) / 2;
if order[mid].0 < hash {
lo = mid + 1;
} else {
hi = mid;
}
}
while lo < order.len() && order[lo].0 == hash {
if str_cmp(names[order[lo].1], name) == 0 {
return order[lo].1;
}
lo += 1;
}
panic!("dlsym: symbol name not found")
}
const fn fnv1a(s: &str) -> u64 {
let bytes = s.as_bytes();
let mut hash = 0xcbf2_9ce4_8422_2325u64;
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(0x0100_0000_01b3);
i += 1;
}
hash
}
const fn str_cmp(a: &str, b: &str) -> i32 {
let (a, b) = (a.as_bytes(), b.as_bytes());
let mut i = 0;
while i < a.len() && i < b.len() {
if a[i] != b[i] {
return if a[i] < b[i] { -1 } else { 1 };
}
i += 1;
}
if a.len() < b.len() {
-1
} else if a.len() > b.len() {
1
} else {
0
}
}
pub const fn link_name(s: &'static str) -> &'static CStr {
let bytes = s.as_bytes();
let bytes = if bytes.len() > 2 && bytes[0] == b'r' && bytes[1] == b'#' {
bytes.split_at(2).1
} else {
bytes
};
match CStr::from_bytes_with_nul(bytes) {
Ok(name) => name,
Err(_) => panic!("dlsym: symbol name contains a NUL byte"),
}
}
pub const fn assert_sync<T: ?Sized + Sync>() {}
pub fn bitfield_get(storage: &[u8], bit: u64, width: u32) -> u64 {
let mut value = 0u64;
for i in 0..u64::from(width) {
let b = bit + i;
if storage[(b / 8) as usize] & bit_mask(b) != 0 {
value |= 1 << value_bit(i, width);
}
}
value
}
pub fn bitfield_set(storage: &mut [u8], bit: u64, width: u32, value: u64) {
for i in 0..u64::from(width) {
let b = bit + i;
let byte = &mut storage[(b / 8) as usize];
if value & (1 << value_bit(i, width)) != 0 {
*byte |= bit_mask(b);
} else {
*byte &= !bit_mask(b);
}
}
}
const fn bit_mask(b: u64) -> u8 {
if cfg!(target_endian = "little") {
1 << (b % 8)
} else {
0x80 >> (b % 8)
}
}
const fn value_bit(i: u64, width: u32) -> u64 {
if cfg!(target_endian = "little") {
i
} else {
width as u64 - 1 - i
}
}
pub struct ObjectStorage<T> {
ptr: *mut T,
}
impl<T> ObjectStorage<T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let layout = std::alloc::Layout::new::<T>();
assert!(layout.size() != 0, "C++ objects are never zero-sized");
let ptr = unsafe { std::alloc::alloc(layout) }.cast::<T>();
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
ObjectStorage { ptr }
}
pub fn as_ptr(&self) -> *mut T {
self.ptr
}
pub fn into_raw(self) -> *mut T {
let ptr = self.ptr;
std::mem::forget(self);
ptr
}
pub unsafe fn free(ptr: *mut T) {
unsafe { std::alloc::dealloc(ptr.cast(), std::alloc::Layout::new::<T>()) };
}
}
impl<T> Drop for ObjectStorage<T> {
fn drop(&mut self) {
unsafe { Self::free(self.ptr) };
}
}
pub fn errno_error() -> std::io::Error {
#[cfg(not(any(windows, target_os = "fullrust")))]
{
std::io::Error::last_os_error()
}
#[cfg(target_os = "fullrust")]
{
std::io::Error::from_raw_os_error(imp::errno())
}
#[cfg(windows)]
{
unsafe extern "C" {
fn _errno() -> *mut std::ffi::c_int;
}
let location = unsafe { _errno() };
let errno = unsafe { *location };
std::io::Error::other(format!("errno {errno}"))
}
}