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) -> Result<*mut c_void> {
match imp::lookup(lib.handle(), name) {
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]) -> [usize; N] {
assert!(names.len() == N);
let mut order = [0; N];
let mut i = 0;
while i < N {
order[i] = i;
i += 1;
}
let mut start = N / 2;
while start > 0 {
start -= 1;
sift_down(names, &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(names, &mut order, 0, end);
}
order
}
const fn sift_down(names: &[&str], order: &mut [usize], mut root: usize, end: usize) {
loop {
let mut child = 2 * root + 1;
if child >= end {
return;
}
if child + 1 < end && str_cmp(names[order[child]], names[order[child + 1]]) < 0 {
child += 1;
}
if str_cmp(names[order[root]], names[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: &[usize], name: &str) -> usize {
let (mut lo, mut hi) = (0, order.len());
while lo < hi {
let mid = lo + (hi - lo) / 2;
let c = str_cmp(names[order[mid]], name);
if c == 0 {
return order[mid];
} else if c < 0 {
lo = mid + 1;
} else {
hi = mid;
}
}
panic!("dlsym: symbol name not found")
}
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
}
}