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

Add initial zeroslice! and zerovec! macros #3453

Merged
merged 2 commits into from
May 26, 2023
Merged
Changes from 1 commit
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
81 changes: 81 additions & 0 deletions utils/zerovec/src/zerovec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,87 @@ impl<T: AsULE> FromIterator<T> for ZeroVec<'_, T> {
}
}

/// Convenience wrapper for [`ZeroSlice::from_ule_slice`]. The value will be created at compile-time.
///
/// # Arguments
///
/// * `$aligned` - The type of an element in its canonical, aligned form, e.g., `char`.
/// * `$array_fn` - A const function that converts an array of `$aligned` elements into an array
/// of their unaligned equivalents, e.g.,
/// `const fn from_array<const N: usize>(arr: [char; N]) -> [<char as AsULE>::ULE; N]`.
/// * `$x` - The elements that the `ZeroSlice` will hold.
///
/// # Examples
///
/// Using array-conversion functions provided by this crate:
///
/// ```
/// use zerovec::{ZeroSlice, zeroslice, ule::AsULE};
///
/// const SIGNATURE: &ZeroSlice<char> = zeroslice![char; <char as AsULE>::ULE::from_array; 'b', 'y', 'e', '✌'];
/// const EMPTY: &ZeroSlice<u32> = zeroslice![];
/// let empty: &ZeroSlice<u32> = zeroslice![];
/// let nums = zeroslice![u32; <u32 as AsULE>::ULE::from_array; 1, 2, 3, 4, 5];
/// assert_eq!(nums.last().unwrap(), 5);
/// ```
///
/// Using a custom array-conversion function:
///
/// ```
/// use zerovec::{ZeroSlice, zeroslice};
///
/// mod conversion {
/// use zerovec::ule::RawBytesULE;
/// pub(super) const fn i16_array_to_be_array<const N: usize>(arr: [i16; N]) -> [RawBytesULE<2>; N] {
/// let mut result = [RawBytesULE([0; 2]); N];
/// let mut i = 0;
/// while i < N {
/// result[i] = RawBytesULE(arr[i].to_be_bytes());
/// i += 1;
/// }
/// result
/// }
/// }
///
/// const NUMBERS: &ZeroSlice<i16> = zeroslice![i16; conversion::i16_array_to_be_array; 1, -2, 3, -4, 5];
/// ```
#[macro_export]
macro_rules! zeroslice {
() => (
$crate::ZeroSlice::new_empty()
);
($aligned:ty; $array_fn:expr; $($x:expr),+ $(,)?) => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: add a case without $array_fn that uses <$aligned as $crate::ule::AsULE>::ULE::from_array.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please have a look at #3454

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the type strictly necessary? We could probably make do with using _ if the type is omitted and rely on context

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the unaligned type to declare the const slice, and the cleanest way to get that is to ask for the aligned type.

Copy link
Member Author

@skius skius May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To expand on that, the internal const X is necessary for non-const contexts:

let x: &ZeroSlice<char> = zeroslice![<char as AsULE>::ULE::from_array; 'a'];
println!("{:?}", x); // error: creates a temporary value which is freed while still in use ^

Copy link
Member

@sffc sffc May 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fortunately or unfortunately, I can get the compiler to extend the lifetime of the inner array (avoiding the need for the temporary) by using transmute directly instead of calling the ZeroSlice constructor...

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2ea56560dc8e192e744faf2f1abe60bb

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like @Manishearth's thoughts on if there is some other way we can avoid the type. It's totally something the compiler should be able to infer; I just don't know how in this context

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that code is sound. You're extending the lifetime of the type, but the temporary array still gets dropped at the end of the expression. In fact if you run Miri you get

error: Undefined Behavior: pointer to alloc8209 was dereferenced after this allocation got freed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline const can solve this in the future. However in the follow up we're using the type to look up the conversion function, and I'd rather specify the type than the function.

$crate::ZeroSlice::<$aligned>::from_ule_slice(
{const X: &[<$aligned as $crate::ule::AsULE>::ULE] = &$array_fn([$($x),+]); X}
)
);
}

/// Creates a borrowed `ZeroVec`. Convenience wrapper for `zeroslice![...].as_zerovec()`.
///
/// See [`zeroslice!`](crate::zeroslice) for more information.
///
/// # Examples
///
/// ```
/// use zerovec::{ZeroVec, zerovec, ule::AsULE};
///
/// const SIGNATURE: ZeroVec<char> = zerovec![char; <char as AsULE>::ULE::from_array; 'a', 'y', 'e', '✌'];
/// assert!(!SIGNATURE.is_owned());
///
/// const EMPTY: ZeroVec<u32> = zerovec![];
/// assert!(!EMPTY.is_owned());
/// ```
#[macro_export]
macro_rules! zerovec {
() => (
$crate::ZeroVec::new()
);
($aligned:ty; $array_fn:expr; $($x:expr),+ $(,)?) => (
$crate::zeroslice![$aligned; $array_fn; $($x),+].as_zerovec()
);
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down