#![doc(html_root_url = "https://docs.rs/inventory/0.3.12")]
#![no_std]
#![allow(
clippy::doc_markdown,
clippy::empty_enum,
clippy::expl_impl_clone_on_copy,
clippy::let_underscore_untyped,
clippy::let_unit_value,
clippy::must_use_candidate,
clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
)]
#[doc(hidden)]
pub extern crate core;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
#[doc(hidden)]
pub struct Registry {
head: AtomicPtr<Node>,
}
#[doc(hidden)]
pub struct Node {
pub value: &'static dyn ErasedNode,
pub next: UnsafeCell<Option<&'static Node>>,
}
unsafe impl Sync for Node {}
#[doc(hidden)]
pub trait ErasedNode: Sync {
unsafe fn submit(&self, node: &'static Node);
}
impl<T: Collect> ErasedNode for T {
unsafe fn submit(&self, node: &'static Node) {
T::registry().submit(node);
}
}
pub trait Collect: Sync + Sized + 'static {
#[doc(hidden)]
fn registry() -> &'static Registry;
}
impl Registry {
pub const fn new() -> Self {
Registry {
head: AtomicPtr::new(ptr::null_mut()),
}
}
unsafe fn submit(&'static self, new: &'static Node) {
let mut head = self.head.load(Ordering::Relaxed);
loop {
*new.next.get() = head.as_ref();
let new_ptr = new as *const Node as *mut Node;
match self
.head
.compare_exchange(head, new_ptr, Ordering::Release, Ordering::Relaxed)
{
Ok(_) => return,
Err(prev) => head = prev,
}
}
}
}
macro_rules! document_iter {
($iter:item) => {
$iter
};
}
#[cfg(doc)]
document_iter! {
#[allow(non_camel_case_types)]
pub struct iter<T>;
}
#[cfg(not(doc))]
mod void_iter {
enum Void {}
#[repr(C, packed)]
pub struct Iter<T>([*const T; 0], Void);
unsafe impl<T> Send for Iter<T> {}
unsafe impl<T> Sync for Iter<T> {}
}
#[cfg(not(doc))]
mod value_iter {
pub use crate::iter::iter;
}
#[cfg(not(doc))]
document_iter! {
#[allow(non_camel_case_types)]
pub enum iter<T> {
__Phantom(void_iter::Iter<T>),
iter,
}
}
#[cfg(not(doc))]
#[doc(hidden)]
pub use crate::value_iter::*;
const ITER: () = {
fn into_iter<T: Collect>() -> Iter<T> {
let head = T::registry().head.load(Ordering::Acquire);
Iter {
node: unsafe { head.as_ref() },
marker: PhantomData,
}
}
impl<T: Collect> IntoIterator for iter<T> {
type Item = &'static T;
type IntoIter = Iter<T>;
fn into_iter(self) -> Self::IntoIter {
into_iter()
}
}
#[doc(hidden)]
impl<T: Collect> Deref for iter<T> {
type Target = fn() -> Iter<T>;
fn deref(&self) -> &Self::Target {
&(into_iter as fn() -> Iter<T>)
}
}
#[derive(Clone)]
pub struct Iter<T: 'static> {
node: Option<&'static Node>,
marker: PhantomData<T>,
}
impl<T: 'static> Iterator for Iter<T> {
type Item = &'static T;
fn next(&mut self) -> Option<Self::Item> {
let node = self.node?;
unsafe {
let value_ptr = (node.value as *const dyn ErasedNode).cast::<T>();
self.node = *node.next.get();
Some(&*value_ptr)
}
}
}
};
#[macro_export]
macro_rules! collect {
($ty:ty) => {
impl $crate::Collect for $ty {
#[inline]
fn registry() -> &'static $crate::Registry {
static REGISTRY: $crate::Registry = $crate::Registry::new();
®ISTRY
}
}
};
}
#[macro_export]
macro_rules! submit {
($($value:tt)*) => {
$crate::__do_submit! {
{ $($value)* }
{ $($value)* }
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __do_submit {
(used={ $($used:tt)+ } $($value:tt)*) => {
#[allow(non_upper_case_globals)]
const _: () = {
static __INVENTORY: $crate::Node = $crate::Node {
value: &{ $($value)* },
next: $crate::core::cell::UnsafeCell::new($crate::core::option::Option::None),
};
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
unsafe extern "C" fn __ctor() {
unsafe { $crate::ErasedNode::submit(__INVENTORY.value, &__INVENTORY) }
}
$($used)+
#[cfg_attr(
any(
target_os = "linux",
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
),
link_section = ".init_array",
)]
#[cfg_attr(any(target_os = "macos", target_os = "ios"), link_section = "__DATA,__mod_init_func")]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
};
({ #![used($($used:tt)+)] $($value:tt)* } { $pound:tt $bang:tt $brackets:tt $($dup:tt)* }) => {
$crate::__do_submit! {
used={ $pound $brackets }
$($value)*
}
};
({ $($value:tt)* } { $($dup:tt)* }) => {
$crate::__do_submit! {
used={ #[used] }
$($value)*
}
};
}
#[allow(dead_code)]
fn unused() {
let () = ITER;
}