use std::fmt;
use std::ops::{
Shl, ShlAssign,
Shr, ShrAssign,
Not,
BitAnd, BitAndAssign,
BitOr, BitOrAssign,
BitXor, BitXorAssign,
};
use std::cmp::{
PartialOrd,
Ord,
Ordering,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Mask {
value: u8,
}
impl fmt::Display for Mask {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut value = self.value;
let mut result = vec![0u8; 8];
for i in 0..8 {
result[7 - i] = '0' as u8 + (value & 1);
value >>= 1;
}
f.pad(std::str::from_utf8(result.as_slice()).unwrap())
}
}
impl fmt::Debug for Mask {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl Mask {
pub const BITS: u32 = std::mem::size_of::<u8>() as u32 * 8;
pub const VALUES: [Mask; 8] = unsafe { [
Mask::new_unchecked(0b1000_0000),
Mask::new_unchecked(0b0100_0000),
Mask::new_unchecked(0b0010_0000),
Mask::new_unchecked(0b0001_0000),
Mask::new_unchecked(0b0000_1000),
Mask::new_unchecked(0b0000_0100),
Mask::new_unchecked(0b0000_0010),
Mask::new_unchecked(0b0000_0001),
] };
pub fn new(value: u8) -> Mask {
assert_eq!(
crate::count_ones(value),
1,
"Bitmasks have to be a power of two.",
);
unsafe {
Mask::new_unchecked(value)
}
}
#[inline(always)]
pub const unsafe fn new_unchecked(value: u8) -> Mask {
Mask {
value,
}
}
#[inline]
pub fn offset(&self) -> usize {
match self.value {
0b1000_0000 => 0,
0b0100_0000 => 1,
0b0010_0000 => 2,
0b0001_0000 => 3,
0b0000_1000 => 4,
0b0000_0100 => 5,
0b0000_0010 => 6,
0b0000_0001 => 7,
_ => unreachable!("Invalid bitmask."),
}
}
#[inline(always)]
pub fn check(&self, byte: u8) -> bool {
self.value & byte == self.value
}
#[inline]
pub fn set(&self, byte: &mut u8, value: bool) {
*byte = if value {
*byte | *self
}
else {
*byte & !*self
}
}
}
impl Into<u8> for Mask {
fn into(self) -> u8 {
self.value
}
}
impl From<u8> for Mask {
fn from(value: u8) -> Mask {
Self::new(value)
}
}
impl Shr<u32> for Mask {
type Output = Mask;
fn shr(self, mut other: u32) -> Mask {
other %= Mask::BITS;
Mask {
value: self.value.overflowing_shr(other).0 | self.value.overflowing_shl(Mask::BITS - other).0,
}
}
}
impl ShrAssign<u32> for Mask {
#[inline(always)]
fn shr_assign(&mut self, other: u32) {
*self = *self >> other;
}
}
impl Shl<u32> for Mask {
type Output = Mask;
fn shl(self, mut other: u32) -> Mask {
other %= Mask::BITS;
Mask {
value: self.value.overflowing_shl(other).0 | self.value.overflowing_shr(Mask::BITS - other).0,
}
}
}
impl ShlAssign<u32> for Mask {
#[inline(always)]
fn shl_assign(&mut self, other: u32) {
*self = *self << other;
}
}
impl Not for Mask {
type Output = Mask;
#[inline(always)]
fn not(self) -> Mask {
Mask {
value: !self.value
}
}
}
impl BitAnd<u8> for Mask {
type Output = u8;
#[inline(always)]
fn bitand(self, other: u8) -> u8 {
self.value & other
}
}
impl BitOr<u8> for Mask {
type Output = u8;
#[inline(always)]
fn bitor(self, other: u8) -> u8 {
self.value | other
}
}
impl BitXor<u8> for Mask {
type Output = u8;
#[inline(always)]
fn bitxor(self, other: u8) -> u8 {
self.value ^ other
}
}
impl BitAnd<Mask> for u8 {
type Output = u8;
#[inline(always)]
fn bitand(self, other: Mask) -> u8 {
self & other.value
}
}
impl BitOr<Mask> for u8 {
type Output = u8;
#[inline(always)]
fn bitor(self, other: Mask) -> u8 {
self | other.value
}
}
impl BitXor<Mask> for u8 {
type Output = u8;
#[inline(always)]
fn bitxor(self, other: Mask) -> u8 {
self ^ other.value
}
}
impl BitAndAssign<Mask> for u8 {
#[inline(always)]
fn bitand_assign(&mut self, other: Mask) {
*self &= other.value;
}
}
impl BitOrAssign<Mask> for u8 {
#[inline(always)]
fn bitor_assign(&mut self, other: Mask) {
*self |= other.value;
}
}
impl BitXorAssign<Mask> for u8 {
#[inline(always)]
fn bitxor_assign(&mut self, other: Mask) {
*self ^= other.value;
}
}
impl PartialOrd for Mask {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Mask {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
match self.value.cmp(&other.value) {
Ordering::Greater => Ordering::Less,
Ordering::Equal => Ordering::Equal,
Ordering::Less => Ordering::Greater,
}
}
}
#[cfg(test)]
mod mask_tests {
use super::*;
#[test]
fn offsets() {
for i in 0..8 {
assert_eq!(Mask::VALUES[i].offset(), i);
}
}
#[test]
fn checks() {
let byte = 0b11001010;
assert_eq!(Mask::VALUES[0].check(byte), true);
assert_eq!(Mask::VALUES[1].check(byte), true);
assert_eq!(Mask::VALUES[2].check(byte), false);
assert_eq!(Mask::VALUES[3].check(byte), false);
assert_eq!(Mask::VALUES[4].check(byte), true);
assert_eq!(Mask::VALUES[5].check(byte), false);
assert_eq!(Mask::VALUES[6].check(byte), true);
assert_eq!(Mask::VALUES[7].check(byte), false);
}
#[test]
fn shifts() {
let mask = Mask::VALUES[0];
for i in 0..32 {
assert_eq!(mask >> i, Mask::VALUES[i as usize % 8], "i = {}", i);
}
for i in 0..32 {
assert_eq!(mask << i, Mask::VALUES[Mask::BITS as usize - (i + 7) as usize % 8 - 1], "i = {}", i);
}
}
}