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_STRING: 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    static ref SALT_STRING: InternedString = InternedString::from_string("salt".to_string());
26}
27
28const TAG: &str = "InternedString";
29
30#[derive(Clone, Debug, Eq)]
31pub struct InternedString {
32    pub hash: u64,
33    pub value: Arc<String>,
34}
35
36impl InternedString {
37    pub fn from_str_ref(value: &str) -> Self {
38        let hash = hashing::hash_one(value);
39        let value = InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(value));
40        Self { hash, value }
41    }
42
43    pub fn from_string(value: String) -> Self {
44        let hash = hashing::hash_one(value.as_str());
45        let value = InternedString::get_or_create_memoized_string(hash, Cow::Owned(value));
46        Self { hash, value }
47    }
48
49    pub fn from_bool(value: bool) -> Self {
50        if value {
51            TRUE_STRING.clone()
52        } else {
53            FALSE_STRING.clone()
54        }
55    }
56
57    pub fn from_dynamic_string(value: &DynamicString) -> Self {
58        let hash = value.hash_value;
59        let value =
60            InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(&value.value));
61        Self { hash, value }
62    }
63
64    pub fn as_str(&self) -> &str {
65        self.value.as_str()
66    }
67
68    /// Clones the value out of the Arc. This is not performant.
69    /// Please only use this if we are giving a value to a caller outside of this library.
70    pub fn unperformant_to_string(&self) -> String {
71        self.value.to_string()
72    }
73
74    pub fn empty_ref() -> &'static Self {
75        &EMPTY_STRING
76    }
77
78    pub fn empty() -> Self {
79        EMPTY_STRING.clone()
80    }
81
82    pub fn salt_ref() -> &'static Self {
83        &SALT_STRING
84    }
85
86    fn get_or_create_memoized_string(hash: u64, input: Cow<'_, str>) -> Arc<String> {
87        if let Some(value) = INTERNED_STORE.try_get_interned_value(hash) {
88            return value;
89        }
90
91        let value = input.to_string();
92        let value_arc = Arc::new(value);
93        INTERNED_STORE.set_interned_value(hash, &value_arc);
94        value_arc
95    }
96}
97
98impl_interned_value!(InternedString, String);
99
100impl<'de> Deserialize<'de> for InternedString {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: Deserializer<'de>,
104    {
105        #[derive(Deserialize)]
106        #[serde(untagged)]
107        enum CowString<'a> {
108            #[serde(borrow)]
109            Borrowed(&'a str), // zero-copy for serde_json::from_str
110            Owned(String), // allocation for serde_json::from_value
111        }
112
113        let raw: CowString<'de> = CowString::deserialize(deserializer)?;
114        match raw {
115            CowString::Borrowed(raw) => {
116                let hash = hashing::hash_one(raw);
117                let value = InternedString::get_or_create_memoized_string(hash, Cow::Borrowed(raw));
118                Ok(InternedString { hash, value })
119            }
120            CowString::Owned(raw) => {
121                let hash = hashing::hash_one(raw.as_str());
122                let value = InternedString::get_or_create_memoized_string(hash, Cow::Owned(raw));
123                Ok(InternedString { hash, value })
124            }
125        }
126    }
127}
128
129impl Serialize for InternedString {
130    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131    where
132        S: Serializer,
133    {
134        self.value.serialize(serializer)
135    }
136}
137
138impl FromRawValue for String {
139    fn from_raw_value(raw_value: Cow<'_, RawValue>) -> Self {
140        match serde_json::from_str(raw_value.get()) {
141            Ok(value) => value,
142            Err(e) => {
143                log_e!(TAG, "Failed to convert raw value to String: {}", e);
144                String::new()
145            }
146        }
147    }
148}
149
150impl PartialEq for InternedString {
151    fn eq(&self, other: &Self) -> bool {
152        self.hash == other.hash
153    }
154}
155
156impl Hash for InternedString {
157    fn hash<H: Hasher>(&self, state: &mut H) {
158        self.hash.hash(state);
159    }
160}
161
162impl PartialEq<&str> for InternedString {
163    fn eq(&self, other: &&str) -> bool {
164        self.as_str() == *other
165    }
166}
167
168impl PartialEq<str> for InternedString {
169    fn eq(&self, other: &str) -> bool {
170        self.as_str() == other
171    }
172}
173
174impl PartialEq<String> for InternedString {
175    fn eq(&self, other: &String) -> bool {
176        self.as_str() == *other
177    }
178}
179
180impl Deref for InternedString {
181    type Target = str;
182
183    fn deref(&self) -> &Self::Target {
184        self.as_str()
185    }
186}
187
188impl Display for InternedString {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        self.value.fmt(f)
191    }
192}
193
194impl Default for InternedString {
195    fn default() -> Self {
196        EMPTY_STRING.clone()
197    }
198}