Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor state #414

Merged
merged 10 commits into from
Sep 13, 2023
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ usvg = {version = "0.33.0", default-features = false}
webbrowser = "0.8.8"
wgpu = {version = "0.16.0"}
winit = {version = "0.28.5", default-features = false, features = ["x11", "wayland", "wayland-dlopen"]}
zerocopy = "0.7.0-alpha.3"
zerocopy = "0.7.3"

[workspace.metadata.release]
shared-version = true
Expand Down
9 changes: 8 additions & 1 deletion algo/src/cow_rc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
borrow::Borrow,
borrow::{Borrow, BorrowMut},
fmt::Debug,
hash::Hash,
ops::{Bound, Range},
Expand Down Expand Up @@ -88,6 +88,13 @@
}
}

impl<B: ?Sized + ToOwned> std::ops::DerefMut for CowArc<B>
where
B::Owned: Clone + std::borrow::BorrowMut<B>,
{
fn deref_mut(&mut self) -> &mut B { self.to_mut().borrow_mut() }

Check warning on line 95 in algo/src/cow_rc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/cow_rc.rs#L95

Added line #L95 was not covered by tests
}

impl<T: ToOwned + ?Sized + 'static> Clone for CowArc<T> {
#[inline]
fn clone(&self) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions algo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ pub use cow_rc::{CowArc, Substr};
pub use frame_cache::*;
mod resource;
pub use resource::*;
mod sc;
pub use sc::*;
227 changes: 227 additions & 0 deletions algo/src/sc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
use std::{
any::Any,
cell::Cell,
fmt::{Debug, Display, Formatter, Pointer},
ptr::NonNull,
};

/// A single-thread smart pointer with strong reference count only.
/// This is a simplified version of `std::rc::Sc` with the weak reference count.
/// Use it when you are sure that there is no cycle in the reference graph or in
/// a inner resource manage that will break the cycle by itself.
pub struct Sc<T: ?Sized>(NonNull<ScBox<T>>);

struct ScBox<T: ?Sized> {
ref_cnt: Cell<usize>,
value: T,
}

impl<T> Sc<T> {
/// Constructs a new `Sc<T>`.
///
/// # Examples
///
/// ```
/// use ribir_algo::Sc;
///
/// let five = Sc::new(5);
/// ```
#[inline]
pub fn new(value: T) -> Self {
Self::from_inner(Box::leak(Box::new(ScBox { ref_cnt: Cell::new(1), value })).into())
}

/// Returns the inner value, if the `Sc` has exactly one strong reference.
///
/// Otherwise, an [`Err`] is returned with the same `Sc` that was
/// passed in.
///
/// This will succeed even if there are outstanding weak references.
///
/// # Examples
///
/// ```
/// use ribir_algo::Sc;
///
/// let x = Sc::new(3);
/// assert_eq!(Sc::try_unwrap(x), Ok(3));
///
/// let x = Sc::new(4);
/// let _y = Sc::clone(&x);
/// assert_eq!(*Sc::try_unwrap(x).unwrap_err(), 4);
/// ```
#[inline]
pub fn try_unwrap(this: Self) -> Result<T, Self> {
if Sc::ref_count(&this) == 1 {

Check warning on line 55 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L54-L55

Added lines #L54 - L55 were not covered by tests
unsafe {
let val = std::ptr::read(&*this); // copy the contained object

// avoid to call `drop` but release the memory.
let layout = std::alloc::Layout::for_value(this.0.as_ref());
let ptr = this.0.as_ptr();
std::mem::forget(this);
std::alloc::dealloc(ptr as *mut _, layout);

Ok(val)

Check warning on line 65 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L57-L65

Added lines #L57 - L65 were not covered by tests
}
} else {
Err(this)

Check warning on line 68 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L68

Added line #L68 was not covered by tests
}
}

Check warning on line 70 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L70

Added line #L70 was not covered by tests
}

impl Sc<dyn Any> {
///
/// todo: prefer implement `CoerceUnsized` if it stable.
#[inline]
pub fn new_any<T: Any>(value: T) -> Self {
let inner: Box<ScBox<dyn Any>> = Box::new(ScBox { ref_cnt: Cell::new(1), value });
Self::from_inner(Box::leak(inner).into())
}

Check warning on line 80 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L77-L80

Added lines #L77 - L80 were not covered by tests
}

impl<T: ?Sized> Sc<T> {
// Gets the number of pointers to this allocation.
///
/// # Examples
///
/// ```
/// use ribir_algo::Sc;
///
/// let five = Sc::new(5);
/// let _also_five = Sc::clone(&five);
///
/// assert_eq!(2, Sc::ref_count(&five));
/// ```
#[inline]
pub fn ref_count(&self) -> usize { self.inner().ref_cnt() }

/// Returns `true` if the two `Sc`s point to the same allocation in a vein
/// similar to [`ptr::eq`]. See [that function][`ptr::eq`] for caveats when
/// comparing `dyn Trait` pointers.
///
/// # Examples
///
/// ```
/// use ribir_algo::Sc;
///
/// let five = Sc::new(5);
/// let same_five = Sc::clone(&five);
/// let other_five = Sc::new(5);
///
/// assert!(Sc::ptr_eq(&five, &same_five));
/// assert!(!Sc::ptr_eq(&five, &other_five));
/// ```
pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.0.as_ptr() == other.0.as_ptr() }

Check warning on line 115 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L115

Added line #L115 was not covered by tests

fn from_inner(ptr: NonNull<ScBox<T>>) -> Self { Self(ptr) }

fn inner(&self) -> &ScBox<T> {
// Safety: we're guaranteed that the inner pointer is valid when the `Sc` is
// alive
unsafe { self.0.as_ref() }
}
}

impl Sc<dyn Any> {
/// Attempt to downcast the `Sc<dyn Any>` to a concrete type.
///
/// # Examples
///
/// ```
/// use std::any::Any;
/// use ribir_algo::Sc;
///
/// fn print_if_string(value: Sc<dyn Any>) {
/// if let Ok(string) = value.downcast::<String>() {
/// println!("String ({}): {}", string.len(), string);
/// }
/// }
///
/// let my_string = "Hello World".to_string();
/// print_if_string(Sc::new_any(my_string));
/// print_if_string(Sc::new_any(0i8));
/// ```
pub fn downcast<T: Any>(self) -> Result<Sc<T>, Sc<dyn Any>> {
if (*self).is::<T>() {
let ptr = self.0.cast::<ScBox<T>>();
std::mem::forget(self);
Ok(Sc::from_inner(ptr))

Check warning on line 149 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L145-L149

Added lines #L145 - L149 were not covered by tests
} else {
Err(self)

Check warning on line 151 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L151

Added line #L151 was not covered by tests
}
}

Check warning on line 153 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L153

Added line #L153 was not covered by tests
}

impl<T: ?Sized> ScBox<T> {
fn inc(&self) { self.ref_cnt.set(self.ref_cnt.get() + 1); }
fn dec(&self) { self.ref_cnt.set(self.ref_cnt.get() - 1) }
fn ref_cnt(&self) -> usize { self.ref_cnt.get() }
}

impl<T: ?Sized> std::ops::Deref for Sc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target { &self.inner().value }
}

impl<T: ?Sized> Drop for Sc<T> {
fn drop(&mut self) {
self.inner().dec();
if self.inner().ref_cnt() == 0 {
unsafe {
let layout = std::alloc::Layout::for_value(self.0.as_ref());
let ptr = self.0.as_ptr();
std::ptr::drop_in_place(ptr);
std::alloc::dealloc(ptr as *mut _, layout)
}
}
}
}

impl<T> Clone for Sc<T> {
#[inline]
fn clone(&self) -> Self {
self.inner().inc();
Self(self.0)
}
}

impl<T: ?Sized + Display> Display for Sc<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(&**self, f) }

Check warning on line 191 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L191

Added line #L191 was not covered by tests
}

impl<T: ?Sized + Debug> Debug for Sc<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Debug::fmt(&**self, f) }

Check warning on line 195 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L195

Added line #L195 was not covered by tests
}

impl<T: ?Sized> Pointer for Sc<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Pointer::fmt(&(&**self as *const T), f)
}

Check warning on line 201 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L199-L201

Added lines #L199 - L201 were not covered by tests
}

impl<T: Default> Default for Sc<T> {
#[inline]
fn default() -> Sc<T> { Sc::new(Default::default()) }
}

impl<T: ?Sized + PartialEq> PartialEq for Sc<T> {
#[inline]
fn eq(&self, other: &Sc<T>) -> bool { **self == **other }

Check warning on line 211 in algo/src/sc.rs

View check run for this annotation

Codecov / codecov/patch

algo/src/sc.rs#L211

Added line #L211 was not covered by tests
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sc() {
let a = Sc::new(1);
assert_eq!(Sc::ref_count(&a), 1);
let b = Sc::clone(&a);
assert_eq!(Sc::ref_count(&b), 2);
drop(a);
assert_eq!(Sc::ref_count(&b), 1);
drop(b);
}
}
2 changes: 0 additions & 2 deletions core/src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ mod lerp;
pub use lerp::Lerp;
mod repeat;
pub use repeat::*;
mod property;
pub use property::{LerpProp, Prop, Property};
Loading