#![no_std]
use core::{
cmp, fmt,
iter::FusedIterator,
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not},
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
#[repr(transparent)]
pub struct PackedBools(u8);
impl PackedBools {
pub fn new() -> Self {
Self(0)
}
pub fn new_vals(vals: [bool; 8]) -> PackedBools {
let mut out = 0;
vals.into_iter()
.zip(0..8)
.for_each(|(b, idx)| out |= u8::from(b) << idx);
PackedBools(out)
}
pub fn set_all(&mut self, vals: [bool; 8]) {
vals.into_iter()
.zip(0..8)
.for_each(|(val, idx)| self.set(val, idx))
}
pub fn get_all(&self) -> [bool; 8] {
let mut arr = [false; 8];
arr.iter_mut()
.zip(0..8)
.for_each(|(b, idx)| *b = self.get(idx));
arr
}
pub fn get(&self, idx: u8) -> bool {
self.try_get(idx)
.expect("The index cannot be greater than 7.")
}
pub fn try_get(&self, idx: u8) -> Option<bool> {
if idx < 8 {
Some(((self.0 >> idx) & 1) != 0)
} else {
None
}
}
pub fn set(&mut self, val: bool, idx: u8) {
self.try_set(val, idx)
.expect("The index cannot be greater than 7.")
}
pub fn try_set(&mut self, val: bool, idx: u8) -> Option<()> {
if idx < 8 {
match val {
true => self.0 |= 1 << idx,
false => self.0 &= !(1 << idx),
};
Some(())
} else {
None
}
}
pub fn toggle(&mut self, idx: u8) {
self.try_toggle(idx)
.expect("The index cannot be greater than 7.")
}
pub fn try_toggle(&mut self, idx: u8) -> Option<()> {
if idx < 8 {
self.0 ^= 1 << idx;
Some(())
} else {
None
}
}
}
impl From<[bool; 8]> for PackedBools {
fn from(bools: [bool; 8]) -> Self {
Self::new_vals(bools)
}
}
impl PartialOrd for PackedBools {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PackedBools {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.get_all().cmp(&other.get_all())
}
}
impl BitAnd for PackedBools {
type Output = PackedBools;
fn bitand(self, rhs: Self) -> Self::Output {
PackedBools(self.0 & rhs.0)
}
}
impl BitAndAssign for PackedBools {
fn bitand_assign(&mut self, rhs: Self) {
*self = self.bitand(rhs)
}
}
impl BitOr for PackedBools {
type Output = PackedBools;
fn bitor(self, rhs: Self) -> Self::Output {
PackedBools(self.0 | rhs.0)
}
}
impl BitOrAssign for PackedBools {
fn bitor_assign(&mut self, rhs: Self) {
*self = self.bitor(rhs)
}
}
impl BitXor for PackedBools {
type Output = PackedBools;
fn bitxor(self, rhs: Self) -> Self::Output {
PackedBools(self.0 ^ rhs.0)
}
}
impl BitXorAssign for PackedBools {
fn bitxor_assign(&mut self, rhs: Self) {
*self = self.bitxor(rhs)
}
}
impl Not for PackedBools {
type Output = PackedBools;
fn not(self) -> Self::Output {
PackedBools(!self.0)
}
}
impl IntoIterator for PackedBools {
type Item = bool;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
impl fmt::Debug for PackedBools {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:#b}", self.0))
}
}
#[repr(transparent)]
struct PackedU8Range(u8);
impl PackedU8Range {
fn new(start: u8, end: u8) -> Self {
let mut val = start << 4;
val |= end;
PackedU8Range(val)
}
fn get_start(&self) -> u8 {
self.0 >> 4
}
fn get_end(&self) -> u8 {
self.0 & 0b00001111
}
fn iter_next(&mut self) -> Option<u8> {
let start = self.get_start();
(self.0 < 0b11110000 && start < self.get_end()).then(|| {
self.0 += 0b00010000; start
})
}
fn iter_next_back(&mut self) -> Option<u8> {
let end = self.get_end();
(end > 0 && self.get_start() < end).then(|| {
self.0 -= 1; end
})
}
fn len(&self) -> u8 {
self.get_end() - self.get_start()
}
}
#[repr(C)]
pub struct IntoIter {
bools: PackedBools,
range: PackedU8Range,
}
impl IntoIter {
fn new(bools: PackedBools) -> Self {
Self {
bools,
range: PackedU8Range::new(0, 8),
}
}
}
impl Iterator for IntoIter {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
self.range.iter_next().map(|idx| self.bools.get(idx))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.range.len().into();
(len, Some(len))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let n = u8::try_from(n).ok().filter(|&n| n < self.range.len())?;
self.range.0 += n << 4;
self.next()
}
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
}
impl DoubleEndedIterator for IntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.range.iter_next_back().map(|idx| self.bools.get(idx))
}
}
impl ExactSizeIterator for IntoIter {
fn len(&self) -> usize {
self.range.len().into()
}
}
impl FusedIterator for IntoIter {}
#[cfg(test)]
mod tests {
use super::PackedBools;
#[test]
fn set_get() {
let mut pkd = PackedBools::new();
pkd.set_all([false, true, false, true, true, false, false, true]);
pkd.set(false, 3);
pkd.set(true, 4);
assert_eq!(
pkd.get_all(),
[false, true, false, false, true, false, false, true]
);
}
#[test]
fn new_vals() {
let mut pkd = PackedBools::new();
let arr = [false, true, false, false, true, false, true, true];
pkd.set_all(arr);
assert_eq!(pkd, PackedBools::new_vals(arr));
}
#[test]
fn iter() {
let pkd = PackedBools::new();
assert_eq!(pkd.into_iter().len(), 8);
for b in pkd.into_iter() {
assert!(!b);
}
let arr = [false, true, false, true, false, false, false, true];
PackedBools::new_vals(arr)
.into_iter()
.zip(arr.into_iter())
.for_each(|(b1, b2)| assert_eq!(b1, b2));
}
}