use core::{
fmt::{
self,
Binary,
Debug,
Formatter,
LowerHex,
Octal,
UpperHex,
},
slice,
};
use funty::IsNumber;
use tap::{
pipe::Pipe,
tap::Tap,
};
use wyz::fmt::FmtForward;
use crate::{
index::{
BitIdx,
BitTail,
},
order::BitOrder,
slice::BitSlice,
store::BitStore,
};
macro_rules! bit_domain {
($t:ident $(=> $m:ident)? $(@ $a:ident)?) => {
#[derive(Debug)]
pub enum $t <'a, O, T>
where
O: BitOrder,
T: BitStore,
{
Enclave {
head: BitIdx<T::Mem>,
body: &'a $($m)? BitSlice<O, T>,
tail: BitTail<T::Mem>,
},
Region {
head: &'a $($m)? BitSlice<O, T>,
body: &'a $($m)? BitSlice<O, T::Unalias>,
tail: &'a $($m)? BitSlice<O, T>,
},
}
impl<'a, O, T> $t <'a, O, T>
where
O: BitOrder,
T: BitStore,
{
pub fn enclave(self) -> Option<(
BitIdx<T::Mem>,
&'a $($m)? BitSlice<O, T>,
BitTail<T::Mem>,
)> {
if let Self::Enclave { head, body, tail } = self {
Some((head, body, tail))
}
else {
None
}
}
pub fn region(self) -> Option<(
&'a $($m)? BitSlice<O, T>,
&'a $($m)? BitSlice<O, T::Unalias>,
&'a $($m)? BitSlice<O, T>,
)> {
if let Self::Region { head, body, tail } = self {
Some((head, body, tail))
}
else {
None
}
}
pub(crate) fn new(slice: &'a $($m)? BitSlice<O, T>) -> Self {
let bitspan = slice.as_bitspan();
let h = bitspan.head();
let (e, t) = h.span(bitspan.len());
let w = T::Mem::BITS as u8;
match (h.value(), e, t.value()) {
(_, 0, _) => Self::empty(),
(0, _, t) if t == w => Self::spanning(slice),
(_, _, t) if t == w => Self::partial_head(slice, h),
(0, ..) => Self::partial_tail(slice, h, t),
(_, 1, _) => Self::minor(slice, h, t),
_ => Self::major(slice, h, t),
}
}
fn empty() -> Self {
Self::Region {
head: Default::default(),
body: Default::default(),
tail: Default::default(),
}
}
fn major(
slice: &'a $($m)? BitSlice<O, T>,
head: BitIdx<T::Mem>,
tail: BitTail<T::Mem>,
) -> Self {
let (head, rest) = bit_domain!(split $($m)?
slice,
(T::Mem::BITS as u8 - head.value()) as usize,
);
let (body, tail) = bit_domain!(split $($m)?
rest,
rest.len() - (tail.value() as usize),
);
Self::Region {
head: bit_domain!(retype $($m)? head),
body: bit_domain!(retype $($m)? body),
tail: bit_domain!(retype $($m)? tail),
}
}
fn minor(
slice: &'a $($m)? BitSlice<O, T>,
head: BitIdx<T::Mem>,
tail: BitTail<T::Mem>,
) -> Self {
Self::Enclave {
head,
body: slice,
tail,
}
}
fn partial_head(
slice: &'a $($m)? BitSlice<O, T>,
head: BitIdx<T::Mem>,
) -> Self {
let (head, rest) = bit_domain!(split $($m)?
slice,
(T::Mem::BITS as u8 - head.value()) as usize,
);
let (head, body) = (
bit_domain!(retype $($m)? head),
bit_domain!(retype $($m)? rest),
);
Self::Region {
head,
body,
tail: Default::default(),
}
}
fn partial_tail(
slice: &'a $($m)? BitSlice<O, T>,
_head: BitIdx<T::Mem>,
tail: BitTail<T::Mem>,
) -> Self {
let (rest, tail) = bit_domain!(split $($m)?
slice,
slice.len() - (tail.value() as usize),
);
let (body, tail) = (
bit_domain!(retype $($m)? rest),
bit_domain!(retype $($m)? tail),
);
Self::Region {
head: Default::default(),
body,
tail,
}
}
fn spanning(slice: &'a $($m)? BitSlice<O, T>) -> Self {
Self::Region {
head: Default::default(),
body: bit_domain!(retype $($m)? slice),
tail: Default::default(),
}
}
}
};
(retype mut $slice:ident $(,)? ) => {
unsafe { &mut *($slice as *mut BitSlice<O, _> as *mut BitSlice<O, _>) }
};
(retype $slice:ident $(,)? ) => {
unsafe { &*($slice as *const BitSlice<O, _> as *const BitSlice<O, _>) }
};
(split mut $slice:ident, $at:expr $(,)? ) => {
unsafe { $slice.split_at_unchecked_mut($at) }
};
(split $slice:ident, $at:expr $(,)? ) => {
unsafe { $slice.split_at_unchecked($at) }
};
}
bit_domain!(BitDomain);
bit_domain!(BitDomainMut => mut @ Alias);
#[cfg(not(tarpaulin_include))]
impl<O, T> Clone for BitDomain<'_, O, T>
where
O: BitOrder,
T: BitStore,
{
fn clone(&self) -> Self {
*self
}
}
impl<O, T> Copy for BitDomain<'_, O, T>
where
O: BitOrder,
T: BitStore,
{
}
macro_rules! domain {
($t:ident $(=> $m:ident @ $a:ident)?) => {
#[derive(Debug)]
pub enum $t <'a, T>
where
T: BitStore,
{
Enclave {
head: BitIdx<T::Mem>,
elem: &'a T $(::$a)?,
tail: BitTail<T::Mem>,
},
Region {
head: Option<(BitIdx<T::Mem>, &'a T $(::$a)?)>,
body: &'a $($m)? [T::Unalias],
tail: Option<(&'a T $(::$a)?, BitTail<T::Mem>)>,
}
}
impl<'a, T> $t <'a, T>
where
T: BitStore,
{
pub fn enclave(self) -> Option<(
BitIdx<T::Mem>,
&'a T $(::$a)?,
BitTail<T::Mem>,
)> {
if let Self::Enclave { head, elem, tail } = self {
Some((head, elem, tail))
} else {
None
}
}
pub fn region(self) -> Option<(
Option<(BitIdx<T::Mem>, &'a T $(::$a)?)>,
&'a $($m)? [T::Unalias],
Option<(&'a T $(::$a)?, BitTail<T::Mem>)>,
)> {
if let Self::Region { head, body, tail } = self {
Some((head,body,tail))
}
else {
None
}
}
pub(crate) fn new<O>(slice: &'a $($m)? BitSlice<O, T>) -> Self
where O: BitOrder {
let bitspan = slice.as_bitspan();
let head = bitspan.head();
let elts = bitspan.elements();
let tail = bitspan.tail();
let bits = T::Mem::BITS as u8;
let base = bitspan.address().to_const() as *const _;
match (head.value(), elts, tail.value()) {
(_, 0, _) => Self::empty(),
(0, _, t) if t == bits => Self::spanning(base, elts),
(_, _, t) if t == bits => Self::partial_head(base, elts, head),
(0, ..) => Self::partial_tail(base, elts, tail),
(_, 1, _) => Self::minor(base, head, tail),
_ => Self::major(base, elts, head, tail),
}
}
fn empty() -> Self {
Self::Region {
head: None,
body: & $($m)? [],
tail: None,
}
}
fn major(
base: *const T $(::$a)?,
elts: usize,
head: BitIdx<T::Mem>,
tail: BitTail<T::Mem>,
) -> Self {
let h = unsafe { &*base };
let t = unsafe { &*base.add(elts - 1) };
let body = domain!(slice $($m)? base.add(1), elts - 2);
Self::Region {
head: Some((head, h)),
body,
tail: Some((t, tail)),
}
}
fn minor(
addr: *const T $(::$a)?,
head: BitIdx<T::Mem>,
tail: BitTail<T::Mem>,
) -> Self {
Self::Enclave {
head,
elem: unsafe { &*addr },
tail,
}
}
fn partial_head(
base: *const T $(::$a)?,
elts: usize,
head: BitIdx<T::Mem>,
) -> Self {
let h = unsafe { &*base };
let body = domain!(slice $($m)? base.add(1), elts - 1);
Self::Region {
head: Some((head, h)),
body,
tail: None,
}
}
fn partial_tail(
base: *const T $(::$a)?,
elts: usize,
tail: BitTail<T::Mem>,
) -> Self {
let t = unsafe { &*base.add(elts - 1) };
let body = domain!(slice $($m)? base, elts - 1);
Self::Region {
head: None,
body,
tail: Some((t, tail)),
}
}
fn spanning(base: *const T $(::$a)?, elts: usize) -> Self {
Self::Region {
head: None,
body: domain!(slice $($m)? base, elts),
tail: None,
}
}
}
};
(slice mut $base:expr, $elts:expr) => {
unsafe { slice::from_raw_parts_mut($base as *const _ as *mut _, $elts) }
};
(slice $base:expr, $elts:expr) => {
unsafe { slice::from_raw_parts($base as *const _, $elts) }
};
}
domain!(Domain);
domain!(DomainMut => mut @ Access);
#[cfg(not(tarpaulin_include))]
impl<T> Clone for Domain<'_, T>
where T: BitStore
{
fn clone(&self) -> Self {
*self
}
}
impl<'a, T> Iterator for Domain<'a, T>
where T: BitStore
{
type Item = T::Mem;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Enclave { elem, .. } => {
elem.load_value().pipe(Some).tap(|_| *self = Self::empty())
},
Self::Region { head, body, tail } => {
if let Some((_, elem)) = *head {
return elem.load_value().pipe(Some).tap(|_| *head = None);
}
if let Some((elem, rest)) = body.split_first() {
*body = rest;
return elem.load_value().into();
}
if let Some((elem, _)) = *tail {
return elem.load_value().pipe(Some).tap(|_| *tail = None);
}
None
},
}
}
}
impl<'a, T> DoubleEndedIterator for Domain<'a, T>
where T: BitStore
{
fn next_back(&mut self) -> Option<Self::Item> {
match self {
Self::Enclave { elem, .. } => {
elem.load_value().pipe(Some).tap(|_| *self = Self::empty())
},
Self::Region { head, body, tail } => {
if let Some((elem, _)) = *tail {
return elem.load_value().pipe(Some).tap(|_| *tail = None);
}
if let Some((elem, rest)) = body.split_last() {
*body = rest;
return elem.load_value().into();
}
if let Some((_, elem)) = *head {
return elem.load_value().pipe(Some).tap(|_| *head = None);
}
None
},
}
}
}
impl<T> ExactSizeIterator for Domain<'_, T>
where T: BitStore
{
fn len(&self) -> usize {
match self {
Self::Enclave { .. } => 1,
Self::Region { head, body, tail } => {
head.is_some() as usize + body.len() + tail.is_some() as usize
},
}
}
}
impl<T> core::iter::FusedIterator for Domain<'_, T> where T: BitStore
{
}
impl<T> Copy for Domain<'_, T> where T: BitStore
{
}
macro_rules! fmt {
($($f:ty => $fwd:ident),+ $(,)?) => { $(
impl<T> $f for Domain<'_, T>
where T: BitStore
{
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_list()
.entries(self.into_iter().map(FmtForward::$fwd))
.finish()
}
}
)+ };
}
fmt!(
Binary => fmt_binary,
LowerHex => fmt_lower_hex,
Octal => fmt_octal,
UpperHex => fmt_upper_hex,
);
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn domain_iter() {
let data = [1u32, 2, 3];
let bits = &data.view_bits::<LocalBits>()[4 .. 92];
for (iter, elem) in bits.domain().rev().zip([3, 2, 1].iter().copied()) {
assert_eq!(iter, elem);
}
}
}