diff --git a/crates/fidc-core/src/numeric_factors.rs b/crates/fidc-core/src/numeric_factors.rs index be44e83..3570fc9 100644 --- a/crates/fidc-core/src/numeric_factors.rs +++ b/crates/fidc-core/src/numeric_factors.rs @@ -38,6 +38,11 @@ impl NumericFactorMap { self.entries.clear(); } + /// Reserve known new fields without geometric spare capacity per snapshot. + pub fn reserve_exact(&mut self, additional: usize) { + self.entries.reserve_exact(additional); + } + pub fn get(&self, key: &str) -> Option<&f64> { self.entries .binary_search_by(|(name, _)| name.as_str().cmp(key)) @@ -253,6 +258,28 @@ impl<'de> Deserialize<'de> for NumericFactorMap { mod tests { use super::*; + #[test] + fn exact_reservation_preserves_values_and_avoids_growth_during_known_inserts() { + let mut map = NumericFactorMap::from([ + (Cow::Borrowed("amount"), 125.25), + (Cow::Borrowed("nullable_value"), f64::from_bits(0x7ff8000000000042)), + (Cow::Borrowed("signal"), -0.0), + ]); + let original = map.iter().map(|(key, value)| (key.to_string(), value.to_bits())).collect::>(); + map.reserve_exact(2); + assert_eq!(map.iter().map(|(key, value)| (key.to_string(), value.to_bits())).collect::>(), original); + let buffer = map.entries.as_ptr(); + map.insert(Cow::Borrowed("pre_close"), 12.5); + map.insert(Cow::Borrowed("no_limit"), 0.0); + assert_eq!(map.entries.as_ptr(), buffer); + assert_eq!(map.len(), 5); + assert_eq!(map["signal"].to_bits(), (-0.0_f64).to_bits()); + assert_eq!(map["nullable_value"].to_bits(), 0x7ff8000000000042); + let before = map.entries.as_ptr(); + map.reserve_exact(0); + assert_eq!(map.entries.as_ptr(), before); + } + #[test] fn compact_keys_inline_dynamic_names_and_keep_long_static_storage() { const LONG: &str = "a_long_static_factor_identifier_that_must_remain_borrowed";