add feature to make gradual taiko calc sync

This commit is contained in:
MaxOhn
2024-02-23 14:20:24 +01:00
parent b4f843fac1
commit 08e5d52006
17 changed files with 307 additions and 146 deletions
+11 -3
View File
@@ -42,8 +42,16 @@ jobs:
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --no-fail-fast --failure-output=immediate-final
- name: Run tests on default features
run: cargo nextest run --no-default-features --no-fail-fast --failure-output=immediate-final
- name: Run tests with sync feature
run: >
cargo nextest run
--features sync
--filter-expr 'test(util::sync::tests::share_gradual_taiko)'
--filter-expr 'test(taiko::difficulty::gradual::tests::next_and_nth)'
--no-fail-fast --failure-output=immediate-final
- name: Run doctests
run: cargo test --doc
run: cargo test --no-default-features --doc
+1
View File
@@ -5,6 +5,7 @@ edition = "2021"
[features]
default = []
sync = []
tracing = ["rosu-map/tracing"]
[dependencies]
+3 -2
View File
@@ -48,7 +48,7 @@ let stars = diff_attrs.stars();
// Calculate performance attributes
let perf_attrs = map.performance()
// To speed up the calculation, we can use the previous attributes.
// **Note** that this should only be done if the map, mode, mods,
// **Note** that this should only be done if the map, mode, mods,
// clock rate, and amount of passed objects stay the same.
// Otherwise, the final attributes will be incorrect.
.attributes(diff_attrs)
@@ -79,7 +79,8 @@ TODO
| Flag | Description | Dependencies
| --------- | ----------- | ------------
| `default` | No features |
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`]
| `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
### Bindings
+3 -2
View File
@@ -42,7 +42,7 @@
//! // Calculate performance attributes
//! let perf_attrs = map.performance()
//! // To speed up the calculation, we can use the previous attributes.
//! // **Note** that this should only be done if the map, mode, mods,
//! // **Note** that this should only be done if the map, mode, mods,
//! // clock rate, and amount of passed objects stay the same.
//! // Otherwise, the final attributes will be incorrect.
//! .attributes(diff_attrs)
@@ -73,7 +73,8 @@
//! | Flag | Description | Dependencies
//! | --------- | ----------- | ------------
//! | `default` | No features |
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`]
//! | `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
//!
//! ## Bindings
//!
@@ -1,41 +1,39 @@
use std::{
cell::RefCell,
rc::{Rc, Weak},
use crate::{
taiko::difficulty::object::TaikoDifficultyObject,
util::sync::{RefCount, Weak},
};
use crate::taiko::difficulty::object::TaikoDifficultyObject;
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
#[derive(Debug)]
pub struct AlternatingMonoPattern {
pub mono_streaks: Vec<Rc<RefCell<MonoStreak>>>,
pub parent: Option<Weak<RefCell<RepeatingHitPatterns>>>,
pub mono_streaks: Vec<RefCount<MonoStreak>>,
pub parent: Option<Weak<RepeatingHitPatterns>>,
pub idx: usize,
}
impl AlternatingMonoPattern {
pub fn new() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
pub fn new() -> RefCount<Self> {
RefCount::new(Self {
mono_streaks: Vec::new(),
parent: None,
idx: 0,
}))
})
}
pub fn is_repetition_of(&self, other: &Self) -> bool {
self.has_identical_mono_len(other)
&& self.mono_streaks.len() == other.mono_streaks.len()
&& self.mono_streaks[0].borrow().hit_type() == other.mono_streaks[0].borrow().hit_type()
&& self.mono_streaks[0].get().hit_type() == other.mono_streaks[0].get().hit_type()
}
pub fn has_identical_mono_len(&self, other: &Self) -> bool {
self.mono_streaks[0].borrow().run_len() == other.mono_streaks[0].borrow().run_len()
self.mono_streaks[0].get().run_len() == other.mono_streaks[0].get().run_len()
}
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.mono_streaks
.first()
.and_then(|mono| mono.borrow().first_hit_object())
.and_then(|mono| mono.get().first_hit_object())
}
}
+4 -7
View File
@@ -1,7 +1,4 @@
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use crate::util::sync::{RefCount, Weak};
use self::{
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
@@ -15,7 +12,7 @@ pub mod repeating_hit_patterns;
#[derive(Debug, Default)]
pub struct TaikoDifficultyColor {
pub mono_streak: Option<Weak<RefCell<MonoStreak>>>,
pub alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>,
pub mono_streak: Option<Weak<MonoStreak>>,
pub alternating_mono_pattern: Option<Weak<AlternatingMonoPattern>>,
pub repeating_hit_patterns: Option<RefCount<RepeatingHitPatterns>>,
}
+10 -12
View File
@@ -1,26 +1,24 @@
use std::{
cell::RefCell,
rc::{Rc, Weak},
use crate::{
taiko::{difficulty::object::TaikoDifficultyObject, object::HitType},
util::sync::{RefCount, Weak},
};
use crate::taiko::{difficulty::object::TaikoDifficultyObject, object::HitType};
use super::alternating_mono_pattern::AlternatingMonoPattern;
#[derive(Debug)]
pub struct MonoStreak {
pub hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>,
pub parent: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub hit_objects: Vec<Weak<TaikoDifficultyObject>>,
pub parent: Option<Weak<AlternatingMonoPattern>>,
pub idx: usize,
}
impl MonoStreak {
pub fn new() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
pub fn new() -> RefCount<Self> {
RefCount::new(Self {
hit_objects: Vec::new(),
parent: None,
idx: 0,
}))
})
}
pub fn run_len(&self) -> usize {
@@ -31,10 +29,10 @@ impl MonoStreak {
self.hit_objects
.first()
.and_then(Weak::upgrade)
.map(|h| h.borrow().base_hit_type)
.map(|h| h.get().base_hit_type)
}
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.hit_objects.first().and_then(Weak::upgrade)
}
}
+47 -48
View File
@@ -1,10 +1,9 @@
use std::{
cell::{Ref, RefCell},
collections::VecDeque,
rc::Rc,
};
use std::collections::VecDeque;
use crate::taiko::difficulty::object::TaikoDifficultyObjects;
use crate::{
taiko::difficulty::object::TaikoDifficultyObjects,
util::sync::{Ref, RefCount},
};
use super::{
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
@@ -18,54 +17,54 @@ impl ColorDifficultyPreprocessor {
let hit_patterns = Self::encode(hit_objects);
for repeating_hit_pattern in hit_patterns {
if let Some(obj) = repeating_hit_pattern.borrow().first_hit_object() {
obj.borrow_mut().color.repeating_hit_patterns =
Some(Rc::clone(&repeating_hit_pattern));
if let Some(obj) = repeating_hit_pattern.get().first_hit_object() {
obj.get_mut().color.repeating_hit_patterns =
Some(RefCount::clone(&repeating_hit_pattern));
}
let mono_patterns = Ref::map(repeating_hit_pattern.borrow(), |repeating| {
let mono_patterns = Ref::map(repeating_hit_pattern.get(), |repeating| {
repeating.alternating_mono_patterns.as_slice()
});
for (i, mono_pattern) in mono_patterns.iter().enumerate() {
{
let mut mono_pattern = mono_pattern.borrow_mut();
mono_pattern.parent = Some(Rc::downgrade(&repeating_hit_pattern));
let mut mono_pattern = mono_pattern.get_mut();
mono_pattern.parent = Some(RefCount::downgrade(&repeating_hit_pattern));
mono_pattern.idx = i;
}
if let Some(obj) = mono_pattern.borrow().first_hit_object() {
obj.borrow_mut().color.alternating_mono_pattern =
Some(Rc::downgrade(mono_pattern));
if let Some(obj) = mono_pattern.get().first_hit_object() {
obj.get_mut().color.alternating_mono_pattern =
Some(RefCount::downgrade(mono_pattern));
}
let mono_streaks = Ref::map(mono_pattern.borrow(), |alternating| {
let mono_streaks = Ref::map(mono_pattern.get(), |alternating| {
alternating.mono_streaks.as_slice()
});
for (j, mono_streak) in mono_streaks.iter().enumerate() {
{
let mut borrowed = mono_streak.borrow_mut();
borrowed.parent = Some(Rc::downgrade(mono_pattern));
let mut borrowed = mono_streak.get_mut();
borrowed.parent = Some(RefCount::downgrade(mono_pattern));
borrowed.idx = j;
}
if let Some(obj) = mono_streak.borrow().first_hit_object() {
obj.borrow_mut().color.mono_streak = Some(Rc::downgrade(mono_streak));
if let Some(obj) = mono_streak.get().first_hit_object() {
obj.get_mut().color.mono_streak = Some(RefCount::downgrade(mono_streak));
};
}
}
}
}
fn encode(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
fn encode(data: &TaikoDifficultyObjects) -> Vec<RefCount<RepeatingHitPatterns>> {
let mono_streaks = Self::encode_mono_streaks(data);
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
Self::encode_repeating_hit_patterns(alternating_mono_patterns)
}
fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<MonoStreak>>> {
fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<RefCount<MonoStreak>> {
let mut data_iter = data.objects.iter();
let Some(taiko_object) = data_iter.next() else {
@@ -76,15 +75,15 @@ impl ColorDifficultyPreprocessor {
let mut curr_mono_streak = mono_streaks.last();
if let Some(curr) = curr_mono_streak {
curr.borrow_mut()
curr.get_mut()
.hit_objects
.push(Rc::downgrade(taiko_object));
.push(RefCount::downgrade(taiko_object));
}
for taiko_object in data_iter {
let condition = data
.previous_note(&taiko_object.borrow(), 0)
.filter(|prev| taiko_object.borrow().base_hit_type == prev.borrow().base_hit_type);
.previous_note(&taiko_object.get(), 0)
.filter(|prev| taiko_object.get().base_hit_type == prev.get().base_hit_type);
if condition.is_none() {
mono_streaks.push(MonoStreak::new());
@@ -92,9 +91,9 @@ impl ColorDifficultyPreprocessor {
}
if let Some(curr) = curr_mono_streak {
curr.borrow_mut()
curr.get_mut()
.hit_objects
.push(Rc::downgrade(taiko_object));
.push(RefCount::downgrade(taiko_object));
}
}
@@ -102,8 +101,8 @@ impl ColorDifficultyPreprocessor {
}
fn encode_alternating_mono_pattern(
data: Vec<Rc<RefCell<MonoStreak>>>,
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
data: Vec<RefCount<MonoStreak>>,
) -> VecDeque<RefCount<AlternatingMonoPattern>> {
let mut data = data.into_iter();
let Some(mono) = data.next() else {
@@ -114,14 +113,14 @@ impl ColorDifficultyPreprocessor {
mono_patterns.push_back(AlternatingMonoPattern::new());
let mut curr_mono_pattern = mono_patterns.back();
let mut prev_run_len = mono.borrow().run_len();
let mut prev_run_len = mono.get().run_len();
if let Some(curr) = curr_mono_pattern {
curr.borrow_mut().mono_streaks.push(mono);
curr.get_mut().mono_streaks.push(mono);
}
for mono in data {
let run_len = mono.borrow().run_len();
let run_len = mono.get().run_len();
if run_len != prev_run_len {
mono_patterns.push_back(AlternatingMonoPattern::new());
@@ -131,7 +130,7 @@ impl ColorDifficultyPreprocessor {
prev_run_len = run_len;
if let Some(curr_mono_pattern) = curr_mono_pattern {
curr_mono_pattern.borrow_mut().mono_streaks.push(mono);
curr_mono_pattern.get_mut().mono_streaks.push(mono);
}
}
@@ -139,50 +138,50 @@ impl ColorDifficultyPreprocessor {
}
fn encode_repeating_hit_patterns(
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
mut data: VecDeque<RefCount<AlternatingMonoPattern>>,
) -> Vec<RefCount<RepeatingHitPatterns>> {
let mut hit_patterns = Vec::new();
let mut curr_hit_pattern = None;
while !data.is_empty() {
let old = curr_hit_pattern.as_ref().map(Rc::downgrade);
let old = curr_hit_pattern.as_ref().map(RefCount::downgrade);
let curr_hit_pattern = &*curr_hit_pattern.insert(RepeatingHitPatterns::new(old));
let mut is_coupled = data.get(2).map_or(false, |other| {
data[0].borrow().is_repetition_of(&other.borrow())
});
let mut is_coupled = data
.get(2)
.map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
if is_coupled {
while is_coupled {
curr_hit_pattern
.borrow_mut()
.get_mut()
.alternating_mono_patterns
.push(data.pop_front().unwrap());
is_coupled = data.get(2).map_or(false, |other| {
data[0].borrow().is_repetition_of(&other.borrow())
});
is_coupled = data
.get(2)
.map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
}
for front in data.drain(..2) {
curr_hit_pattern
.borrow_mut()
.get_mut()
.alternating_mono_patterns
.push(front);
}
} else {
curr_hit_pattern
.borrow_mut()
.get_mut()
.alternating_mono_patterns
.push(data.pop_front().unwrap());
}
hit_patterns.push(Rc::clone(curr_hit_pattern));
hit_patterns.push(RefCount::clone(curr_hit_pattern));
}
hit_patterns
.iter_mut()
.for_each(|pattern| pattern.borrow_mut().find_repetition_interval());
.for_each(|pattern| pattern.get_mut().find_repetition_interval());
hit_patterns
}
@@ -1,10 +1,9 @@
use std::{
cell::RefCell,
cmp,
rc::{Rc, Weak},
};
use std::cmp;
use crate::taiko::difficulty::object::TaikoDifficultyObject;
use crate::{
taiko::difficulty::object::TaikoDifficultyObject,
util::sync::{RefCount, Weak},
};
use super::alternating_mono_pattern::AlternatingMonoPattern;
@@ -12,18 +11,18 @@ const MAX_REPETITION_INTERVAL: usize = 16;
#[derive(Debug)]
pub struct RepeatingHitPatterns {
pub alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>,
pub prev: Option<Weak<RefCell<Self>>>,
pub alternating_mono_patterns: Vec<RefCount<AlternatingMonoPattern>>,
pub prev: Option<Weak<Self>>,
pub repetition_interval: usize,
}
impl RepeatingHitPatterns {
pub fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
pub fn new(prev: Option<Weak<Self>>) -> RefCount<Self> {
RefCount::new(Self {
alternating_mono_patterns: Vec::new(),
prev,
repetition_interval: 0,
}))
})
}
pub fn find_repetition_interval(&mut self) {
@@ -34,13 +33,13 @@ impl RepeatingHitPatterns {
let mut interval = 1;
while interval < MAX_REPETITION_INTERVAL {
if self.is_repetition_of(&other.borrow()) {
if self.is_repetition_of(&other.get()) {
self.repetition_interval = cmp::min(interval, MAX_REPETITION_INTERVAL);
return;
}
let Some(next) = other.borrow().prev.as_ref().and_then(Weak::upgrade) else {
let Some(next) = other.get().prev.as_ref().and_then(Weak::upgrade) else {
break;
};
@@ -60,16 +59,12 @@ impl RepeatingHitPatterns {
.iter()
.zip(other.alternating_mono_patterns.iter())
.take(2)
.all(|(self_pat, other_pat)| {
self_pat
.borrow()
.has_identical_mono_len(&other_pat.borrow())
})
.all(|(self_pat, other_pat)| self_pat.get().has_identical_mono_len(&other_pat.get()))
}
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.alternating_mono_patterns
.first()
.and_then(|mono| mono.borrow().first_hit_object())
.and_then(|mono| mono.get().first_hit_object())
}
}
+7 -6
View File
@@ -1,8 +1,9 @@
use std::{cell::RefCell, mem, rc::Rc, slice::Iter};
use std::{mem, slice::Iter};
use crate::{
model::{beatmap::HitWindows, hit_object::HitObject},
taiko::TaikoBeatmap,
util::sync::RefCount,
ModeDifficulty,
};
@@ -52,7 +53,7 @@ pub struct TaikoGradualDifficulty {
pub(crate) clock_rate: f64,
attrs: TaikoDifficultyAttributes,
diff_objects: TaikoDifficultyObjects,
diff_objects_iter: Iter<'static, Rc<RefCell<TaikoDifficultyObject>>>,
diff_objects_iter: Iter<'static, RefCount<TaikoDifficultyObject>>,
peaks: Peaks,
total_hits: usize,
first_combos: FirstTwoCombos,
@@ -132,8 +133,8 @@ impl TaikoGradualDifficulty {
}
fn extend_lifetime(
iter: Iter<'_, Rc<RefCell<TaikoDifficultyObject>>>,
) -> Iter<'static, Rc<RefCell<TaikoDifficultyObject>>> {
iter: Iter<'_, RefCount<TaikoDifficultyObject>>,
) -> Iter<'static, RefCount<TaikoDifficultyObject>> {
// SAFETY: The underlying data will never be moved.
unsafe { mem::transmute(iter) }
}
@@ -149,7 +150,7 @@ impl Iterator for TaikoGradualDifficulty {
if self.idx >= 2 {
loop {
let curr = self.diff_objects_iter.next()?;
let borrowed = curr.borrow();
let borrowed = curr.get();
PeaksSkill::new(&mut self.peaks, &self.diff_objects).process(&borrowed);
if borrowed.base_hit_type.is_hit() {
@@ -236,7 +237,7 @@ impl Iterator for TaikoGradualDifficulty {
for _ in 0..take {
loop {
let curr = self.diff_objects_iter.next()?;
let borrowed = curr.borrow();
let borrowed = curr.get();
peaks.process(&borrowed);
if borrowed.base_hit_type.is_hit() {
+1 -1
View File
@@ -98,7 +98,7 @@ impl DifficultyValues {
let mut peaks = PeaksSkill::new(&mut peaks, &diff_objects);
for hit_object in diff_objects.iter().take(n_diff_objects) {
peaks.process(&hit_object.borrow());
peaks.process(&hit_object.get());
}
}
+17 -16
View File
@@ -1,8 +1,9 @@
use std::{cell::RefCell, rc::Rc, slice::Iter};
use std::slice::Iter;
use crate::{
any::difficulty::object::IDifficultyObject,
taiko::object::{HitType, TaikoObject},
util::sync::RefCount,
};
use super::{color::TaikoDifficultyColor, rhythm::HitObjectRhythm};
@@ -27,7 +28,7 @@ impl TaikoDifficultyObject {
clock_rate: f64,
idx: usize,
objects: &mut TaikoDifficultyObjects,
) -> Rc<RefCell<Self>> {
) -> RefCount<Self> {
let delta_time = (hit_object.start_time - last_object.start_time) / clock_rate;
let rhythm = closest_rhythm(delta_time, last_object, last_last_object, clock_rate);
let color = TaikoDifficultyColor::default();
@@ -47,7 +48,7 @@ impl TaikoDifficultyObject {
HitType::NonHit => MonoIndex::None,
};
let this = Rc::new(RefCell::new(Self {
let this = RefCount::new(Self {
idx,
delta_time,
start_time: hit_object.start_time / clock_rate,
@@ -56,16 +57,16 @@ impl TaikoDifficultyObject {
note_idx,
rhythm,
color,
}));
});
match hit_object.hit_type {
HitType::Center => {
objects.note_objects.push(Rc::clone(&this));
objects.center_hit_objects.push(Rc::clone(&this));
objects.note_objects.push(RefCount::clone(&this));
objects.center_hit_objects.push(RefCount::clone(&this));
}
HitType::Rim => {
objects.note_objects.push(Rc::clone(&this));
objects.rim_hit_objects.push(Rc::clone(&this));
objects.note_objects.push(RefCount::clone(&this));
objects.rim_hit_objects.push(RefCount::clone(&this));
}
HitType::NonHit => {}
}
@@ -82,10 +83,10 @@ pub enum MonoIndex {
}
pub struct TaikoDifficultyObjects {
pub objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
pub center_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
pub rim_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
pub note_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
pub objects: Vec<RefCount<TaikoDifficultyObject>>,
pub center_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
pub rim_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
pub note_objects: Vec<RefCount<TaikoDifficultyObject>>,
}
impl TaikoDifficultyObjects {
@@ -101,7 +102,7 @@ impl TaikoDifficultyObjects {
}
}
pub fn push(&mut self, hit_object: Rc<RefCell<TaikoDifficultyObject>>) {
pub fn push(&mut self, hit_object: RefCount<TaikoDifficultyObject>) {
self.objects.push(hit_object);
}
@@ -109,7 +110,7 @@ impl TaikoDifficultyObjects {
self.objects.is_empty()
}
pub fn iter(&self) -> Iter<'_, Rc<RefCell<TaikoDifficultyObject>>> {
pub fn iter(&self) -> Iter<'_, RefCount<TaikoDifficultyObject>> {
self.objects.iter()
}
@@ -117,7 +118,7 @@ impl TaikoDifficultyObjects {
&self,
curr: &TaikoDifficultyObject,
mut backwards_idx: usize,
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> {
) -> Option<&RefCount<TaikoDifficultyObject>> {
backwards_idx += 1;
match curr.mono_idx {
@@ -135,7 +136,7 @@ impl TaikoDifficultyObjects {
&self,
curr: &TaikoDifficultyObject,
backwards_idx: usize,
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> {
) -> Option<&RefCount<TaikoDifficultyObject>> {
curr.note_idx
.checked_sub(backwards_idx + 1)
.and_then(|idx| self.note_objects.get(idx))
+9 -12
View File
@@ -1,8 +1,4 @@
use std::{
cell::RefCell,
f64::consts::E,
rc::{Rc, Weak},
};
use std::f64::consts::E;
use crate::{
any::difficulty::{
@@ -16,6 +12,7 @@ use crate::{
},
object::{TaikoDifficultyObject, TaikoDifficultyObjects},
},
util::sync::{RefCount, Weak},
};
const SKILL_MULTIPLIER: f64 = 0.12;
@@ -65,7 +62,7 @@ impl Skill<'_, Color> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr
.previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time);
.map_or(0.0, |prev| prev.get().start_time);
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
}
@@ -113,8 +110,8 @@ impl ColorEvaluator {
sigmoid * (height / 2.0) + middle
}
fn evaluate_diff_of_mono_streak(mono_streak: &Rc<RefCell<MonoStreak>>) -> f64 {
let mono_streak = mono_streak.borrow();
fn evaluate_diff_of_mono_streak(mono_streak: &RefCount<MonoStreak>) -> f64 {
let mono_streak = mono_streak.get();
let parent_eval = mono_streak
.parent
@@ -127,9 +124,9 @@ impl ColorEvaluator {
}
fn evaluate_diff_of_alternating_mono_pattern(
alternating_mono_pattern: &Rc<RefCell<AlternatingMonoPattern>>,
alternating_mono_pattern: &RefCount<AlternatingMonoPattern>,
) -> f64 {
let alternating_mono_pattern = alternating_mono_pattern.borrow();
let alternating_mono_pattern = alternating_mono_pattern.get();
let parent_eval = alternating_mono_pattern
.parent
@@ -142,9 +139,9 @@ impl ColorEvaluator {
}
fn evaluate_diff_of_repeating_hit_patterns(
repeating_hit_patterns: &Rc<RefCell<RepeatingHitPatterns>>,
repeating_hit_patterns: &RefCount<RepeatingHitPatterns>,
) -> f64 {
let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64;
let repetition_interval = repeating_hit_patterns.get().repetition_interval as f64;
2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0))
}
+1 -1
View File
@@ -159,7 +159,7 @@ impl Skill<'_, Rhythm> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr
.previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time);
.map_or(0.0, |prev| prev.get().start_time);
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
}
+2 -2
View File
@@ -37,7 +37,7 @@ impl Skill<'_, Stamina> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr
.previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time);
.map_or(0.0, |prev| prev.get().start_time);
self.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
}
@@ -118,7 +118,7 @@ impl StaminaEvaluator {
if let Some(key_prev) = key_prev {
// * Add a base strain to all objects
0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.borrow().start_time)
0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.get().start_time)
} else {
// * There is no previous hit object hit by the current key
0.0
+1
View File
@@ -4,3 +4,4 @@ pub mod map_or_attrs;
pub mod mods;
pub mod random;
pub mod sort;
pub mod sync;
+163
View File
@@ -0,0 +1,163 @@
use std::fmt;
pub use inner::*;
#[cfg(not(feature = "sync"))]
mod inner {
use std::{cell::RefCell, rc::Rc};
pub struct RefCount<T>(pub(super) Rc<RefCell<T>>);
pub struct Weak<T>(pub(super) std::rc::Weak<RefCell<T>>);
pub type Ref<'a, T> = std::cell::Ref<'a, T>;
pub type RefMut<'a, T> = std::cell::RefMut<'a, T>;
impl<T> RefCount<T> {
pub fn new(inner: T) -> Self {
Self(Rc::new(RefCell::new(inner)))
}
pub fn clone(this: &Self) -> Self {
Self(Rc::clone(&this.0))
}
pub fn downgrade(&self) -> Weak<T> {
Weak(Rc::downgrade(&self.0))
}
pub fn get(&self) -> Ref<'_, T> {
self.0.borrow()
}
pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}
}
}
#[cfg(feature = "sync")]
mod inner {
use std::{
marker::PhantomData,
ops,
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
};
pub struct RefCount<T>(pub(super) Arc<RwLock<T>>);
pub struct Weak<T>(pub(super) std::sync::Weak<RwLock<T>>);
pub struct Ref<'a, T: ?Sized>(RwLockReadGuard<'a, T>);
pub type RefMut<'a, T> = RwLockWriteGuard<'a, T>;
impl<T> RefCount<T> {
pub fn new(inner: T) -> Self {
Self(Arc::new(RwLock::new(inner)))
}
pub fn clone(this: &Self) -> Self {
Self(Arc::clone(&this.0))
}
pub fn downgrade(&self) -> Weak<T> {
Weak(Arc::downgrade(&self.0))
}
pub fn get(&self) -> Ref<'_, T> {
Ref(self.0.read().unwrap())
}
pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.write().unwrap()
}
}
impl<T> Ref<'_, T> {
pub fn map<U: ?Sized, F>(orig: Ref<'_, T>, f: F) -> RefWrap<'_, T, U, F>
where
F: Copy + FnOnce(&T) -> &U,
{
RefWrap {
orig,
access: f,
_phantom: PhantomData,
}
}
}
pub struct RefWrap<'a, T, U: ?Sized, F> {
orig: Ref<'a, T>,
access: F,
_phantom: PhantomData<U>,
}
impl<T, U: ?Sized, F> ops::Deref for RefWrap<'_, T, U, F>
where
F: Copy + FnOnce(&T) -> &U,
{
type Target = U;
fn deref(&self) -> &Self::Target {
(self.access)(&self.orig)
}
}
impl<T: ?Sized> ops::Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
ops::Deref::deref(&self.0)
}
}
}
impl<T> Weak<T> {
pub fn upgrade(&self) -> Option<RefCount<T>> {
self.0.upgrade().map(RefCount)
}
}
impl<T: fmt::Debug> fmt::Debug for RefCount<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<T: fmt::Debug> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
/// ```compile_fail
/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
///
/// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
/// let difficulty = ModeDifficulty::new();
/// let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
///
/// // Rc<RefCell<_>> cannot be shared across threads so compilation should fail
/// std::thread::spawn(move || { let _ = gradual.next(); });
/// ```
#[cfg(not(feature = "sync"))]
fn _share_gradual_taiko() {}
#[cfg(all(test, feature = "sync"))]
mod tests {
#[test]
fn share_gradual_taiko() {
use crate::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
let difficulty = ModeDifficulty::new();
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
// Arc<RwLock<_>> *can* be shared across threads so this should compile
std::thread::spawn(move || {
let _ = gradual.next();
});
}
}