#![doc = concat!("```rust\n", include_str!("../examples/iterable.rs"), "\n```\n")]
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
const UNSET_INDEX: usize = 0;
pub struct Ref<T: 'static> {
next: AtomicPtr<Ref<T>>,
index: AtomicUsize,
value: T,
}
impl<T> Ref<T> {
pub const fn new(value: T) -> Self {
Self {
next: AtomicPtr::new(core::ptr::null_mut()),
index: AtomicUsize::new(UNSET_INDEX),
value,
}
}
pub fn offset_of(this: &Self) -> usize {
this.loaded_index().saturating_sub(1)
}
#[inline]
fn loaded_index(&self) -> usize {
let index = self.index.load(Ordering::Acquire);
if index == UNSET_INDEX {
panic!("node index is unset; was this `Ref` submitted to its collection?");
}
index
}
}
impl<T> ::core::ops::Deref for Ref<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> ::core::fmt::Debug for Ref<T>
where
T: ::core::fmt::Debug,
{
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
(**self).fmt(f)
}
}
#[doc(hidden)]
pub struct __ScatteredIterableState<T: 'static> {
head: AtomicPtr<Ref<T>>,
len: AtomicUsize,
}
impl<T> __ScatteredIterableState<T> {
#[doc(hidden)]
pub const fn new() -> Self {
Self {
head: AtomicPtr::new(core::ptr::null_mut()),
len: AtomicUsize::new(0),
}
}
}
#[doc(hidden)]
pub fn submit<T: 'static>(state: &__ScatteredIterableState<T>, node: &'static Ref<T>) {
loop {
let head = state.head.load(Ordering::Relaxed);
node.next.store(head, Ordering::Relaxed);
if state
.head
.compare_exchange_weak(
head,
core::ptr::from_ref(node).cast_mut(),
Ordering::Release,
Ordering::Relaxed,
)
.is_ok()
{
let index = state.len.fetch_add(1, Ordering::Relaxed);
node.index.store(index + 1, Ordering::Release);
return;
}
}
}
#[inline]
#[allow(unsafe_code)]
fn remaining_from_node<T>(node: *const Ref<T>) -> usize {
if node.is_null() {
return 0;
}
unsafe { (*node).loaded_index() }
}
pub struct Iter<'a, T: 'static> {
current: *const Ref<T>,
remaining: usize,
_marker: ::core::marker::PhantomData<&'a T>,
}
impl<'a, T: 'static> Iterator for Iter<'a, T> {
type Item = &'a T;
#[allow(unsafe_code)]
fn next(&mut self) -> Option<Self::Item> {
if self.current.is_null() {
return None;
}
let node = unsafe { &*self.current };
let _ = node.loaded_index();
let item = &node.value;
self.current = node.next.load(Ordering::Acquire);
self.remaining = remaining_from_node(self.current);
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<T: 'static> ExactSizeIterator for Iter<'_, T> {
fn len(&self) -> usize {
self.remaining
}
}
#[doc = concat!("```rust\n", include_str!("../examples/iterable.rs"), "\n```\n")]
pub struct ScatteredIterable<T: 'static> {
state: &'static __ScatteredIterableState<T>,
}
impl<T: 'static> ScatteredIterable<T> {
#[doc(hidden)]
pub const fn new(state: &'static __ScatteredIterableState<T>) -> Self {
Self { state }
}
pub fn len(&self) -> usize {
self.state.len.load(Ordering::Acquire)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T: 'static> ::core::iter::IntoIterator for &'static ScatteredIterable<T> {
type Item = &'static T;
type IntoIter = Iter<'static, T>;
fn into_iter(self) -> Self::IntoIter {
let current = self.state.head.load(Ordering::Acquire);
Iter {
remaining: remaining_from_node(current),
current,
_marker: ::core::marker::PhantomData,
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __iterable_state {
($name:ident) => {
$crate::__support::ident_concat!( () (__ $name __state__) () )
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __iterable {
(gather $vis:vis $name:ident: $ty:ty) => {
$crate::__iterable!(@gather $vis static $name: ScatteredIterable<$ty>;);
};
(@gather $(#[$meta:meta])* $vis:vis static $name:ident: $collection:ident < $ty:ty >;) => {
$crate::__support::ident_concat!((#[doc(hidden)] #[macro_export] macro_rules!) (__ $name __iterable_private_macro__) ({
($passthru:tt) => {
$crate::__iterable!(@scatter [$name] $passthru);
};
}));
$crate::__support::ident_concat!((#[doc(hidden)] $vis use) (__ $name __iterable_private_macro__) (as $name;));
$crate::__support::ident_concat!((static ) (__ $name __state__) (: $crate::iterable::__ScatteredIterableState<$ty> = $crate::iterable::__ScatteredIterableState::new();));
$(#[$meta])*
$vis static $name: $collection<$ty> = {
$crate::iterable::ScatteredIterable::new(&$crate::__iterable_state!($name))
};
};
(@scatter [$collection_name:ident] ([$($meta:tt)*] => $(#[$imeta:meta])* $vis:vis static $name:ident: $ty:ty = $expr:expr;)) => {
$(#[$imeta])*
$vis static $name: $crate::iterable::Ref<$ty> = $crate::iterable::Ref::new($expr);
$crate::__support::ctor::declarative::ctor!(
#[ctor(unsafe, anonymous, priority = 0)]
fn __iterable_submit() {
$crate::iterable::submit(&$crate::__iterable_state!($($meta)*), &$name);
}
);
};
}
#[cfg(all(test, not(miri)))]
mod tests {
use crate::iterable::{Ref, ScatteredIterable};
__iterable!(gather pub TEST_ITERABLE: u32);
__iterable!(@scatter [TEST_ITERABLE] ([TEST_ITERABLE] => pub static ITERABLE_ITEM_A: u32 = 1;));
__iterable!(@scatter [TEST_ITERABLE] ([TEST_ITERABLE] => pub static ITERABLE_ITEM_B: u32 = 3;));
__iterable!(@scatter [TEST_ITERABLE] ([TEST_ITERABLE] => pub static ITERABLE_ITEM_C: u32 = 2;));
fn offset_for_value(value: u32) -> usize {
match value {
1 => Ref::offset_of(&ITERABLE_ITEM_A),
3 => Ref::offset_of(&ITERABLE_ITEM_B),
2 => Ref::offset_of(&ITERABLE_ITEM_C),
_ => panic!("unexpected value: {value}"),
}
}
#[test]
fn test_scattered_iterable() {
assert_eq!(TEST_ITERABLE.len(), 3);
assert!(!TEST_ITERABLE.is_empty());
assert_eq!(*ITERABLE_ITEM_A, 1);
assert_eq!(*ITERABLE_ITEM_B, 3);
assert_eq!(*ITERABLE_ITEM_C, 2);
let mut scatter_offsets = [
Ref::offset_of(&ITERABLE_ITEM_A),
Ref::offset_of(&ITERABLE_ITEM_B),
Ref::offset_of(&ITERABLE_ITEM_C),
];
scatter_offsets.sort();
assert_eq!(scatter_offsets, [0, 1, 2]);
let total = TEST_ITERABLE.len();
let mut it = (&TEST_ITERABLE).into_iter();
assert_eq!(it.len(), total);
let mut collected_offsets = Vec::new();
for step in 0..total {
let len_before = it.len();
let value = it.next().copied().expect("item");
let offset = offset_for_value(value);
assert_eq!(
len_before,
offset + 1,
"ExactSizeIterator::len before consuming offset {offset}"
);
assert_eq!(it.len(), total - step - 1);
collected_offsets.push(offset);
}
assert_eq!(it.len(), 0);
assert_eq!(it.next(), None);
collected_offsets.sort();
assert_eq!(collected_offsets, scatter_offsets);
let items: Vec<u32> = (&TEST_ITERABLE).into_iter().copied().collect();
assert_eq!(items.len(), total);
assert!(items.contains(&1));
assert!(items.contains(&2));
assert!(items.contains(&3));
}
}