diff --git a/crates/engine/src/cmd/array.rs b/crates/engine/src/cmd/array.rs index 839d71efc..1cc97a496 100644 --- a/crates/engine/src/cmd/array.rs +++ b/crates/engine/src/cmd/array.rs @@ -193,7 +193,19 @@ pub(crate) fn explode_array(d: &Drawing, e: &Entity) -> Option> { let p = d.array_params(e)?; let blk = d.block(&ins.block)?; let m = ins.transform(blk.base.xy()); - let mut out = Vec::new(); + // Count first: a hostile array must be refused before any entity is cloned. + let total = blk + .entities + .iter() + .map(|it| match &it.kind { + EntityKind::Insert(i) if i.block.eq_ignore_ascii_case(&p.source) => d.block(&i.block).map_or(1, |b| b.entities.len()), + _ => 1, + }) + .fold(0usize, usize::saturating_add); + if total > MAX_EXPLODED_OBJECTS { + return None; + } + let mut out = Vec::with_capacity(total); for item in blk.entities.iter() { let item = placed(e, item, &m); let source = match &item.kind { @@ -202,9 +214,6 @@ pub(crate) fn explode_array(d: &Drawing, e: &Entity) -> Option> { }; match source { Some((im, b)) => { - if out.len().saturating_add(b.entities.len()) > MAX_EXPLODED_OBJECTS { - return None; - } out.extend(b.entities.iter().map(|se| placed(&item, se, &im))); } None => out.push(item), diff --git a/crates/engine/src/cmd/blocks/extract.rs b/crates/engine/src/cmd/blocks/extract.rs index 3e7707cc8..de85956c7 100644 --- a/crates/engine/src/cmd/blocks/extract.rs +++ b/crates/engine/src/cmd/blocks/extract.rs @@ -131,6 +131,7 @@ fn selected_tags(ents: &[std::sync::Arc], d: &Drawing, o: selected.push(tag); } }; + let mut scanned = std::collections::HashSet::new(); for entity in ents { let EntityKind::Insert(ins) = &entity.kind else { continue }; let anonymous = ins.block.starts_with('*') || d.block(&ins.block).is_some_and(|b| b.anonymous); @@ -140,9 +141,14 @@ fn selected_tags(ents: &[std::sync::Arc], d: &Drawing, o: for attribute in &ins.attribs { add(&attribute.tag); } - if let Some(block) = d.block(&ins.block) { + // A block's constants are read once, however many inserts reference it. + if scanned.insert(ins.block.as_str()) + && let Some(block) = d.block(&ins.block) + { for entity in block.entities.iter() { - if let EntityKind::AttDef(attribute) = &entity.kind && attribute.constant { + if let EntityKind::AttDef(attribute) = &entity.kind + && attribute.constant + { add(&attribute.tag); } }