-
Notifications
You must be signed in to change notification settings - Fork 182
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
Make DataPayload
constructible from &'static M::Yokeable
#3467
Changes from all commits
b200ea5
3cd7c5b
fc9635f
3833d6c
4e95e9e
229d7b7
64c8173
810fab7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,11 +71,11 @@ pub struct DataResponseMetadata { | |
/// | ||
/// assert_eq!("Demo", payload.get().message); | ||
/// ``` | ||
pub struct DataPayload<M> | ||
where | ||
M: DataMarker, | ||
{ | ||
pub(crate) yoke: Yoke<M::Yokeable, Option<Cart>>, | ||
pub struct DataPayload<M: DataMarker>(pub(crate) DataPayloadInner<M>); | ||
|
||
pub(crate) enum DataPayloadInner<M: DataMarker> { | ||
Yoke(Yoke<M::Yokeable, Option<Cart>>), | ||
StaticRef(&'static M::Yokeable), | ||
} | ||
|
||
/// The type of the "cart" that is used by `DataPayload`. | ||
|
@@ -136,9 +136,10 @@ where | |
for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone, | ||
{ | ||
fn clone(&self) -> Self { | ||
Self { | ||
yoke: self.yoke.clone(), | ||
} | ||
Self(match &self.0 { | ||
DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke.clone()), | ||
DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(*r), | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -193,17 +194,23 @@ where | |
/// ``` | ||
#[inline] | ||
pub fn from_owned(data: M::Yokeable) -> Self { | ||
Self { | ||
yoke: Yoke::new_owned(data), | ||
} | ||
Self(DataPayloadInner::Yoke(Yoke::new_owned(data))) | ||
} | ||
|
||
#[doc(hidden)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be public? |
||
#[inline] | ||
pub const fn from_static_ref(data: &'static M::Yokeable) -> Self { | ||
Self(DataPayloadInner::StaticRef(data)) | ||
} | ||
|
||
/// Convert a DataPayload that was created via [`DataPayload::from_owned()`] back into the | ||
/// concrete type used to construct it. | ||
pub fn try_unwrap_owned(self) -> Result<M::Yokeable, DataError> { | ||
self.yoke | ||
.try_into_yokeable() | ||
.map_err(|_| DataErrorKind::InvalidState.with_str_context("try_unwrap_owned")) | ||
match self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke.try_into_yokeable().ok(), | ||
DataPayloadInner::StaticRef(_) => None, | ||
} | ||
.ok_or(DataErrorKind::InvalidState.with_str_context("try_unwrap_owned")) | ||
} | ||
|
||
/// Mutate the data contained in this DataPayload. | ||
|
@@ -244,8 +251,15 @@ where | |
pub fn with_mut<'a, F>(&'a mut self, f: F) | ||
where | ||
F: 'static + for<'b> FnOnce(&'b mut <M::Yokeable as Yokeable<'a>>::Output), | ||
M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>, | ||
{ | ||
self.yoke.with_mut(f) | ||
if let DataPayloadInner::StaticRef(r) = self.0 { | ||
self.0 = DataPayloadInner::Yoke(Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))); | ||
} | ||
match &mut self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke.with_mut(f), | ||
_ => unreachable!(), | ||
} | ||
} | ||
|
||
/// Borrows the underlying data. | ||
|
@@ -266,7 +280,10 @@ where | |
#[inline] | ||
#[allow(clippy::needless_lifetimes)] | ||
pub fn get<'a>(&'a self) -> &'a <M::Yokeable as Yokeable<'a>>::Output { | ||
self.yoke.get() | ||
match &self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke.get(), | ||
DataPayloadInner::StaticRef(r) => Yokeable::transform(*r), | ||
} | ||
} | ||
|
||
/// Maps `DataPayload<M>` to `DataPayload<M2>` by projecting it with [`Yoke::map_project`]. | ||
|
@@ -318,10 +335,15 @@ where | |
<M::Yokeable as Yokeable<'a>>::Output, | ||
PhantomData<&'a ()>, | ||
) -> <M2::Yokeable as Yokeable<'a>>::Output, | ||
M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>, | ||
{ | ||
DataPayload { | ||
yoke: self.yoke.map_project(f), | ||
} | ||
DataPayload(DataPayloadInner::Yoke( | ||
match self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke, | ||
DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, this isn't expensive anymore, neat |
||
} | ||
.map_project(f), | ||
)) | ||
} | ||
|
||
/// Version of [`DataPayload::map_project()`] that borrows `self` instead of moving `self`. | ||
|
@@ -362,9 +384,16 @@ where | |
PhantomData<&'a ()>, | ||
) -> <M2::Yokeable as Yokeable<'a>>::Output, | ||
{ | ||
DataPayload { | ||
yoke: self.yoke.map_project_cloned(f), | ||
} | ||
DataPayload(DataPayloadInner::Yoke(match &self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke.map_project_cloned(f), | ||
DataPayloadInner::StaticRef(r) => { | ||
let output: <M2::Yokeable as Yokeable<'static>>::Output = | ||
f(Yokeable::transform(*r), PhantomData); | ||
// Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: just downgrade the thing to a Yoke via ZeroFrom instead of doing these unsafe gymnastics There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, well there's still necessarily a I think the safety comment is not quite right; the invariant is "The returned value must be destroyed before the data from was borrowing from is." The fact that you're setting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think "The returned value must be destroyed before the data from was borrowing from is." is irrelevant. The point here is that the types are the same but the compiler doesn't know, I could also use a transmute. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well I can't use a transmute because the compiler doesn't know they're the same size There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would ideally like to avoid having any unsafety here. I think all we need here is |
||
let yokeable: M2::Yokeable = unsafe { M2::Yokeable::make(output) }; | ||
Yoke::new_owned(yokeable) | ||
} | ||
})) | ||
} | ||
|
||
/// Version of [`DataPayload::map_project()`] that bubbles up an error from `f`. | ||
|
@@ -411,10 +440,15 @@ where | |
<M::Yokeable as Yokeable<'a>>::Output, | ||
PhantomData<&'a ()>, | ||
) -> Result<<M2::Yokeable as Yokeable<'a>>::Output, E>, | ||
M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>, | ||
{ | ||
Ok(DataPayload { | ||
yoke: self.yoke.try_map_project(f)?, | ||
}) | ||
Ok(DataPayload(DataPayloadInner::Yoke( | ||
match self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke, | ||
DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r)), | ||
} | ||
.try_map_project(f)?, | ||
))) | ||
} | ||
|
||
/// Version of [`DataPayload::map_project_cloned()`] that bubbles up an error from `f`. | ||
|
@@ -465,9 +499,15 @@ where | |
PhantomData<&'a ()>, | ||
) -> Result<<M2::Yokeable as Yokeable<'a>>::Output, E>, | ||
{ | ||
Ok(DataPayload { | ||
yoke: self.yoke.try_map_project_cloned(f)?, | ||
}) | ||
Ok(DataPayload(DataPayloadInner::Yoke(match &self.0 { | ||
DataPayloadInner::Yoke(yoke) => yoke.try_map_project_cloned(f)?, | ||
DataPayloadInner::StaticRef(r) => { | ||
let output: <M2::Yokeable as Yokeable<'static>>::Output = | ||
f(Yokeable::transform(*r), PhantomData)?; | ||
// Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable | ||
sffc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Yoke::new_owned(unsafe { M2::Yokeable::make(output) }) | ||
} | ||
}))) | ||
} | ||
|
||
/// Convert between two [`DataMarker`] types that are compatible with each other. | ||
|
@@ -497,7 +537,10 @@ where | |
where | ||
M2: DataMarker<Yokeable = M::Yokeable>, | ||
{ | ||
DataPayload { yoke: self.yoke } | ||
DataPayload(match self.0 { | ||
DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke), | ||
DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(r), | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -507,19 +550,17 @@ impl DataPayload<BufferMarker> { | |
let yoke = Yoke::attach_to_cart(SelectedRc::new(buffer), |b| &**b); | ||
// Safe because cart is wrapped | ||
let yoke = unsafe { yoke.replace_cart(|b| Some(Cart(b))) }; | ||
Self { yoke } | ||
Self(DataPayloadInner::Yoke(yoke)) | ||
} | ||
|
||
/// Converts a yoked byte buffer into a `DataPayload<BufferMarker>`. | ||
pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self { | ||
Self { yoke } | ||
Self(DataPayloadInner::Yoke(yoke)) | ||
} | ||
|
||
/// Converts a static byte buffer into a `DataPayload<BufferMarker>`. | ||
pub fn from_static_buffer(buffer: &'static [u8]) -> Self { | ||
Self { | ||
yoke: Yoke::new_owned(buffer), | ||
} | ||
Self(DataPayloadInner::Yoke(Yoke::new_owned(buffer))) | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: worried about stack size increase, and worried about runtime cost
Admittedly I do not know if there is a way around this, I just want to register the concern. It may be insurmountable and we can decide to do this anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though this does become easy to cfg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Runtime cost: solved by using
as_borrowed
, and the fact that we don't need toZeroFrom
everything.Stack size: it seems to be, at worst, one extra word per payload. Maybe if we make Yoke have better niches, we could get this to be stack-neutral.