scripts/BTREEMAP_TO_VEC_PLAN.md
calculated_positions maps layout-tree node indices (0..N contiguous) to their
absolute positions. Using BTreeMap<usize, LogicalPosition> means:
With Vec<LogicalPosition>, every access is O(1) with perfect cache locality.
| File | Type sigs | Access patterns | Notes |
|---|---|---|---|
solver3/cache.rs | 7 | .get(), .insert(), .get_mut(), .contains_key(), .clone() | LayoutCache.calculated_positions field + all positioning fns |
solver3/fc.rs | 2 | LayoutOutput.positions field, return type | Also a BTreeMap<usize, LP> — separate map, relative positions |
solver3/display_list.rs | 2 | .get() (read-only) | generate_display_list + DisplayListContext |
solver3/positioning.rs | 3 | .get(), .get_mut(), .insert() | adjust_relative_positions, position_out_of_flow_elements |
solver3/mod.rs | 1 | .get(), .clone(), .contains_key(), .insert() | layout_document + helper |
solver3/paged_layout.rs | 1 | .clone(), .contains_key(), .insert() | compute_layout_with_fragmentation |
window.rs | 2 | .get(), .clone() | LayoutResult.calculated_positions + hit-testing |
| File | References | Notes |
|---|---|---|
dll/src/desktop/wr_translate2.rs | 3 | .get(&layout_index) |
dll/src/desktop/shell2/common/layout_v2.rs | 2 | .get(&layout_index) |
dll/src/desktop/shell2/common/debug_server.rs | 1 | .get(&layout_index) |
LayoutOutput.positions in fc.rsThis is a separate BTreeMap used in layout_bfc, layout_ifc, etc. to
collect child positions relative to a container. The keys are also contiguous
layout-tree indices, but they're a subset of all nodes (only the children of
one container). Options:
tree.nodes.len(), index directlyVec<Option<LogicalPosition>>Decision: Convert to Vec too. The indices are layout-tree indices (same space), and fc.rs is called thousands of times. Pre-size to max possible index.
// In a common module (e.g. solver3/mod.rs or a new solver3/types.rs):
pub type PositionVec = Vec<LogicalPosition>;
// Wrapper functions for the transition:
#[inline(always)]
fn pos_get(positions: &PositionVec, idx: usize) -> Option<&LogicalPosition> {
positions.get(idx).filter(|p| p.x != f32::MIN)
}
#[inline(always)]
fn pos_set(positions: &mut PositionVec, idx: usize, pos: LogicalPosition) {
if idx >= positions.len() {
positions.resize(idx + 1, LogicalPosition::new(f32::MIN, f32::MIN));
}
positions[idx] = pos;
}
#[inline(always)]
fn pos_contains(positions: &PositionVec, idx: usize) -> bool {
positions.get(idx).map_or(false, |p| p.x != f32::MIN)
}
Use LogicalPosition { x: f32::MIN, y: f32::MIN } as "not set". This avoids
wrapping in Option<> and keeps the Vec tightly packed (8 bytes per entry vs
12+ with Option).
No real position will ever be f32::MIN.
// In cache.rs, when creating/cloning calculated_positions:
let mut calculated_positions = vec![
LogicalPosition::new(f32::MIN, f32::MIN);
new_tree.nodes.len()
];
PositionVec type alias and pos_get/pos_set/pos_contains helpersPOSITION_UNSETcache.rs LayoutCache structLayoutCache::default() / initialization.clone() calls (Vec::clone is faster than BTreeMap::clone)reposition_clean_subtrees + shift_subtree_positioncalculate_layout_for_subtree (the main one)position_bfc_child + position_bfc_child_descendantsposition_ifc_childrenposition_flex_children + position_grid_childrenLayoutOutput.positions typelayout_bfc_children_block, layout_ifc, etc.position_ifc_linesadjust_relative_positionsposition_out_of_flow_elementsresolve_sticky_positionsgenerate_display_list parameterDisplayListContext.calculated_positions fieldlayout_document() in mod.rscompute_layout_with_fragmentation() in paged_layout.rsget_containing_block_for_node() helpers in both filesLayoutResult.calculated_positions fieldwr_translate2.rs — 3x .get()layout_v2.rs — 2x .get()debug_server.rs — 1x .get()cargo build -p azul-layout --features text_layoutcargo build (full, including dll if possible)git revert one commit