chore: Replace `generational-arena` with `slotmap`

This commit is contained in:
TÖRÖK Attila 2024-03-01 22:09:00 +01:00 committed by Nathan Adams
parent 90bf13ebd2
commit 55773f0205
19 changed files with 80 additions and 79 deletions

19
Cargo.lock generated
View File

@ -2272,15 +2272,6 @@ dependencies = [
"synstructure",
]
[[package]]
name = "generational-arena"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7"
dependencies = [
"cfg-if",
]
[[package]]
name = "generator"
version = "0.7.5"
@ -4464,7 +4455,6 @@ dependencies = [
"flv-rs",
"fnv",
"futures",
"generational-arena",
"hashbrown 0.14.3",
"image",
"indexmap",
@ -4489,6 +4479,7 @@ dependencies = [
"scopeguard",
"serde",
"serde_json",
"slotmap",
"smallvec",
"swf",
"symphonia",
@ -4525,7 +4516,6 @@ dependencies = [
"fontdb",
"futures",
"futures-lite 2.2.0",
"generational-arena",
"gilrs",
"image",
"isahc",
@ -4536,6 +4526,7 @@ dependencies = [
"ruffle_render",
"ruffle_render_wgpu",
"ruffle_video_software",
"slotmap",
"sys-locale",
"tokio",
"tracing",
@ -4717,8 +4708,8 @@ dependencies = [
name = "ruffle_video"
version = "0.1.0"
dependencies = [
"generational-arena",
"ruffle_render",
"slotmap",
"swf",
"thiserror",
]
@ -4728,7 +4719,6 @@ name = "ruffle_video_software"
version = "0.1.0"
dependencies = [
"flate2",
"generational-arena",
"h263-rs",
"h263-rs-deblock",
"log",
@ -4737,6 +4727,7 @@ dependencies = [
"nihav_duck",
"ruffle_render",
"ruffle_video",
"slotmap",
"swf",
"thiserror",
]
@ -4751,7 +4742,6 @@ dependencies = [
"console_error_panic_hook",
"futures",
"futures-util",
"generational-arena",
"getrandom",
"gloo-net",
"js-sys",
@ -4765,6 +4755,7 @@ dependencies = [
"ruffle_web_common",
"serde",
"serde-wasm-bindgen",
"slotmap",
"thiserror",
"tracing",
"tracing-log",

View File

@ -51,6 +51,7 @@ wgpu = "0.19.3"
egui = "0.26.2"
clap = { version = "4.5.1", features = ["derive"] }
anyhow = "1.0"
slotmap = "1.0.7"
[workspace.lints.rust]
# Clippy nightly often adds new/buggy lints that we want to ignore.

View File

@ -17,7 +17,7 @@ bitstream-io = "2.2.0"
flate2 = "1.0.28"
fnv = "1.0.7"
gc-arena = { package = "ruffle_gc_arena", path = "../ruffle_gc_arena" }
generational-arena = "0.2.9"
slotmap = { workspace = true }
indexmap = "2.2.5"
tracing = { workspace = true }
ruffle_render = { path = "../render", features = ["tessellator"] }

View File

@ -7,7 +7,7 @@ use crate::{
};
use downcast_rs::Downcast;
use gc_arena::Collect;
use generational_arena::{Arena, Index};
use slotmap::{DefaultKey, Key, SlotMap};
#[cfg(feature = "audio")]
pub mod decoders;
@ -35,8 +35,8 @@ mod decoders {
use thiserror::Error;
use web_time::Duration;
pub type SoundHandle = Index;
pub type SoundInstanceHandle = Index;
pub type SoundHandle = DefaultKey;
pub type SoundInstanceHandle = DefaultKey;
pub type DecodeError = decoders::Error;
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
@ -205,14 +205,14 @@ struct NullSound {
/// Audio backend that ignores all audio.
pub struct NullAudioBackend {
sounds: Arena<NullSound>,
sounds: SlotMap<SoundHandle, NullSound>,
volume: f32,
}
impl NullAudioBackend {
pub fn new() -> NullAudioBackend {
NullAudioBackend {
sounds: Arena::new(),
sounds: SlotMap::new(),
volume: 1.0,
}
}
@ -259,7 +259,7 @@ impl AudioBackend for NullAudioBackend {
_sound: SoundHandle,
_sound_info: &swf::SoundInfo,
) -> Result<SoundInstanceHandle, DecodeError> {
Ok(SoundInstanceHandle::from_raw_parts(0, 0))
Ok(SoundInstanceHandle::null())
}
fn start_stream(
@ -267,7 +267,7 @@ impl AudioBackend for NullAudioBackend {
_clip_data: crate::tag_utils::SwfSlice,
_handle: &swf::SoundStreamHead,
) -> Result<SoundInstanceHandle, DecodeError> {
Ok(SoundInstanceHandle::from_raw_parts(0, 0))
Ok(SoundInstanceHandle::null())
}
fn start_substream(
@ -275,7 +275,7 @@ impl AudioBackend for NullAudioBackend {
_stream_data: Substream,
_handle: &SoundStreamInfo,
) -> Result<SoundInstanceHandle, DecodeError> {
Ok(SoundInstanceHandle::from_raw_parts(0, 0))
Ok(SoundInstanceHandle::null())
}
fn stop_sound(&mut self, _sound: SoundInstanceHandle) {}

View File

@ -3,7 +3,7 @@ use super::{SoundHandle, SoundInstanceHandle, SoundStreamInfo, SoundTransform};
use crate::backend::audio::{DecodeError, RegisterError};
use crate::buffer::Substream;
use crate::tag_utils::SwfSlice;
use generational_arena::Arena;
use slotmap::SlotMap;
use std::io::Cursor;
use std::sync::{Arc, Mutex, RwLock};
use swf::AudioCompression;
@ -54,10 +54,10 @@ impl CircBuf {
// all sounds and mix the audio into an output buffer audio stream.
pub struct AudioMixer {
/// The currently registered sounds.
sounds: Arena<Sound>,
sounds: SlotMap<SoundHandle, Sound>,
/// The list of actively playing sound instances.
sound_instances: Arc<Mutex<Arena<SoundInstance>>>,
sound_instances: Arc<Mutex<SlotMap<SoundInstanceHandle, SoundInstance>>>,
/// The master volume of the audio from [0.0, 1.0].
volume: Arc<RwLock<f32>>,
@ -239,8 +239,8 @@ impl AudioMixer {
/// Creates a new `AudioMixer` with the given number of channels and sample rate.
pub fn new(num_output_channels: u8, output_sample_rate: u32) -> Self {
Self {
sounds: Arena::new(),
sound_instances: Arc::new(Mutex::new(Arena::new())),
sounds: SlotMap::new(),
sound_instances: Arc::new(Mutex::new(SlotMap::new())),
volume: Arc::new(RwLock::new(1.0)),
num_output_channels,
output_sample_rate,
@ -428,7 +428,7 @@ impl AudioMixer {
/// Refill the output buffer by stepping through all active sounds
/// and mixing in their output.
fn mix_audio<'a, T>(
sound_instances: &mut Arena<SoundInstance>,
sound_instances: &mut SlotMap<SoundInstanceHandle, SoundInstance>,
volume: f32,
num_channels: u8,
mut output_buffer: &mut [T],
@ -724,7 +724,7 @@ impl AudioMixer {
/// to perform audio mixing on a different thread.
pub struct AudioMixerProxy {
/// The list of actively playing sound instances.
sound_instances: Arc<Mutex<Arena<SoundInstance>>>,
sound_instances: Arc<Mutex<SlotMap<SoundInstanceHandle, SoundInstance>>>,
/// The master volume of the audio from [0.0, 1.0].
volume: Arc<RwLock<f32>>,

View File

@ -32,8 +32,8 @@ use crate::vminterface::Instantiator;
use crate::{avm2_stub_method, avm2_stub_method_context};
use encoding_rs::UTF_8;
use gc_arena::{Collect, GcCell};
use generational_arena::{Arena, Index};
use ruffle_render::utils::{determine_jpeg_tag_format, JpegTagFormat};
use slotmap::{DefaultKey, SlotMap};
use std::borrow::Borrow;
use std::fmt;
use std::sync::{Arc, Mutex, Weak};
@ -42,7 +42,7 @@ use swf::read::{extract_swz, read_compression_type};
use thiserror::Error;
use url::{form_urlencoded, ParseError, Url};
pub type Handle = Index;
pub type Handle = DefaultKey;
/// The depth of AVM1 movies that AVM2 loads.
const LOADER_INSERTED_AVM1_DEPTH: i32 = -0xF000;
@ -224,7 +224,7 @@ impl From<crate::avm1::Error<'_>> for Error {
}
/// Holds all in-progress loads for the player.
pub struct LoadManager<'gc>(Arena<Loader<'gc>>);
pub struct LoadManager<'gc>(SlotMap<Handle, Loader<'gc>>);
unsafe impl<'gc> Collect for LoadManager<'gc> {
fn trace(&self, cc: &gc_arena::Collection) {
@ -237,7 +237,7 @@ unsafe impl<'gc> Collect for LoadManager<'gc> {
impl<'gc> LoadManager<'gc> {
/// Construct a new `LoadManager`.
pub fn new() -> Self {
Self(Arena::new())
Self(SlotMap::new())
}
/// Add a new loader to the `LoadManager`.
@ -355,15 +355,24 @@ impl<'gc> LoadManager<'gc> {
///
/// This also removes all movie loaders that have completed.
pub fn movie_clip_on_load(&mut self, queue: &mut ActionQueue<'gc>) {
let mut invalidated_loaders = vec![];
// FIXME: This relies on the iteration order of the slotmap, which
// is not defined. The container should be replaced with something
// that preserves insertion order, such as `LinkedHashMap` -
// unfortunately that doesn't provide automatic key generation.
let mut loaders: Vec<_> = self.0.keys().collect();
// `SlotMap` doesn't provide reverse iteration, so reversing afterwards.
loaders.reverse();
for (index, loader) in self.0.iter_mut().rev() {
if loader.movie_clip_loaded(queue) {
invalidated_loaders.push(index);
}
}
// Removing the keys from `loaders` whose movie hasn't loaded yet.
loaders.retain(|handle| {
self.0
.get_mut(*handle)
.expect("valid key")
.movie_clip_loaded(queue)
});
for index in invalidated_loaders {
// Cleaning up the loaders that are done.
for index in loaders {
self.0.remove(index);
}
}
@ -1692,7 +1701,7 @@ impl<'gc> Loader<'gc> {
}
/// Report a movie loader start event to script code.
fn movie_loader_start(handle: Index, uc: &mut UpdateContext<'_, 'gc>) -> Result<(), Error> {
fn movie_loader_start(handle: Handle, uc: &mut UpdateContext<'_, 'gc>) -> Result<(), Error> {
let me = uc.load_manager.get_loader_mut(handle);
if me.is_none() {
return Err(Error::Cancelled);
@ -2080,7 +2089,7 @@ impl<'gc> Loader<'gc> {
///
/// The current and total length are always reported as compressed lengths.
fn movie_loader_progress(
handle: Index,
handle: Handle,
uc: &mut UpdateContext<'_, 'gc>,
cur_len: usize,
total_len: usize,
@ -2146,7 +2155,7 @@ impl<'gc> Loader<'gc> {
/// Report a movie loader completion to script code.
fn movie_loader_complete(
handle: Index,
handle: Handle,
uc: &mut UpdateContext<'_, 'gc>,
dobj: Option<DisplayObject<'gc>>,
status: u16,
@ -2315,7 +2324,7 @@ impl<'gc> Loader<'gc> {
/// This is an associated function because we cannot borrow both the update
/// context and one of it's loaders.
fn movie_loader_error(
handle: Index,
handle: Handle,
uc: &mut UpdateContext<'_, 'gc>,
msg: AvmString<'gc>,
status: u16,

View File

@ -2,9 +2,9 @@ use crate::avm1::Object as Avm1Object;
use crate::avm2::object::LocalConnectionObject;
use crate::string::AvmString;
use gc_arena::Collect;
use generational_arena::{Arena, Index};
use slotmap::{DefaultKey, SlotMap};
pub type LocalConnectionHandle = Index;
pub type LocalConnectionHandle = DefaultKey;
#[derive(Collect)]
#[collect(no_drop)]
@ -41,7 +41,7 @@ impl<'gc> LocalConnection<'gc> {
/// Manages the collection of local connections.
pub struct LocalConnections<'gc> {
connections: Arena<LocalConnection<'gc>>,
connections: SlotMap<LocalConnectionHandle, LocalConnection<'gc>>,
}
unsafe impl<'gc> Collect for LocalConnections<'gc> {
@ -55,7 +55,7 @@ unsafe impl<'gc> Collect for LocalConnections<'gc> {
impl<'gc> LocalConnections<'gc> {
pub fn empty() -> Self {
Self {
connections: Arena::new(),
connections: SlotMap::new(),
}
}

View File

@ -10,12 +10,12 @@ use crate::Player;
use flash_lso::packet::{Header, Message, Packet};
use flash_lso::types::{AMFVersion, Value as AmfValue};
use gc_arena::{Collect, DynamicRoot, Rootable};
use generational_arena::{Arena, Index};
use slotmap::{DefaultKey, SlotMap};
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use std::sync::{Mutex, Weak};
pub type NetConnectionHandle = Index;
pub type NetConnectionHandle = DefaultKey;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ResponderCallback {
@ -76,7 +76,7 @@ impl<'gc> From<Avm2NetConnectionObject<'gc>> for NetConnectionObject<'gc> {
/// Manages the collection of NetConnections.
pub struct NetConnections<'gc> {
connections: Arena<NetConnection<'gc>>,
connections: SlotMap<NetConnectionHandle, NetConnection<'gc>>,
}
unsafe impl<'gc> Collect for NetConnections<'gc> {
@ -90,7 +90,7 @@ unsafe impl<'gc> Collect for NetConnections<'gc> {
impl<'gc> Default for NetConnections<'gc> {
fn default() -> Self {
Self {
connections: Arena::new(),
connections: SlotMap::new(),
}
}
}

View File

@ -13,13 +13,13 @@ use crate::{
};
use async_channel::{unbounded, Receiver, Sender as AsyncSender, Sender};
use gc_arena::Collect;
use generational_arena::{Arena, Index};
use slotmap::{DefaultKey, SlotMap};
use std::{
cell::{Cell, RefCell},
time::Duration,
};
pub type SocketHandle = Index;
pub type SocketHandle = DefaultKey;
#[derive(Copy, Clone, Collect)]
#[collect(no_drop)]
@ -62,7 +62,7 @@ pub enum SocketAction {
/// Manages the collection of Sockets.
pub struct Sockets<'gc> {
sockets: Arena<Socket<'gc>>,
sockets: SlotMap<SocketHandle, Socket<'gc>>,
receiver: Receiver<SocketAction>,
sender: Sender<SocketAction>,
@ -81,7 +81,7 @@ impl<'gc> Sockets<'gc> {
let (sender, receiver) = unbounded();
Self {
sockets: Arena::new(),
sockets: SlotMap::new(),
receiver,
sender,
}

View File

@ -25,7 +25,7 @@ ruffle_render_wgpu = { path = "../render/wgpu", features = ["clap"] }
ruffle_video_software = { path = "../video/software", optional = true }
tracing = { workspace = true}
tracing-subscriber = { workspace = true }
generational-arena = "0.2.9"
slotmap = { workspace = true }
winit = "0.29.13"
webbrowser = "0.8.12"
url = "2.5.0"

View File

@ -677,7 +677,7 @@ mod tests {
macro_rules! dummy_handle {
() => {
SocketHandle::from_raw_parts(4, 2)
SocketHandle::default()
};
}

View File

@ -3,9 +3,9 @@
use crate::custom_event::RuffleEvent;
use crate::task::Task;
use async_channel::{unbounded, Receiver, Sender};
use generational_arena::{Arena, Index};
use ruffle_core::backend::navigator::OwnedFuture;
use ruffle_core::loader::Error;
use slotmap::{DefaultKey, SlotMap};
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use winit::event_loop::EventLoopProxy;
@ -17,7 +17,7 @@ use winit::event_loop::EventLoopProxy;
#[derive(Clone)]
struct TaskHandle {
/// The arena handle for a given task.
handle: Index,
handle: DefaultKey,
/// The executor the task belongs to.
///
@ -28,7 +28,7 @@ struct TaskHandle {
impl TaskHandle {
/// Construct a handle to a given task.
fn for_task(task: Index, executor: Weak<Mutex<WinitAsyncExecutor>>) -> Self {
fn for_task(task: DefaultKey, executor: Weak<Mutex<WinitAsyncExecutor>>) -> Self {
Self {
handle: task,
executor,
@ -128,7 +128,7 @@ impl TaskHandle {
pub struct WinitAsyncExecutor {
/// List of all spawned tasks.
task_queue: Arena<Task>,
task_queue: SlotMap<DefaultKey, Task>,
/// Source of tasks sent to us by the `NavigatorBackend`.
channel: Receiver<OwnedFuture<(), Error>>,
@ -152,7 +152,7 @@ impl WinitAsyncExecutor {
let (send, recv) = unbounded();
let new_self = Arc::new_cyclic(|self_ref| {
Mutex::new(Self {
task_queue: Arena::new(),
task_queue: SlotMap::new(),
channel: recv,
self_ref: self_ref.clone(),
event_loop: event_loop.clone(),
@ -200,7 +200,7 @@ impl WinitAsyncExecutor {
}
/// Mark a task as ready to proceed.
fn wake(&mut self, task: Index) {
fn wake(&mut self, task: DefaultKey) {
if let Some(task) = self.task_queue.get_mut(task) {
if !task.is_completed() {
task.set_ready();

View File

@ -13,7 +13,7 @@ workspace = true
[dependencies]
swf = { path = "../swf" }
ruffle_render = { path = "../render" }
generational-arena = "0.2.9"
slotmap = { workspace = true }
thiserror = "1.0"
[features]

View File

@ -14,7 +14,7 @@ workspace = true
ruffle_render = { path = "../../render" }
ruffle_video = { path = ".." }
swf = { path = "../../swf" }
generational-arena = "0.2.9"
slotmap = { workspace = true }
thiserror = "1.0"
flate2 = "1.0.28"
log = "0.4"

View File

@ -1,17 +1,17 @@
use crate::decoder::VideoDecoder;
use generational_arena::Arena;
use ruffle_render::backend::RenderBackend;
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, PixelRegion};
use ruffle_video::backend::VideoBackend;
use ruffle_video::error::Error;
use ruffle_video::frame::{EncodedFrame, FrameDependency};
use ruffle_video::VideoStreamHandle;
use slotmap::SlotMap;
use swf::{VideoCodec, VideoDeblocking};
/// Software video backend that proxies to CPU-only codec implementations that
/// ship with Ruffle.
pub struct SoftwareVideoBackend {
streams: Arena<VideoStream>,
streams: SlotMap<VideoStreamHandle, VideoStream>,
}
impl Default for SoftwareVideoBackend {
@ -23,7 +23,7 @@ impl Default for SoftwareVideoBackend {
impl SoftwareVideoBackend {
pub fn new() -> Self {
Self {
streams: Arena::new(),
streams: SlotMap::new(),
}
}
}

View File

@ -1,10 +1,10 @@
#![deny(clippy::unwrap_used)]
use generational_arena::Index;
use slotmap::DefaultKey;
pub mod backend;
pub mod error;
pub mod frame;
pub mod null;
pub type VideoStreamHandle = Index;
pub type VideoStreamHandle = DefaultKey;

View File

@ -2,13 +2,13 @@ use crate::backend::VideoBackend;
use crate::error::Error;
use crate::frame::{EncodedFrame, FrameDependency};
use crate::VideoStreamHandle;
use generational_arena::Arena;
use ruffle_render::backend::RenderBackend;
use ruffle_render::bitmap::BitmapInfo;
use slotmap::SlotMap;
use swf::{VideoCodec, VideoDeblocking};
pub struct NullVideoBackend {
streams: Arena<()>,
streams: SlotMap<VideoStreamHandle, ()>,
}
/// Implementation of video that does not decode any video.
@ -22,7 +22,7 @@ pub struct NullVideoBackend {
impl NullVideoBackend {
pub fn new() -> Self {
Self {
streams: Arena::new(),
streams: SlotMap::new(),
}
}
}

View File

@ -32,7 +32,7 @@ profiling = []
[dependencies]
console_error_panic_hook = { version = "0.1.7", optional = true }
generational-arena = "0.2.9"
slotmap = { workspace = true }
js-sys = "0.3.68"
tracing = { workspace = true, features = ["log"] }
tracing-subscriber = { version = "0.3.18", default-features = false, features = ["registry"] }

View File

@ -7,7 +7,6 @@ mod navigator;
mod storage;
mod ui;
use generational_arena::{Arena, Index};
use js_sys::{Array, Error as JsError, Function, Object, Promise, Uint8Array};
use ruffle_core::backend::navigator::OpenURLMode;
use ruffle_core::backend::ui::FontDefinition;
@ -29,6 +28,7 @@ use ruffle_render::quality::StageQuality;
use ruffle_video_software::backend::SoftwareVideoBackend;
use ruffle_web_common::JsResult;
use serde::{Deserialize, Serialize};
use slotmap::{DefaultKey, SlotMap};
use std::collections::BTreeMap;
use std::rc::Rc;
use std::str::FromStr;
@ -52,7 +52,7 @@ thread_local! {
/// We store the actual instances of the ruffle core in a static pool.
/// This gives us a clear boundary between the JS side and Rust side, avoiding
/// issues with lifetimes and type parameters (which cannot be exported with wasm-bindgen).
static INSTANCES: RefCell<Arena<RefCell<RuffleInstance>>> = RefCell::new(Arena::new());
static INSTANCES: RefCell<SlotMap<DefaultKey, RefCell<RuffleInstance>>> = RefCell::new(SlotMap::new());
static CURRENT_CONTEXT: RefCell<Option<*mut UpdateContext<'static, 'static>>> = const { RefCell::new(None) };
}
@ -339,7 +339,7 @@ struct MovieMetadata {
/// This type is exported to JS, and is used to interact with the library.
#[wasm_bindgen]
#[derive(Clone, Copy)]
pub struct Ruffle(Index);
pub struct Ruffle(DefaultKey);
#[wasm_bindgen]
impl Ruffle {