use crate::util::Address;
pub type BitOffset = u8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitByteRange {
Bytes {
start: Address,
end: Address,
},
BitsInByte {
addr: Address,
bit_start: BitOffset,
bit_end: BitOffset,
},
}
pub fn break_bit_range<V>(
start_addr: Address,
start_bit: BitOffset,
end_addr: Address,
end_bit: BitOffset,
forwards: bool,
visitor: &mut V,
) -> bool
where
V: FnMut(BitByteRange) -> bool,
{
if start_addr == end_addr && start_bit == end_bit {
return false;
}
if start_bit == 0 && end_bit == 0 {
return visitor(BitByteRange::Bytes {
start: start_addr,
end: end_addr,
});
}
if start_addr == end_addr {
return visitor(BitByteRange::BitsInByte {
addr: start_addr,
bit_start: start_bit,
bit_end: end_bit,
});
}
if start_addr + 1usize == end_addr && end_bit == 0 {
return visitor(BitByteRange::BitsInByte {
addr: start_addr,
bit_start: start_bit,
bit_end: 8_u8,
});
}
let start_aligned = start_bit == 0;
let end_aligned = end_bit == 0;
let visit_start = |v: &mut V| {
if !start_aligned {
v(BitByteRange::BitsInByte {
addr: start_addr,
bit_start: start_bit,
bit_end: 8_u8,
})
} else {
false
}
};
let visit_middle = |v: &mut V| {
let start = if start_aligned {
start_addr
} else {
start_addr + 1usize
};
let end = end_addr;
if start < end {
v(BitByteRange::Bytes { start, end })
} else {
false
}
};
let visit_end = |v: &mut V| {
if !end_aligned {
v(BitByteRange::BitsInByte {
addr: end_addr,
bit_start: 0_u8,
bit_end: end_bit,
})
} else {
false
}
};
if forwards {
visit_start(visitor) || visit_middle(visitor) || visit_end(visitor)
} else {
visit_end(visitor) || visit_middle(visitor) || visit_start(visitor)
}
}
#[cfg(test)]
mod tests {
use crate::util::constants::BITS_IN_BYTE;
use super::*;
fn mk_addr(addr: usize) -> Address {
unsafe { Address::from_usize(addr) }
}
fn break_bit_range_wrapped(
start_addr: Address,
start_bit: usize,
end_addr: Address,
end_bit: usize,
) -> Vec<BitByteRange> {
let mut vec = vec![];
break_bit_range(
start_addr,
start_bit as u8,
end_addr,
end_bit as u8,
true,
&mut |range| {
vec.push(range);
false
},
);
vec
}
#[test]
fn test_empty_range() {
let base = mk_addr(0x1000);
for bit in 0..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base, bit, base, bit);
assert!(
result.is_empty(),
"Not empty. bit: {bit}, result: {result:?}"
);
}
}
#[test]
fn test_subbyte_range() {
let base = mk_addr(0x1000);
for bit0 in 0..BITS_IN_BYTE {
for bit1 in (bit0 + 1)..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base, bit0, base, bit1);
assert_eq!(
result,
vec![BitByteRange::BitsInByte {
addr: base,
bit_start: bit0 as u8,
bit_end: bit1 as u8
}],
"Not equal. bit0: {bit0}, bit1: {bit1}",
);
}
}
}
#[test]
fn test_end_byte_range() {
let base = mk_addr(0x1000);
for bit0 in 1..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base, bit0, base + 1usize, 0);
assert_eq!(
result,
vec![BitByteRange::BitsInByte {
addr: base,
bit_start: bit0 as u8,
bit_end: BITS_IN_BYTE as u8
}],
"Not equal. bit0: {bit0}",
);
}
}
#[test]
fn test_adjacent_grain_range() {
let base = mk_addr(0x1000);
for bit0 in 1..BITS_IN_BYTE {
for bit1 in 1..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base, bit0, base + 1usize, bit1);
assert_eq!(
result,
vec![
BitByteRange::BitsInByte {
addr: base,
bit_start: bit0 as u8,
bit_end: BITS_IN_BYTE as u8,
},
BitByteRange::BitsInByte {
addr: base + 1usize,
bit_start: 0,
bit_end: bit1 as u8,
},
],
"Not equal. bit0: {bit0}, bit1: {bit1}",
);
}
}
}
#[test]
fn test_left_and_whole_range() {
let base = mk_addr(0x1000);
for bit0 in 1..BITS_IN_BYTE {
for byte1 in 2usize..8 {
let result = break_bit_range_wrapped(base, bit0, base + byte1, 0);
assert_eq!(
result,
vec![
BitByteRange::BitsInByte {
addr: base,
bit_start: bit0 as u8,
bit_end: BITS_IN_BYTE as u8,
},
BitByteRange::Bytes {
start: base + 1usize,
end: base + byte1,
},
],
"Not equal. bit0: {bit0}, byte1: {byte1}",
);
}
}
}
#[test]
fn test_whole_and_right_range() {
let base = mk_addr(0x1000);
for byte0 in 1..8 {
for bit1 in 1..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base - byte0, 0, base, bit1);
assert_eq!(
result,
vec![
BitByteRange::Bytes {
start: base - byte0,
end: base,
},
BitByteRange::BitsInByte {
addr: base,
bit_start: 0,
bit_end: bit1 as u8,
},
],
"Not equal. byte0: {byte0}, bit1: {bit1}",
);
}
}
}
#[test]
fn test_whole_range() {
let base = mk_addr(0x1000);
let result = break_bit_range_wrapped(base, 0, base + 42usize, 0);
assert_eq!(
result,
vec![BitByteRange::Bytes {
start: base,
end: base + 42usize,
},],
);
}
#[test]
fn test_left_whole_right_range() {
let base0 = mk_addr(0x1000);
let base1 = mk_addr(0x2000);
for bit0 in 1..BITS_IN_BYTE {
for bit1 in 1..BITS_IN_BYTE {
let result = break_bit_range_wrapped(base0 - 1usize, bit0, base1, bit1);
assert_eq!(
result,
vec![
BitByteRange::BitsInByte {
addr: base0 - 1usize,
bit_start: bit0 as u8,
bit_end: BITS_IN_BYTE as u8,
},
BitByteRange::Bytes {
start: base0,
end: base1,
},
BitByteRange::BitsInByte {
addr: base1,
bit_start: 0,
bit_end: bit1 as u8,
},
],
"Not equal. bit0: {bit0}, bit1: {bit1}",
);
}
}
}
}