use std::{
fmt::{self, Display},
io,
path::{Path,PathBuf},
sync::atomic,
};
use core_extensions::prelude::*;
use libloading::{
Library as LibLoadingLibrary,
Symbol as LLSymbol,
};
use abi_stable_derive_lib::{
mangle_library_getter_ident,
mangle_initialize_globals_with_ident,
};
use crate::{
abi_stability::{
AbiInfoWrapper,
stable_abi_trait::SharedStableAbi,
},
globals::{self,InitializeGlobalsWithFn},
lazy_static_ref::LazyStaticRef,
prefix_type::PrefixTypeTrait,
version::{ParseVersionError, VersionNumber, VersionStrings},
utils::leak_value,
std_types::{RVec,RBoxError},
};
#[derive(Copy, Clone)]
pub struct Library {
path:&'static Path,
library: &'static LibLoadingLibrary,
}
#[derive(Debug,Copy,Clone,PartialEq,Eq,Ord,PartialOrd,Hash)]
pub enum LibrarySuffix{
NoSuffix,
Suffix,
}
impl Library {
pub fn get_library_path(
folder: &Path,
base_name: &str,
suffix:LibrarySuffix,
)->PathBuf{
let formatted:String;
let (prefix,extension) = match (cfg!(windows), cfg!(target_os="macos")) {
(false, false) => ("lib","so"),
(false, true) => ("lib","dylib"),
(true, false) => ("","dll"),
_ => unreachable!("system is both windows and mac"),
};
let is_64_bits =
cfg!(any(x86_64, powerpc64, aarch64)) || ::std::mem::size_of::<usize>() == 8;
let bits = if is_64_bits { "64" } else { "32" };
let maybe_suffixed_name=match suffix {
LibrarySuffix::NoSuffix=>{
formatted=format!("{}-{}", base_name, bits);
&*formatted
}
LibrarySuffix::Suffix=>{
base_name
}
};
let name=format!("{}{}.{}",prefix, maybe_suffixed_name, extension);
folder.join(name)
}
pub fn load_at(full_path:&Path) -> Result<&'static Self,LibraryError> {
LibLoadingLibrary::new(full_path)
.map_err(|io|{
LibraryError::OpenError{ path:full_path.to_owned(), io }
})?
.piped(leak_value)
.piped(|library| Self { path:leak_value(full_path.to_owned()), library })
.piped(leak_value)
.piped(Ok)
}
pub fn load_in(
folder: &Path,
base_name: &str,
suffix:LibrarySuffix,
) -> Result<&'static Self,LibraryError> {
let path=Self::get_library_path(folder,base_name,suffix);
Self::load_at(&path)
}
unsafe fn get_static<T>(
&self,
symbol_name: &[u8]
) -> Result<LLSymbol<'static,T>,LibraryError>
where T:'static
{
match self.library.get::<T>(symbol_name) {
Ok(symbol)=>Ok(symbol),
Err(io)=>{
let symbol=symbol_name.to_owned();
Err(LibraryError::GetSymbolError{
library:self.path.clone(),
symbol,
io
})
}
}
}
}
pub type LibraryGetterFn<T>=
extern "C" fn() -> WithLayout<T>;
pub trait RootModule: Sized+SharedStableAbi {
fn raw_library_ref()->&'static LazyStaticRef<Library>;
const BASE_NAME: &'static str;
const NAME: &'static str;
const VERSION_STRINGS: VersionStrings;
const LOADER_FN: &'static str;
fn get_library_path(directory:&Path)-> PathBuf {
let base_name=Self::BASE_NAME;
Library::get_library_path(directory, base_name,LibrarySuffix::Suffix)
}
fn load_from_library_in(directory: &Path) -> Result<&'static Self, LibraryError>{
Self::raw_library_ref()
.try_init(||{
let path=Self::get_library_path(directory);
Library::load_at(&path)
})
.and_then(Self::load_with)
}
fn load_from_library_at(full_path: &Path) -> Result<&'static Self, LibraryError>{
Self::raw_library_ref()
.try_init(|| Library::load_at(full_path) )
.and_then(Self::load_with)
}
fn load_with(raw_library:&'static Library)->Result<&'static Self,LibraryError>{
let library_getter: LLSymbol<'static,LibraryGetterFn<Self>> =unsafe{
let mut mangled=mangle_library_getter_ident(Self::LOADER_FN);
mangled.push('\0');
raw_library.get_static::<LibraryGetterFn<Self>>(mangled.as_bytes())?
};
let initialize_globals_with: LLSymbol<'static,InitializeGlobalsWithFn>=unsafe{
let mut mangled=mangle_initialize_globals_with_ident(Self::LOADER_FN);
mangled.push('\0');
raw_library.get_static::<InitializeGlobalsWithFn>(mangled.as_bytes())?
};
let globals=globals::initialized_globals();
initialize_globals_with(globals);
let items = library_getter();
let expected_version = Self::VERSION_STRINGS
.piped(VersionNumber::new)?;
let actual_version = items.version_strings().piped(VersionNumber::new)?;
if expected_version.major != actual_version.major ||
(expected_version.major==0) && expected_version.minor > actual_version.minor
{
return Err(LibraryError::IncompatibleVersionNumber {
library_name: Self::NAME,
expected_version,
actual_version,
});
}
items.check_layout()?
.initialization()
}
fn initialization(self: &'static Self) -> Result<&'static Self, LibraryError> {
Ok(self)
}
}
mod with_layout {
use super::*;
#[repr(C)]
#[derive(StableAbi)]
#[sabi(inside_abi_stable_crate)]
pub struct WithLayout <T:'static>{
magic_number: usize,
version_strings:VersionStrings,
layout: &'static AbiInfoWrapper,
value: &'static T,
}
impl<T> WithLayout<T> {
pub fn from_prefix(ref_:&'static T)->Self
where
T: RootModule,
{
Self {
magic_number: MAGIC_NUMBER,
version_strings:T::VERSION_STRINGS,
layout: <&T>::S_ABI_INFO,
value:ref_,
}
}
pub fn new<M>(value:M) -> Self
where
M:PrefixTypeTrait<Prefix=T>+'static,
T: RootModule,
{
value.leak_into_prefix()
.piped(Self::from_prefix)
}
pub fn version_strings(&self)->VersionStrings{
self.version_strings
}
pub fn check_layout(self) -> Result<&'static T, LibraryError>
where
T: RootModule,
{
if self.magic_number != MAGIC_NUMBER {
return Err(LibraryError::InvalidMagicNumber(self.magic_number));
}
(globals::initialized_globals().layout_checking)
(<&T>::S_ABI_INFO, self.layout)
.into_result()
.map_err(LibraryError::AbiInstability)?;
atomic::compiler_fence(atomic::Ordering::SeqCst);
Ok(self.value)
}
}
}
pub use self::with_layout::WithLayout;
const MAGIC_NUMBER: usize = 0xAB1_A_0002;
#[derive(Debug)]
pub enum LibraryError {
OpenError{
path:PathBuf,
io:io::Error,
},
GetSymbolError{
library:&'static Path,
symbol:Vec<u8>,
io:io::Error,
},
ParseVersionError(ParseVersionError),
IncompatibleVersionNumber {
library_name: &'static str,
expected_version: VersionNumber,
actual_version: VersionNumber,
},
AbiInstability(RBoxError),
InvalidMagicNumber(usize),
Many(RVec<Self>),
}
impl From<ParseVersionError> for LibraryError {
fn from(v: ParseVersionError) -> LibraryError {
LibraryError::ParseVersionError(v)
}
}
impl Display for LibraryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\n")?;
match self {
LibraryError::OpenError{path,io} => writeln!(
f,
"Could not open library at:\n\t{}\nbecause:\n\t{}",
path.display(),io
),
LibraryError::GetSymbolError{library,symbol,io} => writeln!(
f,
"Could load symbol:\n\t{}\nin library:\n\t{}\nbecause:\n\t{}",
String::from_utf8_lossy(symbol),
library.display(),
io
),
LibraryError::ParseVersionError(x) => fmt::Display::fmt(x, f),
LibraryError::IncompatibleVersionNumber {
library_name,
expected_version,
actual_version,
} => writeln!(
f,
"\n'{}' library version mismatch:\nuser:{}\nlibrary:{}",
library_name, expected_version, actual_version,
),
LibraryError::AbiInstability(x) => fmt::Display::fmt(x, f),
LibraryError::InvalidMagicNumber(found) => write!(
f,
"magic number used to load a library was {},when this library expected {}",
found, MAGIC_NUMBER,
),
LibraryError::Many(list)=>{
for e in list {
Display::fmt(e,f)?;
}
Ok(())
}
}?;
f.write_str("\n")?;
Ok(())
}
}
impl ::std::error::Error for LibraryError {}