extern crate alloc;
use alloc::vec::Vec;
use super::pure::{
ANCHOR_TAG_AFTER, ANCHOR_TAG_BEFORE, ANCHOR_TAG_ORIGIN, PureLocus, SNAPSHOT_WIRE_V1, chainable,
rank_successor,
};
struct Region {
start: usize,
len: usize,
}
fn factor(loci: &[PureLocus]) -> Vec<Region> {
let mut regions = Vec::new();
let mut start = 0usize;
while start < loci.len() {
let mut end = start + 1;
while end < loci.len() {
let previous = &loci[end - 1];
let next = &loci[end];
let consecutive = previous.station == next.station
&& previous.index < u64::MAX
&& next.index == previous.index + 1;
if consecutive && chainable(previous, next) {
end += 1;
} else {
break;
}
}
regions.push(Region {
start,
len: end - start,
});
start = end;
}
regions
}
fn push_anchor(out: &mut Vec<u8>, locus: &PureLocus) {
out.push(locus.anchor_tag);
if locus.anchor_tag == ANCHOR_TAG_AFTER || locus.anchor_tag == ANCHOR_TAG_BEFORE {
out.extend_from_slice(&locus.anchor_station.to_be_bytes());
out.extend_from_slice(&locus.anchor_index.to_be_bytes());
}
}
fn push_rank(out: &mut Vec<u8>, locus: &PureLocus) {
out.push(super::pure::RANK_WIRE_V1);
out.extend_from_slice(&locus.physical.to_be_bytes());
out.extend_from_slice(&locus.logical.to_be_bytes());
out.extend_from_slice(&locus.kairotic.to_be_bytes());
out.extend_from_slice(&locus.rank_station.to_be_bytes());
}
pub fn encode_loci(loci: &[PureLocus]) -> Vec<u8> {
let regions = factor(loci);
let mut run_count: u32 = 0;
let mut free_count: u32 = 0;
for region in ®ions {
if region.len >= 2 {
run_count = run_count.saturating_add(1);
} else {
free_count = free_count.saturating_add(1);
}
}
let mut out = Vec::new();
out.push(SNAPSHOT_WIRE_V1);
out.extend_from_slice(&run_count.to_be_bytes());
out.extend_from_slice(&free_count.to_be_bytes());
for region in ®ions {
if region.len < 2 {
continue;
}
let head = &loci[region.start];
out.extend_from_slice(&head.station.to_be_bytes());
out.extend_from_slice(&head.index.to_be_bytes());
out.extend_from_slice(&u32::try_from(region.len).unwrap_or(u32::MAX).to_be_bytes());
out.push(head.anchor_tag);
if head.anchor_tag == ANCHOR_TAG_ORIGIN {
out.extend_from_slice(&0u32.to_be_bytes());
out.extend_from_slice(&0u64.to_be_bytes());
} else {
out.extend_from_slice(&head.anchor_station.to_be_bytes());
out.extend_from_slice(&head.anchor_index.to_be_bytes());
}
push_rank(&mut out, head);
}
for region in ®ions {
if region.len < 2 {
continue;
}
for k in 1..region.len {
let previous = &loci[region.start + k - 1];
let next = &loci[region.start + k];
let (successor_physical, successor_logical) =
rank_successor(previous.physical, previous.logical);
let step = if next.physical == successor_physical && next.logical == successor_logical {
0u64
} else {
next.physical - previous.physical
};
let step_bytes = step.to_be_bytes();
out.extend_from_slice(&step_bytes[2..8]);
}
}
for region in ®ions {
if region.len >= 2 {
continue;
}
let locus = &loci[region.start];
out.extend_from_slice(&locus.station.to_be_bytes());
out.extend_from_slice(&locus.index.to_be_bytes());
push_anchor(&mut out, locus);
push_rank(&mut out, locus);
}
out
}