-
Notifications
You must be signed in to change notification settings - Fork 174
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
Magic zeroslice!
macros
#3454
Magic zeroslice!
macros
#3454
Changes from all commits
d043e45
d220e86
6469df6
2d2a47e
5b9b0c6
8e7be7f
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 |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// expand me by cd'ing into properties, then `cargo expand expandme` | ||
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] | ||
#[allow(clippy::exhaustive_structs)] | ||
#[zerovec::make_ule(AlignedULE)] | ||
pub struct Aligned { | ||
field_one: u8, | ||
field_two: char, | ||
field_three: i32, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,6 +159,8 @@ fn make_ule_enum_impl( | |
|
||
let doc = format!("[`ULE`](zerovec::ule::ULE) type for {name}"); | ||
|
||
let from_aligned_doc = format!("Converts a [`{name}`] to a [`{ule_name}`]. This is equivalent to calling [`AsULE::to_unaligned`]."); | ||
|
||
// Safety (based on the safety checklist on the ULE trait): | ||
// 1. ULE type does not include any uninitialized or padding bytes. | ||
// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant | ||
|
@@ -177,6 +179,24 @@ fn make_ule_enum_impl( | |
#[doc = #doc] | ||
#vis struct #ule_name(u8); | ||
|
||
impl #ule_name { | ||
#[doc = #from_aligned_doc] | ||
#[inline] | ||
pub const fn from_aligned(a: #name) -> #ule_name { | ||
// safety: the enum is repr(u8) and can be cast to a u8 | ||
unsafe { | ||
::core::mem::transmute(a) | ||
} | ||
} | ||
|
||
zerovec::impl_ule_from_array!( | ||
#name, | ||
#ule_name, | ||
// safety: the enum is repr(u8), can be cast to a u8 and contains at least one variant | ||
unsafe { ::core::mem::transmute(0u8) } | ||
); | ||
} | ||
|
||
unsafe impl zerovec::ule::ULE for #ule_name { | ||
#[inline] | ||
fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> { | ||
|
@@ -268,6 +288,7 @@ fn make_ule_struct_impl( | |
|
||
let mut as_ule_conversions = vec![]; | ||
let mut from_ule_conversions = vec![]; | ||
let mut const_from_aligned_conversions = vec![]; | ||
|
||
for (i, field) in struc.fields.iter().enumerate() { | ||
let ty = &field.ty; | ||
|
@@ -278,10 +299,15 @@ fn make_ule_struct_impl( | |
from_ule_conversions.push( | ||
quote!(#ident: <#ty as zerovec::ule::AsULE>::from_unaligned(unaligned.#ident)), | ||
); | ||
// TODO: need to match here and below on #ty, and then call the appropriate function, as in zeroslice! macro. | ||
const_from_aligned_conversions | ||
.push(quote!(#ident: zerovec::const_ule_conversion_fn!(#ty)(aligned.#ident))); | ||
} else { | ||
as_ule_conversions.push(quote!(<#ty as zerovec::ule::AsULE>::to_unaligned(self.#i))); | ||
from_ule_conversions | ||
.push(quote!(<#ty as zerovec::ule::AsULE>::from_unaligned(unaligned.#i))); | ||
const_from_aligned_conversions | ||
.push(quote!(zerovec::const_ule_conversion_fn!(#ty)(aligned.#i))); | ||
}; | ||
} | ||
|
||
|
@@ -334,6 +360,23 @@ fn make_ule_struct_impl( | |
quote!() | ||
}; | ||
|
||
let ule_doc = "The same as [`AsULE::to_unaligned`]."; | ||
|
||
let const_from_aligned_conversions = | ||
utils::wrap_field_inits(&const_from_aligned_conversions, &struc.fields); | ||
let ule_impl = quote!( | ||
impl #ule_name { | ||
#[doc = #ule_doc] | ||
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. nit: Probably just directly include the doc comment here? There's no interpolation |
||
#[inline] | ||
pub const fn from_aligned(aligned: #name) -> Self { | ||
Self #const_from_aligned_conversions | ||
} | ||
|
||
// TODO: need to come up with a safe zero expression for the array initializer. | ||
// zerovec::impl_ule_from_array!(#name, #ule_name, unsafe { core::mem::transmute([0; core::mem::size_of::<#ule_name>()]) }) | ||
} | ||
); | ||
|
||
quote!( | ||
#asule_impl | ||
|
||
|
@@ -344,5 +387,7 @@ fn make_ule_struct_impl( | |
#maybe_ord_impls | ||
|
||
#maybe_hash | ||
|
||
#ule_impl | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,9 +9,31 @@ | |
/// Pass any (cheap to construct) value. | ||
#[macro_export] | ||
macro_rules! impl_ule_from_array { | ||
($aligned:ty, $unaligned:ty, $default:expr, $single:path, $fn_name:ident) => { | ||
#[doc = concat!("Convert an array of `", stringify!($aligned), "` to an array of `", stringify!($unaligned), "`.")] | ||
pub const fn $fn_name<const N: usize>(arr: [$aligned; N]) -> [Self; N] { | ||
let mut result = [$default; N]; | ||
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. thought: could we potentially use |
||
let mut i = 0; | ||
// Won't panic because i < N and arr has length N | ||
#[allow(clippy::indexing_slicing)] | ||
while i < N { | ||
result[i] = $single(arr[i]); | ||
i += 1; | ||
} | ||
result | ||
} | ||
}; | ||
($aligned:ty, $unaligned:ty, $default:expr) => { | ||
$crate::impl_ule_from_array!($aligned, $unaligned, $default, Self::from_aligned, from_array); | ||
}; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! impl_const_as_ule_array { | ||
($aligned:ty, $unaligned:ty, $default:expr, $single:path) => { | ||
#[doc = concat!("Convert an array of `", stringify!($aligned), "` to an array of `", stringify!($unaligned), "`.")] | ||
pub const fn from_array<const N: usize>(arr: [$aligned; N]) -> [Self; N] { | ||
#[allow(dead_code)] | ||
pub const fn aligned_to_unaligned_array<const N: usize>(arr: [$aligned; N]) -> [$unaligned; N] { | ||
let mut result = [$default; N]; | ||
let mut i = 0; | ||
// Won't panic because i < N and arr has length N | ||
|
@@ -24,6 +46,46 @@ macro_rules! impl_ule_from_array { | |
} | ||
}; | ||
($aligned:ty, $unaligned:ty, $default:expr) => { | ||
impl_ule_from_array!($aligned, $unaligned, $default, Self::from_aligned); | ||
$crate::impl_const_as_ule_array!($aligned, $unaligned, $default, Self::aligned_to_unaligned); | ||
}; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! const_ule_conversion_fn { | ||
(u8) => { | ||
core::convert::identity | ||
}; | ||
(i8) => { | ||
core::convert::identity | ||
}; | ||
(u16) => { | ||
<u16 as $crate::ule::AsULE>::ULE::from_unsigned | ||
}; | ||
(i16) => { | ||
<i16 as $crate::ule::AsULE>::ULE::from_signed | ||
}; | ||
(u32) => { | ||
<u32 as $crate::ule::AsULE>::ULE::from_unsigned | ||
}; | ||
(i32) => { | ||
<i32 as $crate::ule::AsULE>::ULE::from_signed | ||
}; | ||
(u64) => { | ||
<u64 as $crate::ule::AsULE>::ULE::from_unsigned | ||
}; | ||
(i64) => { | ||
<i64 as $crate::ule::AsULE>::ULE::from_signed | ||
}; | ||
(u128) => { | ||
<u128 as $crate::ule::AsULE>::ULE::from_unsigned | ||
}; | ||
(i128) => { | ||
<i128 as $crate::ule::AsULE>::ULE::from_signed | ||
}; | ||
(UnvalidatedChar) => { | ||
<UnvalidatedChar as $crate::ule::AsULE>::ULE::from_unvalidated_char | ||
}; | ||
($aligned:ty) => { | ||
<$aligned as $crate::ule::AsULE>::ULE::from_aligned | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -239,6 +239,37 @@ where | |
} | ||
} | ||
|
||
/// A trait for any type that can be const-mapped to its ULE type. It is particularly | ||
/// useful in combination with [`zeroslice!`](crate::zeroslice). | ||
/// | ||
/// This trait serves only as a redirection to a type that contains the required associated const | ||
/// functions for conversion. This is currently necessary due to the lack of const traits in Rust. | ||
/// | ||
/// # Implementation | ||
/// | ||
/// Using the [`#[make_ule]`](crate::make_ule) will implement this trait for you and you do not | ||
/// have to read further. | ||
/// | ||
/// If you implement `ConstAsULE` manually for your type `T`, the associated type `ConstConvert` | ||
/// must have the following associated `const` functions: | ||
/// * `ConstConvert::aligned_to_unaligned(T) -> T::ULE` | ||
/// * `ConstConvert::aligned_to_unaligned_array<const N: usize>([T; N]) -> [T::ULE; N]` | ||
/// | ||
/// For `T::ULE` types that only have one canonical aligned type, `T`, the associated type | ||
/// `ConstConvert` can safely be set to `T::ULE` and the required functions can be implemented | ||
/// there. | ||
/// | ||
/// Issues arise when `T::ULE` has multiple or no canonical aligned types, as is the case with e.g. | ||
/// `RawBytesULE<4>` and `i32`/`u32`. In this case, `ConstConvert` must be set to a different type | ||
/// for each `impl ConstAsULE for T`. This crate does that by providing associated functions on a | ||
/// custom type for each pair of `T` and `T::ULE`, namely | ||
/// `crate::ule::constconvert::ConstConvert<T, T::ULE>`. Unfortunately, you will not be able | ||
/// to reuse this type for your own `ConstAsULE` implementations, since you cannot provide | ||
/// implementations for types outside of your crate. | ||
pub trait ConstAsULE: AsULE { | ||
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. issue: doesn't look like we're using it anymore (I am also in general against adding such a trait) |
||
type ConstConvert; | ||
} | ||
|
||
/// Variable-width, byte-aligned data that can be cast to and from a little-endian byte slice. | ||
/// | ||
/// If you need to implement this trait, consider using [`#[make_varule]`](crate::make_varule) or | ||
|
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.
nit: eventually should be removed, yes?