naga/compact/
handle_set_map.rs

1use alloc::vec::Vec;
2
3use crate::arena::{Arena, Handle, HandleSet, Range};
4
5type Index = crate::non_max_u32::NonMaxU32;
6
7/// A map from old handle indices to new, compressed handle indices.
8pub struct HandleMap<T> {
9    /// The indices assigned to handles in the compacted module.
10    ///
11    /// If `new_index[i]` is `Some(n)`, then `n` is the `Index` of the
12    /// compacted `Handle` corresponding to the pre-compacted `Handle`
13    /// whose index is `i`.
14    new_index: Vec<Option<Index>>,
15
16    /// This type is indexed by values of type `T`.
17    as_keys: core::marker::PhantomData<T>,
18}
19
20impl<T: 'static> HandleMap<T> {
21    pub fn from_set(set: HandleSet<T>) -> Self {
22        let mut next_index = Index::new(0).unwrap();
23        Self {
24            new_index: set
25                .all_possible()
26                .map(|handle| {
27                    if set.contains(handle) {
28                        // This handle will be retained in the compacted version,
29                        // so assign it a new index.
30                        let this = next_index;
31                        next_index = next_index.checked_add(1).unwrap();
32                        Some(this)
33                    } else {
34                        // This handle will be omitted in the compacted version.
35                        None
36                    }
37                })
38                .collect(),
39            as_keys: core::marker::PhantomData,
40        }
41    }
42
43    /// Return true if `old` is used in the compacted module.
44    pub fn used(&self, old: Handle<T>) -> bool {
45        self.new_index[old.index()].is_some()
46    }
47
48    /// Return the counterpart to `old` in the compacted module.
49    ///
50    /// If we thought `old` wouldn't be used in the compacted module, return
51    /// `None`.
52    pub fn try_adjust(&self, old: Handle<T>) -> Option<Handle<T>> {
53        log::trace!(
54            "adjusting {} handle [{}] -> [{:?}]",
55            core::any::type_name::<T>(),
56            old.index(),
57            self.new_index[old.index()]
58        );
59        self.new_index[old.index()].map(Handle::new)
60    }
61
62    /// Return the counterpart to `old` in the compacted module.
63    ///
64    /// If we thought `old` wouldn't be used in the compacted module, panic.
65    pub fn adjust(&self, handle: &mut Handle<T>) {
66        *handle = self.try_adjust(*handle).unwrap();
67    }
68
69    /// Like `adjust`, but for optional handles.
70    pub fn adjust_option(&self, handle: &mut Option<Handle<T>>) {
71        if let Some(ref mut handle) = *handle {
72            self.adjust(handle);
73        }
74    }
75
76    /// Shrink `range` to include only used handles.
77    ///
78    /// Fortunately, compaction doesn't arbitrarily scramble the expressions
79    /// in the arena, but instead preserves the order of the elements while
80    /// squeezing out unused ones. That means that a contiguous range in the
81    /// pre-compacted arena always maps to a contiguous range in the
82    /// post-compacted arena. So we just need to adjust the endpoints.
83    ///
84    /// Compaction may have eliminated the endpoints themselves.
85    ///
86    /// Use `compacted_arena` to bounds-check the result.
87    pub fn adjust_range(&self, range: &mut Range<T>, compacted_arena: &Arena<T>) {
88        let mut index_range = range.index_range();
89        let compacted;
90        if let Some(first) = index_range.find_map(|i| self.new_index[i as usize]) {
91            // The first call to `find_map` mutated `index_range` to hold the
92            // remainder of original range, which is exactly the range we need
93            // to search for the new last handle.
94            if let Some(last) = index_range.rev().find_map(|i| self.new_index[i as usize]) {
95                // Build an end-exclusive range, given the two included indices
96                // `first` and `last`.
97                compacted = first.get()..last.get() + 1;
98            } else {
99                // The range contains only a single live handle, which
100                // we identified with the first `find_map` call.
101                compacted = first.get()..first.get() + 1;
102            }
103        } else {
104            compacted = 0..0;
105        };
106        *range = Range::from_index_range(compacted, compacted_arena);
107    }
108}