statsig_rust/
interned_value_store.rs1use std::{
2 borrow::Cow,
3 sync::{Arc, Weak},
4 time::Duration,
5};
6
7use ahash::HashMap;
8use parking_lot::Mutex;
9use serde_json::value::RawValue;
10
11use crate::log_e;
12
13pub trait FromRawValue {
14 fn from_raw_value(raw_value: Cow<'_, RawValue>) -> Self;
15}
16
17pub struct InternedValueStore<T: FromRawValue> {
18 tag: &'static str,
19 pub(crate) values: Mutex<HashMap<u64, Weak<T>>>,
20}
21
22impl<T: FromRawValue> InternedValueStore<T> {
23 pub fn new(tag: &'static str) -> Self {
24 Self {
25 tag,
26 values: Mutex::new(HashMap::default()),
27 }
28 }
29
30 pub fn get_or_create_interned_value(
31 &self,
32 hash: u64,
33 raw_value: Cow<'_, RawValue>,
34 ) -> (u64, Arc<T>) {
35 if let Some(value) = self.try_get_interned_value(hash) {
36 return (hash, value);
37 }
38
39 let value = T::from_raw_value(raw_value);
40 let value_arc = Arc::new(value);
41 self.set_interned_value(hash, &value_arc);
42
43 (hash, value_arc)
44 }
45
46 pub fn try_remove_interned_value(&self, hash: u64) {
47 let mut memoized_values = match self.values.try_lock_for(Duration::from_secs(5)) {
48 Some(values) => values,
49 None => return,
50 };
51
52 let found = match memoized_values.get(&hash) {
53 Some(value) => value,
54 None => return,
55 };
56
57 if found.strong_count() == 1 {
58 memoized_values.remove(&hash);
59 }
60 }
61
62 pub fn set_interned_value(&self, hash: u64, value: &Arc<T>) {
63 let mut memoized_values = match self.values.try_lock_for(Duration::from_secs(5)) {
64 Some(values) => values,
65 None => return,
66 };
67 memoized_values.insert(hash, Arc::downgrade(value));
68 }
69
70 pub fn try_get_interned_value(&self, hash: u64) -> Option<Arc<T>> {
71 let memoized_values = match self.values.try_lock_for(Duration::from_secs(5)) {
72 Some(values) => values,
73 None => {
74 log_e!(self.tag, "Failed to lock interned values map");
75 return None;
76 }
77 };
78
79 memoized_values.get(&hash).and_then(|value| value.upgrade())
80 }
81}
82
83#[macro_export]
84macro_rules! impl_interned_value {
85 ($struct_name:ident, $memoized_type:ident) => {
86 lazy_static::lazy_static! {
87 pub(crate) static ref INTERNED_STORE: $crate::interned_value_store::InternedValueStore<$memoized_type> = $crate::interned_value_store::InternedValueStore::new(stringify!($struct_name));
88 }
89
90 impl Drop for $struct_name {
91 fn drop(&mut self) {
92 INTERNED_STORE.try_remove_interned_value(self.hash);
93 }
94 }
95
96 impl $struct_name {
97 #[allow(dead_code)]
98 fn get_or_create_memoized(raw_value: Cow<'_, RawValue>) -> (u64, std::sync::Arc<$memoized_type>) {
99 let hash = $crate::hashing::hash_one(raw_value.get());
100 INTERNED_STORE.get_or_create_interned_value(hash, raw_value)
101 }
102 }
103 };
104}