role_system/
performance.rs1use std::borrow::Cow;
4use std::collections::VecDeque;
5use std::sync::Mutex;
6
7pub struct StringPool {
9 pool: Mutex<VecDeque<String>>,
10 max_size: usize,
11}
12
13impl StringPool {
14 pub fn new(max_size: usize) -> Self {
16 Self {
17 pool: Mutex::new(VecDeque::with_capacity(max_size)),
18 max_size,
19 }
20 }
21
22 pub fn get_string(&self) -> String {
24 self.pool.lock().unwrap().pop_front().unwrap_or_default()
25 }
26
27 pub fn return_string(&self, mut s: String) {
29 s.clear();
30 let mut pool = self.pool.lock().unwrap();
31 if pool.len() < self.max_size {
32 pool.push_back(s);
33 }
34 }
35
36 pub fn pool_size(&self) -> usize {
38 self.pool.lock().unwrap().len()
39 }
40
41 pub fn clear(&self) {
43 self.pool.lock().unwrap().clear();
44 }
45}
46
47impl Default for StringPool {
48 fn default() -> Self {
49 Self::new(100) }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
55pub struct CacheKey<'a> {
56 pub subject_id: Cow<'a, str>,
57 pub permission_key: Cow<'a, str>,
58}
59
60impl<'a> CacheKey<'a> {
61 pub fn borrowed(subject_id: &'a str, permission_key: &'a str) -> Self {
63 Self {
64 subject_id: Cow::Borrowed(subject_id),
65 permission_key: Cow::Borrowed(permission_key),
66 }
67 }
68
69 pub fn owned(subject_id: String, permission_key: String) -> Self {
71 Self {
72 subject_id: Cow::Owned(subject_id),
73 permission_key: Cow::Owned(permission_key),
74 }
75 }
76
77 pub fn into_owned(self) -> CacheKey<'static> {
79 CacheKey {
80 subject_id: Cow::Owned(self.subject_id.into_owned()),
81 permission_key: Cow::Owned(self.permission_key.into_owned()),
82 }
83 }
84}
85
86pub struct ObjectPool<T> {
88 pool: Mutex<VecDeque<T>>,
89 factory: Box<dyn Fn() -> T + Send + Sync>,
90 reset: Box<dyn Fn(&mut T) + Send + Sync>,
91 max_size: usize,
92}
93
94impl<T> ObjectPool<T> {
95 pub fn new<F, R>(max_size: usize, factory: F, reset: R) -> Self
97 where
98 F: Fn() -> T + Send + Sync + 'static,
99 R: Fn(&mut T) + Send + Sync + 'static,
100 {
101 Self {
102 pool: Mutex::new(VecDeque::with_capacity(max_size)),
103 factory: Box::new(factory),
104 reset: Box::new(reset),
105 max_size,
106 }
107 }
108
109 pub fn get<'a>(&'a self) -> PooledObject<'a, T> {
111 let obj = self
112 .pool
113 .lock()
114 .unwrap()
115 .pop_front()
116 .unwrap_or_else(|| (self.factory)());
117
118 PooledObject::new(obj, self)
119 }
120
121 fn return_object(&self, mut obj: T) {
123 (self.reset)(&mut obj);
124 let mut pool = self.pool.lock().unwrap();
125 if pool.len() < self.max_size {
126 pool.push_back(obj);
127 }
128 }
129
130 pub fn pool_size(&self) -> usize {
132 self.pool.lock().unwrap().len()
133 }
134}
135
136pub struct PooledObject<'a, T> {
138 obj: Option<T>,
139 pool: &'a ObjectPool<T>,
140}
141
142impl<'a, T> PooledObject<'a, T> {
143 fn new(obj: T, pool: &'a ObjectPool<T>) -> Self {
144 Self {
145 obj: Some(obj),
146 pool,
147 }
148 }
149}
150
151impl<'a, T> std::ops::Deref for PooledObject<'a, T> {
152 type Target = T;
153
154 fn deref(&self) -> &Self::Target {
155 self.obj.as_ref().unwrap()
156 }
157}
158
159impl<'a, T> std::ops::DerefMut for PooledObject<'a, T> {
160 fn deref_mut(&mut self) -> &mut Self::Target {
161 self.obj.as_mut().unwrap()
162 }
163}
164
165impl<'a, T> Drop for PooledObject<'a, T> {
166 fn drop(&mut self) {
167 if let Some(obj) = self.obj.take() {
168 self.pool.return_object(obj);
169 }
170 }
171}
172
173pub mod string_ops {
175 use super::*;
176 use std::collections::HashMap;
177
178 thread_local! {
179 static STRING_POOL: StringPool = StringPool::default();
180 }
181
182 pub fn get_pooled_string() -> String {
184 STRING_POOL.with(|pool| pool.get_string())
185 }
186
187 pub fn return_pooled_string(s: String) {
189 STRING_POOL.with(|pool| pool.return_string(s));
190 }
191
192 pub fn create_permission_key(action: &str, resource_id: &str, context_hash: &str) -> String {
194 if context_hash.is_empty() {
195 format!("{}:{}", action, resource_id)
196 } else {
197 format!("{}:{}:{}", action, resource_id, context_hash)
198 }
199 }
200
201 pub fn hash_context(context: &HashMap<String, String>) -> String {
203 if context.is_empty() {
204 String::new()
205 } else {
206 use std::collections::hash_map::DefaultHasher;
207 use std::hash::{Hash, Hasher};
208
209 let mut hasher = DefaultHasher::new();
210
211 let mut sorted_keys: Vec<_> = context.keys().collect();
213 sorted_keys.sort();
214
215 for key in sorted_keys {
216 key.hash(&mut hasher);
217 context[key].hash(&mut hasher);
218 }
219
220 format!("{:x}", hasher.finish())
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use std::collections::HashMap;
229
230 #[test]
231 fn test_string_pool() {
232 let pool = StringPool::new(5);
233
234 let s1 = pool.get_string();
236 let s2 = pool.get_string();
237
238 pool.return_string(s1);
239 pool.return_string(s2);
240
241 assert_eq!(pool.pool_size(), 2);
242
243 for _ in 0..10 {
245 pool.return_string(String::new());
246 }
247 assert_eq!(pool.pool_size(), 5);
248 }
249
250 #[test]
251 fn test_cache_key() {
252 let key1 = CacheKey::borrowed("user1", "read:docs");
253 let key2 = CacheKey::owned("user1".to_string(), "read:docs".to_string());
254
255 assert_eq!(key1, key2);
256
257 let owned_key = key1.into_owned();
258 assert_eq!(owned_key.subject_id, "user1");
259 }
260
261 #[test]
262 fn test_object_pool() {
263 let pool = ObjectPool::new(3, Vec::<i32>::new, |v| v.clear());
264
265 {
266 let mut obj1 = pool.get();
267 obj1.push(42);
268 assert_eq!(obj1[0], 42);
269 } {
272 let obj2 = pool.get();
273 assert!(obj2.is_empty()); }
275
276 assert_eq!(pool.pool_size(), 1);
277 }
278
279 #[test]
280 fn test_string_ops() {
281 use super::string_ops::*;
282
283 let key = create_permission_key("read", "doc1", "");
284 assert_eq!(key, "read:doc1");
285
286 let key_with_context = create_permission_key("read", "doc1", "abc123");
287 assert_eq!(key_with_context, "read:doc1:abc123");
288
289 let mut context = HashMap::new();
290 context.insert("user".to_string(), "admin".to_string());
291 context.insert("time".to_string(), "day".to_string());
292
293 let hash1 = hash_context(&context);
294 let hash2 = hash_context(&context);
295 assert_eq!(hash1, hash2); let empty_hash = hash_context(&HashMap::new());
298 assert!(empty_hash.is_empty());
299 }
300}