From ff5184fd44cf6dcc7813f583401ac5b1004f12a8 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Tue, 7 May 2024 20:55:22 +0800 Subject: [PATCH 1/6] missing debug implementations --- crates/core/component/Cargo.toml | 27 +++ crates/core/component/src/lib.rs | 254 ++++++++++++++++++++++++++++ crates/core/component/src/map.rs | 217 ++++++++++++++++++++++++ crates/core/global-cx/src/lib.rs | 3 +- crates/util/downcast/src/lib.rs | 12 +- crates/util/edcode/Cargo.toml | 6 +- crates/util/edcode/src/lib.rs | 37 ++-- crates/util/serde-update/src/lib.rs | 3 +- 8 files changed, 528 insertions(+), 31 deletions(-) create mode 100644 crates/core/component/Cargo.toml create mode 100644 crates/core/component/src/lib.rs create mode 100644 crates/core/component/src/map.rs diff --git a/crates/core/component/Cargo.toml b/crates/core/component/Cargo.toml new file mode 100644 index 00000000..c8f857ae --- /dev/null +++ b/crates/core/component/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "rimecraft-component" +version = "0.1.0" +edition = "2021" +authors = ["JieningYu "] +description = "Minecraft Component implementation" +repository = "https://github.com/rimecraft-rs/rimecraft/" +license = "AGPL-3.0-or-later" +categories = [] + +[badges] +maintenance = { status = "passively-maintained" } + +[dependencies] +serde = "1.0" +erased-serde = "0.4" +bytes = "1.6" +ahash = "0.8" +rimecraft-edcode = { path = "../../util/edcode" } +rimecraft-registry = { path = "../../util/registry" } +rimecraft-global-cx = { path = "../global-cx" } +rimecraft-maybe = { path = "../../util/maybe" } + +[features] + +[lints] +workspace = true diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs new file mode 100644 index 00000000..c8020c4c --- /dev/null +++ b/crates/core/component/src/lib.rs @@ -0,0 +1,254 @@ +//! Minecraft Component implementation. + +use std::{ + any::{Any, TypeId}, + hash::Hash, + marker::PhantomData, +}; + +use rimecraft_edcode::{Decode, Encode}; +use rimecraft_global_cx::ProvideIdTy; +use rimecraft_registry::{ProvideRegistry, Reg}; +use serde::{de::DeserializeOwned, Serialize}; + +type Object = dyn Any + Send + Sync; + +pub mod map; + +/// Type of a component data. +/// +/// The type `T` should be unique for each component, as it's used to identify the component. +/// +/// For the type-erased variant, see [`RawErasedComponentType`]. +#[derive(Debug)] +#[doc(alias = "DataComponentType")] +pub struct ComponentType { + serde_codec: Option, + packet_codec: PacketCodec, + util: DynUtil, + _marker: PhantomData, +} + +impl ComponentType { + /// Returns whether the component is transient. + #[inline] + #[doc(alias = "should_skip_serialization")] + pub fn is_transient(&self) -> bool { + self.serde_codec.is_none() + } +} + +impl ComponentType +where + T: Clone + Eq + Send + Sync + 'static, +{ + const UTIL: DynUtil = DynUtil { + clone: |obj| Box::new(obj.downcast_ref::().expect("mismatched type").clone()), + eq: |a, b| { + a.downcast_ref::() + .zip(b.downcast_ref::()) + .map(|(a, b)| a == b) + .unwrap_or_default() + }, + }; +} + +impl ComponentType +where + T: Clone + Eq + Encode + Decode + Send + Sync + 'static, +{ + const PACKET_CODEC: PacketCodec = PacketCodec { + encode: |obj, buf| { + obj.downcast_ref::() + .expect("mismatched type") + .encode(buf) + }, + decode: |buf| Ok(Box::new(T::decode(buf)?)), + upd: |obj, buf| { + obj.downcast_mut::() + .expect("mismatched type") + .decode_in_place(buf) + }, + }; + + /// Creates a new transient component type. + /// + /// Transient components are not serialized. + /// + /// This function requires the type to be `'static`. If the type is not `'static`, transmutes + /// the type to `'static`, which is unsound but works. + #[inline] + pub const fn transient() -> Self { + Self { + serde_codec: None, + packet_codec: Self::PACKET_CODEC, + util: Self::UTIL, + _marker: PhantomData, + } + } +} + +impl ComponentType +where + T: Clone + Eq + Encode + Decode + Serialize + DeserializeOwned + Send + Sync + 'static, +{ + const SERDE_CODEC: SerdeCodec = SerdeCodec { + ser: |obj, serializer| { + erased_serde::Serialize::erased_serialize( + obj.downcast_ref::() + .expect("the erased type should matches the actual type"), + serializer, + ) + }, + de: |deserializer| { + erased_serde::deserialize::(deserializer).map(|v| { + let v: Box = Box::new(v); + v + }) + }, + upd: |obj, deserializer| { + *obj.downcast_mut::() + .expect("the erased type should matches the actual type") = + erased_serde::deserialize::(deserializer)?; + Ok(()) + }, + }; + + /// Creates a new persistent component type. + /// + /// Persistent components are serialized. + /// + /// This function requires the type to be `'static`. If the type is not `'static`, transmutes + /// the type to `'static`, which is unsound but works. + #[allow(clippy::missing_panics_doc)] + pub const fn persistent() -> Self { + Self { + serde_codec: Some(Self::SERDE_CODEC), + packet_codec: Self::PACKET_CODEC, + util: Self::UTIL, + _marker: PhantomData, + } + } +} + +impl Hash for ComponentType { + #[inline] + fn hash(&self, state: &mut H) { + TypeId::of::().hash(state); + } +} + +impl PartialEq for ComponentType { + #[inline] + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl ComponentType where T: Encode + Decode {} + +impl Default for ComponentType +where + T: Clone + Eq + Encode + Decode + Send + Sync + 'static, +{ + #[inline] + fn default() -> Self { + Self::transient() + } +} + +impl Copy for ComponentType {} + +impl Clone for ComponentType { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +/// [`ComponentType`] with erased type. +/// +/// This contains the type ID of the component and the codecs for serialization and packet encoding. +#[derive(Debug)] +pub struct RawErasedComponentType { + ty: TypeId, + serde_codec: Option, + packet_codec: PacketCodec, + util: DynUtil, + _marker: PhantomData, +} + +#[derive(Debug, Clone, Copy)] +struct SerdeCodec { + ser: fn(&Object, &mut dyn erased_serde::Serializer) -> erased_serde::Result<()>, + de: fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result>, + upd: fn(&mut Object, &mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result<()>, +} + +#[derive(Debug, Clone, Copy)] +struct PacketCodec { + encode: fn(&Object, &mut dyn bytes::BufMut) -> Result<(), std::io::Error>, + decode: fn(&mut dyn bytes::Buf) -> Result, std::io::Error>, + upd: fn(&mut Object, &mut dyn bytes::Buf) -> Result<(), std::io::Error>, +} + +#[derive(Debug, Clone, Copy)] +struct DynUtil { + clone: fn(&Object) -> Box, + eq: fn(&Object, &Object) -> bool, +} + +impl RawErasedComponentType { + /// Downcasts this type-erased component type into a typed data component type. + #[inline] + pub fn downcast(&self) -> Option> { + (TypeId::of::() == self.ty).then_some(ComponentType { + serde_codec: self.serde_codec, + packet_codec: self.packet_codec, + util: self.util, + _marker: PhantomData, + }) + } +} + +impl From<&ComponentType> for RawErasedComponentType { + #[inline] + fn from(value: &ComponentType) -> Self { + Self { + ty: TypeId::of::(), + serde_codec: value.serde_codec, + packet_codec: value.packet_codec, + util: value.util, + _marker: PhantomData, + } + } +} + +impl Hash for RawErasedComponentType { + #[inline] + fn hash(&self, state: &mut H) { + self.ty.hash(state); + } +} + +impl PartialEq for RawErasedComponentType { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ty == other.ty + } +} + +impl Eq for RawErasedComponentType {} + +impl<'r, K, Cx> ProvideRegistry<'r, K, Self> for RawErasedComponentType +where + Cx: ProvideRegistry<'r, K, Self>, +{ + #[inline] + fn registry() -> &'r rimecraft_registry::Registry { + Cx::registry() + } +} + +/// Registration wrapper of [`RawErasedComponentType`]. +pub type ErasedComponentType<'a, Cx> = Reg<'a, ::Id, RawErasedComponentType>; diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs new file mode 100644 index 00000000..0d9452d2 --- /dev/null +++ b/crates/core/component/src/map.rs @@ -0,0 +1,217 @@ +//! Component map implementation. + +use std::{any::TypeId, borrow::Borrow, hash::Hash}; + +use ahash::AHashMap; +use rimecraft_global_cx::ProvideIdTy; +use rimecraft_maybe::{Maybe, SimpleOwned}; + +use crate::{ComponentType, ErasedComponentType, Object, RawErasedComponentType}; + +#[repr(transparent)] +struct CompTyCell<'a, Cx: ProvideIdTy>(ErasedComponentType<'a, Cx>); + +pub struct ComponentMap<'a, Cx>(MapInner<'a, Cx>) +where + Cx: ProvideIdTy; + +enum MapInner<'a, Cx> +where + Cx: ProvideIdTy, +{ + Empty, + Patched { + base: &'a ComponentMap<'a, Cx>, + changes: AHashMap, Option>>, + }, + Simple(AHashMap, Box>), +} + +impl<'a, Cx> ComponentMap<'a, Cx> +where + Cx: ProvideIdTy, +{ + /// Gets the component with given type. + pub fn get(&self, ty: &ComponentType) -> Option<&T> { + match &self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => changes + .get(&RawErasedComponentType::from(ty)) + .map(|o| o.as_ref().and_then(|o| o.downcast_ref::())) + .unwrap_or_else(|| base.get(ty)), + MapInner::Simple(map) => map + .get(&RawErasedComponentType::from(ty)) + .and_then(|any| any.downcast_ref()), + } + } + + #[inline] + fn get_raw(&self, ty: &ComponentType) -> Option<&Object> { + match &self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => changes + .get(&RawErasedComponentType::from(ty)) + .map(Option::as_deref) + .unwrap_or_else(|| base.get_raw(ty)), + MapInner::Simple(map) => map.get(&RawErasedComponentType::from(ty)).map(|b| &**b), + } + } + + /// Gets the component and its type registration with given type. + pub fn get_key_value( + &self, + ty: &ComponentType, + ) -> Option<(ErasedComponentType<'a, Cx>, &T)> { + match &self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => changes + .get_key_value(&RawErasedComponentType::from(ty)) + .map(|(k, o)| { + o.as_ref() + .and_then(|o| o.downcast_ref::()) + .map(|o| (k.0, o)) + }) + .unwrap_or_else(|| base.get_key_value(ty)), + MapInner::Simple(map) => map + .get_key_value(&RawErasedComponentType::from(ty)) + .and_then(|(k, any)| any.downcast_ref().map(|a| (k.0, a))), + } + } + + #[inline] + fn get_raw_key_value( + &self, + ty: &ComponentType, + ) -> Option<(ErasedComponentType<'a, Cx>, &Object)> { + match &self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => changes + .get_key_value(&RawErasedComponentType::from(ty)) + .map(|(a, b)| b.as_deref().map(|b| (a.0, b))) + .unwrap_or_else(|| base.get_raw_key_value(ty)), + MapInner::Simple(map) => map + .get_key_value(&RawErasedComponentType::from(ty)) + .map(|(k, any)| (k.0, &**any)), + } + } + + /// Returns whether a component with given type exist. + pub fn contains(&self, ty: &ComponentType) -> bool { + match &self.0 { + MapInner::Empty => false, + MapInner::Patched { base, changes } => changes + .get(&RawErasedComponentType::from(ty)) + .map(|opt| opt.is_some()) + .unwrap_or_else(|| base.contains(ty)), + MapInner::Simple(map) => map.contains_key(&RawErasedComponentType::from(ty)), + } + } + + /// Gets the component with given type, with mutable access. + pub fn get_mut(&mut self, ty: &ComponentType) -> Option<&mut T> { + match &mut self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => { + if !changes.contains_key(&RawErasedComponentType::from(ty)) { + let (k, v) = base.get_raw_key_value(ty)?; + changes.insert(CompTyCell(k), Some((ty.util.clone)(v))); + } + changes + .get_mut(&RawErasedComponentType::from(ty)) + .and_then(Option::as_mut) + .and_then(|obj| obj.downcast_mut::()) + } + MapInner::Simple(map) => map + .get_mut(&RawErasedComponentType::from(ty)) + .and_then(|any| any.downcast_mut()), + } + } + + /// Inserts a component into this map, and returns the old one if valid. + /// + /// This function receives a type-erased component type, because it contains the registration information, + /// which is useful for interacting with Minecraft protocol. + /// + /// # Panics + /// + /// This function panics when the given component type's type information does not match with + /// the given static type. + pub fn insert(&mut self, ty: ErasedComponentType<'a, Cx>, val: T) -> Option> + where + T: Send + Sync + 'static, + { + assert_eq! { + ty.ty, + TypeId::of::(), + "the component type should matches the type of given value", + }; + match &mut self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => { + let old = base.get_raw(&ty.downcast::()?); + if old.is_some_and(|old| (ty.util.eq)(&val, old)) { + changes.remove(&CompTyCell(ty)) + } else if let Some(v) = changes.insert(CompTyCell(ty), Some(Box::new(val))) { + Some(v) + } else { + return old + .and_then(|old| old.downcast_ref::()) + .map(Maybe::Borrowed); + } + .flatten() + } + MapInner::Simple(map) => map.insert(CompTyCell(ty), Box::new(val)), + } + .and_then(|obj| obj.downcast().ok()) + .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))) + } + + /// Removes a component with given type, and returns it if valid. + pub fn remove(&mut self, ty: &ComponentType) -> Option> { + match &mut self.0 { + MapInner::Empty => None, + MapInner::Patched { base, changes } => { + let old = base.get_raw_key_value(ty); + let now = changes.get_mut(&RawErasedComponentType::from(ty)); + match (old, now) { + (Some((k, v)), None) => { + changes.insert(CompTyCell(k), None); + v.downcast_ref::().map(Maybe::Borrowed) + } + (_, Some(now)) => now + .take() + .and_then(|obj| obj.downcast().ok()) + .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))), + (None, None) => None, + } + } + MapInner::Simple(map) => map + .remove(&RawErasedComponentType::from(ty)) + .and_then(|obj| obj.downcast().ok()) + .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))), + } + } +} + +impl PartialEq for CompTyCell<'_, Cx> { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for CompTyCell<'_, Cx> {} + +impl Hash for CompTyCell<'_, Cx> { + #[inline] + fn hash(&self, state: &mut H) { + (*self.0).hash(state) + } +} + +impl Borrow> for CompTyCell<'_, Cx> { + #[inline] + fn borrow(&self) -> &RawErasedComponentType { + &self.0 + } +} diff --git a/crates/core/global-cx/src/lib.rs b/crates/core/global-cx/src/lib.rs index 6064e080..090a3f2a 100644 --- a/crates/core/global-cx/src/lib.rs +++ b/crates/core/global-cx/src/lib.rs @@ -16,7 +16,6 @@ extern crate std; use core::{fmt::Display, hash::Hash}; use alloc::boxed::Box; -use serde::Deserializer; /// Marker trait for global contexts. pub trait GlobalContext: Sized + 'static {} @@ -40,7 +39,7 @@ pub trait ProvideNbtTy: GlobalContext { type LongArray: Into> + From>; /// Function that converts a `Compound` to a [`Deserializer`]. - fn compound_to_deserializer(compound: &Self::Compound) -> impl Deserializer<'_>; + fn compound_to_deserializer(compound: &Self::Compound) -> impl serde::Deserializer<'_>; } #[cfg(feature = "std")] diff --git a/crates/util/downcast/src/lib.rs b/crates/util/downcast/src/lib.rs index 93ded4da..06a0c613 100644 --- a/crates/util/downcast/src/lib.rs +++ b/crates/util/downcast/src/lib.rs @@ -69,8 +69,12 @@ where impl Downcast { /// Downcasts the value into a concrete type, returning an immutable reference. + /// + /// # Safety + /// + /// This function is unsafe because it could not make sure the lifetime is safe. #[inline] - pub fn downcast_ref(&self) -> Option<&V> { + pub unsafe fn downcast_ref(&self) -> Option<&V> { if self.is_safe::() { unsafe { Some(&*(&self.value as *const T as *const V)) } } else { @@ -79,8 +83,12 @@ impl Downcast { } /// Downcasts the value into a concrete type, returning a mutable reference. + /// + /// # Safety + /// + /// This function is unsafe because it could not make sure the lifetime is safe. #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut V> { + pub unsafe fn downcast_mut(&mut self) -> Option<&mut V> { if self.is_safe::() { unsafe { Some(&mut *(&mut self.value as *mut T as *mut V)) } } else { diff --git a/crates/util/edcode/Cargo.toml b/crates/util/edcode/Cargo.toml index e9764ba2..51fb8e7d 100644 --- a/crates/util/edcode/Cargo.toml +++ b/crates/util/edcode/Cargo.toml @@ -12,7 +12,7 @@ categories = ["encoding"] maintenance = { status = "passively-maintained" } [dependencies] -bytes = "1.5" +bytes = "1.6" serde = { version = "1.0", optional = true } rimecraft-edcode-derive = { path = "../edcode-derive", optional = true } # custom formats @@ -20,10 +20,10 @@ fastnbt = { version = "2.4", optional = true } serde_json = { version = "1.0", optional = true } # integrations uuid = { version = "1.7", optional = true } -glam = { version = "0.25", optional = true } +glam = { version = "0.27.0", optional = true } [features] -# default = ["serde", "nbt", "json", "uuid", "glam"] +# default = ["serde", "json", "uuid", "glam"] serde = ["dep:serde"] derive = ["dep:rimecraft-edcode-derive"] # custom formats diff --git a/crates/util/edcode/src/lib.rs b/crates/util/edcode/src/lib.rs index 57838c3e..006c02f5 100644 --- a/crates/util/edcode/src/lib.rs +++ b/crates/util/edcode/src/lib.rs @@ -24,26 +24,6 @@ pub trait Encode { B: bytes::BufMut; } -/// [`Encode`], but can be used as trait objects. -pub trait BytesEncode { - /// Encodes into a bytes buffer. - /// - /// # Errors - /// - /// Returns an error if the encoding failed. - fn encode_bytes(&self, bytes: &mut bytes::BytesMut) -> Result<(), io::Error>; -} - -impl BytesEncode for T -where - T: Encode, -{ - #[inline(always)] - fn encode_bytes(&self, bytes: &mut bytes::BytesMut) -> Result<(), io::Error> { - self.encode(bytes) - } -} - /// Describes types that can be decoded from a packet buffer. pub trait Decode: Sized { /// Decode from a buffer. @@ -54,6 +34,20 @@ pub trait Decode: Sized { fn decode(buf: B) -> Result where B: bytes::Buf; + + /// Decode from a buffer in place. + /// + /// # Errors + /// + /// Returns an error if the decoding failed. + #[inline] + fn decode_in_place(&mut self, buf: B) -> Result<(), io::Error> + where + B: bytes::Buf, + { + *self = Self::decode(buf)?; + Ok(()) + } } /// Represents types that can be updated from a buffer. @@ -77,8 +71,7 @@ where where B: bytes::Buf, { - *self = Self::decode(buf)?; - Ok(()) + self.decode_in_place(buf) } } diff --git a/crates/util/serde-update/src/lib.rs b/crates/util/serde-update/src/lib.rs index 7fcd8bf5..68840ce2 100644 --- a/crates/util/serde-update/src/lib.rs +++ b/crates/util/serde-update/src/lib.rs @@ -26,7 +26,6 @@ where where D: serde::Deserializer<'de>, { - *self = Self::deserialize(deserializer)?; - Ok(()) + serde::Deserialize::deserialize_in_place(deserializer, self) } } From 97276ee8d2b0dd8e313276f9d8d88dea1fbe1b8a Mon Sep 17 00:00:00 2001 From: JieningYu Date: Wed, 8 May 2024 18:08:28 +0800 Subject: [PATCH 2/6] iterator --- crates/core/component/src/lib.rs | 54 ++++---- crates/core/component/src/map.rs | 227 ++++++++++++++++++++++--------- 2 files changed, 192 insertions(+), 89 deletions(-) diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index c8020c4c..8f29af47 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -23,9 +23,7 @@ pub mod map; #[derive(Debug)] #[doc(alias = "DataComponentType")] pub struct ComponentType { - serde_codec: Option, - packet_codec: PacketCodec, - util: DynUtil, + f: &'static Funcs, _marker: PhantomData, } @@ -34,7 +32,7 @@ impl ComponentType { #[inline] #[doc(alias = "should_skip_serialization")] pub fn is_transient(&self) -> bool { - self.serde_codec.is_none() + self.f.serde_codec.is_none() } } @@ -44,12 +42,7 @@ where { const UTIL: DynUtil = DynUtil { clone: |obj| Box::new(obj.downcast_ref::().expect("mismatched type").clone()), - eq: |a, b| { - a.downcast_ref::() - .zip(b.downcast_ref::()) - .map(|(a, b)| a == b) - .unwrap_or_default() - }, + eq: |a, b| a.downcast_ref::() == b.downcast_ref::(), }; } @@ -80,9 +73,11 @@ where #[inline] pub const fn transient() -> Self { Self { - serde_codec: None, - packet_codec: Self::PACKET_CODEC, - util: Self::UTIL, + f: &Funcs { + serde_codec: None, + packet_codec: Self::PACKET_CODEC, + util: Self::UTIL, + }, _marker: PhantomData, } } @@ -123,9 +118,11 @@ where #[allow(clippy::missing_panics_doc)] pub const fn persistent() -> Self { Self { - serde_codec: Some(Self::SERDE_CODEC), - packet_codec: Self::PACKET_CODEC, - util: Self::UTIL, + f: &Funcs { + serde_codec: Some(Self::SERDE_CODEC), + packet_codec: Self::PACKET_CODEC, + util: Self::UTIL, + }, _marker: PhantomData, } } @@ -172,40 +169,43 @@ impl Clone for ComponentType { #[derive(Debug)] pub struct RawErasedComponentType { ty: TypeId, - serde_codec: Option, - packet_codec: PacketCodec, - util: DynUtil, + f: &'static Funcs, _marker: PhantomData, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] struct SerdeCodec { ser: fn(&Object, &mut dyn erased_serde::Serializer) -> erased_serde::Result<()>, de: fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result>, upd: fn(&mut Object, &mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result<()>, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] struct PacketCodec { encode: fn(&Object, &mut dyn bytes::BufMut) -> Result<(), std::io::Error>, decode: fn(&mut dyn bytes::Buf) -> Result, std::io::Error>, upd: fn(&mut Object, &mut dyn bytes::Buf) -> Result<(), std::io::Error>, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] struct DynUtil { clone: fn(&Object) -> Box, eq: fn(&Object, &Object) -> bool, } +#[derive(Debug)] +struct Funcs { + serde_codec: Option, + packet_codec: PacketCodec, + util: DynUtil, +} + impl RawErasedComponentType { /// Downcasts this type-erased component type into a typed data component type. #[inline] pub fn downcast(&self) -> Option> { (TypeId::of::() == self.ty).then_some(ComponentType { - serde_codec: self.serde_codec, - packet_codec: self.packet_codec, - util: self.util, + f: self.f, _marker: PhantomData, }) } @@ -216,9 +216,7 @@ impl From<&ComponentType> for RawErasedComponentType { fn from(value: &ComponentType) -> Self { Self { ty: TypeId::of::(), - serde_codec: value.serde_codec, - packet_codec: value.packet_codec, - util: value.util, + f: value.f, _marker: PhantomData, } } diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 0d9452d2..103f70ba 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -1,6 +1,6 @@ //! Component map implementation. -use std::{any::TypeId, borrow::Borrow, hash::Hash}; +use std::{any::TypeId, borrow::Borrow, collections::hash_map, hash::Hash}; use ahash::AHashMap; use rimecraft_global_cx::ProvideIdTy; @@ -23,6 +23,7 @@ where Patched { base: &'a ComponentMap<'a, Cx>, changes: AHashMap, Option>>, + changes_count: isize, }, Simple(AHashMap, Box>), } @@ -33,27 +34,19 @@ where { /// Gets the component with given type. pub fn get(&self, ty: &ComponentType) -> Option<&T> { - match &self.0 { - MapInner::Empty => None, - MapInner::Patched { base, changes } => changes - .get(&RawErasedComponentType::from(ty)) - .map(|o| o.as_ref().and_then(|o| o.downcast_ref::())) - .unwrap_or_else(|| base.get(ty)), - MapInner::Simple(map) => map - .get(&RawErasedComponentType::from(ty)) - .and_then(|any| any.downcast_ref()), - } + self.get_raw(&RawErasedComponentType::from(ty)) + .and_then(Object::downcast_ref) } #[inline] - fn get_raw(&self, ty: &ComponentType) -> Option<&Object> { + fn get_raw(&self, ty: &RawErasedComponentType) -> Option<&Object> { match &self.0 { MapInner::Empty => None, - MapInner::Patched { base, changes } => changes - .get(&RawErasedComponentType::from(ty)) + MapInner::Patched { base, changes, .. } => changes + .get(ty) .map(Option::as_deref) .unwrap_or_else(|| base.get_raw(ty)), - MapInner::Simple(map) => map.get(&RawErasedComponentType::from(ty)).map(|b| &**b), + MapInner::Simple(map) => map.get(ty).map(|b| &**b), } } @@ -62,81 +55,90 @@ where &self, ty: &ComponentType, ) -> Option<(ErasedComponentType<'a, Cx>, &T)> { - match &self.0 { - MapInner::Empty => None, - MapInner::Patched { base, changes } => changes - .get_key_value(&RawErasedComponentType::from(ty)) - .map(|(k, o)| { - o.as_ref() - .and_then(|o| o.downcast_ref::()) - .map(|o| (k.0, o)) - }) - .unwrap_or_else(|| base.get_key_value(ty)), - MapInner::Simple(map) => map - .get_key_value(&RawErasedComponentType::from(ty)) - .and_then(|(k, any)| any.downcast_ref().map(|a| (k.0, a))), - } + self.get_key_value_raw(&RawErasedComponentType::from(ty)) + .and_then(|(k, v)| v.downcast_ref().map(|v| (k, v))) } #[inline] - fn get_raw_key_value( + fn get_key_value_raw( &self, - ty: &ComponentType, + ty: &RawErasedComponentType, ) -> Option<(ErasedComponentType<'a, Cx>, &Object)> { match &self.0 { MapInner::Empty => None, - MapInner::Patched { base, changes } => changes - .get_key_value(&RawErasedComponentType::from(ty)) + MapInner::Patched { base, changes, .. } => changes + .get_key_value(ty) .map(|(a, b)| b.as_deref().map(|b| (a.0, b))) - .unwrap_or_else(|| base.get_raw_key_value(ty)), - MapInner::Simple(map) => map - .get_key_value(&RawErasedComponentType::from(ty)) - .map(|(k, any)| (k.0, &**any)), + .unwrap_or_else(|| base.get_key_value_raw(ty)), + MapInner::Simple(map) => map.get_key_value(ty).map(|(k, any)| (k.0, &**any)), } } /// Returns whether a component with given type exist. pub fn contains(&self, ty: &ComponentType) -> bool { + self.contains_raw(&RawErasedComponentType::from(ty)) + } + + #[inline] + fn contains_raw(&self, ty: &RawErasedComponentType) -> bool { match &self.0 { MapInner::Empty => false, - MapInner::Patched { base, changes } => changes - .get(&RawErasedComponentType::from(ty)) + MapInner::Patched { base, changes, .. } => changes + .get(ty) .map(|opt| opt.is_some()) - .unwrap_or_else(|| base.contains(ty)), - MapInner::Simple(map) => map.contains_key(&RawErasedComponentType::from(ty)), + .unwrap_or_else(|| base.contains_raw(ty)), + MapInner::Simple(map) => map.contains_key(ty), } } /// Gets the component with given type, with mutable access. pub fn get_mut(&mut self, ty: &ComponentType) -> Option<&mut T> { + self.get_mut_raw(&RawErasedComponentType::from(ty)) + .and_then(Object::downcast_mut) + } + + #[inline] + fn get_mut_raw(&mut self, ty: &RawErasedComponentType) -> Option<&mut Object> { match &mut self.0 { MapInner::Empty => None, - MapInner::Patched { base, changes } => { - if !changes.contains_key(&RawErasedComponentType::from(ty)) { - let (k, v) = base.get_raw_key_value(ty)?; - changes.insert(CompTyCell(k), Some((ty.util.clone)(v))); + MapInner::Patched { base, changes, .. } => { + if !changes.contains_key(ty) { + let (k, v) = base.get_key_value_raw(ty)?; + changes.insert(CompTyCell(k), Some((ty.f.util.clone)(v))); } - changes - .get_mut(&RawErasedComponentType::from(ty)) - .and_then(Option::as_mut) - .and_then(|obj| obj.downcast_mut::()) + changes.get_mut(ty).and_then(Option::as_mut) } - MapInner::Simple(map) => map - .get_mut(&RawErasedComponentType::from(ty)) - .and_then(|any| any.downcast_mut()), + MapInner::Simple(map) => map.get_mut(ty), } + .map(Box::as_mut) } /// Inserts a component into this map, and returns the old one if valid. /// - /// This function receives a type-erased component type, because it contains the registration information, - /// which is useful for interacting with Minecraft protocol. + /// This function receives a type-erased component type, because it contains the registration + /// information, which is useful for interacting with Minecraft protocol. /// /// # Panics /// /// This function panics when the given component type's type information does not match with /// the given static type. pub fn insert(&mut self, ty: ErasedComponentType<'a, Cx>, val: T) -> Option> + where + T: Send + Sync + 'static, + { + let value = self.insert_untracked(ty, val); + if value.is_none() { + self.track_add() + } + value + } + + #[inline] + fn insert_untracked( + &mut self, + ty: ErasedComponentType<'a, Cx>, + val: T, + ) -> Option> where T: Send + Sync + 'static, { @@ -147,9 +149,9 @@ where }; match &mut self.0 { MapInner::Empty => None, - MapInner::Patched { base, changes } => { - let old = base.get_raw(&ty.downcast::()?); - if old.is_some_and(|old| (ty.util.eq)(&val, old)) { + MapInner::Patched { base, changes, .. } => { + let old = base.get_raw(&ty); + if old.is_some_and(|old| (ty.f.util.eq)(&val, old)) { changes.remove(&CompTyCell(ty)) } else if let Some(v) = changes.insert(CompTyCell(ty), Some(Box::new(val))) { Some(v) @@ -168,20 +170,34 @@ where /// Removes a component with given type, and returns it if valid. pub fn remove(&mut self, ty: &ComponentType) -> Option> { + let value = self.remove_untracked(ty); + if value.is_some() { + self.track_rm() + } + value + } + + #[inline] + fn remove_untracked(&mut self, ty: &ComponentType) -> Option> { match &mut self.0 { MapInner::Empty => None, - MapInner::Patched { base, changes } => { - let old = base.get_raw_key_value(ty); - let now = changes.get_mut(&RawErasedComponentType::from(ty)); + MapInner::Patched { base, changes, .. } => { + let era_ty = &RawErasedComponentType::from(ty); + let old = base.get_key_value_raw(era_ty); + let now = changes.get_mut(era_ty); match (old, now) { (Some((k, v)), None) => { changes.insert(CompTyCell(k), None); v.downcast_ref::().map(Maybe::Borrowed) } - (_, Some(now)) => now + (Some(_), Some(now)) => now .take() .and_then(|obj| obj.downcast().ok()) .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))), + (None, Some(_)) => changes + .remove(era_ty)? + .and_then(|obj| obj.downcast().ok()) + .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))), (None, None) => None, } } @@ -191,6 +207,43 @@ where .map(|boxed| Maybe::Owned(SimpleOwned(*boxed))), } } + + #[inline] + fn track_add(&mut self) { + if let MapInner::Patched { changes_count, .. } = &mut self.0 { + *changes_count += 1; + } + } + + #[inline] + fn track_rm(&mut self) { + if let MapInner::Patched { changes_count, .. } = &mut self.0 { + *changes_count -= 1; + } + } + + /// Returns the count of valid components. + pub fn len(&self) -> usize { + self._len() + } + + #[inline(always)] + fn _len(&self) -> usize { + match &self.0 { + MapInner::Empty => 0, + MapInner::Patched { + base, + changes_count, + .. + } => ((base.len() as isize) + changes_count) as usize, + MapInner::Simple(map) => map.len(), + } + } + + /// Returns whether this map is empty. + pub fn is_empty(&self) -> bool { + self._len() == 0 + } } impl PartialEq for CompTyCell<'_, Cx> { @@ -215,3 +268,55 @@ impl Borrow> for CompTyCell<'_, Cx> &self.0 } } + +pub struct Iter<'a, Cx>(IterInner<'a, Cx>) +where + Cx: ProvideIdTy; + +enum IterInner<'a, Cx: ProvideIdTy> { + Empty, + Patched { + changes: &'a AHashMap, Option>>, + base_it: Box>, + changes_it: hash_map::Iter<'a, CompTyCell<'a, Cx>, Option>>, + }, + Simple(hash_map::Iter<'a, CompTyCell<'a, Cx>, Box>), +} + +impl<'a, Cx> Iterator for Iter<'a, Cx> +where + Cx: ProvideIdTy, +{ + type Item = (ErasedComponentType<'a, Cx>, &'a Object); + + fn next(&mut self) -> Option { + match &mut self.0 { + IterInner::Empty => None, + IterInner::Patched { + changes, + base_it, + changes_it, + } => { + for (k, v) in changes_it { + if let Some(v) = v { + return Some((k.0, &**v)); + } + } + + for (k, v) in base_it { + let patched = changes.get(&CompTyCell(k)); + match patched { + Some(Some(opt)) => { + return Some((k, &**opt)); + } + Some(None) => continue, + None => return Some((k, v)), + } + } + + None + } + IterInner::Simple(it) => it.next().map(|(k, v)| (k.0, &**v)), + } + } +} From 7a8c95dc0065d7cdab374a409c5d248061a8ae84 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Thu, 9 May 2024 23:45:13 +0800 Subject: [PATCH 3/6] serialize --- crates/core/component/src/lib.rs | 11 +- crates/core/component/src/map.rs | 228 ++++++++++++++++++++++++++++++- crates/core/global-cx/src/lib.rs | 10 +- 3 files changed, 237 insertions(+), 12 deletions(-) diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 8f29af47..7a2b52b8 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -88,12 +88,9 @@ where T: Clone + Eq + Encode + Decode + Serialize + DeserializeOwned + Send + Sync + 'static, { const SERDE_CODEC: SerdeCodec = SerdeCodec { - ser: |obj, serializer| { - erased_serde::Serialize::erased_serialize( - obj.downcast_ref::() - .expect("the erased type should matches the actual type"), - serializer, - ) + ser: |obj| { + obj.downcast_ref::() + .expect("the erased type should matches the actual type") }, de: |deserializer| { erased_serde::deserialize::(deserializer).map(|v| { @@ -175,7 +172,7 @@ pub struct RawErasedComponentType { #[derive(Debug)] struct SerdeCodec { - ser: fn(&Object, &mut dyn erased_serde::Serializer) -> erased_serde::Result<()>, + ser: for<'a> fn(&'a Object) -> &'a dyn erased_serde::Serialize, de: fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result>, upd: fn(&mut Object, &mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result<()>, } diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 103f70ba..7a7ba9c9 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -1,16 +1,18 @@ //! Component map implementation. -use std::{any::TypeId, borrow::Borrow, collections::hash_map, hash::Hash}; +use std::{any::TypeId, borrow::Borrow, collections::hash_map, fmt::Debug, hash::Hash}; use ahash::AHashMap; use rimecraft_global_cx::ProvideIdTy; use rimecraft_maybe::{Maybe, SimpleOwned}; +use serde::{Deserialize, Serialize}; -use crate::{ComponentType, ErasedComponentType, Object, RawErasedComponentType}; +use crate::{ComponentType, ErasedComponentType, Object, RawErasedComponentType, SerdeCodec}; #[repr(transparent)] struct CompTyCell<'a, Cx: ProvideIdTy>(ErasedComponentType<'a, Cx>); +/// A map that stores components. pub struct ComponentMap<'a, Cx>(MapInner<'a, Cx>) where Cx: ProvideIdTy; @@ -28,10 +30,44 @@ where Simple(AHashMap, Box>), } +impl Default for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy, +{ + #[inline] + fn default() -> Self { + Self::empty() + } +} + impl<'a, Cx> ComponentMap<'a, Cx> where Cx: ProvideIdTy, { + /// Creates an empty component map. + #[inline] + pub const fn empty() -> Self { + Self(MapInner::Empty) + } + + /// Creates a **patched** component map with given base map. + #[inline] + pub fn new(base: &'a ComponentMap<'a, Cx>) -> Self { + Self(MapInner::Patched { + base, + changes: AHashMap::new(), + changes_count: 0, + }) + } + + /// Returns a builder for creating a simple component map. + #[inline] + pub fn builder() -> Builder<'a, Cx> { + Builder { + map: AHashMap::new(), + } + } + /// Gets the component with given type. pub fn get(&self, ty: &ComponentType) -> Option<&T> { self.get_raw(&RawErasedComponentType::from(ty)) @@ -244,6 +280,36 @@ where pub fn is_empty(&self) -> bool { self._len() == 0 } + + /// Returns an iterator over the components in this map. + #[inline] + pub fn iter(&self) -> Iter<'_, Cx> { + self.into_iter() + } +} + +impl<'a, Cx> IntoIterator for &'a ComponentMap<'a, Cx> +where + Cx: ProvideIdTy, +{ + type Item = as Iterator>::Item; + + type IntoIter = Iter<'a, Cx>; + + fn into_iter(self) -> Self::IntoIter { + Iter( + match &self.0 { + MapInner::Empty => IterInner::Empty, + MapInner::Patched { base, changes, .. } => IterInner::Patched { + base_it: Box::new(base.into_iter()), + changes, + changes_it: changes.iter(), + }, + MapInner::Simple(map) => IterInner::Simple(map.iter()), + }, + self, + ) + } } impl PartialEq for CompTyCell<'_, Cx> { @@ -269,7 +335,8 @@ impl Borrow> for CompTyCell<'_, Cx> } } -pub struct Iter<'a, Cx>(IterInner<'a, Cx>) +/// Iterates over the components in this map. +pub struct Iter<'a, Cx>(IterInner<'a, Cx>, &'a ComponentMap<'a, Cx>) where Cx: ProvideIdTy; @@ -319,4 +386,159 @@ where IterInner::Simple(it) => it.next().map(|(k, v)| (k.0, &**v)), } } + + fn size_hint(&self) -> (usize, Option) { + let len = self.1.len(); + (len, Some(len)) + } +} + +/// A builder for creating a simple component map. +pub struct Builder<'a, Cx> +where + Cx: ProvideIdTy, +{ + map: AHashMap, Box>, +} + +impl<'a, Cx> Builder<'a, Cx> +where + Cx: ProvideIdTy, +{ + /// Inserts a component into this map. + /// + /// # Panics + /// + /// This function panics when the given component type's type information does not match with + /// the given static type. + #[inline] + pub fn insert(mut self, ty: ErasedComponentType<'a, Cx>, val: T) -> Self + where + T: Send + Sync + 'static, + { + assert_eq!( + ty.ty, + TypeId::of::(), + "the component type should matches the type of given value" + ); + self.map.insert(CompTyCell(ty), Box::new(val)); + self + } + + /// Builds the component map. + pub fn build(self) -> ComponentMap<'a, Cx> { + ComponentMap(MapInner::Simple(self.map)) + } +} + +impl<'a, Cx> From> for ComponentMap<'a, Cx> +where + Cx: ProvideIdTy, +{ + #[inline] + fn from(builder: Builder<'a, Cx>) -> Self { + builder.build() + } +} + +impl Debug for CompTyCell<'_, Cx> +where + Cx: ProvideIdTy + Debug, + Cx::Id: Debug, +{ + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Debug for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy + Debug, + Cx::Id: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.0 { + MapInner::Empty => f.write_str("EmptyComponentMap"), + MapInner::Patched { + base, + changes, + changes_count, + } => f + .debug_struct("PatchedComponentMap") + .field("base", base) + .field("changes", changes) + .field("changes_count", changes_count) + .finish(), + MapInner::Simple(map) => f.debug_tuple("SimpleComponentMap").field(&map).finish(), + } + } +} + +impl Debug for Iter<'_, Cx> +where + Cx: ProvideIdTy + Debug, + Cx::Id: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.0 { + IterInner::Empty => f.write_str("EmptyComponentMapIter"), + IterInner::Patched { + changes, + base_it, + changes_it, + } => f + .debug_struct("PatchedComponentMapIter") + .field("changes", changes) + .field("base_it", base_it) + .field("changes_it", changes_it) + .finish(), + IterInner::Simple(it) => f.debug_tuple("SimpleComponentMapIter").field(it).finish(), + } + } +} + +impl Debug for Builder<'_, Cx> +where + Cx: ProvideIdTy + Debug, + Cx::Id: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ComponentMapBuilder") + .field("map", &self.map) + .finish() + } +} + +impl Serialize for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy, + Cx::Id: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(None)?; + for (ty, val) in self { + if let Some(codec) = &ty.f.serde_codec { + map.serialize_entry(&ty, (codec.ser)(val))?; + } + } + map.end() + } +} + +impl<'a, 'de, Cx> Deserialize<'de> for ComponentMap<'a, Cx> +where + Cx: ProvideIdTy, + Cx::Id: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + todo!() + } } diff --git a/crates/core/global-cx/src/lib.rs b/crates/core/global-cx/src/lib.rs index 090a3f2a..418d6dd0 100644 --- a/crates/core/global-cx/src/lib.rs +++ b/crates/core/global-cx/src/lib.rs @@ -18,7 +18,13 @@ use core::{fmt::Display, hash::Hash}; use alloc::boxed::Box; /// Marker trait for global contexts. -pub trait GlobalContext: Sized + 'static {} +/// +/// # Safety +/// +/// The type should be zero-sized, and should contains no valid instances, +/// as it is used as a marker trait, and this guarantees that the type is +/// FFI-safe. +pub unsafe trait GlobalContext: Sized + 'static {} /// Marker trait for global contexts that provide an identifier type. pub trait ProvideIdTy: GlobalContext { @@ -42,8 +48,8 @@ pub trait ProvideNbtTy: GlobalContext { fn compound_to_deserializer(compound: &Self::Compound) -> impl serde::Deserializer<'_>; } -#[cfg(feature = "std")] /// NBT `edcode`-ing related marker traits. +#[cfg(feature = "std")] pub mod nbt_edcode { use std::io; From fab6d726cdced37bef658706cf4037348280b281 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Fri, 10 May 2024 17:06:37 +0800 Subject: [PATCH 4/6] deserialize --- crates/core/component/Cargo.toml | 2 +- crates/core/component/src/map.rs | 77 +++++++++++++++++++++++++++++--- crates/core/global-cx/src/lib.rs | 6 +-- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/crates/core/component/Cargo.toml b/crates/core/component/Cargo.toml index c8f857ae..c4b60830 100644 --- a/crates/core/component/Cargo.toml +++ b/crates/core/component/Cargo.toml @@ -17,7 +17,7 @@ erased-serde = "0.4" bytes = "1.6" ahash = "0.8" rimecraft-edcode = { path = "../../util/edcode" } -rimecraft-registry = { path = "../../util/registry" } +rimecraft-registry = { path = "../../util/registry", features = ["serde"] } rimecraft-global-cx = { path = "../global-cx" } rimecraft-maybe = { path = "../../util/maybe" } diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 7a7ba9c9..dd8bd496 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -1,10 +1,13 @@ //! Component map implementation. -use std::{any::TypeId, borrow::Borrow, collections::hash_map, fmt::Debug, hash::Hash}; +use std::{ + any::TypeId, borrow::Borrow, collections::hash_map, fmt::Debug, hash::Hash, marker::PhantomData, +}; use ahash::AHashMap; use rimecraft_global_cx::ProvideIdTy; use rimecraft_maybe::{Maybe, SimpleOwned}; +use rimecraft_registry::ProvideRegistry; use serde::{Deserialize, Serialize}; use crate::{ComponentType, ErasedComponentType, Object, RawErasedComponentType, SerdeCodec}; @@ -427,7 +430,11 @@ where /// Builds the component map. pub fn build(self) -> ComponentMap<'a, Cx> { - ComponentMap(MapInner::Simple(self.map)) + if self.map.is_empty() { + ComponentMap(MapInner::Empty) + } else { + ComponentMap(MapInner::Simple(self.map)) + } } } @@ -532,13 +539,73 @@ where impl<'a, 'de, Cx> Deserialize<'de> for ComponentMap<'a, Cx> where - Cx: ProvideIdTy, - Cx::Id: Deserialize<'de>, + Cx: ProvideIdTy + ProvideRegistry<'a, Cx::Id, RawErasedComponentType>, + Cx::Id: Deserialize<'de> + Hash + Eq, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - todo!() + struct Visitor<'a, Cx>(PhantomData<&'a Cx>); + + impl<'a, 'de, Cx> serde::de::Visitor<'de> for Visitor<'a, Cx> + where + Cx: ProvideIdTy + ProvideRegistry<'a, Cx::Id, RawErasedComponentType>, + Cx::Id: Deserialize<'de> + Hash + Eq, + { + type Value = ComponentMap<'a, Cx>; + + #[inline] + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut m = if let Some(sz) = map.size_hint() { + AHashMap::with_capacity(sz) + } else { + AHashMap::new() + }; + struct DeSeed<'a, Cx>(&'a SerdeCodec, PhantomData<&'a Cx>); + + impl<'a, 'de, Cx> serde::de::DeserializeSeed<'de> for DeSeed<'a, Cx> + where + Cx: ProvideIdTy + ProvideRegistry<'a, Cx::Id, RawErasedComponentType>, + Cx::Id: Deserialize<'de> + Hash + Eq, + { + type Value = Box; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + (self.0.de)(&mut >::erase( + deserializer, + )) + .map_err(serde::de::Error::custom) + } + } + while let Some(k) = map.next_key::>()? { + let codec = k.f.serde_codec.as_ref().ok_or_else(|| { + serde::de::Error::invalid_type( + serde::de::Unexpected::Other("transient component type"), + &"persistent component type", + ) + })?; + m.insert( + CompTyCell(k), + map.next_value_seed(DeSeed(codec, PhantomData::<&Cx>))?, + ); + } + m.shrink_to_fit(); + Ok(Builder { map: m }.build()) + } + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a component map") + } + } + + deserializer.deserialize_map(Visitor(PhantomData)) } } diff --git a/crates/core/global-cx/src/lib.rs b/crates/core/global-cx/src/lib.rs index 418d6dd0..6e2059a0 100644 --- a/crates/core/global-cx/src/lib.rs +++ b/crates/core/global-cx/src/lib.rs @@ -15,8 +15,6 @@ extern crate std; use core::{fmt::Display, hash::Hash}; -use alloc::boxed::Box; - /// Marker trait for global contexts. /// /// # Safety @@ -39,10 +37,10 @@ pub trait ProvideNbtTy: GlobalContext { type Compound; /// [`i32`] array type. - type IntArray: Into> + From>; + type IntArray: Into> + From>; /// [`i64`] array type. - type LongArray: Into> + From>; + type LongArray: Into> + From>; /// Function that converts a `Compound` to a [`Deserializer`]. fn compound_to_deserializer(compound: &Self::Compound) -> impl serde::Deserializer<'_>; From f94cd79f58fcd50998bdadb4f6bb397b5aa9f2c2 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Wed, 15 May 2024 08:56:06 +0800 Subject: [PATCH 5/6] partial component changes --- crates/core/component/Cargo.toml | 2 +- crates/core/component/src/changes.rs | 128 +++++++++++++++++++++++++++ crates/core/component/src/lib.rs | 60 +++++++------ crates/core/component/src/map.rs | 18 +++- crates/util/maybe/src/lib.rs | 16 +++- crates/util/registry/src/lib.rs | 6 ++ 6 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 crates/core/component/src/changes.rs diff --git a/crates/core/component/Cargo.toml b/crates/core/component/Cargo.toml index c4b60830..9fddd1e2 100644 --- a/crates/core/component/Cargo.toml +++ b/crates/core/component/Cargo.toml @@ -18,7 +18,7 @@ bytes = "1.6" ahash = "0.8" rimecraft-edcode = { path = "../../util/edcode" } rimecraft-registry = { path = "../../util/registry", features = ["serde"] } -rimecraft-global-cx = { path = "../global-cx" } +rimecraft-global-cx = { path = "../global-cx", features = ["nbt", "std"] } rimecraft-maybe = { path = "../../util/maybe" } [features] diff --git a/crates/core/component/src/changes.rs b/crates/core/component/src/changes.rs new file mode 100644 index 00000000..d4ec5a73 --- /dev/null +++ b/crates/core/component/src/changes.rs @@ -0,0 +1,128 @@ +//! `ComponentChanges` implementation. + +use std::{fmt::Debug, marker::PhantomData, str::FromStr}; + +use ahash::AHashMap; +use rimecraft_edcode::{Encode, VarI32}; +use rimecraft_global_cx::ProvideIdTy; +use rimecraft_maybe::Maybe; +use rimecraft_registry::{ProvideRegistry, Reg}; +use serde::{Deserialize, Serialize}; + +use crate::{map::CompTyCell, ErasedComponentType, Object, RawErasedComponentType}; + +/// Changes of components. +pub struct ComponentChanges<'a, 'cow, Cx> +where + Cx: ProvideIdTy, +{ + pub(crate) changes: Maybe<'cow, AHashMap, Option>>>, +} + +const REMOVED_PREFIX: char = '!'; + +struct Type<'a, Cx> +where + Cx: ProvideIdTy, +{ + ty: ErasedComponentType<'a, Cx>, + rm: bool, +} + +impl Serialize for Type<'_, Cx> +where + Cx: ProvideIdTy, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let id = Reg::id(self.ty); + serializer.serialize_str(&if self.rm { + format!("{}{}", REMOVED_PREFIX, id) + } else { + id.to_string() + }) + } +} + +impl<'a, 'de, Cx> Deserialize<'de> for Type<'a, Cx> +where + Cx: ProvideIdTy + ProvideRegistry<'a, Cx::Id, RawErasedComponentType>, + Cx::Id: FromStr, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor<'a, Cx> + where + Cx: ProvideIdTy, + { + _marker: PhantomData<&'a Cx>, + } + + impl<'a, Cx> serde::de::Visitor<'_> for Visitor<'a, Cx> + where + Cx: ProvideIdTy + ProvideRegistry<'a, Cx::Id, RawErasedComponentType>, + Cx::Id: FromStr, + { + type Value = Type<'a, Cx>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a string") + } + + #[inline] + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + let stripped = value.strip_prefix(REMOVED_PREFIX); + let any = stripped.unwrap_or(value); + let id: Cx::Id = any.parse().ok().ok_or_else(|| { + E::custom(format!("unable to deserialize the identifier {}", any)) + })?; + + Ok(Type { + ty: Cx::registry().get(&id).ok_or_else(|| { + E::custom(format!("unable to find the component type {}", id)) + })?, + rm: stripped.is_some(), + }) + } + } + + deserializer.deserialize_str(Visitor { + _marker: PhantomData, + }) + } +} + +//TODO: implement encode and decode +/* +impl Encode for ComponentChanges<'_, '_, Cx> +where + Cx: ProvideIdTy, +{ + fn encode(&self, buf: B) -> Result<(), std::io::Error> + where + B: bytes::BufMut, + { + let c_valid = self.changes.iter().filter(|(_, v)| v.is_some()).count(); + let c_rm = self.changes.len() - c_valid; + VarI32(c_valid as i32).encode(buf)?; + VarI32(c_rm as i32).encode(buf)?; + } +} +*/ + +impl Debug for ComponentChanges<'_, '_, Cx> +where + Cx: ProvideIdTy + Debug, + Cx::Id: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.changes, f) + } +} diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 7a2b52b8..4721f880 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -13,6 +13,7 @@ use serde::{de::DeserializeOwned, Serialize}; type Object = dyn Any + Send + Sync; +pub mod changes; pub mod map; /// Type of a component data. @@ -23,7 +24,7 @@ pub mod map; #[derive(Debug)] #[doc(alias = "DataComponentType")] pub struct ComponentType { - f: &'static Funcs, + f: Funcs, _marker: PhantomData, } @@ -48,9 +49,10 @@ where impl ComponentType where - T: Clone + Eq + Encode + Decode + Send + Sync + 'static, + T: Encode + Decode + Send + Sync + 'static, { - const PACKET_CODEC: PacketCodec = PacketCodec { + /// Codec for packet encoding and decoding. + pub const PACKET_CODEC: PacketCodec = PacketCodec { encode: |obj, buf| { obj.downcast_ref::() .expect("mismatched type") @@ -63,7 +65,12 @@ where .decode_in_place(buf) }, }; +} +impl ComponentType +where + T: Clone + Eq + Send + Sync + 'static, +{ /// Creates a new transient component type. /// /// Transient components are not serialized. @@ -71,12 +78,12 @@ where /// This function requires the type to be `'static`. If the type is not `'static`, transmutes /// the type to `'static`, which is unsound but works. #[inline] - pub const fn transient() -> Self { + pub const fn transient(packet_codec: Option<&'static PacketCodec>) -> Self { Self { - f: &Funcs { + f: Funcs { serde_codec: None, - packet_codec: Self::PACKET_CODEC, - util: Self::UTIL, + packet_codec, + util: &Self::UTIL, }, _marker: PhantomData, } @@ -85,7 +92,7 @@ where impl ComponentType where - T: Clone + Eq + Encode + Decode + Serialize + DeserializeOwned + Send + Sync + 'static, + T: Clone + Eq + Serialize + DeserializeOwned + Send + Sync + 'static, { const SERDE_CODEC: SerdeCodec = SerdeCodec { ser: |obj| { @@ -112,13 +119,12 @@ where /// /// This function requires the type to be `'static`. If the type is not `'static`, transmutes /// the type to `'static`, which is unsound but works. - #[allow(clippy::missing_panics_doc)] - pub const fn persistent() -> Self { + pub const fn persistent(packet_codec: Option<&'static PacketCodec>) -> Self { Self { - f: &Funcs { - serde_codec: Some(Self::SERDE_CODEC), - packet_codec: Self::PACKET_CODEC, - util: Self::UTIL, + f: Funcs { + serde_codec: Some(&Self::SERDE_CODEC), + packet_codec, + util: &Self::UTIL, }, _marker: PhantomData, } @@ -143,11 +149,11 @@ impl ComponentType where T: Encode + Decode {} impl Default for ComponentType where - T: Clone + Eq + Encode + Decode + Send + Sync + 'static, + T: Clone + Eq + Send + Sync + 'static, { #[inline] fn default() -> Self { - Self::transient() + Self::transient(None) } } @@ -166,35 +172,39 @@ impl Clone for ComponentType { #[derive(Debug)] pub struct RawErasedComponentType { ty: TypeId, - f: &'static Funcs, + f: Funcs, _marker: PhantomData, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] struct SerdeCodec { ser: for<'a> fn(&'a Object) -> &'a dyn erased_serde::Serialize, de: fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result>, upd: fn(&mut Object, &mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result<()>, } -#[derive(Debug)] -struct PacketCodec { +/// Codec for packet encoding and decoding. +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub struct PacketCodec { encode: fn(&Object, &mut dyn bytes::BufMut) -> Result<(), std::io::Error>, decode: fn(&mut dyn bytes::Buf) -> Result, std::io::Error>, upd: fn(&mut Object, &mut dyn bytes::Buf) -> Result<(), std::io::Error>, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] struct DynUtil { clone: fn(&Object) -> Box, eq: fn(&Object, &Object) -> bool, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] struct Funcs { - serde_codec: Option, - packet_codec: PacketCodec, - util: DynUtil, + serde_codec: Option<&'static SerdeCodec>, + packet_codec: Option<&'static PacketCodec>, + util: &'static DynUtil, } impl RawErasedComponentType { diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index dd8bd496..17b7b852 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -10,10 +10,13 @@ use rimecraft_maybe::{Maybe, SimpleOwned}; use rimecraft_registry::ProvideRegistry; use serde::{Deserialize, Serialize}; -use crate::{ComponentType, ErasedComponentType, Object, RawErasedComponentType, SerdeCodec}; +use crate::{ + changes::ComponentChanges, ComponentType, ErasedComponentType, Object, RawErasedComponentType, + SerdeCodec, +}; #[repr(transparent)] -struct CompTyCell<'a, Cx: ProvideIdTy>(ErasedComponentType<'a, Cx>); +pub(crate) struct CompTyCell<'a, Cx: ProvideIdTy>(ErasedComponentType<'a, Cx>); /// A map that stores components. pub struct ComponentMap<'a, Cx>(MapInner<'a, Cx>) @@ -289,6 +292,17 @@ where pub fn iter(&self) -> Iter<'_, Cx> { self.into_iter() } + + /// Returns the changes of this map. + pub fn changes(&self) -> Option> { + if let MapInner::Patched { changes, .. } = &self.0 { + Some(ComponentChanges { + changes: Maybe::Borrowed(changes), + }) + } else { + None + } + } } impl<'a, Cx> IntoIterator for &'a ComponentMap<'a, Cx> diff --git a/crates/util/maybe/src/lib.rs b/crates/util/maybe/src/lib.rs index ad82e56b..ce7f2866 100644 --- a/crates/util/maybe/src/lib.rs +++ b/crates/util/maybe/src/lib.rs @@ -5,7 +5,7 @@ extern crate alloc; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use alloc::borrow::ToOwned; @@ -32,6 +32,20 @@ where Maybe::Owned(owned) => owned, } } + + /// Returns a mutable reference to the owned value, cloning it if necessary. + pub fn get_mut(this: &mut Self) -> &mut Owned + where + Owned: DerefMut, + { + match this { + Maybe::Borrowed(val) => { + *this = Maybe::Owned(val.to_owned().into()); + Self::get_mut(this) + } + Maybe::Owned(owned) => owned, + } + } } impl Deref for Maybe<'_, T, Owned> diff --git a/crates/util/registry/src/lib.rs b/crates/util/registry/src/lib.rs index a32bc1ce..df5cdc19 100644 --- a/crates/util/registry/src/lib.rs +++ b/crates/util/registry/src/lib.rs @@ -208,6 +208,12 @@ impl<'a, K, T> Reg<'a, K, T> { pub fn registry(this: Self) -> &'a Registry { this.registry } + + /// Gets the ID of this registration. + #[inline] + pub fn id(this: Self) -> &'a K { + <&RefEntry<_, _>>::from(this).key().value() + } } impl<'a, K, T> From> for &'a RefEntry { From 7ea4afb52b4e322a90fc6a3e1122b9f4e14b2371 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Wed, 15 May 2024 08:57:10 +0800 Subject: [PATCH 6/6] introduce rime-target --- crates/core/component/rime-target.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 crates/core/component/rime-target.toml diff --git a/crates/core/component/rime-target.toml b/crates/core/component/rime-target.toml new file mode 100644 index 00000000..e3a9858f --- /dev/null +++ b/crates/core/component/rime-target.toml @@ -0,0 +1,2 @@ +[java_edition] +version = "1.20.6"