1 use super::SmallCString; 2 use std::cell::RefCell; 3 use std::collections::BTreeMap; 4 5 /// Maps parameter names to parameter indices. 6 #[derive(Default, Clone, Debug)] 7 // BTreeMap seems to do better here unless we want to pull in a custom hash 8 // function. 9 pub(crate) struct ParamIndexCache(RefCell<BTreeMap<SmallCString, usize>>); 10 11 impl ParamIndexCache { get_or_insert_with<F>(&self, s: &str, func: F) -> Option<usize> where F: FnOnce(&std::ffi::CStr) -> Option<usize>,12 pub fn get_or_insert_with<F>(&self, s: &str, func: F) -> Option<usize> 13 where 14 F: FnOnce(&std::ffi::CStr) -> Option<usize>, 15 { 16 let mut cache = self.0.borrow_mut(); 17 // Avoid entry API, needs allocation to test membership. 18 if let Some(v) = cache.get(s) { 19 return Some(*v); 20 } 21 // If there's an internal nul in the name it couldn't have been a 22 // parameter, so early return here is ok. 23 let name = SmallCString::new(s).ok()?; 24 let val = func(&name)?; 25 cache.insert(name, val); 26 Some(val) 27 } 28 } 29 30 #[cfg(test)] 31 mod test { 32 use super::*; 33 #[test] test_cache()34 fn test_cache() { 35 let p = ParamIndexCache::default(); 36 let v = p.get_or_insert_with("foo", |cstr| { 37 assert_eq!(cstr.to_str().unwrap(), "foo"); 38 Some(3) 39 }); 40 assert_eq!(v, Some(3)); 41 let v = p.get_or_insert_with("foo", |_| { 42 panic!("shouldn't be called this time"); 43 }); 44 assert_eq!(v, Some(3)); 45 let v = p.get_or_insert_with("gar\0bage", |_| { 46 panic!("shouldn't be called here either"); 47 }); 48 assert_eq!(v, None); 49 let v = p.get_or_insert_with("bar", |cstr| { 50 assert_eq!(cstr.to_str().unwrap(), "bar"); 51 None 52 }); 53 assert_eq!(v, None); 54 let v = p.get_or_insert_with("bar", |cstr| { 55 assert_eq!(cstr.to_str().unwrap(), "bar"); 56 Some(30) 57 }); 58 assert_eq!(v, Some(30)); 59 } 60 } 61