statsig_rust/
interned_string.rs

1use std::{
2    borrow::Cow,
3    fmt::Display,
4    hash::{Hash, Hasher},
5    ops::Deref,
6    sync::Arc,
7};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use serde_json::value::RawValue;
11
12use crate::{
13    evaluation::dynamic_string::DynamicString, hashing, impl_interned_value,
14    interned_value_store::FromRawValue, log_e,
15};
16
17lazy_static::lazy_static! {
18    static ref EMPTY: InternedString = InternedString {
19        hash: 0,
20        value: Arc::new(String::new()),
21    };
22
23    static ref TRUE_STRING: InternedString = InternedString::from_string("true".to_string());
24    static ref FALSE_STRING: InternedString = InternedString::from_string("false".to_string());
25
26    static ref DEFAULT_RULE_ID: InternedString = InternedString::from_str_ref("default");
27}
28
29const TAG: &str = "InternedString";
30
31#[derive(Clone, Debug, Eq)]
32pub struct InternedString {
33    pub hash: u64,
34    pub value: Arc<String>,
35}
36
37#[macro_export]
38macro_rules! interned_str {
39    // String literal -> from_str_ref
40    ($value:literal) => {
41        InternedString::from_str_ref($value)
42    };
43    // Bool -> from_bool
44    (bool: $value:literal) => {
45        InternedString::from_bool($value)
46    };
47    // String (owned) -> from_string
48    ($value:expr) => {
49        InternedString::from_string($value)
50    };
51    // DynamicString -> from_dynamic_string
52    ($value:expr) => {
53        InternedString::from_dynamic_string($value)
54    };
55    // String parts (slice) -> from_str_parts
56    ( $value:expr) => {
57        InternedString::from_str_parts($value)
58    };
59}
60
61impl InternedString {
62    pub fn default_rule_id_ref() -> &'static Self {
63        &DEFAULT_RULE_ID
64    }
65
66    pub fn default_rule_id() -> Self {
67        DEFAULT_RULE_ID.clone()
68    }
69
70    pub fn from_str_ref(value: &str) -> Self {
71        let hash = hashing::hash_one(value);
72        let value = InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(value));
73        Self { hash, value }
74    }
75
76    pub fn from_string(value: String) -> Self {
77        let hash = hashing::hash_one(value.as_str());
78        let value = InternedString::get_or_create_memoized_string(hash, Cow::Owned(value));
79        Self { hash, value }
80    }
81
82    pub fn from_bool(value: bool) -> Self {
83        if value {
84            TRUE_STRING.clone()
85        } else {
86            FALSE_STRING.clone()
87        }
88    }
89
90    pub fn from_dynamic_string(value: &DynamicString) -> Self {
91        let hash = value.hash_value;
92        let value =
93            InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(&value.value));
94        Self { hash, value }
95    }
96
97    pub fn from_str_parts(parts: &[&str]) -> Self {
98        let mut value = String::new();
99        for v in parts {
100            value.push_str(v);
101        }
102
103        Self::from_string(value)
104    }
105
106    pub fn as_str(&self) -> &str {
107        self.value.as_str()
108    }
109
110    /// Clones the value out of the Arc. This is not performant.
111    /// Please only use this if we are giving a value to a caller outside of this library.
112    pub fn unperformant_to_string(&self) -> String {
113        self.value.to_string()
114    }
115
116    pub fn empty_ref() -> &'static Self {
117        &EMPTY
118    }
119
120    pub fn empty() -> Self {
121        EMPTY.clone()
122    }
123
124    pub fn is_empty(&self) -> bool {
125        self.value.is_empty()
126    }
127
128    fn get_or_create_memoized_string(hash: u64, input: Cow<'_, str>) -> Arc<String> {
129        if let Some(value) = INTERNED_STORE.try_get_interned_value(hash) {
130            return value;
131        }
132
133        let value = input.to_string();
134        let value_arc = Arc::new(value);
135        INTERNED_STORE.set_interned_value(hash, &value_arc);
136        value_arc
137    }
138}
139
140impl_interned_value!(InternedString, String);
141
142impl<'de> Deserialize<'de> for InternedString {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: Deserializer<'de>,
146    {
147        #[derive(Deserialize)]
148        #[serde(untagged)]
149        enum CowString<'a> {
150            #[serde(borrow)]
151            Borrowed(&'a str), // zero-copy for serde_json::from_str
152            Owned(String), // allocation for serde_json::from_value
153        }
154
155        let raw: CowString<'de> = CowString::deserialize(deserializer)?;
156        match raw {
157            CowString::Borrowed(raw) => {
158                let hash = hashing::hash_one(raw);
159                let value = InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(raw));
160                Ok(InternedString { hash, value })
161            }
162            CowString::Owned(raw) => {
163                let hash = hashing::hash_one(raw.as_str());
164                let value = InternedString::get_or_create_memoized_string(hash, Cow::Owned(raw));
165                Ok(InternedString { hash, value })
166            }
167        }
168    }
169}
170
171impl Serialize for InternedString {
172    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
173    where
174        S: Serializer,
175    {
176        self.value.serialize(serializer)
177    }
178}
179
180impl FromRawValue for String {
181    fn from_raw_value(raw_value: Cow<'_, RawValue>) -> Self {
182        match serde_json::from_str(raw_value.get()) {
183            Ok(value) => value,
184            Err(e) => {
185                log_e!(TAG, "Failed to convert raw value to String: {}", e);
186                String::new()
187            }
188        }
189    }
190}
191
192impl PartialEq for InternedString {
193    fn eq(&self, other: &Self) -> bool {
194        self.as_str() == other.as_str()
195    }
196}
197
198impl Hash for InternedString {
199    fn hash<H: Hasher>(&self, state: &mut H) {
200        self.hash.hash(state);
201    }
202}
203
204impl PartialEq<&str> for InternedString {
205    fn eq(&self, other: &&str) -> bool {
206        self.as_str() == *other
207    }
208}
209
210impl PartialEq<str> for InternedString {
211    fn eq(&self, other: &str) -> bool {
212        self.as_str() == other
213    }
214}
215
216impl PartialEq<String> for InternedString {
217    fn eq(&self, other: &String) -> bool {
218        self.as_str() == *other
219    }
220}
221
222impl Deref for InternedString {
223    type Target = str;
224
225    fn deref(&self) -> &Self::Target {
226        self.as_str()
227    }
228}
229
230impl Display for InternedString {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        self.value.fmt(f)
233    }
234}
235
236impl Default for InternedString {
237    fn default() -> Self {
238        EMPTY.clone()
239    }
240}