mirror of
https://github.com/Lokathor/bytemuck.git
synced 2024-11-24 07:42: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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
fn derive_marker_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
|
||||
derive_marker_trait_inner::<Trait>(input)
|
||||
|
@ -113,8 +113,8 @@ pub use transparent::*;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use bytemuck_derive::{
|
||||
AnyBitPattern, CheckedBitPattern, Contiguous, NoUninit, Pod,
|
||||
TransparentWrapper, Zeroable,
|
||||
AnyBitPattern, ByteEq, ByteHash, CheckedBitPattern, Contiguous, NoUninit,
|
||||
Pod, TransparentWrapper, Zeroable,
|
||||
};
|
||||
|
||||
/// The things that can go wrong when casting between [`Pod`] data forms.
|
||||
|
@ -1,9 +1,9 @@
|
||||
#![cfg(feature = "derive")]
|
||||
#![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)]
|
||||
struct Test {
|
||||
a: u16,
|
||||
|
Loading…
Reference in New Issue
Block a user