Skip to content

Commit

Permalink
Drop generational-arena dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Dec 28, 2024
1 parent 82bfecd commit 3e84739
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 66 deletions.
1 change: 0 additions & 1 deletion oxisynth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ log.workspace = true
bitflags = "2.4"
byte-slice-cast = "1.0.0"
byteorder = "1.4.3"
generational-arena = "0.2.8"

# i16-out
getrandom = { version = "0.2", features = ["js"], optional = true }
Expand Down
205 changes: 169 additions & 36 deletions oxisynth/src/arena.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,204 @@
use generational_arena::{Arena, Index};
use std::marker::PhantomData;

use std::{cmp::PartialEq, fmt, marker::PhantomData};
enum Slot<T> {
Free,
Occupied { generation: usize, value: T },
}

pub struct TypedIndex<T>(Index, PhantomData<T>);
impl<T> Slot<T> {
fn is_free(&self) -> bool {
matches!(self, Self::Free)
}

impl<T> From<Index> for TypedIndex<T> {
fn from(id: Index) -> Self {
Self(id, PhantomData)
fn is_occupied(&self) -> bool {
!self.is_free()
}
}

impl<T> fmt::Display for TypedIndex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
pub struct Index<T> {
id: usize,
generation: usize,
_ph: PhantomData<fn() -> T>,
}

impl<T> PartialEq for Index<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.generation == other.generation
}
}

impl<T> fmt::Debug for TypedIndex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
impl<T> Eq for Index<T> {}

impl<T> std::fmt::Debug for Index<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Index")
.field("id", &self.id)
.field("generation", &self.generation)
.finish()
}
}

impl<T> Clone for TypedIndex<T> {
impl<T> Copy for Index<T> {}
impl<T> Clone for Index<T> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
*self
}
}

impl<T> Copy for TypedIndex<T> {}
pub(crate) struct Arena<T> {
slots: Vec<Slot<T>>,
generation: usize,
}

impl<T> PartialEq for TypedIndex<T> {
fn eq(&self, other: &TypedIndex<T>) -> bool {
self.0 == other.0
impl<T> Default for Arena<T> {
fn default() -> Self {
Self::new()
}
}

pub(crate) struct TypedArena<T>(Arena<T>);

impl<T> TypedArena<T> {
impl<T> Arena<T> {
pub fn new() -> Self {
Self(Arena::new())
Self {
slots: Vec::new(),
generation: 0,
}
}

pub fn insert(&mut self, value: T) -> TypedIndex<T> {
self.0.insert(value).into()
pub fn insert(&mut self, value: T) -> Index<T> {
let entry = self.slots.iter_mut().enumerate().find(|(_, e)| e.is_free());

match entry {
Some((id, entry)) => {
*entry = Slot::Occupied {
generation: self.generation,
value,
};

Index {
id,
generation: self.generation,
_ph: PhantomData,
}
}
None => {
// No free slots found, insert new one

let id = self.slots.len();

self.slots.push(Slot::Occupied {
generation: self.generation,
value,
});

Index {
id,
generation: self.generation,
_ph: PhantomData,
}
}
}
}

pub fn get(&self, index: TypedIndex<T>) -> Option<&T> {
self.0.get(index.0)
pub fn len(&self) -> usize {
self.slots.iter().filter(|v| v.is_occupied()).count()
}

pub fn remove(&mut self, index: TypedIndex<T>) -> Option<T> {
self.0.remove(index.0)
pub fn get(&self, index: Index<T>) -> Option<&T> {
match self.slots.get(index.id)? {
Slot::Occupied { generation, value } if *generation == index.generation => Some(value),
_ => None,
}
}

pub fn remove(&mut self, index: Index<T>) -> Option<T> {
match self.slots.get_mut(index.id)? {
Slot::Occupied { generation, .. } if *generation == index.generation => {
let Slot::Occupied { value, .. } =
std::mem::replace(&mut self.slots[index.id], Slot::Free)
else {
unreachable!();
};

self.generation += 1;
Some(value)
}
_ => None,
}
}
}

impl<T> std::ops::Deref for TypedArena<T> {
type Target = Arena<T>;
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn insert() {
let mut arena = Arena::new();

fn deref(&self) -> &Self::Target {
&self.0
let a = arena.insert(1);
let b = arena.insert(2);
let c = arena.insert(3);

assert_eq!((a.generation, a.id), (0, 0));
assert_eq!((b.generation, b.id), (0, 1));
assert_eq!((c.generation, c.id), (0, 2));

assert_eq!(arena.get(a), Some(&1));
assert_eq!(arena.get(b), Some(&2));
assert_eq!(arena.get(c), Some(&3));
}
}

impl<T> std::ops::DerefMut for TypedArena<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
#[test]
fn remove() {
let mut arena = Arena::new();

let a = arena.insert(1);
let b = arena.insert(2);
let c = arena.insert(3);

assert_eq!((a.generation, a.id), (0, 0));
assert_eq!((b.generation, b.id), (0, 1));
assert_eq!((c.generation, c.id), (0, 2));

assert_eq!(arena.remove(a), Some(1));
assert_eq!(arena.remove(b), Some(2));
assert_eq!(arena.remove(c), Some(3));

assert_eq!(arena.get(a), None);
assert_eq!(arena.get(b), None);
assert_eq!(arena.get(c), None);
}

#[test]
fn remove_and_reinsert() {
let mut arena = Arena::new();

let a = arena.insert(1);
let b = arena.insert(2);
let c = arena.insert(3);

assert_eq!((a.generation, a.id), (0, 0));
assert_eq!((b.generation, b.id), (0, 1));
assert_eq!((c.generation, c.id), (0, 2));

assert_eq!(arena.remove(a), Some(1));
assert_eq!(arena.remove(b), Some(2));
assert_eq!(arena.remove(c), Some(3));

let aa = arena.insert(1);
let bb = arena.insert(2);
let cc = arena.insert(3);

assert_eq!((aa.generation, aa.id), (3, 0));
assert_eq!((bb.generation, bb.id), (3, 1));
assert_eq!((cc.generation, cc.id), (3, 2));

assert_eq!(arena.get(a), None);
assert_eq!(arena.get(b), None);
assert_eq!(arena.get(c), None);

assert_eq!(arena.get(aa), Some(&1));
assert_eq!(arena.get(bb), Some(&2));
assert_eq!(arena.get(cc), Some(&3));
}
}
10 changes: 5 additions & 5 deletions oxisynth/src/core/channel_pool/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use super::super::soundfont::{Preset, SoundFont};

use crate::arena::TypedIndex;
use crate::arena::Index;
use crate::GeneratorType;
use crate::Tuning;

Expand Down Expand Up @@ -48,7 +48,7 @@ impl Default for InterpolationMethod {
pub struct Channel {
id: usize,

sfontnum: Option<TypedIndex<SoundFont>>,
sfontnum: Option<Index<SoundFont>>,

banknum: u32,
prognum: u8,
Expand Down Expand Up @@ -171,11 +171,11 @@ impl Channel {
self.id
}

pub fn sfontnum(&self) -> Option<TypedIndex<SoundFont>> {
pub fn sfontnum(&self) -> Option<Index<SoundFont>> {
self.sfontnum
}

pub fn set_sfontnum(&mut self, sfontnum: Option<TypedIndex<SoundFont>>) {
pub fn set_sfontnum(&mut self, sfontnum: Option<Index<SoundFont>>) {
self.sfontnum = sfontnum;
}

Expand Down Expand Up @@ -283,7 +283,7 @@ impl Channel {
self.nrpn_active = value;
}

/// Retreive the value of a generator. This function returns the value
/// Retrieve the value of a generator. This function returns the value
/// set by a previous call 'set_gen()' or by an NRPN message.
///
/// Returns the value of the generator.
Expand Down
Loading

0 comments on commit 3e84739

Please sign in to comment.