Skip to main content

linker_utils/
relaxation.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum RelocationModifier {
3    Normal,
4    SkipNextRelocation,
5}
6
7/// Records the number of bytes deleted at a specific offset within an input section.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct RelaxDelta {
10    /// Offset within the input section where the deletion occurs.
11    pub input_offset: u64,
12    /// Cumulative bytes deleted up to and including this entry.
13    pub cumulative_deleted: u64,
14    /// Number of bytes deleted at this position.
15    pub bytes_deleted: u32,
16}
17
18/// Tracks all relaxation-induced byte deletions for a single section.
19#[derive(Debug, Clone, Default)]
20pub struct SectionRelaxDeltas {
21    /// Sorted (by `input_offset`) list of individual deletions.
22    /// Each entry carries a precomputed `cumulative_deleted` field.
23    deltas: Vec<RelaxDelta>,
24}
25
26impl SectionRelaxDeltas {
27    #[must_use]
28    pub fn new(raw: Vec<(u64, u32)>) -> Self {
29        debug_assert!(
30            raw.windows(2).all(|w| w[0].0 < w[1].0),
31            "entries must be sorted by input_offset in strictly ascending order"
32        );
33
34        let mut deltas = Vec::with_capacity(raw.len());
35        let mut running = 0u64;
36        for (input_offset, bytes_deleted) in raw {
37            running += u64::from(bytes_deleted);
38            deltas.push(RelaxDelta {
39                input_offset,
40                cumulative_deleted: running,
41                bytes_deleted,
42            });
43        }
44
45        SectionRelaxDeltas { deltas }
46    }
47
48    #[must_use]
49    pub fn deltas(&self) -> &[RelaxDelta] {
50        &self.deltas
51    }
52
53    #[must_use]
54    pub fn is_empty(&self) -> bool {
55        self.deltas.is_empty()
56    }
57
58    #[must_use]
59    pub fn total_deleted(&self) -> u64 {
60        self.deltas.last().map_or(0, |d| d.cumulative_deleted)
61    }
62
63    /// Merges additional `(input_offset, bytes_deleted)` pairs into this set of deltas.
64    /// The additional pairs must not overlap with existing entries.
65    /// After merging, cumulative deleted counts are recomputed.
66    pub fn merge_additional(&mut self, additional: Vec<(u64, u32)>) {
67        if additional.is_empty() {
68            return;
69        }
70        let mut raw: Vec<(u64, u32)> = self
71            .deltas
72            .iter()
73            .map(|d| (d.input_offset, d.bytes_deleted))
74            .chain(additional)
75            .collect();
76        raw.sort_by_key(|(offset, _)| *offset);
77        debug_assert!(
78            raw.windows(2).all(|w| w[0].0 < w[1].0),
79            "merge_additional: duplicate or overlapping offsets"
80        );
81        *self = SectionRelaxDeltas::new(raw);
82    }
83
84    #[must_use]
85    pub fn has_delta_at(&self, offset: u64) -> bool {
86        self.deltas
87            .binary_search_by_key(&offset, |d| d.input_offset)
88            .is_ok()
89    }
90
91    /// Returns the number of bytes deleted at `offset`, or 0 if there is no delta at that offset.
92    #[must_use]
93    pub fn delta_bytes_at(&self, offset: u64) -> u32 {
94        self.deltas
95            .binary_search_by_key(&offset, |d| d.input_offset)
96            .map_or(0, |i| self.deltas[i].bytes_deleted)
97    }
98
99    // Converts an input section offset to the corresponding output section offset by subtracting
100    // the cumulative bytes deleted strictly before `input_offset`.
101    #[must_use]
102    pub fn input_to_output_offset(&self, input_offset: u64) -> u64 {
103        if self.deltas.is_empty() {
104            return input_offset;
105        }
106
107        // Find the number of deltas whose input_offset is strictly before the
108        // queried input_offset.
109        let idx = self
110            .deltas
111            .partition_point(|d| d.input_offset < input_offset);
112
113        if idx == 0 {
114            input_offset
115        } else {
116            input_offset - self.deltas[idx - 1].cumulative_deleted
117        }
118    }
119
120    // Converts an output section offset back to the corresponding input section offset.
121    #[must_use]
122    pub fn output_to_input_offset(&self, output_offset: u64) -> u64 {
123        if self.deltas.is_empty() {
124            return output_offset;
125        }
126
127        let lo = self.deltas.partition_point(|d| {
128            d.input_offset + u64::from(d.bytes_deleted) - d.cumulative_deleted <= output_offset
129        });
130
131        if lo == 0 {
132            output_offset
133        } else {
134            output_offset + self.deltas[lo - 1].cumulative_deleted
135        }
136    }
137
138    #[must_use]
139    pub fn cursor(&self) -> RelaxCursor<'_> {
140        RelaxCursor {
141            deltas: &self.deltas,
142            index: 0,
143            current_cumulative: 0,
144        }
145    }
146}
147
148pub struct RelaxCursor<'a> {
149    deltas: &'a [RelaxDelta],
150    /// Index of the next delta that has not yet been "consumed".
151    index: usize,
152    /// Cumulative bytes deleted up to (but not including) `deltas[index]`.
153    current_cumulative: u64,
154}
155
156impl RelaxCursor<'_> {
157    // Translates an input section offset to the corresponding output section offset.
158    #[inline]
159    pub fn translate(&mut self, input_offset: u64) -> u64 {
160        // Advance past all deltas that are strictly before the queried offset.
161        while self.index < self.deltas.len() && self.deltas[self.index].input_offset < input_offset
162        {
163            self.current_cumulative = self.deltas[self.index].cumulative_deleted;
164            self.index += 1;
165        }
166        input_offset - self.current_cumulative
167    }
168}
169
170// Translates an input offset through optional relaxation deltas.
171#[inline]
172#[must_use]
173pub fn opt_input_to_output(deltas: Option<&SectionRelaxDeltas>, input_offset: u64) -> u64 {
174    match deltas {
175        Some(d) => d.input_to_output_offset(input_offset),
176        None => input_offset,
177    }
178}
179
180/// Sparse map from section index to [`SectionRelaxDeltas`].
181#[derive(Debug, Clone, Default)]
182pub struct RelaxDeltaMap {
183    /// Sorted by section index
184    entries: Vec<(usize, SectionRelaxDeltas)>,
185}
186
187impl RelaxDeltaMap {
188    #[must_use]
189    pub fn new() -> Self {
190        Self {
191            entries: Vec::new(),
192        }
193    }
194
195    pub fn insert(&mut self, section_index: usize, deltas: SectionRelaxDeltas) {
196        debug_assert!(
197            self.entries
198                .last()
199                .is_none_or(|(idx, _)| *idx < section_index),
200            "entries must be inserted in ascending section index order"
201        );
202        self.entries.push((section_index, deltas));
203    }
204
205    #[must_use]
206    pub fn get(&self, section_index: usize) -> Option<&SectionRelaxDeltas> {
207        self.entries
208            .binary_search_by_key(&section_index, |(idx, _)| *idx)
209            .ok()
210            .map(|i| &self.entries[i].1)
211    }
212
213    #[must_use]
214    pub fn get_mut(&mut self, section_index: usize) -> Option<&mut SectionRelaxDeltas> {
215        self.entries
216            .binary_search_by_key(&section_index, |(idx, _)| *idx)
217            .ok()
218            .map(|i| &mut self.entries[i].1)
219    }
220
221    pub fn insert_sorted(&mut self, section_index: usize, deltas: SectionRelaxDeltas) {
222        let pos = self
223            .entries
224            .partition_point(|(idx, _)| *idx < section_index);
225        debug_assert!(
226            pos >= self.entries.len() || self.entries[pos].0 != section_index,
227            "insert_sorted: duplicate section_index {section_index}"
228        );
229        self.entries.insert(pos, (section_index, deltas));
230    }
231
232    #[must_use]
233    pub fn is_empty(&self) -> bool {
234        self.entries.is_empty()
235    }
236}