mirror of
https://github.com/Lokathor/bytemuck.git
synced 2024-11-24 15:52:24 +00:00
Implement ByteEq
and ByteHash
derives (#144)
* Implement `ByteEq` and `ByteHash` derives This adds the derives `ByteEq` and `ByteHash` that can be used as an alternative to the `Eq` / `PartialEq` and `Hash` derives from the standard library. The difference is that these variants use `bytemuck` to convert their values to byte slices before comparing / hashing them. This allows the comparisons to turn into a simple `memcmp` / `bcmp` (or completely inlined as a few vector instructions) and allows hashers to process all bytes at once, possibly allowing for some vector operations as well. Here's a quick comparison of the generated assembly: ![https://i.imgur.com/CGTSWTZ.png](https://i.imgur.com/CGTSWTZ.png) * Address review comments
This commit is contained in:
parent
6b1b7cecd3
commit
7311e9b4b8
@ -224,6 +224,93 @@ pub fn derive_contiguous(
|
|||||||
proc_macro::TokenStream::from(expanded)
|
proc_macro::TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive the `PartialEq` and `Eq` trait for a type
|
||||||
|
///
|
||||||
|
/// The macro implements `PartialEq` and `Eq` by casting both sides of the
|
||||||
|
/// comparison to a byte slice and then compares those.
|
||||||
|
///
|
||||||
|
/// ## Warning
|
||||||
|
///
|
||||||
|
/// Since this implements a byte wise comparison, the behavior of floating point
|
||||||
|
/// numbers does not match their usual comparison behavior. Additionally other
|
||||||
|
/// custom comparison behaviors of the individual fields are also ignored. This
|
||||||
|
/// also does not implement `StructuralPartialEq` / `StructuralEq` like
|
||||||
|
/// `PartialEq` / `Eq` would. This means you can't pattern match on the values.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bytemuck_derive::{ByteEq, NoUninit};
|
||||||
|
/// #[derive(Copy, Clone, NoUninit, ByteEq)]
|
||||||
|
/// #[repr(C)]
|
||||||
|
/// struct Test {
|
||||||
|
/// a: u32,
|
||||||
|
/// b: char,
|
||||||
|
/// c: f32,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_derive(ByteEq)]
|
||||||
|
pub fn derive_byte_eq(
|
||||||
|
input: proc_macro::TokenStream,
|
||||||
|
) -> proc_macro::TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let ident = input.ident;
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(quote! {
|
||||||
|
impl ::core::cmp::PartialEq for #ident {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
::bytemuck::bytes_of(self) == ::bytemuck::bytes_of(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ::core::cmp::Eq for #ident { }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive the `Hash` trait for a type
|
||||||
|
///
|
||||||
|
/// The macro implements `Hash` by casting the value to a byte slice and hashing
|
||||||
|
/// that.
|
||||||
|
///
|
||||||
|
/// ## Warning
|
||||||
|
///
|
||||||
|
/// The hash does not match the standard library's `Hash` derive.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bytemuck_derive::{ByteHash, NoUninit};
|
||||||
|
/// #[derive(Copy, Clone, NoUninit, ByteHash)]
|
||||||
|
/// #[repr(C)]
|
||||||
|
/// struct Test {
|
||||||
|
/// a: u32,
|
||||||
|
/// b: char,
|
||||||
|
/// c: f32,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_derive(ByteHash)]
|
||||||
|
pub fn derive_byte_hash(
|
||||||
|
input: proc_macro::TokenStream,
|
||||||
|
) -> proc_macro::TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let ident = input.ident;
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(quote! {
|
||||||
|
impl ::core::hash::Hash for #ident {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
::core::hash::Hash::hash_slice(::bytemuck::bytes_of(self), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn hash_slice<H: ::core::hash::Hasher>(data: &[Self], state: &mut H) {
|
||||||
|
::core::hash::Hash::hash_slice(::bytemuck::cast_slice::<_, u8>(data), state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Basic wrapper for error handling
|
/// Basic wrapper for error handling
|
||||||
fn derive_marker_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
|
fn derive_marker_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
|
||||||
derive_marker_trait_inner::<Trait>(input)
|
derive_marker_trait_inner::<Trait>(input)
|
||||||
|
@ -113,8 +113,8 @@ pub use transparent::*;
|
|||||||
|
|
||||||
#[cfg(feature = "derive")]
|
#[cfg(feature = "derive")]
|
||||||
pub use bytemuck_derive::{
|
pub use bytemuck_derive::{
|
||||||
AnyBitPattern, CheckedBitPattern, Contiguous, NoUninit, Pod,
|
AnyBitPattern, ByteEq, ByteHash, CheckedBitPattern, Contiguous, NoUninit,
|
||||||
TransparentWrapper, Zeroable,
|
Pod, TransparentWrapper, Zeroable,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The things that can go wrong when casting between [`Pod`] data forms.
|
/// The things that can go wrong when casting between [`Pod`] data forms.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#![cfg(feature = "derive")]
|
#![cfg(feature = "derive")]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use bytemuck::{Pod, TransparentWrapper, Zeroable};
|
use bytemuck::{ByteEq, ByteHash, Pod, TransparentWrapper, Zeroable};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
#[derive(Copy, Clone, Pod, Zeroable, ByteEq, ByteHash)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Test {
|
struct Test {
|
||||||
a: u16,
|
a: u16,
|
||||||
|
Loading…
Reference in New Issue
Block a user