From da5a3605520c8a025429200163e819eda95f0178 Mon Sep 17 00:00:00 2001 From: Yjn024 Date: Wed, 13 Sep 2023 22:47:56 +0800 Subject: [PATCH] change caches impl to dashmap --- .gitignore | 1 + core/Cargo.toml | 4 +- core/impls.md | 2 +- core/src/block/entity.rs | 6 +- core/src/component.rs | 105 +++++++++--------- core/src/lib.rs | 2 +- core/src/nbt.rs | 210 +++++++++-------------------------- core/src/net/mod.rs | 41 +++++-- core/src/registry/mod.rs | 45 +++++++- core/src/registry/tag.rs | 3 +- core/src/util/collections.rs | 209 ++++++++++++++++++---------------- core/src/util/mod.rs | 5 +- core/src/world/tick.rs | 2 +- 13 files changed, 305 insertions(+), 330 deletions(-) diff --git a/.gitignore b/.gitignore index 9e73456..a00f1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /target /.tuffous/config_gui.json /.vscode +/.idea /.VSCodeCounter /Cargo.lock */Cargo.lock diff --git a/core/Cargo.toml b/core/Cargo.toml index c84ad1d..f2de290 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,9 +21,9 @@ bytes = "1.4" glam = "0.24" anyhow = "*" fastsnbt = "*" -fastnbt-rc = { version = "*", git = "https://github.com/rimecraft-rs/fastnbt" } +fastnbt = "*" parking_lot = "0.12" -lazy-regex = "2" +lazy-regex = "3" cesu8 = "*" dashmap = "5.4" bimap = "0.6" diff --git a/core/impls.md b/core/impls.md index e498994..34d2462 100644 --- a/core/impls.md +++ b/core/impls.md @@ -78,7 +78,7 @@ TODO ## Nbt -implemented by `fastnbt_rc`. +implemented by `fastnbt`. - [x] NbtCompound exts diff --git a/core/src/block/entity.rs b/core/src/block/entity.rs index e9d2bb4..468d30e 100644 --- a/core/src/block/entity.rs +++ b/core/src/block/entity.rs @@ -1,8 +1,4 @@ -use std::{ - any::TypeId, - hash::Hash, - ops::{Deref, DerefMut}, -}; +use std::{any::TypeId, hash::Hash}; use crate::prelude::*; diff --git a/core/src/component.rs b/core/src/component.rs index 70e537f..9bbe3d4 100644 --- a/core/src/component.rs +++ b/core/src/component.rs @@ -25,6 +25,7 @@ pub trait Attach { } /// Manager of components. +#[derive(Default)] pub struct Components { components: HashMap, TypeId)>, } @@ -36,9 +37,7 @@ impl Components { /// To create with external features, /// see [`Self::builder()`]. pub fn new() -> Self { - Self { - components: HashMap::new(), - } + Default::default() } /// Creates a new [`ComponentsBuilder`]. @@ -68,16 +67,13 @@ impl Components { where T: Attach + Send + Sync + 'static, { - self.components - .get(id) - .map(|value| { - if value.1 == TypeId::of::() { - Some(unsafe { &*(&*value.0 as *const (dyn Attach + Send + Sync) as *const T) }) - } else { - None - } - }) - .flatten() + self.components.get(id).and_then(|value| { + if value.1 == TypeId::of::() { + Some(unsafe { &*(&*value.0 as *const (dyn Attach + Send + Sync) as *const T) }) + } else { + None + } + }) } /// Get a mutable static typed component from this instance. @@ -85,18 +81,13 @@ impl Components { where T: Attach + Send + Sync + 'static, { - self.components - .get_mut(id) - .map(|value| { - if value.1 == TypeId::of::() { - Some(unsafe { - &mut *(&mut *value.0 as *mut (dyn Attach + Send + Sync) as *mut T) - }) - } else { - None - } - }) - .flatten() + self.components.get_mut(id).and_then(|value| { + if value.1 == TypeId::of::() { + Some(unsafe { &mut *(&mut *value.0 as *mut (dyn Attach + Send + Sync) as *mut T) }) + } else { + None + } + }) } } @@ -108,7 +99,7 @@ impl Encode for Components { let Component(event) = self.get::) -> anyhow::Result<()>>, - >>(&*NET_SEND_ID) + >>(&NET_SEND_ID) .expect("net send event component not found"); let mut hashmap = HashMap::new(); @@ -127,7 +118,7 @@ impl NetSync for Components { crate::MutOnly< crate::Event) -> anyhow::Result<()>>, >, - >>(&*NET_RECV_ID) + >>(&NET_RECV_ID) .expect("net recv event component not found"); let mut hashmap = HashMap::::decode(buf)?; @@ -143,16 +134,15 @@ impl serde::Serialize for Components { let Component(event) = self .get::) -> fastnbt_rc::error::Result<()>, + dyn Fn(&mut HashMap) -> fastnbt::error::Result<()>, >, - >>(&*NBT_SAVE_ID) + >>(&NBT_SAVE_ID) .expect("net send event component not found"); let mut hashmap = HashMap::new(); use serde::ser::Error; - event.invoker()(&mut hashmap) - .map_err(|err| ::Error::custom(err))?; + event.invoker()(&mut hashmap).map_err(::Error::custom)?; hashmap.serialize(serializer) } } @@ -169,24 +159,37 @@ impl crate::nbt::Update for Components { .get_mut::, - ) -> fastnbt_rc::error::Result<()>, + dyn Fn(&mut HashMap) -> fastnbt::error::Result<()>, >, >, - >>(&*NBT_READ_ID) + >>(&NBT_READ_ID) .expect("net recv event component not found"); use serde::{de::Error, Deserialize}; let mut hashmap = HashMap::deserialize(deserializer)?; event.as_mut().invoker()(&mut hashmap) - .map_err(|err| >::Error::custom(err)) + .map_err(>::Error::custom) } } +impl From for Components { + fn from(value: ComponentsBuilder) -> Self { + value.build() + } +} + +static ATTACH_EVENTS: parking_lot::RwLock> = + parking_lot::RwLock::new(crate::Event::new(|listeners| { + Box::new(move |type_id, components| { + for listener in listeners { + listener(type_id, components) + } + }) + })); + /// [`Components`] builder for creating with external features. pub struct ComponentsBuilder { - inner: Components, + pub inner: Components, } impl ComponentsBuilder { @@ -210,6 +213,14 @@ impl ComponentsBuilder { self } + pub fn register_defaults(mut self) -> Self + where + T: 'static, + { + ATTACH_EVENTS.read().invoker()(TypeId::of::(), &mut self.inner); + self + } + /// Build this instance into [`Components`]. #[inline] pub fn build(self) -> Components { @@ -217,12 +228,6 @@ impl ComponentsBuilder { } } -impl Into for ComponentsBuilder { - fn into(self) -> Components { - self.build() - } -} - /// Represents a simple component without extra /// attach features, which has an empty /// implementation of [`Attach`]. @@ -507,16 +512,14 @@ where let _ = span.enter(); if let Some(Component(event)) = components.get_mut::) -> fastnbt_rc::error::Result<()>, - >, + crate::Event) -> fastnbt::error::Result<()>>, >>(&*NBT_SAVE_ID) { event.register(Box::new(move |map| { let this = unsafe { &*ptr }; map.insert( this.1.clone(), - this.0.serialize(&mut fastnbt_rc::value::Serializer)?, + this.0.serialize(&mut fastnbt::value::Serializer)?, ); Ok(()) @@ -528,7 +531,7 @@ where if let Some(Component(event)) = components.get_mut::) -> fastnbt_rc::error::Result<()>, + dyn Fn(&mut HashMap) -> fastnbt::error::Result<()>, >, >, >>(&*NBT_READ_ID) @@ -615,7 +618,7 @@ where } fn nbt_event_comp() -> Component< - crate::Event) -> fastnbt_rc::error::Result<()>>, + crate::Event) -> fastnbt::error::Result<()>>, > { Component(crate::Event::new(|listeners| { Box::new(move |map| { @@ -630,7 +633,7 @@ fn nbt_event_comp() -> Component< fn nbt_event_comp_mut() -> Component< crate::MutOnly< - crate::Event) -> fastnbt_rc::error::Result<()>>, + crate::Event) -> fastnbt::error::Result<()>>, >, > { Component(crate::MutOnly::new(crate::Event::new(|listeners| { @@ -732,7 +735,7 @@ mod tests { let id_2 = crate::Id::new("test", "comp2".to_string()); components_0.register(id_2.clone(), Component(514_i32)); - let nbt = fastnbt_rc::to_value(components_0).unwrap(); + let nbt = fastnbt::to_value(components_0).unwrap(); let mut components_1 = Components::builder().nbt_storing().build(); diff --git a/core/src/lib.rs b/core/src/lib.rs index a8336b4..4166f28 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,7 +4,7 @@ pub mod entity; pub mod fluid; pub mod item; /// Thin wrapper between Rimecraft modules -/// and [`fastnbt_rc`] and [`fastsnbt`]. +/// and [`fastnbt`] and [`fastsnbt`]. pub mod nbt; pub mod net; /// Registry stuffs for managing almost all parts of in-game components. diff --git a/core/src/nbt.rs b/core/src/nbt.rs index e4b2f61..ca035ed 100644 --- a/core/src/nbt.rs +++ b/core/src/nbt.rs @@ -1,18 +1,16 @@ -pub use fastnbt_rc::Tag as NbtType; -pub use fastnbt_rc::Value as NbtElement; +pub use fastnbt::Tag as NbtType; +pub use fastnbt::Value as NbtElement; -pub use fastnbt_rc::{ +pub use fastnbt::{ from_bytes, from_bytes_with_opts, from_reader, nbt, to_bytes, to_writer, ByteArray, DeOpts, IntArray, LongArray, }; -pub use fastnbt_rc::value::from_value as from_nbt; -pub use fastnbt_rc::value::to_value as to_nbt; +pub use fastnbt::value::from_value as from_nbt; +pub use fastnbt::value::to_value as to_nbt; pub use fastsnbt::from_str; -use serde::de::Error; - pub type NbtCompound = std::collections::HashMap; pub type NbtList = Vec; @@ -123,192 +121,88 @@ impl NbtCompoundExt for NbtCompound { } fn get_i8(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Byte(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Byte(value) => Some(*value), + _ => None, + }) } fn get_i16(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Short(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Short(value) => Some(*value), + _ => None, + }) } fn get_i32(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Int(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Int(value) => Some(*value), + _ => None, + }) } fn get_i64(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Long(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Long(value) => Some(*value), + _ => None, + }) } fn get_f32(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Float(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Float(value) => Some(*value), + _ => None, + }) } fn get_f64(&self, key: &str) -> Option { - self.get(key) - .map(|e| match e { - NbtElement::Double(value) => Some(*value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Double(value) => Some(*value), + _ => None, + }) } fn get_str(&self, key: &str) -> Option<&str> { - self.get(key) - .map(|e| match e { - NbtElement::String(value) => Some(value.as_str()), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::String(value) => Some(value.as_str()), + _ => None, + }) } fn get_i8_slice(&self, key: &str) -> Option<&[i8]> { - self.get(key) - .map(|e| match e { - NbtElement::ByteArray(value) => Some(value.iter().as_slice()), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::ByteArray(value) => Some(value.iter().as_slice()), + _ => None, + }) } fn get_i32_slice(&self, key: &str) -> Option<&[i32]> { - self.get(key) - .map(|e| match e { - NbtElement::IntArray(value) => Some(value.iter().as_slice()), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::IntArray(value) => Some(value.iter().as_slice()), + _ => None, + }) } fn get_i64_slice(&self, key: &str) -> Option<&[i64]> { - self.get(key) - .map(|e| match e { - NbtElement::LongArray(value) => Some(value.iter().as_slice()), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::LongArray(value) => Some(value.iter().as_slice()), + _ => None, + }) } fn get_compound(&self, key: &str) -> Option<&NbtCompound> { - self.get(key) - .map(|e| match e { - NbtElement::Compound(value) => Some(value), - _ => None, - }) - .flatten() + self.get(key).and_then(|e| match e { + NbtElement::Compound(value) => Some(value), + _ => None, + }) } fn get_slice(&self, key: &str) -> Option<&[NbtElement]> { - self.get(key) - .map(|e| match e { - NbtElement::List(value) => Some(value.as_slice()), - _ => None, - }) - .flatten() - } -} - -/// [`fastnbt_rc::input::Input`] implementation for [`bytes::Buf`]. -pub struct BufInput<'a, T: bytes::Buf>(pub &'a mut T); - -impl<'de, T: bytes::Buf> fastnbt_rc::input::Input<'de> for BufInput<'de, T> { - fn consume_byte(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_u8()) - } - - fn ignore_str(&mut self) -> fastnbt_rc::error::Result<()> { - let len = self.0.get_u16() as usize; - self.ignore_bytes(len) - } - - fn ignore_bytes(&mut self, size: usize) -> fastnbt_rc::error::Result<()> { - for _ in 0..size { - self.0.get_u8(); - } - Ok(()) - } - - fn consume_str<'s>( - &'s mut self, - scratch: &'s mut Vec, - ) -> fastnbt_rc::error::Result> { - let n = self.0.get_u16() as usize; - scratch.clear(); - for i in 0..n { - scratch[i] = self.0.get_u8(); - } - - let str = cesu8::from_java_cesu8(scratch).map_err(|_| { - fastnbt_rc::error::Error::custom(format!("Non-unicode string: {:?}", scratch)) - })?; - - Ok(match str { - std::borrow::Cow::Borrowed(_) => fastnbt_rc::input::Reference::Copied(unsafe { - std::str::from_utf8_unchecked(scratch) - }), - std::borrow::Cow::Owned(s) => { - *scratch = s.into_bytes(); - fastnbt_rc::input::Reference::Copied(unsafe { - std::str::from_utf8_unchecked(scratch) - }) - } + self.get(key).and_then(|e| match e { + NbtElement::List(value) => Some(value.as_slice()), + _ => None, }) } - - fn consume_bytes<'s>( - &'s mut self, - n: usize, - scratch: &'s mut Vec, - ) -> fastnbt_rc::error::Result> { - scratch.clear(); - for i in 0..n { - scratch[i] = self.0.get_u8(); - } - Ok(fastnbt_rc::input::Reference::Copied(scratch.as_slice())) - } - - fn consume_i16(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_i16()) - } - - fn consume_i32(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_i32()) - } - - fn consume_i64(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_i64()) - } - - fn consume_f32(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_f32()) - } - - fn consume_f64(&mut self) -> fastnbt_rc::error::Result { - Ok(self.0.get_f64()) - } } pub trait Update: serde::Serialize { diff --git a/core/src/net/mod.rs b/core/src/net/mod.rs index c0895e1..c30c507 100644 --- a/core/src/net/mod.rs +++ b/core/src/net/mod.rs @@ -42,6 +42,38 @@ where /// Layer for encoding and decoding in nbt binary format for packets. pub struct Nbt<'a, T>(pub &'a T); +mod std_rw_imp { + pub struct ReadAdapt<'a, T: 'a>(pub &'a mut T) + where + T: bytes::Buf; + + impl std::io::Read for ReadAdapt<'_, T> + where + T: bytes::Buf, + { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + unsafe { &mut *(self.0 as *mut T as *mut bytes::buf::Reader) }.read(buf) + } + } + + pub struct WriteAdapt<'a, T: 'a>(pub &'a mut T) + where + T: bytes::BufMut; + + impl std::io::Write for WriteAdapt<'_, T> + where + T: bytes::BufMut, + { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + unsafe { &mut *(self.0 as *mut T as *mut bytes::buf::Writer) }.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + unsafe { &mut *(self.0 as *mut T as *mut bytes::buf::Writer) }.flush() + } + } +} + /// Layer for encoding and decoding in json utf8 for packets. pub struct Json<'a, T>(pub &'a T); @@ -359,9 +391,7 @@ mod packet_buf_imp { where B: bytes::BufMut, { - let mut vec = Vec::new(); - fastnbt_rc::to_writer(&mut vec, self.0)?; - buf.put_slice(&vec); + fastnbt::to_writer(super::std_rw_imp::WriteAdapt(buf), self.0)?; Ok(()) } } @@ -376,10 +406,7 @@ mod packet_buf_imp { where B: bytes::Buf, { - Ok(T::deserialize(&mut fastnbt_rc::de::Deserializer::new( - crate::nbt::BufInput(buf), - fastnbt_rc::DeOpts::new(), - ))?) + Ok(fastnbt::from_reader(super::std_rw_imp::ReadAdapt(buf))?) } } diff --git a/core/src/registry/mod.rs b/core/src/registry/mod.rs index a9c1545..019fae9 100644 --- a/core/src/registry/mod.rs +++ b/core/src/registry/mod.rs @@ -10,25 +10,39 @@ pub use tag::Key as TagKey; /// Represents a registration and its id and tags. pub struct Entry { + components: crate::component::Components, key: Key, pub tags: parking_lot::RwLock>>, value: T, } impl Entry { + #[inline] pub fn key(&self) -> &Key { &self.key } /// If this registration is in target tag. + #[inline] pub fn is_in(&self, tag: &tag::Key) -> bool { self.tags.read().contains(tag) } + + #[inline] + pub fn components(&self) -> &crate::component::Components { + &self.components + } + + #[inline] + pub fn components_mut(&mut self) -> &mut crate::component::Components { + &mut self.components + } } impl Deref for Entry { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { &self.value } @@ -49,11 +63,13 @@ pub struct Registry { } impl Registry { + #[inline] /// Whether this registry contains an entry with the target registry key. pub fn contains_key(&self, key: &Key) -> bool { self.key_map.contains_key(key) } + #[inline] /// Whether this registry contains an entry with the target id. pub fn contains_id(&self, id: &Id) -> bool { self.id_map.contains_key(id) @@ -65,6 +81,7 @@ impl Registry { /// /// Panic if a default entry don't exist. /// See [`Self::is_defaulted`]. + #[inline] pub fn default_entry(&self) -> (usize, &Entry) { let def = self .default @@ -73,6 +90,7 @@ impl Registry { } /// Get an entry from a [`Key`]. + #[inline] pub fn get_from_key(&self, key: &Key) -> Option<(usize, &Entry)> { self.key_map .get(key) @@ -80,6 +98,7 @@ impl Registry { } /// Get an entry from an [`Id`]. + #[inline] pub fn get_from_id(&self, id: &Id) -> Option<(usize, &Entry)> { self.id_map .get(id) @@ -87,20 +106,24 @@ impl Registry { } /// Get an entry from its raw id. + #[inline] pub fn get_from_raw(&self, raw_id: usize) -> Option<&Entry> { self.entries.get(raw_id) } /// Whether a default entry exist in this registry. + #[inline] pub fn is_defaulted(&self) -> bool { self.default.is_some() } /// Returns an iterator over the slice of entries. + #[inline] pub fn iter(&self) -> std::slice::Iter<'_, Entry> { self.entries.iter() } + #[inline] pub fn len(&self) -> usize { self.entries.len() } @@ -173,7 +196,10 @@ impl Builder { } } -impl crate::util::Freeze> for Builder { +impl crate::util::Freeze> for Builder +where + T: Registration + 'static, +{ type Opts = (Key>, Option); fn build(self, opts: Self::Opts) -> Registry { @@ -187,27 +213,36 @@ impl crate::util::Freeze> for Builder { value: e.1 .0, key: Key::new(opts.0, e.1 .1.clone()), tags: parking_lot::RwLock::new(Vec::new()), + components: crate::component::Components::builder() + .net_sync() + .register_defaults::>() + .build(), } }) .collect::>(); let id_map = { let mut map = std::collections::HashMap::new(); + for e in entries.iter().enumerate() { map.insert(e.1.key.value().clone(), e.0); } + map }; Registry { - default: opts.1.map(|e| id_map.get(&e).copied()).flatten(), key_map: { let mut map = std::collections::HashMap::new(); + for e in entries.iter().enumerate() { map.insert(e.1.key.clone(), e.0); } + map }, + + default: opts.1.map(|e| id_map.get(&e).copied()).flatten(), entries, id_map, key: opts.0, @@ -228,7 +263,8 @@ pub trait RegistryAccess: Sized { fn registry() -> &'static Registry; } -static KEYS_CACHE: crate::collections::Caches<(Id, Id)> = crate::collections::Caches::new(); +static KEYS_CACHE: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(crate::collections::Caches::new); /// Represents a key for a value in a registry in a context where /// a root registry is available. @@ -249,6 +285,7 @@ impl Key { } /// Whether this registry key belongs to the given registry. + #[inline] pub fn is_of(&self, reg: &Key>) -> bool { self.inner.0 .0 == reg.inner.1 } @@ -266,11 +303,13 @@ impl Key { } /// Value of this key. + #[inline] pub fn value(&self) -> &'static Id { &self.inner.0 .0 } /// Registry of this key. + #[inline] pub fn reg(&self) -> &'static Id { &self.inner.0 .1 } diff --git a/core/src/registry/tag.rs b/core/src/registry/tag.rs index 53610db..1e07b87 100644 --- a/core/src/registry/tag.rs +++ b/core/src/registry/tag.rs @@ -1,6 +1,7 @@ use crate::prelude::*; -static KEYS_CACHE: crate::collections::ArcCaches = crate::collections::ArcCaches::new(); +static KEYS_CACHE: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(crate::collections::ArcCaches::new); /// Represents a tag key. pub struct Key { diff --git a/core/src/util/collections.rs b/core/src/util/collections.rs index eac8317..d51168a 100644 --- a/core/src/util/collections.rs +++ b/core/src/util/collections.rs @@ -1,7 +1,6 @@ -use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, -}; +use crate::net::Encode; +use std::{borrow::Borrow, hash::Hash, ops::Deref}; +use tracing::instrument; pub const DEFAULT_INDEXED_INDEX: i32 = -1; @@ -10,6 +9,11 @@ pub trait Indexed { fn raw_id(&self, value: &T) -> Option; fn get(&self, index: usize) -> Option<&T>; fn len(&self) -> usize; + + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } } /// An id list, just targeting the `IdList` in MCJE. @@ -23,12 +27,9 @@ pub struct IdList { } impl IdList { + #[inline] pub fn new() -> Self { - Self { - next_id: 0, - id_map: std::collections::HashMap::new(), - vec: vec![], - } + Default::default() } pub fn with_capacity(capacity: usize) -> Self { @@ -65,6 +66,20 @@ impl IdList { } } +impl Default for IdList +where + T: Hash + Eq + Clone, +{ + #[inline] + fn default() -> Self { + Self { + next_id: 0, + id_map: std::collections::HashMap::new(), + vec: vec![], + } + } +} + impl Indexed for IdList { fn raw_id(&self, value: &T) -> Option { self.id_map.get(value).copied().map(|e| e as usize) @@ -334,29 +349,25 @@ mod packed_array_tests { } } -/// Hash-based caches that leaked into heap. +/// Thread safe and hash-based caches. /// /// A caches is a collection that provide cached value of /// a given value to reduce memory usage. -/// -/// # Safety -/// -/// Although the values are leaked into heap, they will be -/// dropped when dropping the instance to prevent memory leaking. pub struct Caches where T: Hash + Eq, { - map: parking_lot::RwLock>, + map: dashmap::DashSet>, } impl Caches where T: Hash + Eq, { - pub const fn new() -> Self { + #[inline] + pub fn new() -> Self { Self { - map: parking_lot::RwLock::new(Vec::new()), + map: dashmap::DashSet::new(), } } @@ -365,64 +376,45 @@ where /// If an equaled value dosen't exist in this caches, the value /// will be leaked into heap. pub fn get<'a>(&'a self, value: T) -> &'a T { - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); - - let read = self.map.read(); - - if let Some(entry) = read.iter().find(|entry| entry.0 == hash) { - unsafe { &*entry.1 } + if let Some(v) = self.map.get(&value) { + unsafe { &*(v.deref().deref() as *const T) } } else { - drop(read); - - let reference = Box::leak(Box::new(value)); - self.map.write().push((hash, reference as *const T)); - reference + let boxed = Box::new(value); + let ptr = boxed.deref() as *const T; + self.map.insert(boxed); + unsafe { &*ptr } } } - pub fn contains(&self, value: &T) -> bool { - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); - - self.map.read().iter().any(|value| value.0 == hash) - } -} - -impl Drop for Caches -where - T: Hash + Eq, -{ - fn drop(&mut self) { - for value in self.map.get_mut() { - let _ = unsafe { Box::from_raw(value.1 as *mut T) }; - } + #[inline] + pub fn contains(&self, value: &T) -> bool + where + T: Borrow, + Q: ?Sized, + { + self.map.contains(value) } } -unsafe impl Send for Caches where T: Hash + Eq + Send {} -unsafe impl Sync for Caches where T: Hash + Eq + Sync {} - /// A variant of hash-based [`Caches`], where values are stored in weak /// pointers and values are provided with [`std::sync::Arc`]. /// /// Caches with zero strong count will be soon destroyed. pub struct ArcCaches where - T: Hash + Eq, + T: Hash + Eq + 'static, { - map: parking_lot::RwLock)>>, + map: dashmap::DashSet>, } impl ArcCaches where - T: Hash + Eq, + T: Hash + Eq + 'static, { - pub const fn new() -> Self { + #[inline] + pub fn new() -> Self { Self { - map: parking_lot::RwLock::new(Vec::new()), + map: dashmap::DashSet::new(), } } @@ -431,46 +423,81 @@ where /// If an equaled value dosen't exist in this caches, the value /// will be stored in a new [`std::sync::Arc`]. pub fn get(&self, value: T) -> std::sync::Arc { - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); - - let read = self.map.read(); - - if let Some(entry) = read.iter().enumerate().find(|entry| entry.1 .0 == hash) { - if let Some(arc) = entry.1 .1.upgrade() { - arc + if let Some(v) = self.map.get(&arc_caches_imp::WeakNode::Ref(unsafe { + &*(&value as *const T) + })) { + if let arc_caches_imp::WeakNode::Stored(weak) = v.deref() { + weak.upgrade().expect("invalid weak pointer") } else { - let pos = entry.0; - drop(read); - - let arc = std::sync::Arc::new(value); - self.map.write().get_mut(pos).unwrap().1 = std::sync::Arc::downgrade(&arc); - arc + unreachable!() } } else { - drop(read); - let mut write = self.map.write(); let arc = std::sync::Arc::new(value); - let entry_expected = (hash, std::sync::Arc::downgrade(&arc)); - - if let Some(entry) = write.iter_mut().find(|entry| entry.1.strong_count() == 0) { - *entry = entry_expected - } else { - write.push(entry_expected) - } + self.map + .insert(arc_caches_imp::WeakNode::Stored(std::sync::Arc::downgrade( + &arc, + ))); arc } } + #[inline] pub fn contains(&self, value: &T) -> bool { - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash = hasher.finish(); + self.map.contains(&arc_caches_imp::WeakNode::Ref(unsafe { + &*(value as *const T) + })) + } +} - self.map.read().iter().any(|value| value.0 == hash) +mod arc_caches_imp { + use std::ops::Deref; + use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + sync::Weak, + }; + + pub enum WeakNode<'a, T> { + Stored(Weak), + Ref(&'a T), + } + + impl Hash for WeakNode<'_, T> + where + T: Hash, + { + fn hash(&self, state: &mut H) { + match self { + WeakNode::Stored(value) => { + if let Some(v) = value.upgrade() { + v.hash(state) + } + } + WeakNode::Ref(value) => value.hash(state), + } + } } + + impl PartialEq for WeakNode<'_, T> + where + T: Hash + Eq, + { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (WeakNode::Stored(value0), WeakNode::Stored(value1)) => value0.ptr_eq(value1), + (WeakNode::Ref(value0), WeakNode::Stored(value1)) => { + value1.upgrade().map_or(false, |e| e.deref() == *value0) + } + (WeakNode::Stored(value0), WeakNode::Ref(value1)) => { + value0.upgrade().map_or(false, |e| e.deref() == *value1) + } + (WeakNode::Ref(value0), WeakNode::Ref(value1)) => value0 == value1, + } + } + } + + impl Eq for WeakNode<'_, T> where T: Hash + Eq {} } #[cfg(test)] @@ -502,20 +529,6 @@ mod tests_caches { first_ptr.deref() as *const String as usize ); } - - #[test] - fn arc_destroying() { - let caches: ArcCaches = ArcCaches::new(); - let first_ptr = caches.get("1".to_string()); - let _second_ptr = caches.get("2".to_string()); - - assert_eq!(caches.map.read().len(), 2); - - drop(first_ptr); - let _third_ptr = caches.get("3".to_string()); - - assert_eq!(caches.map.read().len(), 2); - } } pub trait Weighted { diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 6541cc8..990dac9 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -4,7 +4,8 @@ pub mod collections; mod magic_num; pub mod math; -static ID_NAMESPACE_CACHES: crate::collections::Caches = crate::collections::Caches::new(); +static ID_NAMESPACE_CACHES: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(crate::collections::Caches::new); /// An identifier used to identify things. /// @@ -26,7 +27,7 @@ impl Id { pub fn try_new(namespace: &str, path: String) -> Result { let owned_namespace = namespace.to_string(); if Self::is_path_valid(&path) { - if !ID_NAMESPACE_CACHES.contains(&owned_namespace) + if !ID_NAMESPACE_CACHES.contains::(&owned_namespace) && !Self::is_namespace_valid(namespace) { return Err(IdError::InvalidChars { diff --git a/core/src/world/tick.rs b/core/src/world/tick.rs index a1d3823..1d858b7 100644 --- a/core/src/world/tick.rs +++ b/core/src/world/tick.rs @@ -38,7 +38,7 @@ impl Tick { let l: i64 = pos.into(); for nbt in tick_list.iter() { let tick = match nbt { - fastnbt_rc::Value::Compound(value) => Self::from_nbt(value, |n| name_to_type_fn(n)), + fastnbt::Value::Compound(value) => Self::from_nbt(value, |n| name_to_type_fn(n)), _ => None, };