#![warn(rust_2018_idioms, missing_debug_implementations, missing_docs)]
use std::fmt;
use std::ops;
#[derive(Clone, Copy)]
pub struct NibSlice<'a> {
exclude: Exclude,
inner: &'a [u8],
}
impl<'a> NibSlice<'a> {
pub fn from_bytes_exclude(inner: &'a [u8], exclude: Exclude) -> Self {
if inner.len() == 0 {
assert_eq!(exclude, Exclude::None_);
}
if inner.len() == 1 {
assert_ne!(exclude, Exclude::Both);
}
Self {
inner,
exclude,
}
}
pub fn from_bytes_skip_last(inner: &'a [u8]) -> Self {
Self::from_bytes_exclude(inner, Exclude::Last)
}
pub fn from_bytes(inner: &'a [u8]) -> Self {
Self::from_bytes_exclude(inner, Exclude::None_)
}
pub fn len(&self) -> usize {
self.inner.len() * 2 - self.exclude.len_excluded()
}
pub fn split_at(&self, offset: usize) -> (NibSlice<'a>, NibSlice<'a>) {
(self.index(..offset), self.index(offset..))
}
pub fn index<S: SliceIndex<'a>>(&self, idx: S) -> S::Output {
self.get(idx).unwrap()
}
pub fn get<S: SliceIndex<'a>>(&self, idx: S) -> Option<S::Output> {
idx.get(self)
}
pub fn nibble(&self) -> u8 {
self.try_nibble().unwrap()
}
pub fn try_nibble(&self) -> Option<u8> {
if self.len() != 1 {
return None
}
let b = self.inner[0];
Some(match self.exclude {
Exclude::First => { b & 0xf },
Exclude::Last => { b >> 4 },
_ => panic!(),
})
}
pub fn iter(&self) -> Iter<'a> {
Iter { inner: *self }
}
pub fn byte_parts(&self) -> (Option<u8>, &[u8], Option<u8>) {
let (rem, first) = if self.exclude.is_first_excluded() {
(&self.inner[1..], Some(self.inner[0] & 0x0f))
} else {
(self.inner, None)
};
let (rem, last) = if self.exclude.is_last_excluded() {
let l = rem.len();
(&rem[..l - 1], Some(rem[rem.len() - 1] >> 4))
} else {
(rem, None)
};
(first, rem, last)
}
}
#[derive(Debug, Clone)]
pub struct Iter<'a> {
inner: NibSlice<'a>,
}
impl<'a> Iterator for Iter<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
if self.inner.len() == 0 {
return None;
}
let v = if self.inner.exclude.is_first_excluded() {
let v = self.inner.inner[0] & 0x0f;
self.inner.inner = &self.inner.inner[1..];
self.inner.exclude = Exclude::from_excludes(false, self.inner.exclude.is_last_excluded());
v
} else {
let v = self.inner.inner[0] >> 4;
self.inner.exclude = Exclude::from_excludes(true, self.inner.exclude.is_last_excluded());
v
};
if self.inner.inner.len() == 0 {
self.inner.exclude = Exclude::None_;
}
if self.inner.inner.len() == 1 && self.inner.exclude == Exclude::Both {
self.inner.exclude = Exclude::None_;
self.inner.inner = &[];
}
Some(v)
}
}
impl PartialEq<NibSlice<'_>> for NibSlice<'_> {
fn eq(&self, other: &NibSlice<'_>) -> bool {
let i1 = self.iter();
let i2 = other.iter();
i1.eq(i2)
}
}
impl Eq for NibSlice<'_> {}
pub trait SliceIndex<'a> {
type Output;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output>;
}
fn b(exclude: Exclude, offs: usize) -> (usize, bool) {
let index = offs + if exclude.is_first_excluded() { 1 } else { 0 };
let b_idx = index >> 1;
let is_low = index & 1 == 1;
(b_idx, is_low)
}
pub fn decompose_offset(exclude: Exclude, offs: usize) -> (usize, bool) {
b(exclude, offs)
}
impl<'a> SliceIndex<'a> for usize {
type Output = u8;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
if self >= slice.len() {
return None;
}
let (b_idx, is_low) = b(slice.exclude, self);
let b = &slice.inner[b_idx];
Some(if is_low {
b & 0x0f
} else {
b >> 4
})
}
}
impl<'a> SliceIndex<'a> for ops::Range<usize> {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
if self.start > self.end {
return None;
}
if self.end > slice.len() {
return None;
}
let (b_start, exclude_first) = b(slice.exclude, self.start);
let (b_end, end_is_low) = b(slice.exclude, self.end + 1);
Some(NibSlice::from_bytes_exclude(
&slice.inner[b_start..b_end],
Exclude::from_excludes(exclude_first, !end_is_low),
))
}
}
impl<'a> SliceIndex<'a> for ops::RangeFrom<usize> {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
(self.start..slice.len()).get(slice)
}
}
impl<'a> SliceIndex<'a> for ops::RangeTo<usize> {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
(0..self.end).get(slice)
}
}
impl<'a> SliceIndex<'a> for ops::RangeFull {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
Some(*slice)
}
}
impl<'a> SliceIndex<'a> for ops::RangeInclusive<usize> {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
(*self.start()..(*self.end() + 1)).get(slice)
}
}
impl<'a> SliceIndex<'a> for ops::RangeToInclusive<usize> {
type Output = NibSlice<'a>;
fn get(self, slice: &NibSlice<'a>) -> Option<Self::Output> {
(0..self.end + 1).get(slice)
}
}
impl<'a> fmt::Debug for NibSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut dl = f.debug_list();
for i in self.iter() {
dl.entry(&i);
}
dl.finish()
}
}
impl<'a> fmt::Display for NibSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for i in self.iter() {
write!(f, "{}", i)?
}
Ok(())
}
}
#[cfg(test)]
mod test_nibslice {
use super::*;
#[test]
fn build_ok() {
let ns = NibSlice::from_bytes_exclude(&[0xab, 0xcd], Exclude::Last);
assert_eq!(ns.len(), 3);
}
#[test]
fn index_single() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34], Exclude::First);
assert_eq!(ns.len(), 3);
assert_eq!(3, ns.index(1));
}
#[test]
#[should_panic]
fn index_oob() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34], Exclude::Both);
assert_eq!(ns.len(), 2);
let n = ns.index(2);
assert_eq!(n, 0x4);
}
#[test]
fn index_range() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34, 0x56], Exclude::Both);
assert_eq!(ns.len(), 4);
let n = ns.index(1..3);
println!("{:#x?}", n);
assert_eq!(n, NibSlice::from_bytes_exclude(&[0x34], Exclude::None_));
assert_eq!(n.len(), 2);
}
#[test]
fn get_range_oob_exclude_both() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34], Exclude::Both);
assert_ne!(ns.len(), 4);
let x = ns.get(1..3);
assert_eq!(x, None);
}
#[test]
#[should_panic]
fn index_range_bad() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34], Exclude::Both);
assert_ne!(ns.len(), 4);
let _ = ns.index(1..3);
}
#[test]
fn index_2() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x04], Exclude::Both);
eprintln!("n1: {:?}", ns.len());
assert_eq!(ns.len(), 2);
let n = ns.get(1..);
eprintln!("n: {:?}", n.map(|x| x.len()));
assert_eq!(n, Some(NibSlice::from_bytes_exclude(&[0x00], Exclude::First)));
}
#[test]
fn index_to_1() {
let orig_s = NibSlice::from_bytes_exclude(&[0x02, 0x34], Exclude::First);
let nib_s = orig_s.index(..1);
assert_eq!(nib_s.len(), 1);
let nib = nib_s.nibble();
assert_eq!(nib, 0x2);
}
#[test]
fn index_middle() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34, 0x56], Exclude::Both);
let n = ns.index(2..4);
assert_eq!(n, NibSlice::from_bytes(&[0x45]));
}
#[test]
fn iter() {
let ns = NibSlice::from_bytes_exclude(&[0x12, 0x34], Exclude::Both);
let mut i = ns.iter();
assert_eq!(i.next(), Some(2));
assert_eq!(i.next(), Some(3));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
#[test]
fn index_3() {
let n = NibSlice::from_bytes_skip_last(&[0x12, 0x34]);
assert_eq!(n.len(), 3);
let m = n.index(1..);
assert_eq!(m.len(), 2);
assert_eq!(m, NibSlice::from_bytes(&[0x23]));
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Exclude {
First,
Last,
None_,
Both,
}
impl Exclude {
pub fn is_first_excluded(self) -> bool {
match self {
Self::First | Self::Both => true,
_ => false
}
}
pub fn is_last_excluded(self) -> bool {
match self {
Self::Last | Self::Both => true,
_ => false
}
}
pub fn len_excluded(self) -> usize {
match self {
Self::Both => 2,
Self::First | Self::Last => 1,
Self::None_ => 0
}
}
pub fn from_includes(include_first: bool, include_last: bool) -> Self {
Self::from_excludes(!include_first, !include_last)
}
pub fn from_excludes(exclude_first: bool, exclude_last: bool) -> Self {
match (exclude_first, exclude_last) {
(true, true) => Exclude::Both,
(false, true) => Exclude::Last,
(true, false) => Exclude::First,
(false, false) => Exclude::None_,
}
}
}
#[cfg(doctest)]
mod doctest {
use doc_comment::doctest;
doctest!("../README.md");
}