bitit 0.1.2

Bitwise iteration over integers.
Documentation
use std::{
    mem::size_of,
    ops::{
        BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
        ShrAssign,
    },
};

/// A pure binary integer (`u8`, `i8`, `u16`, etc.).
pub trait Binary:
    Sized
    + Copy
    + PartialEq
    + Eq
    + BitOr<Output = Self>
    + BitAnd<Output = Self>
    + BitXor<Output = Self>
    + Not<Output = Self>
    + Shl<u32, Output = Self>
    + Shr<u32, Output = Self>
    + BitOrAssign
    + BitAndAssign
    + BitXorAssign
    + ShlAssign<u32>
    + ShrAssign<u32>
{
    /// The max bit index of the value starting from zero.
    /// Same as the number of bits minus one.
    const MAX_BIT_IDX: u32;

    /// All bits zero.
    const ZERO: Self;

    /// All bits one.
    const FULL: Self;

    /// Only the least significant bit is one.
    const ONE: Self;

    /// Only the most significant bit is one.
    const TOP: Self;

    /// Returns `true` if the value has at least one bit set to 0.
    fn has_zero(self) -> bool {
        self != Self::FULL
    }

    /// Returns `true` if the value has at least one bit set to 1.
    fn has_one(self) -> bool {
        self != Self::ZERO
    }

    /// Returns the number of trailing 0s.
    fn trailing_zeros(self) -> u32;

    /// Returns the number of trailing 1s.
    fn trailing_ones(self) -> u32;

    /// Returns the number of leading 0s.
    fn leading_zeros(self) -> u32;

    /// Returns the number of leading 1s.
    fn leading_ones(self) -> u32;

    /// Returns the value's least significant 0-bit, or `0` if there is none.
    fn lowest_zero(self) -> Self;

    /// Returns the value's least significant 1-bit, or `0` if there is none.
    fn lowest_one(self) -> Self;

    /// Returns the value's most significant 0-bit, or `0` if there is none.
    fn highest_zero(self) -> Self;

    /// Returns the value's most significant 1-bit, or `0` if there is none.
    fn highest_one(self) -> Self;
}

macro_rules! impl_binary {
    ($($t:ident),*) => {
        $(
            impl Binary for $t {
                const MAX_BIT_IDX: u32 = (size_of::<Self>() as u32 * 8) - 1;

                const ZERO: Self = 0 as _;

                const FULL: Self = !Self::ZERO;

                const ONE: Self = 1 as _;

                const TOP: Self = Self::ONE << Self::MAX_BIT_IDX;

                #[inline(always)]
                fn trailing_zeros(self) -> u32 {
                    self.trailing_zeros()
                }

                #[inline(always)]
                fn trailing_ones(self) -> u32 {
                    self.trailing_ones()
                }

                #[inline(always)]
                fn leading_zeros(self) -> u32 {
                    self.leading_zeros()
                }

                #[inline(always)]
                fn leading_ones(self) -> u32 {
                    self.leading_ones()
                }

                #[inline(always)]
                fn lowest_zero(self) -> Self {
                    Self::ONE.unbounded_shl(self.trailing_ones())
                }

                #[inline(always)]
                fn lowest_one(self) -> Self {
                    Self::ONE.unbounded_shl(self.trailing_zeros())
                }

                #[inline(always)]
                fn highest_zero(self) -> Self {
                    Self::TOP.unbounded_shr(self.leading_ones())
                }

                #[inline(always)]
                fn highest_one(self) -> Self {
                    Self::TOP.unbounded_shr(self.leading_zeros())
                }
            }
        )*
    };
}

impl_binary!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);