java-topology/defects/ruffle-0002/patch/ruffle-0002.patch

93 lines
4.1 KiB
Diff

# UNDF: UNDF-2026-000001156
--- a/core/src/display_object/movie_clip.rs
+++ b/core/src/display_object/movie_clip.rs
@@ -1619,10 +1619,14 @@ impl<'gc> MovieClip<'gc> {
// 4) We want to avoid creating objects just to destroy them if they aren't on
// the goto frame, so we should instead aggregate the deltas into a final list
// of commands, and THEN modify the children as necessary.
-
- // This map will maintain a map of depth -> placement commands.
- // TODO: Move this to UpdateContext to avoid allocations.
- let mut goto_commands: Vec<GotoPlaceObject<'_>> = vec![];
+
+ // This map will maintain a map of depth -> placement commands.
+ // TODO: Move this to UpdateContext to avoid allocations.
+ let mut goto_commands: Vec<GotoPlaceObject<'_>> = vec![];
+ // O(1) depth lookup index: depth -> index in goto_commands Vec.
+ // Previously each goto_place_object/goto_remove_object call did
+ // iter().position() O(D) per SWF tag; a gotoAndPlay spanning F frames
+ // with D display objects per frame costs O(F*D^2). HashMap brings it to O(F*D).
+ let mut goto_depth_index: std::collections::HashMap<swf::Depth, usize> =
+ std::collections::HashMap::new();
@@ -1688,8 +1692,12 @@ impl<'gc> MovieClip<'gc> {
Action::Place(version) => {
index += 1;
self.0.goto_place_object(
+ &mut goto_depth_index,
reader,
version,
&mut goto_commands,
is_rewind,
index,
)
}
Action::Remove(version) => self.goto_remove_object(
+ &mut goto_depth_index,
reader,
version,
context,
@@ -2143,6 +2151,7 @@ impl<'gc> MovieClip<'gc> {
fn goto_remove_object<'a>(
mut self,
reader: &mut SwfStream<'a>,
version: u8,
context: &mut UpdateContext<'gc>,
+ goto_depth_index: &mut std::collections::HashMap<swf::Depth, usize>,
goto_commands: &mut Vec<GotoPlaceObject<'a>>,
is_rewind: bool,
from_frame: FrameNumber,
@@ -2158,8 +2167,14 @@ impl<'gc> MovieClip<'gc> {
}?;
let depth: Depth = remove_object.depth.into();
- if let Some(i) = goto_commands.iter().position(|o| o.depth() == depth) {
- goto_commands.swap_remove(i);
+ if let Some(&i) = goto_depth_index.get(&depth) {
+ let last_depth = goto_commands.last().map(|o| o.depth());
+ goto_commands.swap_remove(i);
+ goto_depth_index.remove(&depth);
+ // swap_remove moves the last element to position i; update its index entry.
+ if let Some(d) = last_depth {
+ if d != depth {
+ goto_depth_index.insert(d, i);
+ }
+ }
}
@@ -3301,6 +3316,7 @@ impl<'gc> MovieClip<'gc> {
fn goto_place_object<'a>(
&self,
reader: &mut SwfStream<'a>,
version: u8,
+ goto_depth_index: &mut std::collections::HashMap<swf::Depth, usize>,
goto_commands: &mut Vec<GotoPlaceObject<'a>>,
is_rewind: bool,
index: usize,
@@ -3325,10 +3341,15 @@ impl<'gc> MovieClip<'gc> {
let depth: Depth = place_object.depth.into();
let mut goto_place = GotoPlaceObject::new(
self.current_frame(),
place_object,
is_rewind,
index,
tag_start,
version,
);
- if let Some(i) = goto_commands.iter().position(|o| o.depth() == depth) {
- goto_commands[i].merge(&mut goto_place);
+ if let Some(&i) = goto_depth_index.get(&depth) {
+ goto_commands[i].merge(&mut goto_place);
} else {
+ goto_depth_index.insert(depth, goto_commands.len());
goto_commands.push(goto_place);
}