use crate::{ParseError, Uint}; use core::ops::{ BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index, Not, Shl, ShlAssign, Shr, ShrAssign, }; use derive_more::{From, FromStr, Into}; use std::borrow::Cow; /// A newtype wrapper around [`Uint`] that restricts operations to those /// relevant for bit arrays. #[derive(Clone, Copy, Debug, Default, Eq, From, FromStr, Into, PartialEq, Hash)] pub struct Bits(Uint); impl Bits { /// The size of this integer type in 64-bit limbs. pub const LIMBS: usize = Uint::::LIMBS; /// The size of this integer type in bits. pub const BITS: usize = Uint::::BITS; /// The size of this integer type in bits. pub const BYTES: usize = Uint::::BYTES; /// The value zero. This is the only value that exists in all [`Uint`] /// types. pub const ZERO: Self = Self(Uint::::ZERO); #[must_use] #[inline(always)] pub const fn into_inner(self) -> Uint { self.0 } #[must_use] #[inline(always)] pub const fn as_uint(&self) -> &Uint { &self.0 } #[must_use] #[inline(always)] pub fn as_uint_mut(&mut self) -> &mut Uint { &mut self.0 } } macro_rules! forward_attributes { ($fnname:ident, $item:item $(,must_use: true)?) => { #[doc = concat!("See [`Uint::", stringify!($fnname),"`] for documentation.")] #[allow(clippy::inline_always)] #[inline(always)] #[must_use] $item }; ($fnname:ident, $item:item,must_use: false) => { #[doc = concat!("See [`Uint::", stringify!($fnname),"`] for documentation.")] #[allow(clippy::inline_always)] #[inline(always)] $item }; } // Limitations of declarative macro matching force us to break down on argument // patterns. macro_rules! forward { ($(fn $fnname:ident(self) -> $res:ty;)*) => { $( forward_attributes!( $fnname, pub fn $fnname(self) -> $res { Uint::$fnname(self.0).into() } ); )* }; ($(fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?(&self) -> $res:ty;)*) => { $( forward_attributes!( $fnname, pub fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?(&self) -> $res { Uint::$fnname(&self.0).into() } ); )* }; ($(unsafe fn $fnname:ident(&mut self) -> $res:ty;)*) => { $( forward_attributes!( $fnname, pub unsafe fn $fnname(&mut self) -> $res { Uint::$fnname(&mut self.0).into() } ); )* }; ($(fn $fnname:ident(self, $arg:ident: $arg_ty:ty) -> Option;)*) => { $( forward_attributes!( $fnname, pub fn $fnname(self, $arg: $arg_ty) -> Option { Uint::$fnname(self.0, $arg).map(Bits::from) } ); )* }; ($(fn $fnname:ident(self, $arg:ident: $arg_ty:ty) -> (Self, bool);)*) => { $( forward_attributes!( $fnname, pub fn $fnname(self, $arg: $arg_ty) -> (Self, bool) { let (value, flag) = Uint::$fnname(self.0, $arg); (value.into(), flag) } ); )* }; ($(fn $fnname:ident(self, $arg:ident: $arg_ty:ty) -> $res:ty;)*) => { $( forward_attributes!( $fnname, pub fn $fnname(self, $arg: $arg_ty) -> $res { Uint::$fnname(self.0, $arg).into() } ); )* }; ($(fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?($($arg:ident: $arg_ty:ty),+) -> Option;)*) => { $( forward_attributes!( $fnname, pub fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?($($arg: $arg_ty),+) -> Option { Uint::$fnname($($arg),+).map(Bits::from) } ); )* }; ($(fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?($($arg:ident: $arg_ty:ty),+) -> Result;)*) => { $( forward_attributes!( $fnname, pub fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?($($arg: $arg_ty),+) -> Result { Uint::$fnname($($arg),+).map(Bits::from) }, must_use: false ); )* }; ($(fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?($($arg:ident: $arg_ty:ty),+) -> $res:ty;)*) => { $( forward_attributes!( $fnname, pub fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?($($arg: $arg_ty),+) -> $res { Uint::$fnname($($arg),+).into() } ); )* }; ($(const fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?($($arg:ident: $arg_ty:ty),+) -> Self;)*) => { $( forward_attributes!( $fnname, pub const fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?($($arg: $arg_ty),+) -> Self { Bits(Uint::$fnname($($arg),+)) } ); )* }; ($(const fn $fnname:ident$(<$(const $generic_arg:ident: $generic_ty:ty),+>)?(&self) -> $res_ty:ty;)*) => { $( forward_attributes!( $fnname, pub const fn $fnname$(<$(const $generic_arg: $generic_ty),+>)?(&self) -> $res_ty { Uint::$fnname(&self.0) } ); )* }; } impl Bits { forward! { fn reverse_bits(self) -> Self; } forward! { fn as_le_bytes(&self) -> Cow<'_, [u8]>; fn to_le_bytes(&self) -> [u8; BYTES]; fn to_be_bytes(&self) -> [u8; BYTES]; fn to_be_bytes_vec(&self) -> Vec; fn leading_zeros(&self) -> usize; fn leading_ones(&self) -> usize; fn trailing_zeros(&self) -> usize; fn trailing_ones(&self) -> usize; } forward! { unsafe fn as_limbs_mut(&mut self) -> &mut [u64; LIMBS]; } forward! { fn checked_shl(self, rhs: usize) -> Option; fn checked_shr(self, rhs: usize) -> Option; } forward! { fn overflowing_shl(self, rhs: usize) -> (Self, bool); fn overflowing_shr(self, rhs: usize) -> (Self, bool); } forward! { fn wrapping_shl(self, rhs: usize) -> Self; fn wrapping_shr(self, rhs: usize) -> Self; fn rotate_left(self, rhs: usize) -> Self; fn rotate_right(self, rhs: usize) -> Self; } forward! { fn try_from_be_slice(bytes: &[u8]) -> Option; fn try_from_le_slice(bytes: &[u8]) -> Option; } forward! { fn from_str_radix(src: &str, radix: u64) -> Result; } forward! { fn from_be_bytes(bytes: [u8; BYTES]) -> Self; fn from_le_bytes(bytes: [u8; BYTES]) -> Self; } forward! { const fn from_limbs(limbs: [u64; LIMBS]) -> Self; } forward! { const fn as_limbs(&self) -> &[u64; LIMBS]; } } impl Index for Bits { type Output = bool; fn index(&self, index: usize) -> &Self::Output { if self.0.bit(index) { &true } else { &false } } } impl Not for Bits { type Output = Self; fn not(self) -> Self { self.0.not().into() } } impl Not for &Bits { type Output = Bits; fn not(self) -> Bits { self.0.not().into() } } macro_rules! impl_bit_op { ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { impl $trait_assign> for Bits { #[allow(clippy::inline_always)] #[inline(always)] fn $fn_assign(&mut self, rhs: Bits) { self.0.$fn_assign(&rhs.0); } } impl $trait_assign<&Bits> for Bits { #[allow(clippy::inline_always)] #[inline(always)] fn $fn_assign(&mut self, rhs: &Bits) { self.0.$fn_assign(rhs.0); } } impl $trait> for Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(mut self, rhs: Bits) -> Self::Output { self.0.$fn_assign(rhs.0); self } } impl $trait<&Bits> for Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(mut self, rhs: &Bits) -> Self::Output { self.0.$fn_assign(rhs.0); self } } impl $trait> for &Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, mut rhs: Bits) -> Self::Output { rhs.0.$fn_assign(self.0); rhs } } impl $trait<&Bits> for &Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, rhs: &Bits) -> Self::Output { self.0.clone().$fn(rhs.0).into() } } }; } impl_bit_op!(BitOr, bitor, BitOrAssign, bitor_assign); impl_bit_op!(BitAnd, bitand, BitAndAssign, bitand_assign); impl_bit_op!(BitXor, bitxor, BitXorAssign, bitxor_assign); macro_rules! impl_shift { ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { impl $trait_assign for Bits { #[allow(clippy::inline_always)] #[inline(always)] fn $fn_assign(&mut self, rhs: usize) { self.0.$fn_assign(rhs); } } impl $trait_assign<&usize> for Bits { #[allow(clippy::inline_always)] #[inline(always)] fn $fn_assign(&mut self, rhs: &usize) { self.0.$fn_assign(rhs); } } impl $trait for Bits { type Output = Self; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, rhs: usize) -> Self { self.0.$fn(rhs).into() } } impl $trait for &Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, rhs: usize) -> Self::Output { self.0.$fn(rhs).into() } } impl $trait<&usize> for Bits { type Output = Self; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, rhs: &usize) -> Self { self.0.$fn(rhs).into() } } impl $trait<&usize> for &Bits { type Output = Bits; #[allow(clippy::inline_always)] #[inline(always)] fn $fn(self, rhs: &usize) -> Self::Output { self.0.$fn(rhs).into() } } }; } impl_shift!(Shl, shl, ShlAssign, shl_assign); impl_shift!(Shr, shr, ShrAssign, shr_assign);