From facb4444bb086d29b4db16599c217eae14044497 Mon Sep 17 00:00:00 2001 From: "andries@fam-hiemstra.nl" Date: Sat, 25 Mar 2023 10:04:11 +0100 Subject: [PATCH] 0.6.0 --- CHANGELOG.md | 7 +- Cargo.toml | 3 +- src/auto_id_map.rs | 57 ++++++++---- src/cache.rs | 2 +- src/js_utils/adapters.rs | 5 +- src/js_utils/adapters/promises.rs | 138 ++++++++++++++++-------------- src/js_utils/facades/values.rs | 26 +++--- src/js_utils/mod.rs | 2 +- src/resolvable_future.rs | 4 +- 9 files changed, 139 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f173e25..0ece17a45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 0.5.7 (wip) +# 0.6.0 + +* AutoIdMap IDs are now random instead of sequential +* JSRealmAdapter::promise_cache_consume now returns an Option instead of panic on not found + +# 0.5.7 * impl JsValueConvertable::to_jsValue_facade for serde::Value * impl JsValueFacade::to_json_string for return types like JsValueFacade::JsObject (not input types like JsValueFacade::Object>) diff --git a/Cargo.toml b/Cargo.toml index 7dd0acfd6..f86d39598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hirofa_utils" -version = "0.5.7" +version = "0.5.8" authors = ["HiRoFa "] edition = "2018" description = "Utils project which is depended on by several other projects" @@ -22,6 +22,7 @@ tokio = {version = "1", features = ["rt-multi-thread", "rt", "bytes", "fs", "io- string_cache = "0.8" serde = "1" serde_json = "1" +rand = "0.8.5" [dev-dependencies.cargo-husky] version = "1.5.0" diff --git a/src/auto_id_map.rs b/src/auto_id_map.rs index 388936760..851c7ed7e 100644 --- a/src/auto_id_map.rs +++ b/src/auto_id_map.rs @@ -1,3 +1,5 @@ +use rand::rngs::ThreadRng; +use rand::{thread_rng, Rng}; use std::collections::HashMap; /// AutoIdMap is a wrapper around HashMap which automatically creates a unique id for it's entries @@ -16,7 +18,7 @@ use std::collections::HashMap; /// ``` pub struct AutoIdMap { max_size: usize, - last_id: usize, + rng: ThreadRng, pub map: HashMap, } @@ -29,7 +31,7 @@ impl AutoIdMap { pub fn new_with_max_size(max_size: usize) -> AutoIdMap { AutoIdMap { max_size, - last_id: 0, + rng: thread_rng(), map: HashMap::new(), } } @@ -83,25 +85,27 @@ impl AutoIdMap { /// insert an element and return the new id pub fn insert(&mut self, elem: T) -> usize { - if self.map.len() >= self.max_size { - panic!("AutoIdMap is full"); - } - - self.last_id += 1; - - if self.last_id >= self.max_size { - self.last_id = 0; - } + self.try_insert(elem).expect("map is full") + } - while self.map.contains_key(&self.last_id) { - if self.last_id >= self.max_size { - self.last_id = 0; + /// insert an element and return the new id + pub fn try_insert(&mut self, elem: T) -> Result { + if self.map.len() >= self.max_size { + Err("AutoIdMap is full") + } else { + let mut id = self.rng.gen_range(0..self.max_size); + + while self.map.contains_key(&id) { + if id >= self.max_size - 1 { + id = 0; + } else { + id += 1; + } } - self.last_id += 1; - } - self.map.insert(self.last_id, elem); - self.last_id + self.map.insert(id, elem); + Ok(id) + } } /// replace an element, this will panic if you pass an id that is not present @@ -179,4 +183,21 @@ pub mod tests { assert_eq!(free_id, 5); } + + #[test] + fn test_aim_ms() { + let mut map = AutoIdMap::new_with_max_size(8); + for _x in 0..8 { + map.insert("foo"); + } + assert_eq!(map.len(), 8); + map.remove(&5); + let free_id = map.insert("fail?"); + + assert_eq!(free_id, 5); + + let res = map.try_insert("foobar"); + // should be full + assert!(res.is_err()); + } } diff --git a/src/cache.rs b/src/cache.rs index fe0c4616e..c894f3116 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -164,7 +164,7 @@ pub mod tests { #[test] fn test_cache() { - let producer = |key: &&str| Some(format!("entry: {}", key)); + let producer = |key: &&str| Some(format!("entry: {key}")); let mut cache: Cache<&str, String> = Cache::new(producer, Duration::from_secs(2), 10); test_send(&cache); diff --git a/src/js_utils/adapters.rs b/src/js_utils/adapters.rs index c9e8d7702..f24fcbeb0 100644 --- a/src/js_utils/adapters.rs +++ b/src/js_utils/adapters.rs @@ -647,7 +647,7 @@ pub trait JsRealmAdapter { /// cache a JsPromiseAdapter so it can be accessed later based on an id, while cached the JsPromiseAdapter object will not be garbage collected fn js_promise_cache_add(&self, promise_ref: Box>) -> usize; /// Consume a JsPromiseAdapter (remove from cache) - fn js_promise_cache_consume(&self, id: usize) -> Box>; + fn js_promise_cache_consume(&self, id: usize) -> Option>>; /// cache a Object so it can be accessed later based on an id, while cached the Object will not be garbage collected fn js_cache_add(&self, object: &Self::JsValueAdapterType) -> i32; @@ -683,8 +683,7 @@ pub trait JsRealmAdapter { let constructor = self.js_object_get_property(&global, constructor_name)?; if constructor.js_is_null_or_undefined() { Err(JsError::new_string(format!( - "no such constructor: {}", - constructor_name + "no such constructor: {constructor_name}" ))) } else { Ok(self.js_instance_of(object, &constructor)) diff --git a/src/js_utils/adapters/promises.rs b/src/js_utils/adapters/promises.rs index 2ee86d4b0..2479d77e1 100644 --- a/src/js_utils/adapters/promises.rs +++ b/src/js_utils/adapters/promises.rs @@ -14,11 +14,11 @@ pub fn new_resolving_promise( producer: P, mapper: M, ) -> Result -where - T: JsRealmAdapter + 'static, - R: Send + 'static, - P: FnOnce() -> Result + Send + 'static, - M: FnOnce(&<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType, R) -> Result<<<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType as JsRealmAdapter>::JsValueAdapterType, JsError> + Send + 'static, + where + T: JsRealmAdapter + 'static, + R: Send + 'static, + P: FnOnce() -> Result + Send + 'static, + M: FnOnce(&<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType, R) -> Result<<<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType as JsRealmAdapter>::JsValueAdapterType, JsError> + Send + 'static, { // create promise let promise_ref = realm.js_promise_create()?; @@ -40,39 +40,43 @@ where // in q_js_rt worker thread, resolve promise // retrieve promise - let prom_ref: Box<(dyn JsPromiseAdapter<<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType> + 'static)> = realm.js_promise_cache_consume(id); - //let prom_ref = realm.js_promise_cache_consume(id); - match produced_result { - Ok(ok_res) => { - // map result to JSValueRef - let raw_res = mapper(realm, ok_res); + let prom_ref_opt: Option::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType> + 'static)>> = realm.js_promise_cache_consume(id); + if let Some(prom_ref) = prom_ref_opt { + //let prom_ref = realm.js_promise_cache_consume(id); + match produced_result { + Ok(ok_res) => { + // map result to JSValueRef + let raw_res = mapper(realm, ok_res); - // resolve or reject promise - match raw_res { - Ok(val_ref) => { - prom_ref - .js_promise_resolve(realm, &val_ref) - .expect("prom resolution failed"); - } - Err(err) => { - let err_ref = realm - .js_error_create(err.get_name(), err.get_message(), err.get_stack()) - .expect("could not create str"); - prom_ref - .js_promise_reject(realm, &err_ref) - .expect("prom rejection failed"); + // resolve or reject promise + match raw_res { + Ok(val_ref) => { + prom_ref + .js_promise_resolve(realm, &val_ref) + .expect("prom resolution failed"); + } + Err(err) => { + let err_ref = realm + .js_error_create(err.get_name(), err.get_message(), err.get_stack()) + .expect("could not create str"); + prom_ref + .js_promise_reject(realm, &err_ref) + .expect("prom rejection failed"); + } } } + Err(err) => { + // todo use error:new_error(err) + let err_ref = realm + .js_error_create(err.get_name(), err.get_message(), err.get_stack()) + .expect("could not create str"); + prom_ref + .js_promise_reject(realm, &err_ref) + .expect("prom rejection failed"); + } } - Err(err) => { - // todo use error:new_error(err) - let err_ref = realm - .js_error_create(err.get_name(), err.get_message(), err.get_stack()) - .expect("could not create str"); - prom_ref - .js_promise_reject(realm, &err_ref) - .expect("prom rejection failed"); - } + } else { + log::error!("async promise running for dropped realm: {} promise_id:{}", realm_id, id); } } else { log::error!("async promise running for dropped realm: {}", realm_id); @@ -99,7 +103,7 @@ pub(crate) fn new_resolving_promise_async( where T: JsRealmAdapter + 'static, R: Send + 'static, - P: Future> + Send + 'static, + P: Future> + Send + 'static, M: FnOnce(&<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType, R) -> Result<<<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType as JsRealmAdapter>::JsValueAdapterType, JsError> + Send + 'static, { // create promise @@ -113,7 +117,7 @@ pub(crate) fn new_resolving_promise_async( let realm_id = realm.js_get_realm_id().to_string(); // go async - let _ = add_helper_task_async(async move { + let _ignore_result = add_helper_task_async(async move { // in helper thread, produce result let produced_result = producer.await; if let Some(rti) = rti_ref.upgrade() { @@ -121,39 +125,43 @@ pub(crate) fn new_resolving_promise_async( if let Some(realm) = rt.js_get_realm(realm_id.as_str()) { // in q_js_rt worker thread, resolve promise // retrieve promise - let prom_ref: Box<(dyn JsPromiseAdapter<<<<<<::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType> + 'static)> = realm.js_promise_cache_consume(id); - //let prom_ref = realm.js_promise_cache_consume(id); - match produced_result { - Ok(ok_res) => { - // map result to JSValueRef - let raw_res = mapper(realm, ok_res); + let prom_ref_opt: Option::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeFacadeInnerType as JsRuntimeFacadeInner>::JsRuntimeFacadeType as JsRuntimeFacade>::JsRuntimeAdapterType as JsRuntimeAdapter>::JsRealmAdapterType> + 'static)>> = realm.js_promise_cache_consume(id); + if let Some(prom_ref) = prom_ref_opt { + //let prom_ref = realm.js_promise_cache_consume(id); + match produced_result { + Ok(ok_res) => { + // map result to JSValueRef + let raw_res = mapper(realm, ok_res); - // resolve or reject promise - match raw_res { - Ok(val_ref) => { - prom_ref - .js_promise_resolve(realm, &val_ref) - .expect("prom resolution failed"); - } - Err(err) => { - let err_ref = realm - .js_error_create(err.get_name(), err.get_message(), err.get_stack()) - .expect("could not create err"); - prom_ref - .js_promise_reject(realm, &err_ref) - .expect("prom rejection failed"); + // resolve or reject promise + match raw_res { + Ok(val_ref) => { + prom_ref + .js_promise_resolve(realm, &val_ref) + .expect("prom resolution failed"); + } + Err(err) => { + let err_ref = realm + .js_error_create(err.get_name(), err.get_message(), err.get_stack()) + .expect("could not create err"); + prom_ref + .js_promise_reject(realm, &err_ref) + .expect("prom rejection failed"); + } } } + Err(err) => { + // todo use error:new_error(err) + let err_ref = realm + .js_error_create(err.get_name(), err.get_message(), err.get_stack()) + .expect("could not create str"); + prom_ref + .js_promise_reject(realm, &err_ref) + .expect("prom rejection failed"); + } } - Err(err) => { - // todo use error:new_error(err) - let err_ref = realm - .js_error_create(err.get_name(), err.get_message(), err.get_stack()) - .expect("could not create str"); - prom_ref - .js_promise_reject(realm, &err_ref) - .expect("prom rejection failed"); - } + } else { + log::error!("async promise running on dropped realm: {} promise_id:{}", realm_id, id); } } else { log::error!("async promise running on dropped realm: {}", realm_id); diff --git a/src/js_utils/facades/values.rs b/src/js_utils/facades/values.rs index 488a13486..e419f0c69 100644 --- a/src/js_utils/facades/values.rs +++ b/src/js_utils/facades/values.rs @@ -216,7 +216,7 @@ impl CachedJsPromiseRef { Err(conv_err) => resolver1.resolve(Err(conv_err)), }; send_res - .map_err(|e| JsError::new_string(format!("could not send: {}", e)))?; + .map_err(|e| JsError::new_string(format!("could not send: {e}")))?; realm.js_undefined_create() }, 1, @@ -231,7 +231,7 @@ impl CachedJsPromiseRef { Err(conv_err) => resolver2.resolve(Err(conv_err)), }; send_res - .map_err(|e| JsError::new_string(format!("could not send: {}", e)))?; + .map_err(|e| JsError::new_string(format!("could not send: {e}")))?; realm.js_undefined_create() }, 1, @@ -593,16 +593,16 @@ impl JsValueFacade { pub fn stringify(&self) -> String { match self { JsValueFacade::I32 { val } => { - format!("I32: {}", val) + format!("I32: {val}") } JsValueFacade::F64 { val } => { - format!("F64: {}", val) + format!("F64: {val}") } JsValueFacade::String { val } => { - format!("String: {}", val) + format!("String: {val}") } JsValueFacade::Boolean { val } => { - format!("Boolean: {}", val) + format!("Boolean: {val}") } JsValueFacade::JsObject { cached_object } => { format!( @@ -638,11 +638,11 @@ impl JsValueFacade { JsValueFacade::Function { .. } => "Function".to_string(), JsValueFacade::Null => "Null".to_string(), JsValueFacade::Undefined => "Undefined".to_string(), - JsValueFacade::JsError { val } => format!("{}", val), + JsValueFacade::JsError { val } => format!("{val}"), JsValueFacade::ProxyInstance { .. } => "ProxyInstance".to_string(), JsValueFacade::TypedArray { .. } => "TypedArray".to_string(), - JsValueFacade::JsonStr { json } => format!("JsonStr: '{}'", json), - JsValueFacade::SerdeValue { value } => format!("Serde value: {}", value), + JsValueFacade::JsonStr { json } => format!("JsonStr: '{json}'"), + JsValueFacade::SerdeValue { value } => format!("Serde value: {value}"), } } pub async fn to_serde_value( @@ -680,10 +680,10 @@ impl JsValueFacade { rti: &R, ) -> Result { match self { - JsValueFacade::I32 { val } => Ok(format!("{}", val)), - JsValueFacade::F64 { val } => Ok(format!("{}", val)), + JsValueFacade::I32 { val } => Ok(format!("{val}")), + JsValueFacade::F64 { val } => Ok(format!("{val}")), JsValueFacade::String { val } => Ok(format!("'{}'", val.replace('\'', "\\'"))), - JsValueFacade::Boolean { val } => Ok(format!("{}", val)), + JsValueFacade::Boolean { val } => Ok(format!("{val}")), JsValueFacade::JsObject { cached_object } => cached_object.to_json_string(rti).await, JsValueFacade::JsPromise { cached_promise } => cached_promise.to_json_string(rti).await, JsValueFacade::JsArray { cached_array } => cached_array.to_json_string(rti).await, @@ -694,7 +694,7 @@ impl JsValueFacade { JsValueFacade::Function { .. } => Ok("function () {}".to_string()), JsValueFacade::Null => Ok("null".to_string()), JsValueFacade::Undefined => Ok("undefined".to_string()), - JsValueFacade::JsError { val } => Ok(format!("'{}'", val)), + JsValueFacade::JsError { val } => Ok(format!("'{val}'")), JsValueFacade::ProxyInstance { .. } => Ok("{}".to_string()), JsValueFacade::TypedArray { .. } => Ok("[]".to_string()), JsValueFacade::JsonStr { json } => Ok(json.clone()), diff --git a/src/js_utils/mod.rs b/src/js_utils/mod.rs index 7d5f4013b..9cae26098 100644 --- a/src/js_utils/mod.rs +++ b/src/js_utils/mod.rs @@ -64,7 +64,7 @@ impl std::fmt::Display for JsError { impl From for JsError { fn from(e: Error) -> Self { - JsError::new_string(format!("{}", e)) + JsError::new_string(format!("{e}")) } } diff --git a/src/resolvable_future.rs b/src/resolvable_future.rs index 82b5efd63..1166b7935 100644 --- a/src/resolvable_future.rs +++ b/src/resolvable_future.rs @@ -90,13 +90,13 @@ pub mod tests { match r.resolve("hi".to_string()) { Ok(_) => {} Err(e) => { - println!("could not resolve {}", e); + println!("could not resolve {e}"); } } }); let res = fut.await; - println!("res={}", res); + println!("res={res}"); } }