From 90b2ac03e3560b0e9f78f52b198c869773b5816e Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Sat, 2 Nov 2024 08:10:43 +1100 Subject: [PATCH] hashes: Introduce impl_bytelike_traits macro We have a couple of problems: 1. There are two macros currently for fmt stuff that do similar things, `arr_newtype_fmt_impl` and `hex_fmt_impl` - the difference is not immediately obvious, its the way that the byte array is iterated. 2. Our hash types are missing `AsRef<[u8; len]>` and `Borrow<[u8; len]>`. Introduce a new macro and remove a bunch of other macros. Include extensive docs but hide the macro from public docs because its not really for consumers of the library. The macro requires `$crate::hex` to point to `hex-conservative`. Note the macro is pretty generic (as in general purpose), `hashes` might not be the right home for it. Potentially a better place would be in `hex` itself? --- bitcoin/src/blockdata/transaction.rs | 2 +- hashes/src/internal_macros.rs | 66 +------------- hashes/src/macros.rs | 130 ++++++++++++--------------- 3 files changed, 60 insertions(+), 138 deletions(-) diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index e9207099a..3dc203dab 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -1970,7 +1970,7 @@ mod tests { let display = "0000000000000000000000000000000000000000000000000000000000000000:4294967295"; assert_eq!(display, format!("{}", &outpoint)); - let pretty_debug = "OutPoint {\n txid: 0000000000000000000000000000000000000000000000000000000000000000,\n vout: 4294967295,\n}"; + let pretty_debug = "OutPoint {\n txid: 0x0000000000000000000000000000000000000000000000000000000000000000,\n vout: 4294967295,\n}"; assert_eq!(pretty_debug, format!("{:#?}", &outpoint)); let debug_txid = "0000000000000000000000000000000000000000000000000000000000000000"; diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 929399342..2e4701b38 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -2,49 +2,6 @@ //! Non-public macros -macro_rules! arr_newtype_fmt_impl { - ($ty:ident, $bytes:expr $(, $gen:ident: $gent:ident)*) => { - impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - let case = $crate::hex::Case::Lower; - if <$ty<$($gen),*> as crate::Hash>::DISPLAY_BACKWARD { - $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) - } else { - $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter(), case) - } - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - let case = $crate::hex::Case::Upper; - if <$ty<$($gen),*> as crate::Hash>::DISPLAY_BACKWARD { - $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) - } else { - $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter(), case) - } - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - $crate::_export::_core::fmt::LowerHex::fmt(self, f) - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - write!(f, "{:#}", self) - } - } - } -} -pub(crate) use arr_newtype_fmt_impl; - /// Adds trait impls to the type called `Hash` in the current scope. /// /// Implpements various conversion traits as well as the [`crate::Hash`] trait. @@ -63,28 +20,7 @@ pub(crate) use arr_newtype_fmt_impl; /// `from_engine` obviously implements the finalization algorithm. macro_rules! hash_trait_impls { ($bits:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { - impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for Hash<$($gen),*> { - type Err = $crate::hex::HexToArrayError; - fn from_str(s: &str) -> $crate::_export::_core::result::Result { - use $crate::{hex::{FromHex}}; - - let mut bytes = <[u8; $bits / 8]>::from_hex(s)?; - if $reverse { - bytes.reverse(); - } - Ok(Self::from_byte_array(bytes)) - } - } - - $crate::internal_macros::arr_newtype_fmt_impl!(Hash, $bits / 8 $(, $gen: $gent)*); - $crate::serde_impl!(Hash, { $bits / 8 } $(, $gen: $gent)*); - $crate::borrow_slice_impl!(Hash $(, $gen: $gent)*); - - impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8; $bits / 8]> for Hash<$($gen),*> { - fn as_ref(&self) -> &[u8; $bits / 8] { - &self.0 - } - } + $crate::impl_bytelike_traits!(Hash, { $bits / 8 }, $reverse $(, $gen: $gent)*); impl<$($gen: $gent),*> $crate::GeneralHash for Hash<$($gen),*> { type Engine = HashEngine; diff --git a/hashes/src/macros.rs b/hashes/src/macros.rs index f30063cd7..2d37645c9 100644 --- a/hashes/src/macros.rs +++ b/hashes/src/macros.rs @@ -133,9 +133,7 @@ macro_rules! hash_newtype { $({ $($type_attrs)* })* } - $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, <$newtype as $crate::Hash>::LEN, $newtype); - $crate::serde_impl!($newtype, { <$newtype as $crate::Hash>::LEN }); - $crate::borrow_slice_impl!($newtype); + $crate::impl_bytelike_traits!($newtype, { <$newtype as $crate::Hash>::LEN }, <$newtype as $crate::Hash>::DISPLAY_BACKWARD); #[allow(unused)] // Private wrapper types may not need all functions. impl $newtype { @@ -192,96 +190,84 @@ macro_rules! hash_newtype { fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() } } - - impl $crate::_export::_core::str::FromStr for $newtype { - type Err = $crate::hex::HexToArrayError; - fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { - use $crate::{hex::FromHex}; - - let mut bytes = <[u8; ::LEN]>::from_hex(s)?; - if ::DISPLAY_BACKWARD { - bytes.reverse(); - }; - Ok($newtype(<$hash>::from_byte_array(bytes))) - } - } - - impl $crate::_export::_core::convert::AsRef<[u8; <$hash as $crate::Hash>::LEN]> for $newtype { - fn as_ref(&self) -> &[u8; <$hash as $crate::Hash>::LEN] { - AsRef::<[u8; <$hash as $crate::Hash>::LEN]>::as_ref(&self.0) - } - } )+ }; } -/// Adds hexadecimal formatting implementation of a trait `$imp` to a given type `$ty`. +/// Adds trait impls to a bytelike type. +/// +/// Implements: +/// +/// * `str::FromStr` +/// * `fmt::{LowerHex, UpperHex}` using `hex-conservative`. +/// * `fmt::{Display, Debug}` by calling `LowerHex` +/// * `serde::{Deserialize, Serialize}` +/// * `AsRef[u8; $len]` +/// * `AsRef[u8]` +/// * `Borrow<[u8; $len]>` +/// * `Borrow<[u8]>` +/// +/// Requires: +/// +/// * [`hex-conservative`] to publicly available as `$crate::hex`. +/// * `$ty` must implement `IntoIterator>`. +/// +/// (See also [`hex-conservative::fmt_hex_exact`].) +/// +/// ## Parameters +/// +/// * `ty` - The bytelike type to implement the traits on. +/// * `$len` - The number of bytes this type has. +/// * `$reverse` - `true` if the type should be displayed backwards, `false` otherwise. +/// * `$gen: $gent` - generic type(s) and trait bound(s). +/// +/// [`hex-conservative`]: #[doc(hidden)] #[macro_export] -macro_rules! hex_fmt_impl( - ($reverse:expr, $len:expr, $ty:ident) => ( - $crate::hex_fmt_impl!($reverse, $len, $ty, ); - ); - ($reverse:expr, $len:expr, $ty:ident, $($gen:ident: $gent:ident),*) => ( - impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { +macro_rules! impl_bytelike_traits { + ($ty:ident, $len:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { + impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for $ty<$($gen),*> { + type Err = $crate::hex::HexToArrayError; + + fn from_str(s: &str) -> $crate::_export::_core::result::Result { + use $crate::hex::FromHex; + + let mut bytes = <[u8; { $len }]>::from_hex(s)?; if $reverse { - $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self).iter().rev(), $crate::hex::Case::Lower) - } else { - $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self), $crate::hex::Case::Lower) + bytes.reverse(); } + Ok(Self::from_byte_array(bytes)) } } - impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> { + $crate::hex::impl_fmt_traits! { + #[display_backward($reverse)] + impl<$($gen: $gent),*> fmt_traits for $ty<$($gen),*> { + const LENGTH: usize = ($len); // parens required due to rustc parser weirdness + } + } + + $crate::serde_impl!($ty, $len $(, $gen: $gent)*); + + impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8; { $len }]> for $ty<$($gen),*> { #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - if $reverse { - $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self).iter().rev(), $crate::hex::Case::Upper) - } else { - $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self), $crate::hex::Case::Upper) - } - } + fn as_ref(&self) -> &[u8; { $len }] { self.as_byte_array() } } - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> { + impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8]> for $ty<$($gen),*> { #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - $crate::_export::_core::fmt::LowerHex::fmt(&self, f) - } + fn as_ref(&self) -> &[u8] { self.as_byte_array() } } - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - write!(f, "{}", self) - } + impl<$($gen: $gent),*> $crate::_export::_core::borrow::Borrow<[u8; { $len }]> for $ty<$($gen),*> { + fn borrow(&self) -> &[u8; { $len }] { self.as_byte_array() } } - ); -); -/// Adds slicing traits implementations to a given type `$ty` -#[doc(hidden)] -#[macro_export] -macro_rules! borrow_slice_impl( - ($ty:ident) => ( - $crate::borrow_slice_impl!($ty, ); - ); - ($ty:ident, $($gen:ident: $gent:ident),*) => ( impl<$($gen: $gent),*> $crate::_export::_core::borrow::Borrow<[u8]> for $ty<$($gen),*> { - fn borrow(&self) -> &[u8] { - self.as_byte_array() - } + fn borrow(&self) -> &[u8] { self.as_byte_array() } } - - impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8]> for $ty<$($gen),*> { - fn as_ref(&self) -> &[u8] { - self.as_byte_array() - } - } - ) -); + } +} // Generates the struct only (no impls) //