use crate::{ecs::EcsError, FxBuildHasher};
use my_utils::{
debug_format,
ds::{
ATypeId, BorrowError, BorrowResult, ManagedConstPtr, ManagedMutPtr, NonNullExt, OptVec,
SimpleHolder,
},
Or, With,
};
use std::{
any::Any,
collections::HashMap,
fmt,
hash::{BuildHasher, Hash},
ptr::NonNull,
};
pub mod prelude {
pub use super::{Resource, ResourceDesc, ResourceId, ResourceIndex};
}
pub trait Resource: Send + 'static {
#[doc(hidden)]
fn key() -> ResourceKey {
ResourceKey::of::<Self>()
}
}
#[derive(Debug)]
pub(super) struct ResourceStorage<S = FxBuildHasher> {
owned: HashMap<ResourceKey, Box<dyn Any>, S>,
ptrs: OptVec<SimpleHolder<NonNullExt<u8>>, S>,
imap: HashMap<ResourceKey, ResourceIndex, S>,
is_dedi: Vec<bool>,
res_gens: Vec<u64>,
generation: u64,
}
impl ResourceStorage {
#[cfg(test)]
pub(super) fn new() -> Self {
Self {
owned: HashMap::with_hasher(FxBuildHasher::default()),
ptrs: OptVec::new(),
imap: HashMap::with_hasher(FxBuildHasher::default()),
is_dedi: Vec::new(),
res_gens: Vec::new(),
generation: Self::INIT_GEN,
}
}
}
impl<S> ResourceStorage<S> {
const INIT_GEN: u64 = 1;
pub(super) fn with_hasher<F: FnMut() -> S>(mut hasher: F) -> Self {
Self {
owned: HashMap::with_hasher(hasher()),
ptrs: OptVec::with_hasher(hasher()),
imap: HashMap::with_hasher(hasher()),
is_dedi: Vec::new(),
res_gens: Vec::new(),
generation: Self::INIT_GEN,
}
}
}
impl<S> ResourceStorage<S>
where
S: BuildHasher + Default,
{
pub(super) fn add(
&mut self,
desc: ResourceDesc,
) -> Result<ResourceIndex, EcsError<ResourceDesc>> {
if self.imap.contains_key(&desc.key) {
let reason = debug_format!("detected duplicated resource `{:?}`", desc.key);
return Err(EcsError::DupResource(reason, desc));
}
let ResourceDesc {
dedicated,
key,
data,
} = desc;
let ptr = match data {
Or::A(mut owned) => {
let ptr = unsafe { NonNull::new_unchecked(&mut *owned as *mut dyn Any as *mut u8) };
let must_none = self.owned.insert(key, owned);
debug_assert!(must_none.is_none());
ptr
}
Or::B(ptr) => ptr,
};
let ptr = NonNullExt::from_nonnull(ptr).with_type(*key.get_inner());
let holder = SimpleHolder::new(ptr);
let index = self.ptrs.add(holder);
let ri = ResourceIndex::new(index, self.generation);
self.generation += 1;
while self.res_gens.len() <= index {
self.res_gens.push(0);
}
self.res_gens[index] = ri.generation();
self.imap.insert(key, ri);
if self.is_dedi.len() < index + 1 {
self.is_dedi.resize(index + 1, false);
}
self.is_dedi[index] = dedicated;
Ok(ri)
}
pub(super) fn remove(&mut self, rkey: &ResourceKey) -> Option<Or<Box<dyn Any>, NonNull<u8>>> {
if let Some(ri) = self.imap.remove(rkey) {
let data = self.owned.remove(rkey);
let ptr = self.ptrs.take(ri.index());
debug_assert!(ptr.is_some());
let holder = unsafe { ptr.unwrap_unchecked() };
let ptr = *holder.into_value();
Some(if let Some(data) = data {
Or::A(data)
} else {
Or::B(ptr)
})
} else {
None
}
}
pub(super) fn contains<Q>(&self, key: &Q) -> bool
where
ResourceKey: std::borrow::Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.imap.contains_key(key)
}
pub(super) fn index<Q>(&self, key: &Q) -> Option<ResourceIndex>
where
ResourceKey: std::borrow::Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.imap.get(key).cloned()
}
#[allow(dead_code)]
pub(super) fn is_dedicated(&self, ri: ResourceIndex) -> Option<bool> {
if self.is_valid_index(&ri) {
Some(self.is_dedi[ri.index()])
} else {
None
}
}
pub(super) fn is_dedicated2<Q>(&self, key: &Q) -> Option<bool>
where
ResourceKey: std::borrow::Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.imap.get(key).map(|ri| self.is_dedi[ri.index()])
}
pub(super) fn borrow(&self, ri: ResourceIndex) -> BorrowResult<ManagedConstPtr<u8>> {
if self.is_valid_index(&ri) {
if let Some(holder) = self.ptrs.get(ri.index()) {
return holder
.borrow()
.map(|borrowed| borrowed.map(|ptr| unsafe { ManagedConstPtr::new(ptr) }));
}
}
Err(BorrowError::OutOfBound)
}
pub(super) fn borrow2<Q>(&self, key: &Q) -> BorrowResult<ManagedConstPtr<u8>>
where
ResourceKey: std::borrow::Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(index) = self.index(key) {
self.borrow(index)
} else {
Err(BorrowError::NotFound)
}
}
pub(super) fn borrow_mut(&mut self, ri: ResourceIndex) -> BorrowResult<ManagedMutPtr<u8>> {
if self.is_valid_index(&ri) {
if let Some(holder) = self.ptrs.get_mut(ri.index()) {
return holder
.borrow_mut()
.map(|borrowed| borrowed.map(|ptr| unsafe { ManagedMutPtr::new(ptr) }));
}
}
Err(BorrowError::OutOfBound)
}
pub(super) fn borrow_mut2<Q>(&mut self, key: &Q) -> BorrowResult<ManagedMutPtr<u8>>
where
ResourceKey: std::borrow::Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(index) = self.index(key) {
self.borrow_mut(index)
} else {
Err(BorrowError::NotFound)
}
}
#[cfg(test)]
pub(super) unsafe fn get_ptr(&self, ri: ResourceIndex) -> Option<NonNullExt<u8>> {
if self.is_valid_index(&ri) {
self.ptrs.get(ri.index()).map(|holder| {
let ptr = holder.ptr_inner();
let value = unsafe { *ptr.as_ref() };
value
})
} else {
None
}
}
fn is_valid_index(&self, ri: &ResourceIndex) -> bool {
if let Some(generation) = self.res_gens.get(ri.index()).cloned() {
generation == ri.generation()
} else {
false
}
}
}
impl<S: Default> Default for ResourceStorage<S> {
fn default() -> Self {
Self::with_hasher(|| S::default())
}
}
#[derive(Debug)]
pub struct ResourceDesc {
pub dedicated: bool,
pub(crate) key: ResourceKey,
pub data: Or<Box<dyn Any>, NonNull<u8>>,
}
impl ResourceDesc {
pub fn new() -> Self {
struct Dummy;
impl Resource for Dummy {}
Self {
dedicated: false,
key: Dummy::key(),
data: Or::B(NonNull::dangling()),
}
}
pub fn with_dedicated(mut self, is_dedicated: bool) -> Self {
self.dedicated = is_dedicated;
self
}
pub fn with_owned<R: Resource>(mut self, data: R) -> Self {
self.key = R::key();
self.data = Or::A(Box::new(data));
self
}
pub unsafe fn with_ptr<R: Resource>(mut self, data: *mut R) -> Self {
self.key = R::key();
self.data = Or::B(NonNull::new(data as *mut u8).unwrap());
self
}
}
impl Default for ResourceDesc {
fn default() -> Self {
Self::new()
}
}
impl<R: Resource> From<R> for ResourceDesc {
fn from(value: R) -> Self {
ResourceDesc::new().with_owned(value)
}
}
pub type ResourceKey = ATypeId<ResourceKey_>;
pub struct ResourceKey_;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ResourceId {
ri: ResourceIndex,
ii: With<usize, u64>,
}
impl ResourceId {
pub const fn new(ri: ResourceIndex, ii: With<usize, u64>) -> Self {
Self { ri, ii }
}
pub const fn resource_index(&self) -> ResourceIndex {
self.ri
}
pub const fn item_index(&self) -> With<usize, u64> {
self.ii
}
}
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
#[repr(transparent)]
pub struct ResourceIndex(With<usize, u64>);
impl ResourceIndex {
const DUMMY: Self = Self(With::new(usize::MAX, u64::MAX));
pub const fn new(index: usize, generation: u64) -> Self {
Self(With::new(index, generation))
}
pub const fn dummy() -> Self {
Self::DUMMY
}
pub fn is_dummy(&self) -> bool {
*self == Self::dummy()
}
pub fn index(&self) -> usize {
*self.0
}
pub fn generation(&self) -> u64 {
*self.0.get_back()
}
}
impl Default for ResourceIndex {
fn default() -> Self {
Self::dummy()
}
}
impl fmt::Display for ResourceIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}