use arrayvec::ArrayVec;
use bevy_platform::{
cell::SyncUnsafeCell,
prelude::{Box, Vec},
sync::{
atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, Ordering},
Arc,
},
};
use core::mem::ManuallyDrop;
use log::warn;
use nonmax::NonMaxU32;
use crate::query::DebugCheckedUnwrap;
use super::{Entity, EntityIndex, EntitySetIterator};
struct Slot {
inner: SyncUnsafeCell<Entity>,
}
impl Slot {
const fn empty() -> Self {
let source = Entity::PLACEHOLDER;
Self {
inner: SyncUnsafeCell::new(source),
}
}
#[inline]
const unsafe fn set_entity(&self, entity: Entity) {
unsafe {
self.inner.get().write(entity);
}
}
#[inline]
const unsafe fn get_entity(&self) -> Entity {
unsafe { self.inner.get().read() }
}
}
struct Chunk {
first: AtomicPtr<Slot>,
}
impl Chunk {
const fn new() -> Self {
Self {
first: AtomicPtr::new(core::ptr::null_mut()),
}
}
#[inline]
unsafe fn get(&self, index: u32) -> Entity {
let head = self.first.load(Ordering::Relaxed);
let target = unsafe { &*head.add(index as usize) };
unsafe { target.get_entity() }
}
#[inline]
unsafe fn get_slice(&self, index: u32, ideal_len: u32, chunk_capacity: u32) -> &[Slot] {
let after_index_slice_len = chunk_capacity - index;
let len = after_index_slice_len.min(ideal_len) as usize;
let head = self.first.load(Ordering::Relaxed);
unsafe { core::slice::from_raw_parts(head.add(index as usize), len) }
}
#[inline]
unsafe fn set(&self, index: u32, entity: Entity, chunk_capacity: u32) {
let ptr = self.first.load(Ordering::Relaxed);
let head = if ptr.is_null() {
unsafe { self.init(chunk_capacity) }
} else {
ptr
};
let target = unsafe { &*head.add(index as usize) };
unsafe {
target.set_entity(entity);
}
}
#[cold]
unsafe fn init(&self, chunk_capacity: u32) -> *mut Slot {
let mut buff = ManuallyDrop::new(Vec::new());
buff.reserve_exact(chunk_capacity as usize);
buff.resize_with(chunk_capacity as usize, Slot::empty);
let ptr = buff.as_mut_ptr();
self.first.store(ptr, Ordering::Relaxed);
ptr
}
unsafe fn dealloc(&mut self, chunk_capacity: u32) {
let to_drop = *self.first.get_mut();
if !to_drop.is_null() {
unsafe {
Vec::from_raw_parts(to_drop, chunk_capacity as usize, chunk_capacity as usize);
}
}
}
}
struct FreeBuffer([Chunk; Self::NUM_CHUNKS as usize]);
impl FreeBuffer {
const NUM_CHUNKS: u32 = 24;
const NUM_SKIPPED: u32 = u32::BITS - Self::NUM_CHUNKS;
const fn new() -> Self {
Self([const { Chunk::new() }; Self::NUM_CHUNKS as usize])
}
#[inline]
const fn capacity_of_chunk(chunk_index: u32) -> u32 {
let corrected = if chunk_index == 0 { 1 } else { chunk_index };
let corrected = corrected + Self::NUM_SKIPPED;
1 << corrected
}
#[inline]
const fn index_info(full_index: u32) -> (u32, u32, u32) {
let chunk_index = (Self::NUM_CHUNKS - 1).saturating_sub(full_index.leading_zeros());
let chunk_capacity = Self::capacity_of_chunk(chunk_index);
let index_in_chunk = full_index & !chunk_capacity;
(chunk_index, index_in_chunk, chunk_capacity)
}
#[inline]
fn index_in_chunk(&self, full_index: u32) -> (&Chunk, u32, u32) {
let (chunk_index, index_in_chunk, chunk_capacity) = Self::index_info(full_index);
let chunk = unsafe { self.0.get_unchecked(chunk_index as usize) };
(chunk, index_in_chunk, chunk_capacity)
}
unsafe fn get(&self, full_index: u32) -> Entity {
let (chunk, index, _) = self.index_in_chunk(full_index);
unsafe { chunk.get(index) }
}
#[inline]
unsafe fn set(&self, full_index: u32, entity: Entity) {
let (chunk, index, chunk_capacity) = self.index_in_chunk(full_index);
unsafe { chunk.set(index, entity, chunk_capacity) }
}
#[inline]
unsafe fn iter(&self, indices: core::ops::Range<u32>) -> FreeBufferIterator<'_> {
FreeBufferIterator {
buffer: self,
future_buffer_indices: indices,
current_chunk_slice: [].iter(),
}
}
}
impl Drop for FreeBuffer {
fn drop(&mut self) {
for index in 0..Self::NUM_CHUNKS {
let capacity = Self::capacity_of_chunk(index);
unsafe { self.0[index as usize].dealloc(capacity) };
}
}
}
struct FreeBufferIterator<'a> {
buffer: &'a FreeBuffer,
current_chunk_slice: core::slice::Iter<'a, Slot>,
future_buffer_indices: core::ops::Range<u32>,
}
impl<'a> Iterator for FreeBufferIterator<'a> {
type Item = Entity;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(found) = self.current_chunk_slice.next() {
return Some(unsafe { found.get_entity() });
}
let still_need = self.future_buffer_indices.len() as u32;
if still_need == 0 {
return None;
}
let next_index = self.future_buffer_indices.start;
let (chunk, index, chunk_capacity) = self.buffer.index_in_chunk(next_index);
let slice = unsafe { chunk.get_slice(index, still_need, chunk_capacity) };
self.future_buffer_indices.start += slice.len() as u32;
self.current_chunk_slice = slice.iter();
let next = unsafe { self.current_chunk_slice.next().debug_checked_unwrap() };
Some(unsafe { next.get_entity() })
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.future_buffer_indices.len() + self.current_chunk_slice.len();
(len, Some(len))
}
}
impl<'a> ExactSizeIterator for FreeBufferIterator<'a> {}
impl<'a> core::iter::FusedIterator for FreeBufferIterator<'a> {}
#[derive(Clone, Copy)]
struct FreeCountState(u64);
impl FreeCountState {
const DISABLING_BIT: u64 = 1 << 33;
const LENGTH_MASK: u64 = (1 << 32) | u32::MAX as u64;
const LENGTH_0: u64 = 1 << 32;
const GENERATION_LEAST_BIT: u64 = 1 << 34;
const fn new_zero_len() -> Self {
Self(Self::LENGTH_0)
}
#[inline]
const fn length(self) -> u32 {
let unsigned_length = self.0 & Self::LENGTH_MASK;
unsigned_length.saturating_sub(Self::LENGTH_0) as u32
}
#[inline]
const fn is_disabled(self) -> bool {
(self.0 & Self::DISABLING_BIT) > 0
}
#[inline]
const fn with_length(self, length: u32) -> Self {
let length = length as u64 | Self::LENGTH_0;
Self(self.0 & !Self::LENGTH_MASK | length)
}
#[inline]
const fn encode_pop(num: u32) -> u64 {
let subtract_length = num as u64;
subtract_length | Self::GENERATION_LEAST_BIT
}
#[inline]
const fn pop(self, num: u32) -> Self {
Self(self.0.wrapping_sub(Self::encode_pop(num)))
}
}
struct FreeCount(AtomicU64);
impl FreeCount {
const fn new_zero_len() -> Self {
Self(AtomicU64::new(FreeCountState::new_zero_len().0))
}
#[inline]
fn state(&self, order: Ordering) -> FreeCountState {
FreeCountState(self.0.load(order))
}
#[inline]
fn pop_for_state(&self, num: u32, order: Ordering) -> FreeCountState {
let to_sub = FreeCountState::encode_pop(num);
let raw = self.0.fetch_sub(to_sub, order);
FreeCountState(raw)
}
#[inline]
fn disable_len_for_state(&self, order: Ordering) -> FreeCountState {
FreeCountState(self.0.fetch_or(FreeCountState::DISABLING_BIT, order))
}
#[inline]
fn set_state_risky(&self, state: FreeCountState, order: Ordering) {
self.0.store(state.0, order);
}
#[inline]
fn try_set_state(
&self,
expected_current_state: FreeCountState,
target_state: FreeCountState,
success: Ordering,
failure: Ordering,
) -> Result<(), FreeCountState> {
match self
.0
.compare_exchange(expected_current_state.0, target_state.0, success, failure)
{
Ok(_) => Ok(()),
Err(val) => Err(FreeCountState(val)),
}
}
}
struct FreeList {
buffer: FreeBuffer,
len: FreeCount,
}
impl FreeList {
fn new() -> Self {
Self {
buffer: FreeBuffer::new(),
len: FreeCount::new_zero_len(),
}
}
#[inline]
fn num_free(&self) -> u32 {
self.len.state(Ordering::Relaxed).length()
}
#[inline]
unsafe fn free(&self, entities: &[Entity]) {
let state = self.len.disable_len_for_state(Ordering::Relaxed);
let mut len = state.length();
entities.iter().for_each(|&entity| {
unsafe {
self.buffer.set(len, entity);
}
len += 1;
});
let new_state = state.with_length(len);
self.len.set_state_risky(new_state, Ordering::Release);
}
#[inline]
unsafe fn alloc(&self) -> Option<Entity> {
let len = self.len.pop_for_state(1, Ordering::Relaxed).length();
let index = len.checked_sub(1)?;
Some(unsafe { self.buffer.get(index) })
}
#[inline]
unsafe fn alloc_many(&self, count: u32) -> FreeBufferIterator<'_> {
let len = self.len.pop_for_state(count, Ordering::Relaxed).length();
let index = len.saturating_sub(count);
unsafe { self.buffer.iter(index..len) }
}
#[inline]
fn remote_alloc(&self) -> Option<Entity> {
#[cfg(feature = "std")]
let mut attempts = 1u32;
let mut state = self.len.state(Ordering::Acquire);
loop {
if state.is_disabled() {
#[cfg(feature = "std")]
{
attempts += 1;
if attempts.is_multiple_of(64) {
std::thread::yield_now();
} else {
core::hint::spin_loop();
}
}
#[cfg(not(feature = "std"))]
core::hint::spin_loop();
state = self.len.state(Ordering::Acquire);
continue;
}
let len = state.length();
let index = len.checked_sub(1)?;
let entity = unsafe { self.buffer.get(index) };
let ideal_state = state.pop(1);
match self
.len
.try_set_state(state, ideal_state, Ordering::Relaxed, Ordering::Acquire)
{
Ok(_) => return Some(entity),
Err(new_state) => state = new_state,
}
}
}
}
struct FreshAllocator {
next_entity_index: AtomicU32,
}
impl FreshAllocator {
const MAX_ENTITIES: u32 = u32::MAX;
#[inline]
fn total_entity_indices(&self) -> u32 {
self.next_entity_index.load(Ordering::Relaxed)
}
#[cold]
#[inline]
fn on_overflow() -> ! {
panic!("too many entities")
}
#[inline]
fn alloc(&self) -> Entity {
let index = self.next_entity_index.fetch_add(1, Ordering::Relaxed);
if index == Self::MAX_ENTITIES {
Self::on_overflow();
}
Entity::from_index(unsafe { EntityIndex::new(NonMaxU32::new_unchecked(index)) })
}
fn alloc_many(&self, count: u32) -> AllocUniqueEntityIndexIterator {
let start_new = self.next_entity_index.fetch_add(count, Ordering::Relaxed);
let new = match start_new
.checked_add(count)
.filter(|new| *new < Self::MAX_ENTITIES)
{
Some(new_next_entity_index) => start_new..new_next_entity_index,
None => Self::on_overflow(),
};
AllocUniqueEntityIndexIterator(new)
}
}
pub(super) struct AllocUniqueEntityIndexIterator(core::ops::Range<u32>);
impl Iterator for AllocUniqueEntityIndexIterator {
type Item = Entity;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
.map(|idx| unsafe { EntityIndex::new(NonMaxU32::new_unchecked(idx)) })
.map(Entity::from_index)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl ExactSizeIterator for AllocUniqueEntityIndexIterator {}
impl core::iter::FusedIterator for AllocUniqueEntityIndexIterator {}
struct SharedAllocator {
free: FreeList,
fresh: FreshAllocator,
is_closed: AtomicBool,
}
impl SharedAllocator {
fn new() -> Self {
Self {
free: FreeList::new(),
fresh: FreshAllocator {
next_entity_index: AtomicU32::new(0),
},
is_closed: AtomicBool::new(false),
}
}
#[inline]
unsafe fn alloc(&self) -> Entity {
unsafe { self.free.alloc() }.unwrap_or_else(|| self.fresh.alloc())
}
#[inline]
unsafe fn alloc_many(&self, count: u32) -> AllocEntitiesIterator<'_> {
let reused = unsafe { self.free.alloc_many(count) };
let still_need = count - reused.len() as u32;
let new = self.fresh.alloc_many(still_need);
AllocEntitiesIterator { new, reused }
}
#[inline]
fn remote_alloc(&self) -> Entity {
self.free
.remote_alloc()
.unwrap_or_else(|| self.fresh.alloc())
}
fn close(&self) {
self.is_closed.store(true, Ordering::Release);
}
fn is_closed(&self) -> bool {
self.is_closed.load(Ordering::Acquire)
}
}
pub(crate) struct Allocator {
shared: Arc<SharedAllocator>,
local_free: Box<ArrayVec<Entity, 128>>,
}
impl Default for Allocator {
fn default() -> Self {
Self::new()
}
}
impl Allocator {
pub(super) fn new() -> Self {
Self {
shared: Arc::new(SharedAllocator::new()),
local_free: Box::new(ArrayVec::new()),
}
}
#[inline]
pub(super) fn alloc(&self) -> Entity {
unsafe { self.shared.alloc() }
}
#[inline]
pub(crate) fn total_entity_indices(&self) -> u32 {
self.shared.fresh.total_entity_indices()
}
#[inline]
fn num_free(&self) -> u32 {
self.shared.free.num_free()
}
#[inline]
pub(crate) fn flush_freed(&mut self) {
unsafe {
self.shared.free.free(self.local_free.as_slice());
}
self.local_free.clear();
}
#[inline]
pub(super) fn free(&mut self, entity: Entity) {
if self.local_free.is_full() {
self.flush_freed();
}
unsafe {
self.local_free.push_unchecked(entity);
}
}
#[inline]
pub(super) fn alloc_many(&self, count: u32) -> AllocEntitiesIterator<'_> {
unsafe { self.shared.alloc_many(count) }
}
#[inline]
pub(super) fn free_many(&mut self, entities: &[Entity]) {
if self.local_free.try_extend_from_slice(entities).is_err() {
unsafe {
self.shared.free.free(entities);
}
}
}
}
impl Drop for Allocator {
fn drop(&mut self) {
self.shared.close();
}
}
impl core::fmt::Debug for Allocator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(core::any::type_name::<Self>())
.field("total_indices", &self.total_entity_indices())
.field("total_free", &self.num_free())
.finish()
}
}
pub(super) struct AllocEntitiesIterator<'a> {
new: AllocUniqueEntityIndexIterator,
reused: FreeBufferIterator<'a>,
}
impl<'a> Iterator for AllocEntitiesIterator<'a> {
type Item = Entity;
fn next(&mut self) -> Option<Self::Item> {
self.reused.next().or_else(|| self.new.next())
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.reused.len() + self.new.len();
(len, Some(len))
}
}
impl<'a> ExactSizeIterator for AllocEntitiesIterator<'a> {}
impl<'a> core::iter::FusedIterator for AllocEntitiesIterator<'a> {}
unsafe impl EntitySetIterator for AllocEntitiesIterator<'_> {}
impl Drop for AllocEntitiesIterator<'_> {
fn drop(&mut self) {
let leaking = self.len();
if leaking > 0 {
warn!(
"{} entities being leaked via unfinished `AllocEntitiesIterator`",
leaking
);
}
}
}
#[derive(Clone)]
pub struct RemoteAllocator {
shared: Arc<SharedAllocator>,
}
impl RemoteAllocator {
pub(super) fn new(source: &Allocator) -> Self {
Self {
shared: source.shared.clone(),
}
}
pub(super) fn is_connected_to(&self, source: &Allocator) -> bool {
Arc::ptr_eq(&self.shared, &source.shared)
}
#[inline]
pub fn alloc(&self) -> Entity {
self.shared.remote_alloc()
}
pub fn is_closed(&self) -> bool {
self.shared.is_closed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn chunk_capacity_sums() {
let total: u64 = (0..FreeBuffer::NUM_CHUNKS)
.map(FreeBuffer::capacity_of_chunk)
.map(|x| x as u64)
.sum();
let expected = u32::MAX as u64 + 1;
assert_eq!(total, expected);
}
#[test]
fn chunk_indexing() {
let to_test = vec![
(0, (0, 0, 512)), (1, (0, 1, 512)),
(256, (0, 256, 512)),
(511, (0, 511, 512)),
(512, (1, 0, 512)), (1023, (1, 511, 512)),
(1024, (2, 0, 1024)), (1025, (2, 1, 1024)),
(2047, (2, 1023, 1024)),
(2048, (3, 0, 2048)), (4095, (3, 2047, 2048)),
(4096, (4, 0, 4096)), ];
for (input, output) in to_test {
assert_eq!(FreeBuffer::index_info(input), output);
}
}
#[test]
fn buffer_len_encoding() {
let len = FreeCount::new_zero_len();
assert_eq!(len.state(Ordering::Relaxed).length(), 0);
assert_eq!(len.pop_for_state(200, Ordering::Relaxed).length(), 0);
len.set_state_risky(
FreeCountState::new_zero_len().with_length(5),
Ordering::Relaxed,
);
assert_eq!(len.pop_for_state(2, Ordering::Relaxed).length(), 5);
assert_eq!(len.pop_for_state(2, Ordering::Relaxed).length(), 3);
assert_eq!(len.pop_for_state(2, Ordering::Relaxed).length(), 1);
assert_eq!(len.pop_for_state(2, Ordering::Relaxed).length(), 0);
}
#[test]
fn uniqueness() {
let mut entities = Vec::with_capacity(2000);
let mut allocator = Allocator::new();
entities.extend(allocator.alloc_many(1000));
let pre_len = entities.len();
entities.dedup();
assert_eq!(pre_len, entities.len());
for e in entities.drain(..) {
allocator.free(e);
}
entities.extend(allocator.alloc_many(500));
for _ in 0..1000 {
entities.push(allocator.alloc());
}
entities.extend(allocator.alloc_many(500));
let pre_len = entities.len();
entities.dedup();
assert_eq!(pre_len, entities.len());
}
#[test]
fn allocation_order_correctness() {
let mut allocator = Allocator::new();
let e0 = allocator.alloc();
let e1 = allocator.alloc();
let e2 = allocator.alloc();
let e3 = allocator.alloc();
allocator.free(e0);
allocator.free(e1);
allocator.free(e2);
allocator.free(e3);
allocator.flush_freed();
let r0 = allocator.alloc();
let mut many = allocator.alloc_many(2);
let r1 = many.next().unwrap();
let r2 = many.next().unwrap();
assert!(many.next().is_none());
drop(many);
let r3 = allocator.alloc();
assert_eq!(r0, e3);
assert_eq!(r1, e1);
assert_eq!(r2, e2);
assert_eq!(r3, e0);
}
}