use crate::prelude::*;
use crate::store::StoreOpaque;
use crate::{StoreContext, StoreContextMut};
use core::fmt;
use core::marker;
use core::num::NonZeroU64;
use core::ops::{Index, IndexMut};
use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
#[derive(Copy, Clone)]
pub struct InstanceId(pub(super) usize);
impl InstanceId {
pub fn from_index(idx: usize) -> InstanceId {
InstanceId(idx)
}
}
pub struct StoreData {
id: StoreId,
funcs: Vec<crate::func::FuncData>,
tables: Vec<crate::runtime::vm::ExportTable>,
globals: Vec<crate::runtime::vm::ExportGlobal>,
instances: Vec<crate::instance::InstanceData>,
memories: Vec<crate::runtime::vm::ExportMemory>,
#[cfg(feature = "component-model")]
pub(crate) components: crate::component::ComponentStoreData,
}
pub trait StoredData: Sized {
fn list(data: &StoreData) -> &Vec<Self>;
fn list_mut(data: &mut StoreData) -> &mut Vec<Self>;
}
macro_rules! impl_store_data {
($($field:ident => $t:ty,)*) => ($(
impl StoredData for $t {
#[inline]
fn list(data: &StoreData) -> &Vec<Self> { &data.$field }
#[inline]
fn list_mut(data: &mut StoreData) -> &mut Vec<Self> { &mut data.$field }
}
)*)
}
impl_store_data! {
funcs => crate::func::FuncData,
tables => crate::runtime::vm::ExportTable,
globals => crate::runtime::vm::ExportGlobal,
instances => crate::instance::InstanceData,
memories => crate::runtime::vm::ExportMemory,
}
impl StoreData {
pub fn new() -> StoreData {
StoreData {
id: StoreId::allocate(),
funcs: Vec::new(),
tables: Vec::new(),
globals: Vec::new(),
instances: Vec::new(),
memories: Vec::new(),
#[cfg(feature = "component-model")]
components: Default::default(),
}
}
pub fn id(&self) -> StoreId {
self.id
}
pub fn insert<T>(&mut self, data: T) -> Stored<T>
where
T: StoredData,
{
let list = T::list_mut(self);
let index = list.len();
list.push(data);
Stored::new(self.id, index)
}
pub fn next_id<T>(&self) -> Stored<T>
where
T: StoredData,
{
Stored::new(self.id, T::list(self).len())
}
pub fn contains<T>(&self, id: Stored<T>) -> bool
where
T: StoredData,
{
if id.store_id != self.id {
return false;
}
debug_assert!(id.index() < T::list(self).len());
true
}
pub fn iter<T>(&self) -> impl ExactSizeIterator<Item = Stored<T>>
where
T: StoredData,
{
let id = self.id;
(0..T::list(self).len()).map(move |i| Stored::new(id, i))
}
pub(crate) fn reserve_funcs(&mut self, count: usize) {
self.funcs.reserve(count);
}
}
impl<T> Index<Stored<T>> for StoreData
where
T: StoredData,
{
type Output = T;
#[inline]
fn index(&self, index: Stored<T>) -> &Self::Output {
index.assert_belongs_to(self.id);
&T::list(self)[index.index()]
}
}
impl<T> IndexMut<Stored<T>> for StoreData
where
T: StoredData,
{
#[inline]
fn index_mut(&mut self, index: Stored<T>) -> &mut Self::Output {
index.assert_belongs_to(self.id);
&mut T::list_mut(self)[index.index()]
}
}
impl<I, T> Index<I> for StoreContext<'_, T>
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.store_data.index(index)
}
}
impl<I, T> Index<I> for StoreContextMut<'_, T>
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.store_data.index(index)
}
}
impl<I> Index<I> for StoreOpaque
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.store_data().index(index)
}
}
impl<I> IndexMut<I> for StoreOpaque
where
StoreData: IndexMut<I>,
{
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.store_data_mut().index_mut(index)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)] pub struct StoreId(NonZeroU64);
impl StoreId {
pub fn allocate() -> StoreId {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
let id = NEXT_ID.fetch_add(1, Relaxed);
if id & (1 << 63) != 0 {
NEXT_ID.store(1 << 63, Relaxed);
panic!("store id allocator overflow");
}
StoreId(NonZeroU64::new(id + 1).unwrap())
}
#[inline]
pub fn assert_belongs_to(&self, store: StoreId) {
if *self == store {
return;
}
store_id_mismatch();
}
pub fn as_raw(&self) -> NonZeroU64 {
self.0
}
pub fn from_raw(id: NonZeroU64) -> StoreId {
StoreId(id)
}
}
#[repr(C)] pub struct Stored<T> {
store_id: StoreId,
index: usize,
_marker: marker::PhantomData<fn() -> T>,
}
impl<T> Stored<T> {
fn new(store_id: StoreId, index: usize) -> Stored<T> {
Stored {
store_id,
index,
_marker: marker::PhantomData,
}
}
#[inline]
pub fn assert_belongs_to(&self, store: StoreId) {
self.store_id.assert_belongs_to(store)
}
fn index(&self) -> usize {
self.index
}
}
#[cold]
fn store_id_mismatch() {
panic!("object used with the wrong store");
}
impl<T> PartialEq for Stored<T> {
fn eq(&self, other: &Stored<T>) -> bool {
self.store_id == other.store_id && self.index == other.index
}
}
impl<T> Copy for Stored<T> {}
impl<T> Clone for Stored<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> fmt::Debug for Stored<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "store={}, index={}", self.store_id.0, self.index())
}
}