Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/vertigo/src/computed/computed_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ impl<T: Clone + PartialEq + 'static> Computed<T> {
/// Note that the `callback` is fired only if the value *really* changes. This means that even if computation takes place with different source value,
/// but the resulting value is the same as old one, the `callback` is not fired.
pub fn subscribe<R: 'static, F: Fn(T) -> R + 'static>(self, callback: F) -> DropResource {
let prev_value = ValueMut::new(None);
let prev_value = ValueMut::new_with_eq(None);

let resource_box = ValueMut::new(None);

Expand Down
158 changes: 152 additions & 6 deletions crates/vertigo/src/computed/struct_mut/inner_value.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,173 @@
#![allow(clippy::mut_from_ref)]

use std::cell::UnsafeCell;
use std::marker::PhantomData;

struct InnerValueErased {
data: *mut (),
drop_fn: unsafe fn(*mut ()),
eq_fn: Option<unsafe fn(*const (), *const ()) -> bool>,
fmt_fn: Option<unsafe fn(*const (), &mut std::fmt::Formatter<'_>) -> std::fmt::Result>,
}

impl InnerValueErased {
fn new<T>(value: T) -> Self {
let boxed = Box::new(UnsafeCell::new(value));
let data = Box::into_raw(boxed) as *mut ();

unsafe fn drop_ptr<T>(ptr: *mut ()) {
let _ = unsafe { Box::from_raw(ptr as *mut UnsafeCell<T>) };
}

Self {
data,
drop_fn: drop_ptr::<T>,
eq_fn: None,
fmt_fn: None,
}
}

fn new_with_eq<T: PartialEq>(value: T) -> Self {
let boxed = Box::new(UnsafeCell::new(value));
let data = Box::into_raw(boxed) as *mut ();

unsafe fn drop_ptr<T>(ptr: *mut ()) {
let _ = unsafe { Box::from_raw(ptr as *mut UnsafeCell<T>) };
}

unsafe fn eq_ptr<T: PartialEq>(ptr: *const (), other: *const ()) -> bool {
let this = unsafe { &*(ptr as *const UnsafeCell<T>) };
let other = unsafe { &*(other as *const T) };
unsafe { *this.get() == *other }
}

Self {
data,
drop_fn: drop_ptr::<T>,
eq_fn: Some(eq_ptr::<T>),
fmt_fn: None,
}
}

fn new_with_eq_debug<T: PartialEq + std::fmt::Debug>(value: T) -> Self {

Check failure on line 52 in crates/vertigo/src/computed/struct_mut/inner_value.rs

View workflow job for this annotation

GitHub Actions / Vertigo Clippy Output

associated function `new_with_eq_debug` is never used

error: associated function `new_with_eq_debug` is never used --> crates/vertigo/src/computed/struct_mut/inner_value.rs:52:8 | 13 | impl InnerValueErased { | --------------------- associated function in this implementation ... 52 | fn new_with_eq_debug<T: PartialEq + std::fmt::Debug>(value: T) -> Self { | ^^^^^^^^^^^^^^^^^ | = note: `-D dead-code` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(dead_code)]`
let boxed = Box::new(UnsafeCell::new(value));
let data = Box::into_raw(boxed) as *mut ();

unsafe fn drop_ptr<T>(ptr: *mut ()) {
let _ = unsafe { Box::from_raw(ptr as *mut UnsafeCell<T>) };
}

unsafe fn eq_ptr<T: PartialEq>(ptr: *const (), other: *const ()) -> bool {
let this = unsafe { &*(ptr as *const UnsafeCell<T>) };
let other = unsafe { &*(other as *const T) };
unsafe { *this.get() == *other }
}

unsafe fn fmt_ptr<T: std::fmt::Debug>(
ptr: *const (),
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let this = unsafe { &*(ptr as *const UnsafeCell<T>) };
unsafe { (*this.get()).fmt(f) }
}

Self {
data,
drop_fn: drop_ptr::<T>,
eq_fn: Some(eq_ptr::<T>),
fmt_fn: Some(fmt_ptr::<T>),
}
}

unsafe fn get<T>(&self) -> &T {
let ptr = self.data as *mut UnsafeCell<T>;
unsafe { &*(*ptr).get() }
}

unsafe fn get_mut<T>(&self) -> &mut T {
let ptr = self.data as *mut UnsafeCell<T>;
unsafe { &mut *(*ptr).get() }
}
}

impl Drop for InnerValueErased {
fn drop(&mut self) {
unsafe {
(self.drop_fn)(self.data);
}
}
}

#[derive(Debug)]
pub struct InnerValue<T> {
value: UnsafeCell<T>,
inner: InnerValueErased,
_phantom: PhantomData<T>,
}

impl<T> InnerValue<T> {
pub fn new(value: T) -> InnerValue<T> {
InnerValue {
value: UnsafeCell::new(value),
inner: InnerValueErased::new(value),
_phantom: PhantomData,
}
}

pub fn new_with_eq(value: T) -> InnerValue<T>
where
T: PartialEq,
{
InnerValue {
inner: InnerValueErased::new_with_eq(value),
_phantom: PhantomData,
}
}

pub fn new_with_eq_debug(value: T) -> InnerValue<T>

Check failure on line 124 in crates/vertigo/src/computed/struct_mut/inner_value.rs

View workflow job for this annotation

GitHub Actions / Vertigo Clippy Output

associated function `new_with_eq_debug` is never used

error: associated function `new_with_eq_debug` is never used --> crates/vertigo/src/computed/struct_mut/inner_value.rs:124:12 | 106 | impl<T> InnerValue<T> { | --------------------- associated function in this implementation ... 124 | pub fn new_with_eq_debug(value: T) -> InnerValue<T> | ^^^^^^^^^^^^^^^^^
where
T: PartialEq + std::fmt::Debug,
{
InnerValue {
inner: InnerValueErased::new_with_eq_debug(value),
_phantom: PhantomData,
}
}

pub fn get(&self) -> &T {
unsafe { &*self.value.get() }
unsafe { self.inner.get::<T>() }
}

pub fn get_mut(&self) -> &mut T {
unsafe { &mut *self.value.get() }
unsafe { self.inner.get_mut::<T>() }
}

pub fn is_eq(&self, other: &T) -> bool {
if let Some(eq_fn) = self.inner.eq_fn {
unsafe { eq_fn(self.inner.data, other as *const T as *const ()) }
} else {
// Fallback for cases where we didn't store eq_fn, but it shouldn't happen if we use ValueMut
false
}
}

pub fn into_inner(self) -> T {
self.value.into_inner()
let data = self.inner.data as *mut UnsafeCell<T>;
let inner_value = unsafe {
let boxed = Box::from_raw(data);
boxed.into_inner()
};
// Forget the erased part so it doesn't double-drop
std::mem::forget(self.inner);
inner_value
}
}

impl<T: std::fmt::Debug> std::fmt::Debug for InnerValue<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(fmt_fn) = self.inner.fmt_fn {
unsafe { fmt_fn(self.inner.data, f) }
} else {
f.debug_struct("InnerValue")
.field("value", self.get())
.finish()
}
}
}
17 changes: 12 additions & 5 deletions crates/vertigo/src/computed/struct_mut/value_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ impl<T> ValueMut<T> {
}
}

impl<T: PartialEq> ValueMut<T> {
pub fn new_with_eq(value: T) -> ValueMut<T> {
ValueMut {
value: InnerValue::new_with_eq(value),
}
}
}

impl<T: Default> Default for ValueMut<T> {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -59,12 +67,11 @@ impl<T: Clone> ValueMut<T> {

impl<T: PartialEq> ValueMut<T> {
pub fn set_if_changed(&self, value: T) -> bool {
let state = self.value.get_mut();
if *state != value {
*state = value;
true
} else {
if self.value.is_eq(&value) {
false
} else {
*self.value.get_mut() = value;
true
}
}
}
2 changes: 1 addition & 1 deletion crates/vertigo/src/computed/value_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl<T: PartialEq + Clone + 'static> ValueInner<T> {
pub fn new(value: T) -> ValueInner<T> {
ValueInner {
id: GraphId::new_value(),
value: ValueMut::new(value.clone()),
value: ValueMut::new_with_eq(value.clone()),
events: EventEmitter::default(),
}
}
Expand Down
Loading