From 2286bfa757630244f386f30f4ab6e27f9621dbf6 Mon Sep 17 00:00:00 2001 From: boris Date: Sun, 13 Sep 2026 13:07:51 +0800 Subject: [PATCH] test(engine): retain static schema names across shared factor rows --- crates/fidc-core/src/numeric_factors.rs | 13 +++++++++++++ crates/fidc-core/src/numeric_factors/shared_rows.rs | 13 ++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/fidc-core/src/numeric_factors.rs b/crates/fidc-core/src/numeric_factors.rs index f6cca55..81b985d 100644 --- a/crates/fidc-core/src/numeric_factors.rs +++ b/crates/fidc-core/src/numeric_factors.rs @@ -436,6 +436,19 @@ mod tests { assert!(rows.iter().all(|row| matches!(row.storage, Storage::Owned(_)))); } + #[test] + fn shared_schema_borrows_a_static_key_even_after_a_dynamic_equal_key() { + const KEY: &str = "a_shared_factor_name_longer_than_inline_storage"; + let mut rows = (0..64).map(|index| NumericFactorMap::from([ + (if index == 0 { Cow::Owned(KEY.to_string()) } else { Cow::Borrowed(KEY) }, index as f64), + (Cow::Borrowed("another"), 2.0), + ])).collect::>(); + assert_eq!(NumericFactorMap::share_rows(rows.iter_mut()), rows.len()); + for row in &rows { + assert_eq!(row.keys().find(|key| key.as_str() == KEY).unwrap().as_static_str(), Some(KEY)); + } + } + #[test] fn compact_keys_inline_dynamic_names_and_keep_long_static_storage() { const LONG: &str = "a_long_static_factor_identifier_that_must_remain_borrowed"; diff --git a/crates/fidc-core/src/numeric_factors/shared_rows.rs b/crates/fidc-core/src/numeric_factors/shared_rows.rs index 253335f..263bdd3 100644 --- a/crates/fidc-core/src/numeric_factors/shared_rows.rs +++ b/crates/fidc-core/src/numeric_factors/shared_rows.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::BTreeMap; use std::sync::Arc; use compact_str::CompactString; @@ -103,8 +103,15 @@ pub(super) fn share<'a>(rows: impl IntoIterator let Storage::Owned(entries) = &row.storage else { return None }; sum.checked_add(entries.capacity().checked_mul(std::mem::size_of::<(CompactString, f64)>())?) }) else { return 0 }; - let fields = rows.iter().flat_map(|row| row.keys()).collect::>() - .into_iter().cloned().collect::>(); + let mut names = BTreeMap::<&str, &CompactString>::new(); + for name in rows.iter().flat_map(|row| row.keys()) { + names.entry(name.as_str()).and_modify(|existing| { + if existing.as_static_str().is_none() && name.as_static_str().is_some() { + *existing = name; + } + }).or_insert(name); + } + let fields = names.into_values().cloned().collect::>(); let Some(cells) = rows.len().checked_mul(fields.len()) else { return 0 }; if cells == 0 { return 0;