Skip to content

Commit

Permalink
Merge pull request 'Remove reference-counting from states; introduce …
Browse files Browse the repository at this point in the history
…non-static wrappers to state properties' (#54) from state-v2 into main

Reviewed-on: https://codeberg.org/DM-Earth/rimecraft/pulls/54
Reviewed-by: C191239 <[email protected]>
  • Loading branch information
C191239 committed Aug 25, 2024
2 parents 16eed59 + b989164 commit dff560e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 84 deletions.
2 changes: 1 addition & 1 deletion crates/core/block-entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ where
/// A trait for providing block entities.
///
/// This should be implemented for [`ProvideBlockStateExtTy::BlockStateExt`]s.
pub trait ProvideBlockEntity<'w, Cx>
pub trait ProvideBlockEntity<'w, Cx>: 'w
where
Cx: ProvideBlockStateExtTy<BlockStateExt = Self>,
{
Expand Down
12 changes: 6 additions & 6 deletions crates/core/block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rimecraft_global_cx::{GlobalContext, ProvideIdTy};
use rimecraft_registry::{ProvideRegistry, Reg};
use rimecraft_state::{State, States, StatesMut};

use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
use std::{fmt::Debug, hash::Hash, marker::PhantomData};

pub mod behave;

Expand Down Expand Up @@ -123,7 +123,7 @@ where
pub block: Block<'w, Cx>,

/// The state.
pub state: Arc<State<'w, Cx::BlockStateExt>>,
pub state: &'w State<'w, Cx::BlockStateExt>,
}

impl<Cx> BlockState<'_, Cx>
Expand All @@ -134,7 +134,7 @@ where
/// Returns the luminance level of this block state.
#[inline]
pub fn luminance(&self) -> u32 {
self.state.data().luminance(&self.state)
self.state.data().luminance(self.state)
}
}

Expand All @@ -160,7 +160,7 @@ where
fn clone(&self) -> Self {
Self {
block: self.block,
state: self.state.clone(),
state: self.state,
}
}
}
Expand All @@ -171,7 +171,7 @@ where
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.block == other.block && Arc::ptr_eq(&self.state, &other.state)
self.block == other.block && std::ptr::eq(self.state, other.state)
}
}

Expand All @@ -183,6 +183,6 @@ where
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.block.hash(state);
Arc::as_ptr(&self.state).hash(state);
(self.state as *const State<'_, Cx::BlockStateExt>).hash(state);
}
}
2 changes: 2 additions & 0 deletions crates/core/state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ regex-lite = "0.1"
regex = { version = "1.10", optional = true }
serde = { version = "1.0", optional = true }
rimecraft-serde-update = { path = "../../util/serde-update", optional = true }
ahash = "0.8"
typeid = "1.0"

[features]
default = ["serde"]
Expand Down
101 changes: 46 additions & 55 deletions crates/core/state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,30 @@
//! This corresponds to `net.minecraft.state` in `yarn`.

use std::{
collections::{BTreeMap, HashMap},
collections::BTreeMap,
fmt::{Debug, Display},
sync::{Arc, OnceLock, Weak},
ptr::NonNull,
sync::OnceLock,
};

use property::{BiIndex, ErasedProperty, Property, Wrap};
use ahash::AHashMap;
use property::{BiIndex, ErasedProperty, Property, UnObjSafeErasedWrap as _, Wrap};

#[cfg(feature = "regex")]
use regex::Regex;
#[cfg(not(feature = "regex"))]
use regex_lite::Regex;
use rimecraft_maybe::Maybe;

use crate::property::ErasedWrap;

pub mod property;

// <property> -> <<value> -> <state>>
type Table<'a, T> = HashMap<ErasedProperty<'a>, HashMap<isize, Weak<T>>>;

type MaybeArc<'a, T> = Maybe<'a, T, Arc<T>>;
type Table<'a, T> = AHashMap<ErasedProperty<'a>, AHashMap<isize, NonNull<T>>>;

/// State of an object.
pub struct State<'a, T> {
pub(crate) entries: HashMap<ErasedProperty<'a>, isize>,
pub(crate) entries: AHashMap<ErasedProperty<'a>, isize>,
table: OnceLock<Table<'a, Self>>,
data: T,
}
Expand All @@ -54,7 +53,7 @@ impl<T> State<'_, T> {
///
/// - Panics if the target state was dropped.
/// - Panics if this state is not fully initialized.
pub fn cycle<V, W>(&self, prop: &Property<'_, W>) -> Result<MaybeArc<'_, Self>, Error>
pub fn cycle<V, W>(&self, prop: &Property<'_, W>) -> Result<&Self, Error>
where
W: BiIndex<V>,
for<'w> &'w W: IntoIterator<Item = V>,
Expand All @@ -69,18 +68,18 @@ impl<T> State<'_, T> {
.into_iter()
.filter_map(|value| prop.wrap.index_of(&value)),
) else {
return Ok(MaybeArc::Borrowed(self));
return Ok(self);
};
if next == index {
Ok(MaybeArc::Borrowed(self))
Ok(self)
} else {
self.table
.get()
.expect("state not initialized")
.get(prop.name())
.ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned()))
.and_then(|map| map.get(&next).ok_or(Error::ValueNotFound(index)))
.map(|weak| MaybeArc::Owned(weak.upgrade().expect("state was dropped")))
.map(|ptr| unsafe { ptr.as_ref() })
}
}

Expand All @@ -95,7 +94,7 @@ impl<T> State<'_, T> {
///
/// - Panics if the target state was dropped.
/// - Panics if this state is not fully initialized.
pub fn with<V, W>(&self, prop: &Property<'_, W>, value: V) -> Result<MaybeArc<'_, Self>, Error>
pub fn with<V, W>(&self, prop: &Property<'_, W>, value: V) -> Result<&Self, Error>
where
W: BiIndex<V>,
{
Expand All @@ -105,15 +104,15 @@ impl<T> State<'_, T> {
.ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned()))?;
let value = prop.wrap.index_of(&value).ok_or(Error::InvalidValue)?;
if value == index {
Ok(MaybeArc::Borrowed(self))
Ok(self)
} else {
self.table
.get()
.expect("state not initialized")
.get(prop.name())
.ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned()))
.and_then(|map| map.get(&value).ok_or(Error::ValueNotFound(index)))
.map(|weak| MaybeArc::Owned(weak.upgrade().expect("state was dropped")))
.map(|ptr| unsafe { ptr.as_ref() })
}
}

Expand Down Expand Up @@ -157,9 +156,8 @@ impl<T: Debug> Debug for State<'_, T> {
/// See [`StatesMut`] for creating a new instance.
#[derive(Debug)]
#[doc(alias = "StateManager")]
#[doc(alias = "StateDefinition")]
pub struct States<'a, T> {
states: Vec<Arc<State<'a, T>>>,
states: Vec<NonNull<State<'a, T>>>,
#[allow(unused)]
props: BTreeMap<&'a str, ErasedProperty<'a>>,
}
Expand Down Expand Up @@ -191,23 +189,26 @@ where
}
let list = iter
.into_iter()
.map(|vec| vec.into_iter().collect::<HashMap<_, _>>())
.map(|vec| vec.into_iter().collect::<AHashMap<_, _>>())
.map(|entries| {
Arc::new(State {
NonNull::new(Box::into_raw(Box::new(State {
entries,
table: OnceLock::new(),
data: data.clone(),
})
})))
.expect("failed to allocate state")
})
.collect::<Vec<_>>();

// Initialize tables
for state in list.iter() {
let state = unsafe { state.as_ref() };
let mut table: Table<'a, State<'a, T>> = Table::new();
for (prop, s_val) in state.entries.iter() {
let mut row = HashMap::new();
let mut row = AHashMap::new();
for val in prop.wrap.erased_iter().filter(|v| v != s_val) {
let Some(s) = list.iter().find(|s| {
let s = unsafe { s.as_ref() };
s.entries.iter().all(|(p, v)| {
if p == prop {
*v == val
Expand All @@ -218,7 +219,7 @@ where
}) else {
continue;
};
row.insert(val, Arc::downgrade(s));
row.insert(val, *s);
}
table.insert(prop.clone(), row);
}
Expand All @@ -235,7 +236,7 @@ where
impl<'a, T> States<'a, T> {
/// Gets all states of this state.
#[inline]
pub fn states(&self) -> &[Arc<State<'a, T>>] {
pub fn states(&self) -> &[NonNull<State<'a, T>>] {
&self.states
}

Expand All @@ -245,8 +246,8 @@ impl<'a, T> States<'a, T> {
///
/// Panics if no state is available.
#[inline]
pub fn default_state(&self) -> &Arc<State<'a, T>> {
self.states.first().expect("no state available")
pub fn default_state(&self) -> &State<'a, T> {
unsafe { self.states.first().expect("no state available").as_ref() }
}

/// Gets the length of states.
Expand All @@ -262,6 +263,14 @@ impl<'a, T> States<'a, T> {
}
}

impl<T> Drop for States<'_, T> {
fn drop(&mut self) {
for state in self.states.iter() {
drop(unsafe { Box::from_raw(state.as_ptr()) });
}
}
}

/// Mutable instance of [`States`].
#[derive(Debug)]
pub struct StatesMut<'a, T> {
Expand All @@ -288,9 +297,9 @@ impl<'a, T> StatesMut<'a, T> {
/// - Errors if the states contains duplicated properties.
/// - Errors if any of the value name is invalid.
#[allow(clippy::missing_panics_doc)]
pub fn add<W, G>(&mut self, prop: &'a Property<'_, W>) -> Result<(), Error>
pub fn add<'p, W, G>(&mut self, prop: &'a Property<'p, W>) -> Result<(), Error>
where
W: Wrap<G> + BiIndex<G> + Eq + Send + Sync + 'static,
W: Wrap<G> + BiIndex<G> + Eq + Send + Sync + 'p,
for<'w> &'w W: IntoIterator<Item = G>,
{
static NAME_PAT: OnceLock<Regex> = OnceLock::new();
Expand All @@ -299,7 +308,7 @@ impl<'a, T> StatesMut<'a, T> {
return Err(Error::InvalidPropertyName(prop.name().to_owned()));
}
let mut len = 0;
for val in prop.wrap.erased_iter() {
for val in prop.wrap.erased_iter_typed() {
len += 1;
let name = prop.wrap.erased_to_name(val).expect("invalid value");
if !reg.is_match(&name) {
Expand Down Expand Up @@ -391,11 +400,8 @@ impl Display for Error {

impl std::error::Error for Error {}

/// Serde support for `State`s.
#[cfg(feature = "serde")]
pub mod serde {
use std::sync::Arc;

mod _serde {
use rimecraft_serde_update::Update;
use serde::{ser::SerializeMap, Serialize};

Expand All @@ -422,21 +428,7 @@ pub mod serde {
}
}

/// Updatable state newtype wrapper.
#[derive(Debug)]
pub struct Upd<'a, T>(pub Arc<State<'a, T>>);

impl<T> Serialize for Upd<'_, T> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize(serializer)
}
}

impl<'de, T> Update<'de> for Upd<'_, T> {
impl<'de, T> Update<'de> for &State<'_, T> {
#[inline]
fn update<D>(
&mut self,
Expand All @@ -445,10 +437,10 @@ pub mod serde {
where
D: serde::Deserializer<'de>,
{
struct Visitor<'a, T>(Arc<State<'a, T>>);
struct Visitor<'a, T>(*const State<'a, T>);

impl<'de, 'a, T> serde::de::Visitor<'de> for Visitor<'a, T> {
type Value = Arc<State<'a, T>>;
type Value = *const State<'a, T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a map")
Expand All @@ -459,8 +451,7 @@ pub mod serde {
A: serde::de::MapAccess<'de>,
{
while let Some((prop, val)) = map.next_entry::<String, isize>()? {
self.0 = self
.0
self.0 = unsafe { &*self.0 }
.table
.get()
.expect("state not initialized")
Expand All @@ -474,16 +465,16 @@ pub mod serde {
"value {val} not found in property {prop}"
))
})?
.upgrade()
.expect("state was dropped");
.as_ptr()
.cast_const();
}
Ok(self.0)
}
}

deserializer
.deserialize_map(Visitor(self.0.clone()))
.map(|state| self.0 = state)
.deserialize_map(Visitor(*self))
.map(|state| *self = unsafe { &*state })
}
}
}
Expand Down
Loading

0 comments on commit dff560e

Please sign in to comment.