use core::fmt;
use crate::config::ASSEMBLER_MAX_SEGMENT_COUNT;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TooManyHolesError;
impl fmt::Display for TooManyHolesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "too many holes")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TooManyHolesError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Contig {
hole_size: usize,
data_size: usize,
}
impl fmt::Display for Contig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.has_hole() {
write!(f, "({})", self.hole_size)?;
}
if self.has_hole() && self.has_data() {
write!(f, " ")?;
}
if self.has_data() {
write!(f, "{}", self.data_size)?;
}
Ok(())
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Contig {
fn format(&self, fmt: defmt::Formatter) {
if self.has_hole() {
defmt::write!(fmt, "({})", self.hole_size);
}
if self.has_hole() && self.has_data() {
defmt::write!(fmt, " ");
}
if self.has_data() {
defmt::write!(fmt, "{}", self.data_size);
}
}
}
impl Contig {
const fn empty() -> Contig {
Contig {
hole_size: 0,
data_size: 0,
}
}
fn hole_and_data(hole_size: usize, data_size: usize) -> Contig {
Contig {
hole_size,
data_size,
}
}
fn has_hole(&self) -> bool {
self.hole_size != 0
}
fn has_data(&self) -> bool {
self.data_size != 0
}
fn total_size(&self) -> usize {
self.hole_size + self.data_size
}
fn shrink_hole_by(&mut self, size: usize) {
self.hole_size -= size;
}
fn shrink_hole_to(&mut self, size: usize) {
debug_assert!(self.hole_size >= size);
let total_size = self.total_size();
self.hole_size = size;
self.data_size = total_size - size;
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Assembler {
contigs: [Contig; ASSEMBLER_MAX_SEGMENT_COUNT],
}
impl fmt::Display for Assembler {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[ ")?;
for contig in self.contigs.iter() {
if !contig.has_data() {
break;
}
write!(f, "{contig} ")?;
}
write!(f, "]")?;
Ok(())
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Assembler {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "[ ");
for contig in self.contigs.iter() {
if !contig.has_data() {
break;
}
defmt::write!(fmt, "{} ", contig);
}
defmt::write!(fmt, "]");
}
}
impl Assembler {
pub const fn new() -> Assembler {
const EMPTY: Contig = Contig::empty();
Assembler {
contigs: [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT],
}
}
pub fn clear(&mut self) {
self.contigs.fill(Contig::empty());
}
fn front(&self) -> Contig {
self.contigs[0]
}
pub fn peek_front(&self) -> usize {
let front = self.front();
if front.has_hole() {
0
} else {
front.data_size
}
}
fn back(&self) -> Contig {
self.contigs[self.contigs.len() - 1]
}
pub fn is_empty(&self) -> bool {
!self.front().has_data()
}
fn remove_contig_at(&mut self, at: usize) {
debug_assert!(self.contigs[at].has_data());
for i in at..self.contigs.len() - 1 {
if !self.contigs[i].has_data() {
return;
}
self.contigs[i] = self.contigs[i + 1];
}
self.contigs[self.contigs.len() - 1] = Contig::empty();
}
fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> {
if self.back().has_data() {
return Err(TooManyHolesError);
}
for i in (at + 1..self.contigs.len()).rev() {
self.contigs[i] = self.contigs[i - 1];
}
self.contigs[at] = Contig::empty();
Ok(&mut self.contigs[at])
}
pub fn add(&mut self, mut offset: usize, size: usize) -> Result<(), TooManyHolesError> {
if size == 0 {
return Ok(());
}
let mut i = 0;
loop {
if i == self.contigs.len() {
return Err(TooManyHolesError);
}
let contig = &mut self.contigs[i];
if !contig.has_data() {
*contig = Contig::hole_and_data(offset, size);
return Ok(());
}
if offset <= contig.total_size() {
break;
}
offset -= contig.total_size();
i += 1;
}
let contig = &mut self.contigs[i];
if offset < contig.hole_size {
if offset + size < contig.hole_size {
let new_contig = self.add_contig_at(i)?;
new_contig.hole_size = offset;
new_contig.data_size = size;
self.contigs[i + 1].shrink_hole_by(offset + size);
return Ok(());
}
contig.shrink_hole_to(offset);
}
let mut j = i + 1;
while j < self.contigs.len()
&& self.contigs[j].has_data()
&& offset + size >= self.contigs[i].total_size() + self.contigs[j].hole_size
{
self.contigs[i].data_size += self.contigs[j].total_size();
j += 1;
}
let shift = j - i - 1;
if shift != 0 {
for x in i + 1..self.contigs.len() {
if !self.contigs[x].has_data() {
break;
}
self.contigs[x] = self
.contigs
.get(x + shift)
.copied()
.unwrap_or_else(Contig::empty);
}
}
if offset + size > self.contigs[i].total_size() {
let left = offset + size - self.contigs[i].total_size();
self.contigs[i].data_size += left;
if i + 1 < self.contigs.len() && self.contigs[i + 1].has_data() {
self.contigs[i + 1].hole_size -= left;
}
}
Ok(())
}
pub fn remove_front(&mut self) -> usize {
let front = self.front();
if front.has_hole() || !front.has_data() {
0
} else {
self.remove_contig_at(0);
debug_assert!(front.data_size > 0);
front.data_size
}
}
pub fn add_then_remove_front(
&mut self,
offset: usize,
size: usize,
) -> Result<usize, TooManyHolesError> {
if offset == 0 && size < self.contigs[0].hole_size {
self.contigs[0].hole_size -= size;
return Ok(size);
}
self.add(offset, size)?;
Ok(self.remove_front())
}
pub fn iter_data(&self, first_offset: usize) -> AssemblerIter {
AssemblerIter::new(self, first_offset)
}
}
pub struct AssemblerIter<'a> {
assembler: &'a Assembler,
offset: usize,
index: usize,
left: usize,
right: usize,
}
impl<'a> AssemblerIter<'a> {
fn new(assembler: &'a Assembler, offset: usize) -> AssemblerIter<'a> {
AssemblerIter {
assembler,
offset,
index: 0,
left: 0,
right: 0,
}
}
}
impl<'a> Iterator for AssemblerIter<'a> {
type Item = (usize, usize);
fn next(&mut self) -> Option<(usize, usize)> {
let mut data_range = None;
while data_range.is_none() && self.index < self.assembler.contigs.len() {
let contig = self.assembler.contigs[self.index];
self.left += contig.hole_size;
self.right = self.left + contig.data_size;
data_range = if self.left < self.right {
let data_range = (self.left + self.offset, self.right + self.offset);
self.left = self.right;
Some(data_range)
} else {
None
};
self.index += 1;
}
data_range
}
}
#[cfg(test)]
mod test {
use super::*;
use std::vec::Vec;
impl From<Vec<(usize, usize)>> for Assembler {
fn from(vec: Vec<(usize, usize)>) -> Assembler {
const EMPTY: Contig = Contig::empty();
let mut contigs = [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT];
for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
contigs[i] = Contig {
hole_size,
data_size,
};
}
Assembler { contigs }
}
}
macro_rules! contigs {
[$( $x:expr ),*] => ({
Assembler::from(vec![$( $x ),*])
})
}
#[test]
fn test_new() {
let assr = Assembler::new();
assert_eq!(assr, contigs![]);
}
#[test]
fn test_empty_add_full() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 16), Ok(()));
assert_eq!(assr, contigs![(0, 16)]);
}
#[test]
fn test_empty_add_front() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 4), Ok(()));
assert_eq!(assr, contigs![(0, 4)]);
}
#[test]
fn test_empty_add_back() {
let mut assr = Assembler::new();
assert_eq!(assr.add(12, 4), Ok(()));
assert_eq!(assr, contigs![(12, 4)]);
}
#[test]
fn test_empty_add_mid() {
let mut assr = Assembler::new();
assert_eq!(assr.add(4, 8), Ok(()));
assert_eq!(assr, contigs![(4, 8)]);
}
#[test]
fn test_partial_add_front() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(0, 4), Ok(()));
assert_eq!(assr, contigs![(0, 12)]);
}
#[test]
fn test_partial_add_back() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(12, 4), Ok(()));
assert_eq!(assr, contigs![(4, 12)]);
}
#[test]
fn test_partial_add_front_overlap() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(0, 8), Ok(()));
assert_eq!(assr, contigs![(0, 12)]);
}
#[test]
fn test_partial_add_front_overlap_split() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(2, 6), Ok(()));
assert_eq!(assr, contigs![(2, 10)]);
}
#[test]
fn test_partial_add_back_overlap() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(8, 8), Ok(()));
assert_eq!(assr, contigs![(4, 12)]);
}
#[test]
fn test_partial_add_back_overlap_split() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(10, 4), Ok(()));
assert_eq!(assr, contigs![(4, 10)]);
}
#[test]
fn test_partial_add_both_overlap() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(0, 16), Ok(()));
assert_eq!(assr, contigs![(0, 16)]);
}
#[test]
fn test_partial_add_both_overlap_split() {
let mut assr = contigs![(4, 8)];
assert_eq!(assr.add(2, 12), Ok(()));
assert_eq!(assr, contigs![(2, 12)]);
}
#[test]
fn test_rejected_add_keeps_state() {
let mut assr = Assembler::new();
for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
assert_eq!(assr.add(c * 10, 3), Ok(()));
}
let assr_before = assr.clone();
assert_eq!(assr.add(1, 3), Err(TooManyHolesError));
assert_eq!(assr_before, assr);
}
#[test]
fn test_empty_remove_front() {
let mut assr = contigs![];
assert_eq!(assr.remove_front(), 0);
}
#[test]
fn test_trailing_hole_remove_front() {
let mut assr = contigs![(0, 4)];
assert_eq!(assr.remove_front(), 4);
assert_eq!(assr, contigs![]);
}
#[test]
fn test_trailing_data_remove_front() {
let mut assr = contigs![(0, 4), (4, 4)];
assert_eq!(assr.remove_front(), 4);
assert_eq!(assr, contigs![(4, 4)]);
}
#[test]
fn test_boundary_case_remove_front() {
let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
vec[0] = (0, 2);
let mut assr = Assembler::from(vec);
assert_eq!(assr.remove_front(), 2);
let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
vec[ASSEMBLER_MAX_SEGMENT_COUNT - 1] = (0, 0);
let exp_assr = Assembler::from(vec);
assert_eq!(assr, exp_assr);
}
#[test]
fn test_shrink_next_hole() {
let mut assr = Assembler::new();
assert_eq!(assr.add(100, 10), Ok(()));
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add(40, 30), Ok(()));
assert_eq!(assr, contigs![(40, 30), (30, 10)]);
}
#[test]
fn test_join_two() {
let mut assr = Assembler::new();
assert_eq!(assr.add(10, 10), Ok(()));
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add(15, 40), Ok(()));
assert_eq!(assr, contigs![(10, 50)]);
}
#[test]
fn test_join_two_reversed() {
let mut assr = Assembler::new();
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add(10, 10), Ok(()));
assert_eq!(assr.add(15, 40), Ok(()));
assert_eq!(assr, contigs![(10, 50)]);
}
#[test]
fn test_join_two_overlong() {
let mut assr = Assembler::new();
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add(10, 10), Ok(()));
assert_eq!(assr.add(15, 60), Ok(()));
assert_eq!(assr, contigs![(10, 65)]);
}
#[test]
fn test_iter_empty() {
let assr = Assembler::new();
let segments: Vec<_> = assr.iter_data(10).collect();
assert_eq!(segments, vec![]);
}
#[test]
fn test_iter_full() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 16), Ok(()));
let segments: Vec<_> = assr.iter_data(10).collect();
assert_eq!(segments, vec![(10, 26)]);
}
#[test]
fn test_iter_offset() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 16), Ok(()));
let segments: Vec<_> = assr.iter_data(100).collect();
assert_eq!(segments, vec![(100, 116)]);
}
#[test]
fn test_iter_one_front() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 4), Ok(()));
let segments: Vec<_> = assr.iter_data(10).collect();
assert_eq!(segments, vec![(10, 14)]);
}
#[test]
fn test_iter_one_back() {
let mut assr = Assembler::new();
assert_eq!(assr.add(12, 4), Ok(()));
let segments: Vec<_> = assr.iter_data(10).collect();
assert_eq!(segments, vec![(22, 26)]);
}
#[test]
fn test_iter_one_mid() {
let mut assr = Assembler::new();
assert_eq!(assr.add(4, 8), Ok(()));
let segments: Vec<_> = assr.iter_data(10).collect();
assert_eq!(segments, vec![(14, 22)]);
}
#[test]
fn test_iter_one_trailing_gap() {
let assr = contigs![(4, 8)];
let segments: Vec<_> = assr.iter_data(100).collect();
assert_eq!(segments, vec![(104, 112)]);
}
#[test]
fn test_iter_two_split() {
let assr = contigs![(2, 6), (4, 1)];
let segments: Vec<_> = assr.iter_data(100).collect();
assert_eq!(segments, vec![(102, 108), (112, 113)]);
}
#[test]
fn test_iter_three_split() {
let assr = contigs![(2, 6), (2, 1), (2, 2)];
let segments: Vec<_> = assr.iter_data(100).collect();
assert_eq!(segments, vec![(102, 108), (110, 111), (113, 115)]);
}
#[test]
fn test_issue_694() {
let mut assr = Assembler::new();
assert_eq!(assr.add(0, 1), Ok(()));
assert_eq!(assr.add(2, 1), Ok(()));
assert_eq!(assr.add(1, 1), Ok(()));
}
#[test]
fn test_add_then_remove_front() {
let mut assr = Assembler::new();
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add_then_remove_front(10, 10), Ok(0));
assert_eq!(assr, contigs![(10, 10), (30, 10)]);
}
#[test]
fn test_add_then_remove_front_at_front() {
let mut assr = Assembler::new();
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add_then_remove_front(0, 10), Ok(10));
assert_eq!(assr, contigs![(40, 10)]);
}
#[test]
fn test_add_then_remove_front_at_front_touch() {
let mut assr = Assembler::new();
assert_eq!(assr.add(50, 10), Ok(()));
assert_eq!(assr.add_then_remove_front(0, 50), Ok(60));
assert_eq!(assr, contigs![]);
}
#[test]
fn test_add_then_remove_front_at_front_full() {
let mut assr = Assembler::new();
for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
assert_eq!(assr.add(c * 10, 3), Ok(()));
}
let assr_before = assr.clone();
assert_eq!(assr.add_then_remove_front(1, 3), Err(TooManyHolesError));
assert_eq!(assr_before, assr);
}
#[test]
fn test_add_then_remove_front_at_front_full_offset_0() {
let mut assr = Assembler::new();
for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
assert_eq!(assr.add(c * 10, 3), Ok(()));
}
assert_eq!(assr.add_then_remove_front(0, 3), Ok(3));
}
#[test]
fn test_random() {
use rand::Rng;
const MAX_INDEX: usize = 256;
for max_size in [2, 5, 10, 100] {
for _ in 0..300 {
let mut assr = Assembler::new();
let mut map = [false; MAX_INDEX];
for _ in 0..60 {
let offset = rand::thread_rng().gen_range(0..MAX_INDEX - max_size - 1);
let size = rand::thread_rng().gen_range(1..=max_size);
let res = assr.add(offset, size);
let mut map2 = map;
map2[offset..][..size].fill(true);
let mut contigs = vec![];
let mut hole: usize = 0;
let mut data: usize = 0;
for b in map2 {
if b {
data += 1;
} else {
if data != 0 {
contigs.push((hole, data));
hole = 0;
data = 0;
}
hole += 1;
}
}
let wanted_res = if contigs.len() > ASSEMBLER_MAX_SEGMENT_COUNT {
Err(TooManyHolesError)
} else {
Ok(())
};
assert_eq!(res, wanted_res);
if res.is_ok() {
map = map2;
assert_eq!(assr, Assembler::from(contigs));
}
}
}
}
}
}