Skip to content

Commit 7443a31

Browse files
committed
Merge OptimizedInterner into Interner
1 parent 4bd9b8b commit 7443a31

File tree

3 files changed

+63
-66
lines changed

3 files changed

+63
-66
lines changed

crates/bevy_ecs/src/schedule/set.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::marker::PhantomData;
66

77
pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
88
use bevy_utils::define_label;
9-
use bevy_utils::intern::{Interned, Leak, OptimizedInterner, StaticRef};
9+
use bevy_utils::intern::{Interned, Interner, Leak, StaticRef};
1010
use bevy_utils::label::DynHash;
1111

1212
use crate::system::{
@@ -19,7 +19,7 @@ define_label!(
1919
SCHEDULE_LABEL_INTERNER
2020
);
2121

22-
static SYSTEM_SET_INTERNER: OptimizedInterner<dyn SystemSet> = OptimizedInterner::new();
22+
static SYSTEM_SET_INTERNER: Interner<dyn SystemSet> = Interner::new();
2323
/// A shorthand for `Interned<dyn SystemSet>`.
2424
pub type InternedSystemSet = Interned<dyn SystemSet>;
2525
/// A shorthand for `Interned<dyn ScheduleLabel>`.

crates/bevy_utils/src/intern.rs

Lines changed: 59 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -104,57 +104,10 @@ impl<T: Clone + ?Sized> Leak<T> for Cow<'static, T> {
104104
}
105105
}
106106

107-
/// A thread-safe interner which can be used to create [`Interned<T>`] from `&T`
108-
///
109-
/// For details on interning, see [the module level docs](self).
110-
///
111-
/// The implementation ensures that two equal values return two equal [`Interned<T>`] values.
112-
pub struct Interner<T: ?Sized + 'static>(OnceLock<RwLock<HashSet<&'static T>>>);
113-
114-
impl<T: ?Sized> Interner<T> {
115-
/// Creates a new empty interner
116-
pub const fn new() -> Self {
117-
Self(OnceLock::new())
118-
}
119-
}
120-
121-
impl<T: Hash + Eq + ?Sized> Interner<T> {
122-
/// Return the [`Interned<T>`] corresponding to `value`.
123-
///
124-
/// If it is called the first time for `value`, it will possibly leak the value and return an
125-
/// [`Interned<T>`] using the obtained static reference. Subsequent calls for the same `value`
126-
/// will return [`Interned<T>`] using the same static reference.
127-
pub fn intern<Q: Leak<T>>(&self, value: Q) -> Interned<T> {
128-
let lock = self.0.get_or_init(Default::default);
129-
{
130-
let set = lock.read().unwrap_or_else(PoisonError::into_inner);
131-
if let Some(value) = set.get(value.borrow()) {
132-
return Interned(*value);
133-
}
134-
}
135-
{
136-
let mut set = lock.write().unwrap_or_else(PoisonError::into_inner);
137-
if let Some(value) = set.get(value.borrow()) {
138-
Interned(*value)
139-
} else {
140-
let leaked = value.leak();
141-
set.insert(leaked);
142-
Interned(leaked)
143-
}
144-
}
145-
}
146-
}
147-
148-
impl<T: ?Sized> Default for Interner<T> {
149-
fn default() -> Self {
150-
Self::new()
151-
}
152-
}
153-
154107
/// A type that can provide static references to equal values.
155108
pub trait StaticRef {
156109
/// Returns a static reference to a value equal to `self`, if possible.
157-
/// This method is used by [`OptimizedInterner::intern`] to optimize the interning process.
110+
/// This method is used by [`Interner::intern`] to optimize the interning process.
158111
///
159112
/// # Invariant
160113
///
@@ -181,37 +134,66 @@ pub trait StaticRef {
181134
/// }
182135
/// }
183136
/// ```
184-
fn static_ref(&self) -> Option<&'static Self>;
137+
///
138+
/// # Provided implementation
139+
///
140+
/// The provided implementation always returns `None`.
141+
fn static_ref(&self) -> Option<&'static Self> {
142+
None
143+
}
185144
}
186145

187-
/// An optimized [`Interner`] for types that implement [`StaticRef`].
188-
pub struct OptimizedInterner<T: ?Sized + 'static>(Interner<T>);
146+
impl StaticRef for str {}
147+
148+
/// A thread-safe interner which can be used to create [`Interned<T>`] from `&T`
149+
///
150+
/// For details on interning, see [the module level docs](self).
151+
///
152+
/// The implementation ensures that two equal values return two equal [`Interned<T>`] values.
153+
///
154+
/// To use an [`Interner<T>`], `T` must implement [`StaticRef`], [`Hash`] and [`Eq`].
155+
pub struct Interner<T: ?Sized + 'static>(OnceLock<RwLock<HashSet<&'static T>>>);
189156

190-
impl<T: ?Sized> OptimizedInterner<T> {
157+
impl<T: ?Sized> Interner<T> {
191158
/// Creates a new empty interner
192159
pub const fn new() -> Self {
193-
Self(Interner::new())
160+
Self(OnceLock::new())
194161
}
195162
}
196163

197-
impl<T: StaticRef + Hash + Eq + ?Sized> OptimizedInterner<T> {
164+
impl<T: StaticRef + Hash + Eq + ?Sized> Interner<T> {
198165
/// Return the [`Interned<T>`] corresponding to `value`.
199166
///
200167
/// If it is called the first time for `value`, it will possibly leak the value and return an
201168
/// [`Interned<T>`] using the obtained static reference. Subsequent calls for the same `value`
202169
/// will return [`Interned<T>`] using the same static reference.
203170
///
204-
/// Compared to [`Interner::intern`], this uses [`StaticRef::static_ref`] to short-circuit
205-
/// the interning process.
171+
/// This uses [`StaticRef::static_ref`] to short-circuit the interning process.
206172
pub fn intern<Q: Leak<T>>(&self, value: Q) -> Interned<T> {
207173
if let Some(value) = value.borrow().static_ref() {
208174
return Interned(value);
209175
}
210-
self.0.intern(value)
176+
let lock = self.0.get_or_init(Default::default);
177+
{
178+
let set = lock.read().unwrap_or_else(PoisonError::into_inner);
179+
if let Some(value) = set.get(value.borrow()) {
180+
return Interned(*value);
181+
}
182+
}
183+
{
184+
let mut set = lock.write().unwrap_or_else(PoisonError::into_inner);
185+
if let Some(value) = set.get(value.borrow()) {
186+
Interned(*value)
187+
} else {
188+
let leaked = value.leak();
189+
set.insert(leaked);
190+
Interned(leaked)
191+
}
192+
}
211193
}
212194
}
213195

214-
impl<T: ?Sized> Default for OptimizedInterner<T> {
196+
impl<T: ?Sized> Default for Interner<T> {
215197
fn default() -> Self {
216198
Self::new()
217199
}
@@ -224,33 +206,48 @@ mod tests {
224206
hash::{Hash, Hasher},
225207
};
226208

227-
use crate::intern::{Interned, Interner, Leak};
209+
use crate::intern::{Interned, Interner, Leak, StaticRef};
228210

229211
#[test]
230212
fn zero_sized_type() {
231213
#[derive(PartialEq, Eq, Hash, Debug)]
232214
pub struct A;
233215

216+
impl StaticRef for A {
217+
fn static_ref(&self) -> Option<&'static Self> {
218+
Some(&A)
219+
}
220+
}
221+
234222
impl Leak<A> for A {
235223
fn leak(self) -> &'static Self {
236224
&A
237225
}
238226
}
239227

240228
let interner = Interner::default();
241-
let x = interner.intern(&A);
242-
let y = interner.intern(&A);
229+
let x = interner.intern(A);
230+
let y = interner.intern(A);
243231
assert_eq!(x, y);
244232
}
245233

246234
#[test]
247235
fn fieldless_enum() {
248-
#[derive(PartialEq, Eq, Hash, Debug)]
236+
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
249237
pub enum A {
250238
X,
251239
Y,
252240
}
253241

242+
impl StaticRef for A {
243+
fn static_ref(&self) -> Option<&'static Self> {
244+
Some(match self {
245+
A::X => &A::X,
246+
A::Y => &A::Y,
247+
})
248+
}
249+
}
250+
254251
impl Leak<A> for A {
255252
fn leak(self) -> &'static Self {
256253
match self {

crates/bevy_utils/src/label.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ macro_rules! define_label {
162162
}
163163
}
164164

165-
static $interner_name: ::bevy_utils::intern::OptimizedInterner<dyn $label_trait_name> =
166-
::bevy_utils::intern::OptimizedInterner::new();
165+
static $interner_name: ::bevy_utils::intern::Interner<dyn $label_trait_name> =
166+
::bevy_utils::intern::Interner::new();
167167

168168
impl From<&dyn $label_trait_name>
169169
for ::bevy_utils::intern::Interned<dyn $label_trait_name>

0 commit comments

Comments
 (0)