use crate::{
indices::{
BitIdx,
BitTail,
},
pointer::BitPtr,
store::BitStore,
};
use either::Either;
pub(crate) type Splat<'a, T, A> = Either<(
Option<(BitIdx<T>, &'a A)>,
Option<&'a [A]>,
Option<(&'a A, BitTail<T>)>,
), (BitIdx<T>, &'a A, BitTail<T>)>;
#[derive(Debug)]
pub(crate) enum BitDomain<'a, T>
where T: 'a + BitStore {
Empty,
Minor(BitIdx<T>, &'a T::Access, BitTail<T>),
Major(BitIdx<T>, &'a T::Access, &'a [T::Access], &'a T::Access, BitTail<T>),
PartialHead(BitIdx<T>, &'a T::Access, &'a [T::Access]),
PartialTail(&'a [T::Access], &'a T::Access, BitTail<T>),
Spanning(&'a [T::Access]),
}
impl<'a, T> BitDomain<'a, T>
where T: BitStore {
pub(crate) fn splat(self) -> Splat<'a, T, T::Access> {
match self {
BitDomain::Empty => Either::Left((None, None, None)),
BitDomain::Minor(h, e, t) => Either::Right((h, e, t)),
BitDomain::Major(h, head, body, tail, t) => Either::Left((
Some((h, head)),
Some(body),
Some((tail, t)),
)),
BitDomain::PartialHead(h, head, body) => Either::Left((
Some((h, head)),
Some(body),
None,
)),
BitDomain::PartialTail(body, tail, t) => Either::Left((
None,
Some(body),
Some((tail, t)),
)),
BitDomain::Spanning(body) => Either::Left((None, Some(body), None)),
}
}
#[cfg(test)]
pub(crate) fn is_minor(&self) -> bool {
match self {
BitDomain::Minor(..) => true,
_ => false,
}
}
#[cfg(test)]
pub(crate) fn is_major(&self) -> bool {
match self {
BitDomain::Major(..) => true,
_ => false,
}
}
#[cfg(test)]
pub(crate) fn is_partial_head(&self) -> bool {
match self {
BitDomain::PartialHead(..) => true,
_ => false,
}
}
#[cfg(test)]
pub(crate) fn is_partial_tail(&self) -> bool {
match self {
BitDomain::PartialTail(..) => true,
_ => false,
}
}
pub(crate) fn is_spanning(&self) -> bool {
match self {
BitDomain::Spanning(..) => true,
_ => false,
}
}
}
impl<'a, T> From<BitPtr<T>> for BitDomain<'a, T>
where T: 'a + BitStore {
fn from(bitptr: BitPtr<T>) -> Self {
let h = bitptr.head();
let (e, t) = h.span(bitptr.len());
let w = T::BITS;
let data = bitptr.as_access_slice();
match (*h, e, *t) {
(_, 0, _) => BitDomain::Empty,
(0, _, t) if t == w =>
BitDomain::Spanning(data),
(_, _, t) if t == w => {
let (head, rest) = data
.split_first()
.expect("PartialHead cannot fail to split");
BitDomain::PartialHead(h, head, rest)
},
(0, _, _) => {
let (tail, rest) = data
.split_last()
.expect("PartialTail cannot fail to split");
BitDomain::PartialTail(rest, tail, t)
},
(_, 1, _) => BitDomain::Minor(h, &data[0], t),
(_, _, _) => {
let (head, body) = data
.split_first()
.expect("Major cannot fail to split the head element");
let (tail, body) = body
.split_last()
.expect("Major cannot fail to split the tail element");
BitDomain::Major(h, head, body, tail, t)
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indices::Indexable;
#[test]
fn minor() {
let data: u8 = 0u8;
let bp = BitPtr::new(&data, 1u8.idx(), 6);
assert!(bp.domain().is_minor());
}
#[test]
fn major() {
let data: &[u16] = &[0u16, !0u16];
let bp = BitPtr::new(&data[0], 1u8.idx(), 28);
assert!(bp.domain().is_major());
}
#[test]
fn partial_head() {
let data: u32 = 0u32;
let bp = BitPtr::new(&data, 4u8.idx(), 28);
assert!(bp.domain().is_partial_head());
let data: &[u32] = &[0u32, !0u32];
let bp = BitPtr::new(&data[0], 4u8.idx(), 60);
assert!(bp.domain().is_partial_head());
}
#[test]
fn partial_tail() {
let data: u32 = 0u32;
let bp = BitPtr::new(&data, 0u8.idx(), 60);
assert!(bp.domain().is_partial_tail());
let data: &[u32] = &[0u32, !0u32];
let bp = BitPtr::new(&data[0], 0u8.idx(), 60);
assert!(bp.domain().is_partial_tail());
}
#[test]
fn spanning() {
let data: u8 = 0u8;
let bp = BitPtr::new(&data, 0u8.idx(), 8);
assert!(bp.domain().is_spanning());
let data: &[u16] = &[0u16, !0u16];
let bp = BitPtr::new(&data[0], 0u8.idx(), 32);
assert!(bp.domain().is_spanning());
}
}