use std::sync::{RwLockReadGuard, RwLockWriteGuard};
use smallvec::SmallVec;
use crate::engine::types::{ChunkID, ComponentID};
use crate::engine::storage::TypeErasedAttribute;
use crate::engine::error::{ECSError, ECSResult, ExecutionError};
use super::core::Archetype;
type ReadColumnGuard<'a> = (
ComponentID,
RwLockReadGuard<'a, Box<dyn TypeErasedAttribute>>,
);
type WriteColumnGuard<'a> = (
ComponentID,
RwLockWriteGuard<'a, Box<dyn TypeErasedAttribute>>,
);
type ReadGuardSet<'a> = SmallVec<[ReadColumnGuard<'a>; 8]>;
type WriteGuardSet<'a> = SmallVec<[WriteColumnGuard<'a>; 8]>;
pub struct ChunkBorrow<'a> {
pub length: usize,
pub reads: SmallVec<[(*const u8, usize); 8]>,
pub writes: SmallVec<[*mut u8; 8]>,
_read_guards: ReadGuardSet<'a>,
_write_guards: WriteGuardSet<'a>,
}
impl Archetype {
pub fn borrow_chunk_for<'a>(
&'a self,
chunk: ChunkID,
read_ids: &[ComponentID],
write_ids: &[ComponentID],
) -> ECSResult<ChunkBorrow<'a>> {
let length = self
.chunk_valid_length(chunk as usize)
.map_err(|_| ExecutionError::InternalExecutionError)?;
for &id in read_ids {
if write_ids.contains(&id) {
return Err(ECSError::Execute(ExecutionError::InvalidQueryAccess {
component_id: id,
reason: crate::engine::error::InvalidAccessReason::ReadAndWrite,
}));
}
}
let mut all: SmallVec<[(ComponentID, bool); 8]> =
SmallVec::with_capacity(read_ids.len() + write_ids.len());
for &cid in read_ids {
all.push((cid, false));
}
for &cid in write_ids {
all.push((cid, true));
}
all.sort_unstable_by_key(|(cid, _)| *cid);
let mut read_guards: ReadGuardSet<'a> = SmallVec::with_capacity(read_ids.len());
let mut write_guards: WriteGuardSet<'a> = SmallVec::with_capacity(write_ids.len());
for (cid, is_write) in all {
let attr = self
.find_component(cid)
.ok_or(ExecutionError::MissingComponent { component_id: cid })?;
if is_write {
let g = attr
.write()
.map_err(|_| ExecutionError::InternalExecutionError)?;
write_guards.push((cid, g));
} else {
let g = attr
.read()
.map_err(|_| ExecutionError::InternalExecutionError)?;
read_guards.push((cid, g));
}
}
let mut reads: SmallVec<[(*const u8, usize); 8]> = SmallVec::with_capacity(read_ids.len());
for &cid in read_ids {
let guard = read_guards
.iter()
.find(|(id, _)| *id == cid)
.map(|(_, g)| g)
.ok_or(ExecutionError::InternalExecutionError)?;
let (ptr, bytes) = guard
.as_ref()
.chunk_bytes(chunk, length)
.ok_or(ExecutionError::InternalExecutionError)?;
reads.push((ptr, bytes));
}
let mut writes: SmallVec<[*mut u8; 8]> = SmallVec::with_capacity(write_ids.len());
for &cid in write_ids {
let guard = write_guards
.iter_mut()
.find(|(id, _)| *id == cid)
.map(|(_, g)| g)
.ok_or(ExecutionError::InternalExecutionError)?;
let (ptr, _bytes) = guard
.as_mut()
.chunk_bytes_mut(chunk, length)
.ok_or(ExecutionError::InternalExecutionError)?;
writes.push(ptr);
}
Ok(ChunkBorrow {
length,
reads,
writes,
_read_guards: read_guards,
_write_guards: write_guards,
})
}
}