use super::FilterMap;
use crate::report::section;
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, Bound};
use std::iter::{self, Extend, FromIterator};
use std::{mem, ops};
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct LineNum {
start2end: BTreeMap<u32, u32>,
}
impl LineNum {
pub fn new() -> Self {
Self::default()
}
pub fn insert<R>(&mut self, range: R)
where
R: Into<Range>,
{
self.extend(iter::once(range));
}
pub fn apply(&self, section: &mut section::Value) {
let mut functions = mem::take(&mut section.functions)
.into_iter()
.filter_map(|(key, value)| {
value
.start_line
.map(|start_line| (start_line, 0, key, value))
})
.collect::<Vec<_>>();
functions.sort_by_key(|&(start_line, _, _, _)| start_line);
{
let mut end = u32::MAX;
for data in functions.iter_mut().rev() {
data.1 = end;
end = u32::saturating_sub(data.0, 1);
}
}
let functions = functions
.into_iter()
.filter_map(|(start_line, end_line, key, value)| {
if self.contains(Range::new(start_line, end_line)) {
Some((key, value))
} else {
None
}
});
section.functions.extend(functions);
section.branches.filter_map(|(key, value)| {
if self.contains(Range::from_line(key.line)) {
Some((key, value))
} else {
None
}
});
section.lines.filter_map(|(key, value)| {
if self.contains(Range::from_line(key.line)) {
Some((key, value))
} else {
None
}
});
}
fn contains<R>(&self, range: R) -> bool
where
R: Into<Range>,
{
let range = range.into();
self.start2end
.range((Bound::Unbounded, Bound::Included(range.end)))
.next_back()
.map(|(&_start, &end)| end >= range.start)
.unwrap_or(false)
}
fn normalize(&mut self) {
let mut iter = mem::take(&mut self.start2end)
.into_iter()
.map(|(start, end)| Range::new(start, end));
let mut cur_range = match iter.next() {
Some(cur_range) => cur_range,
None => return,
};
for range in iter {
cur_range = cur_range.join(range).unwrap_or_else(|| {
let _ = self.start2end.insert(cur_range.start, cur_range.end);
range
});
}
let _ = self.start2end.insert(cur_range.start, cur_range.end);
debug_assert!(self.start2end.iter().all(|(s, e)| s <= e));
}
}
impl<R> FromIterator<R> for LineNum
where
R: Into<Range>,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = R>,
{
let mut ranges = Self::new();
ranges.extend(iter);
ranges.normalize();
ranges
}
}
impl<R> Extend<R> for LineNum
where
R: Into<Range>,
{
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = R>,
{
let iter = iter.into_iter().map(R::into).filter(Range::is_valid);
for range in iter {
match self.start2end.entry(range.start) {
Entry::Vacant(e) => {
let _ = e.insert(range.end);
}
Entry::Occupied(mut e) => {
let rend = e.get_mut();
*rend = u32::max(*rend, range.end);
}
}
}
self.normalize();
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct Range {
start: u32,
end: u32,
}
impl From<ops::Range<u32>> for Range {
fn from(range: ops::Range<u32>) -> Self {
Range::new(range.start, u32::saturating_sub(range.end, 1))
}
}
impl From<ops::RangeFrom<u32>> for Range {
fn from(range: ops::RangeFrom<u32>) -> Self {
Range::new(range.start, u32::MAX)
}
}
impl From<ops::RangeTo<u32>> for Range {
fn from(range: ops::RangeTo<u32>) -> Self {
Range::new(0, u32::saturating_sub(range.end, 1))
}
}
impl From<ops::RangeFull> for Range {
fn from(_: ops::RangeFull) -> Self {
Range::new(0, u32::MAX)
}
}
impl Range {
fn new(start: u32, end: u32) -> Self {
Range { start, end }
}
pub fn from_line(line: u32) -> Self {
Range {
start: line,
end: line,
}
}
fn is_valid(&self) -> bool {
self.start <= self.end
}
fn join(self, other: Self) -> Option<Self> {
if !self.is_valid() {
return Some(other);
}
if !other.is_valid() {
return Some(self);
}
if u32::saturating_add(other.end, 1) < self.start
|| u32::saturating_add(self.end, 1) < other.start
{
return None;
}
let start = u32::min(self.start, other.start);
let end = u32::max(self.end, other.end);
Some(Self::new(start, end))
}
}
#[cfg(test)]
mod tests {
use super::{LineNum, Range};
#[test]
fn join() {
fn check(expect: Option<(u32, u32)>, range1: (u32, u32), range2: (u32, u32)) {
let range1 = Range::new(range1.0, range1.1);
let range2 = Range::new(range2.0, range2.1);
let expect = expect.map(|expect| Range::new(expect.0, expect.1));
assert_eq!(expect, range1.join(range2));
assert_eq!(expect, range2.join(range1));
assert_eq!(Some(range1), range1.join(range1));
assert_eq!(Some(range2), range2.join(range2));
}
let max = u32::MAX;
check(Some((0, 3)), (0, 1), (1, 3));
check(Some((0, 3)), (0, 1), (2, 3));
check(Some((0, 3)), (0, 2), (1, 3));
check(Some((0, 3)), (0, 3), (1, 2));
check(Some((0, 4)), (0, 0), (0, 4));
check(Some((0, max)), (0, max), (100, 400));
}
#[test]
fn insert() {
fn check(file: &LineNum, start2end: &[(u32, u32)]) {
assert_eq!(file.start2end, start2end.iter().cloned().collect());
}
let mut file = LineNum::default();
file.insert(Range::new(10, 10));
check(&file, &[(10, 10)]);
file.insert(Range::new(15, 20));
check(&file, &[(10, 10), (15, 20)]);
file.insert(Range::new(15, 40));
check(&file, &[(10, 10), (15, 40)]);
file.insert(Range::new(10, 40));
check(&file, &[(10, 40)]);
file.insert(Range::new(50, 100));
check(&file, &[(10, 40), (50, 100)]);
}
#[test]
fn contains() {
fn gen_file(i: u32, n: u32) -> (LineNum, Vec<bool>) {
let map = (0..n).map(|j| (i & (1 << j)) != 0).collect::<Vec<_>>();
let mut file = LineNum::default();
for (i, &f) in map.iter().enumerate() {
if f {
file.insert(Range::new(i as u32, i as u32));
}
}
file.normalize();
(file, map)
}
let n = 8;
for i in 0..(2u32.pow(n)) {
let (file, map) = gen_file(i, n);
for start in 0..n {
for end in start..n {
let res = file.contains(Range::new(start, end));
let cmp = map[(start as usize)..((end + 1) as usize)]
.iter()
.any(|&f| f);
assert_eq!(res, cmp);
}
}
}
}
}