1use std::cmp::Reverse;
2use std::collections::BinaryHeap;
3
4use crate::types::{Command, CyclePolicy, PlacedCommand};
5
6const COLOR_UNVISITED: u8 = 0;
8const COLOR_ON_PATH: u8 = 1;
9const COLOR_DONE: u8 = 2;
10
11const NO_SCC: usize = usize::MAX;
13
14#[derive(Clone, Copy)]
18struct CopyInfo {
19 src: usize,
20 dst: usize,
21 length: usize,
22}
23
24struct SccList {
26 sccs: Vec<Vec<usize>>, active: Vec<usize>, id: Vec<usize>, }
30
31#[derive(Debug, Default)]
33pub struct InplaceStats {
34 pub num_copies: usize,
35 pub num_adds: usize,
36 pub edges: usize,
37 pub cycles_broken: usize,
38 pub copies_converted: usize,
39 pub bytes_converted: usize,
40}
41
42fn tarjan_scc(adj: &[Vec<usize>], n: usize) -> Vec<Vec<usize>> {
52 let mut index_counter = 0usize;
53 let mut index = vec![usize::MAX; n]; let mut lowlink = vec![0usize; n];
55 let mut on_stack = vec![false; n];
56 let mut tarjan_stack: Vec<usize> = Vec::new();
57 let mut sccs: Vec<Vec<usize>> = Vec::new();
58 let mut call_stack: Vec<(usize, usize)> = Vec::new();
60
61 for start in 0..n {
62 if index[start] != usize::MAX {
63 continue;
64 }
65
66 index[start] = index_counter;
67 lowlink[start] = index_counter;
68 index_counter += 1;
69 on_stack[start] = true;
70 tarjan_stack.push(start);
71 call_stack.push((start, 0));
72
73 while let Some(&(v, ni)) = call_stack.last() {
74 if ni < adj[v].len() {
75 let w = adj[v][ni];
76 call_stack.last_mut().unwrap().1 += 1;
77 if index[w] == usize::MAX {
78 index[w] = index_counter;
80 lowlink[w] = index_counter;
81 index_counter += 1;
82 on_stack[w] = true;
83 tarjan_stack.push(w);
84 call_stack.push((w, 0));
85 } else if on_stack[w] {
86 if index[w] < lowlink[v] {
88 lowlink[v] = index[w];
89 }
90 }
91 } else {
92 call_stack.pop();
93 if let Some(&(parent, _)) = call_stack.last() {
94 if lowlink[v] < lowlink[parent] {
95 lowlink[parent] = lowlink[v];
96 }
97 }
98 if lowlink[v] == index[v] {
99 let mut scc = Vec::new();
100 loop {
101 let w = tarjan_stack.pop().unwrap();
102 on_stack[w] = false;
103 scc.push(w);
104 if w == v {
105 break;
106 }
107 }
108 sccs.push(scc);
109 }
110 }
111 }
112 }
113
114 sccs }
116
117fn find_cycle_in_scc(
135 adj: &[Vec<usize>],
136 scc: &[usize],
137 sid: usize,
138 scc_id: &[usize],
139 removed: &[bool],
140 color: &mut [u8],
141 scan_start: &mut usize,
142) -> Option<Vec<usize>> {
143 let mut path: Vec<usize> = Vec::new();
144
145 while *scan_start < scc.len() {
146 let start = scc[*scan_start];
147 if removed[start] || color[start] != COLOR_UNVISITED {
148 *scan_start += 1;
149 continue;
150 }
151
152 color[start] = COLOR_ON_PATH;
153 path.push(start);
154 let mut stack: Vec<(usize, usize)> = vec![(start, 0)];
155
156 while !stack.is_empty() {
157 let (v, ni) = *stack.last().unwrap();
158 let mut next_ni = ni;
159 let mut advanced = false;
160
161 while next_ni < adj[v].len() {
162 let w = adj[v][next_ni];
163 next_ni += 1;
164 if scc_id[w] != sid || removed[w] {
165 continue;
166 }
167 if color[w] == COLOR_ON_PATH {
168 let pos = path.iter().position(|&x| x == w).unwrap();
170 let cycle = path[pos..].to_vec();
171 for &u in &path {
172 color[u] = COLOR_UNVISITED;
173 }
174 return Some(cycle);
175 }
176 if color[w] == COLOR_UNVISITED {
177 stack.last_mut().unwrap().1 = next_ni;
178 color[w] = COLOR_ON_PATH;
179 path.push(w);
180 stack.push((w, 0));
181 advanced = true;
182 break;
183 }
184 }
185
186 if !advanced {
187 stack.pop();
188 color[v] = COLOR_DONE; path.pop();
190 }
191 }
192
193 *scan_start += 1;
195 }
196
197 None
198}
199
200fn build_crwi_digraph(
208 copy_info: &[CopyInfo],
209 n: usize,
210 stats: &mut InplaceStats,
211) -> (Vec<Vec<usize>>, Vec<usize>) {
212 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
213 let mut in_deg: Vec<usize> = vec![0; n];
214
215 let mut write_sorted: Vec<usize> = (0..n).collect();
217 write_sorted.sort_unstable_by_key(|&j| copy_info[j].dst);
218 let write_starts: Vec<usize> = write_sorted.iter().map(|&j| copy_info[j].dst).collect();
219
220 for i in 0..n {
221 let src = copy_info[i].src;
222 let length = copy_info[i].length;
223 let read_end = src + length;
224 let lo = write_starts.partition_point(|&ws| ws < src);
228 let hi = write_starts.partition_point(|&ws| ws < read_end);
229 if lo > 0 {
230 let j = write_sorted[lo - 1];
231 if j != i {
232 let dj = copy_info[j].dst;
233 let lj = copy_info[j].length;
234 if dj + lj > src {
235 adj[i].push(j);
236 in_deg[j] += 1;
237 stats.edges += 1;
238 }
239 }
240 }
241 for k in lo..hi {
242 let j = write_sorted[k];
243 if j != i {
244 adj[i].push(j);
245 in_deg[j] += 1;
246 stats.edges += 1;
247 }
248 }
249 }
250 (adj, in_deg)
251}
252
253fn build_scc_list(adj: &[Vec<usize>], n: usize) -> SccList {
255 let sccs_raw = tarjan_scc(adj, n);
256 let mut id = vec![NO_SCC; n];
257 let mut sccs: Vec<Vec<usize>> = Vec::new();
258 let mut active: Vec<usize> = Vec::new();
259
260 for scc in &sccs_raw {
261 if scc.len() > 1 {
262 let sid = sccs.len();
263 for &v in scc {
264 id[v] = sid;
265 }
266 active.push(scc.len());
267 sccs.push(scc.clone());
268 }
269 }
270 SccList { sccs, active, id }
271}
272
273fn pick_victim(
278 copy_info: &[CopyInfo],
279 adj: &[Vec<usize>],
280 scc_list: &mut SccList,
281 removed: &[bool],
282 color: &mut [u8],
283 scc_ptr: &mut usize,
284 scan_pos: &mut usize,
285 policy: CyclePolicy,
286 n: usize,
287) -> usize {
288 match policy {
289 CyclePolicy::Constant => (0..n).find(|&i| !removed[i]).unwrap(),
290 CyclePolicy::Localmin => loop {
291 while *scc_ptr < scc_list.sccs.len() && scc_list.active[*scc_ptr] == 0 {
292 *scc_ptr += 1;
293 *scan_pos = 0;
294 }
295 if *scc_ptr >= scc_list.sccs.len() {
296 break (0..n).find(|&i| !removed[i]).unwrap();
298 }
299 let result = find_cycle_in_scc(
300 adj,
301 &scc_list.sccs[*scc_ptr],
302 *scc_ptr,
303 &scc_list.id,
304 removed,
305 color,
306 scan_pos,
307 );
308 match result {
309 Some(cycle) => {
310 break *cycle.iter().min_by_key(|&&i| (copy_info[i].length, i)).unwrap();
311 }
312 None => {
313 *scc_ptr += 1;
315 *scan_pos = 0;
316 }
317 }
318 },
319 }
320}
321
322fn run_kahn(
325 copy_info: &[CopyInfo],
326 adj: &[Vec<usize>],
327 scc_list: &mut SccList,
328 in_deg: &mut [usize],
329 r: &[u8],
330 add_info: &mut Vec<(usize, Vec<u8>)>,
331 policy: CyclePolicy,
332 n: usize,
333 stats: &mut InplaceStats,
334) -> Vec<usize> {
335 let mut removed = vec![false; n];
336 let mut topo_order = Vec::with_capacity(n);
337 let mut color = vec![COLOR_UNVISITED; n];
338 let mut scc_ptr = 0usize;
339 let mut scan_pos = 0usize;
340
341 let mut heap: BinaryHeap<Reverse<(usize, usize)>> = BinaryHeap::new();
342 for i in 0..n {
343 if in_deg[i] == 0 {
344 heap.push(Reverse((copy_info[i].length, i)));
345 }
346 }
347 let mut processed = 0;
348
349 while processed < n {
350 while let Some(Reverse((_, v))) = heap.pop() {
352 if removed[v] {
353 continue;
354 }
355 removed[v] = true;
356 topo_order.push(v);
357 processed += 1;
358 if scc_list.id[v] != NO_SCC {
359 scc_list.active[scc_list.id[v]] -= 1;
360 }
361 for &w in &adj[v] {
362 if !removed[w] {
363 in_deg[w] -= 1;
364 if in_deg[w] == 0 {
365 heap.push(Reverse((copy_info[w].length, w)));
366 }
367 }
368 }
369 }
370
371 if processed >= n {
372 break;
373 }
374
375 let victim = pick_victim(
377 copy_info, adj, scc_list, &removed, &mut color,
378 &mut scc_ptr, &mut scan_pos, policy, n,
379 );
380 let ci = copy_info[victim];
381 add_info.push((ci.dst, r[ci.src..ci.src + ci.length].to_vec()));
382 stats.cycles_broken += 1;
383 stats.copies_converted += 1;
384 stats.bytes_converted += ci.length;
385 removed[victim] = true;
386 processed += 1;
387 if scc_list.id[victim] != NO_SCC {
388 scc_list.active[scc_list.id[victim]] -= 1;
389 }
390 for &w in &adj[victim] {
391 if !removed[w] {
392 in_deg[w] -= 1;
393 if in_deg[w] == 0 {
394 heap.push(Reverse((copy_info[w].length, w)));
395 }
396 }
397 }
398 }
399 topo_order
400}
401
402pub fn make_inplace(
427 r: &[u8],
428 commands: &[Command],
429 policy: CyclePolicy,
430) -> (Vec<PlacedCommand>, InplaceStats) {
431 let mut stats = InplaceStats::default();
432
433 if commands.is_empty() {
434 return (Vec::new(), stats);
435 }
436
437 let mut copy_info: Vec<CopyInfo> = Vec::new();
439 let mut add_info: Vec<(usize, Vec<u8>)> = Vec::new();
440 let mut write_pos = 0usize;
441
442 for cmd in commands {
443 match cmd {
444 Command::Copy { offset, length } => {
445 copy_info.push(CopyInfo { src: *offset, dst: write_pos, length: *length });
446 write_pos += length;
447 }
448 Command::Add { data } => {
449 add_info.push((write_pos, data.clone()));
450 write_pos += data.len();
451 }
452 }
453 }
454
455 let n = copy_info.len();
456 if n == 0 {
457 stats.num_adds = add_info.len();
458 return (
459 add_info
460 .into_iter()
461 .map(|(dst, data)| PlacedCommand::Add { dst, data })
462 .collect(),
463 stats,
464 );
465 }
466
467 let (adj, mut in_deg) = build_crwi_digraph(©_info, n, &mut stats);
469 let mut scc_list = build_scc_list(&adj, n);
470 let topo_order = run_kahn(
471 ©_info, &adj, &mut scc_list, &mut in_deg,
472 r, &mut add_info, policy, n, &mut stats,
473 );
474
475 stats.num_copies = topo_order.len();
477 let mut result: Vec<PlacedCommand> = Vec::new();
478 for &i in &topo_order {
479 let ci = copy_info[i];
480 result.push(PlacedCommand::Copy { src: ci.src, dst: ci.dst, length: ci.length });
481 }
482 for (dst, data) in add_info {
483 result.push(PlacedCommand::Add { dst, data });
484 }
485 stats.num_adds = result.len() - stats.num_copies;
486
487 (result, stats)
488}