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