use super::bits::Bits;
pub struct BigEndian;
pub struct LittleEndian;
pub trait Endian {
fn curr<T: Bits>(count: u8) -> u8;
fn next<T: Bits>(count: u8) -> (u8, bool) {
let next = count.wrapping_add(1) & T::MASK;
let wrap = next == 0;
(next, wrap)
}
fn prev<T: Bits>(count: u8) -> (u8, bool) {
let (next, wrap) = count.overflowing_sub(1);
(next & T::MASK, wrap)
}
fn jump<T: Bits>(count: u8, offset: isize) -> (isize, u8) {
assert!(count < T::WIDTH, "Bit count out of range for the storage type");
match (count as isize).overflowing_add(offset) {
(_, true) => {
let far = Self::curr::<T>(count) as usize + offset as usize;
let elements = (far >> T::BITS) as isize;
let pos = (far & (T::MASK as usize)) as u8;
(elements, pos)
},
(far, _) if far < 0 || far > T::MASK as isize => {
let elements = far >> T::BITS;
let pos = (far & (T::MASK as isize)) as u8;
(elements, Self::curr::<T>(pos))
},
(far, _) => {
(0, Self::curr::<T>(far as u8))
},
}
}
#[doc(hidden)]
const TY: &'static str = "";
}
impl Endian for BigEndian {
fn curr<T: Bits>(count: u8) -> u8 {
assert!(count < T::WIDTH, "Index out of range of the storage type");
T::MASK - count
}
const TY: &'static str = "BigEndian";
}
impl Endian for LittleEndian {
fn curr<T: Bits>(count: u8) -> u8 {
assert!(count < T::WIDTH, "Index out of range of the storage type");
count
}
const TY: &'static str = "LittleEndian";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn incr_edge() {
assert_eq!(LittleEndian::next::<u8>(7), (0, true));
assert_eq!(BigEndian::next::<u8>(7), (0, true));
assert_eq!(LittleEndian::next::<u16>(15), (0, true));
assert_eq!(BigEndian::next::<u16>(15), (0, true));
assert_eq!(LittleEndian::next::<u32>(31), (0, true));
assert_eq!(BigEndian::next::<u32>(31), (0, true));
assert_eq!(LittleEndian::next::<u64>(63), (0, true));
assert_eq!(BigEndian::next::<u64>(63), (0, true));
}
#[test]
fn decr_edge() {
assert_eq!(LittleEndian::prev::<u8>(0), (7, true));
assert_eq!(BigEndian::prev::<u8>(0), (7, true));
assert_eq!(LittleEndian::prev::<u16>(0), (15, true));
assert_eq!(BigEndian::prev::<u16>(0), (15, true));
assert_eq!(LittleEndian::prev::<u32>(0), (31, true));
assert_eq!(BigEndian::prev::<u32>(0), (31, true));
assert_eq!(LittleEndian::prev::<u64>(0), (63, true));
assert_eq!(BigEndian::prev::<u64>(0), (63, true));
}
#[test]
fn jump_inside_elt() {
let (elt, bit) = LittleEndian::jump::<u8>(5, 2);
assert_eq!(elt, 0);
assert_eq!(bit, 7);
let (elt, bit) = BigEndian::jump::<u8>(5, 2);
assert_eq!(elt, 0);
assert_eq!(bit, 0);
let (elt, bit) = LittleEndian::jump::<u32>(20, 8);
assert_eq!(elt, 0);
assert_eq!(bit, 28);
let (elt, bit) = BigEndian::jump::<u32>(20, -8);
assert_eq!(elt, 0);
assert_eq!(bit, 19);
}
#[test]
fn jump_backwards() {
let (elt, bit) = LittleEndian::jump::<u32>(10, -15);
assert_eq!(elt, -1);
assert_eq!(bit, 27);
let (elt, bit) = BigEndian::jump::<u32>(10, -15);
assert_eq!(elt, -1);
assert_eq!(bit, 4);
}
#[test]
fn jump_forwards() {
let (elt, bit) = LittleEndian::jump::<u32>(25, 10);
assert_eq!(elt, 1);
assert_eq!(bit, 3);
let (elt, bit) = BigEndian::jump::<u32>(25, 10);
assert_eq!(elt, 1);
assert_eq!(bit, 28);
}
#[test]
fn jump_overflow() {
let start = 20;
let (elt, bit) = LittleEndian::jump::<u32>(start, core::isize::MAX);
assert_eq!(elt as usize, core::isize::MIN as usize >> u32::BITS);
assert_eq!(bit, start - 1);
let (elt, bit) = BigEndian::jump::<u32>(start, core::isize::MAX);
assert_eq!(elt as usize, core::isize::MIN as usize >> u32::BITS);
assert_eq!(bit, BigEndian::curr::<u32>(start) - 1);
}
#[test]
fn curr_reversible() {
for n in 0 .. 8 {
assert_eq!(n, BigEndian::curr::<u8>(BigEndian::curr::<u8>(n)));
}
for n in 0 .. 16 {
assert_eq!(n, BigEndian::curr::<u16>(BigEndian::curr::<u16>(n)));
}
for n in 0 .. 32 {
assert_eq!(n, BigEndian::curr::<u32>(BigEndian::curr::<u32>(n)));
}
for n in 0 .. 64 {
assert_eq!(n, BigEndian::curr::<u64>(BigEndian::curr::<u64>(n)));
}
for n in 0 .. 8 {
assert_eq!(n, LittleEndian::curr::<u8>(LittleEndian::curr::<u8>(n)));
}
for n in 0 .. 16 {
assert_eq!(n, LittleEndian::curr::<u16>(LittleEndian::curr::<u16>(n)));
}
for n in 0 .. 32 {
assert_eq!(n, LittleEndian::curr::<u32>(LittleEndian::curr::<u32>(n)));
}
for n in 0 .. 64 {
assert_eq!(n, LittleEndian::curr::<u64>(LittleEndian::curr::<u64>(n)));
}
}
}